Skip to content

Components

Everything in niess.components, and the McStas component each one emits. Use this when mapping a .instr TRACE onto niess classes.

Every class takes at least name, position (a scipp vector) and orientation (a scipp quaternion), and is built from a calibration dictionary with SomeClass.from_calibration({...}).

Moderators

niess class emits calibration keys
ESSModerator ESS_butterfly sector, beamline, height, cold_fraction, focus_distance, focus_width, focus_height, cold_performance, thermal_performance, wavelength_minimum, wavelength_maximum, latest_emission_time, n_pulses, accelerator_power
PolygonESSModerator chopper-lib Polygon_ESS_butterfly everything ESSModerator takes, and chopper_train, path_spread_fraction, noise_fraction, use_region, resample, save_polygons, verify_acceptance, filename

wavelength_minimum and wavelength_maximum accept a McCode instrument-parameter specification string such as 'source_lambda_min/"angstrom" = 0.75' instead of a value, which turns them into run-time arguments of the generated instrument.

PolygonESSModerator is opt-in. It emits only rays the chopper train can pass, so it reads the train, and an instrument with it has to be narrowed with niess.chopcalc — see handing the train to a component.

Guides

niess class emits calibration keys
StraightGuide Guide_gravity length, width, height, m (or left/right/top/bottom)
TaperedGuide Guide_gravity length, in_width, out_width, in_height, out_height (or width/height for both ends), m-values as above
EllipticGuide Elliptic_guide_gravity length, horizontal, vertical (each {major, minor, offset} or {in, out, midpoint\|entry\|exit}), m-values as above
StraightGuides, TaperedGuides one instance per segment a dictionary of segment dictionaries

EllipticGuide also accepts an array length with tuple m-values, which emits a segmented guide with DECLAREd arrays.

Apertures

niess class emits calibration keys
Jaw Slit width, height
Slit Slit width, height

Both are run-time adjustable, which is the point of them: Jaw declares the instrument parameters {name}_l and {name}_r, and Slit declares those plus {name}_b and {name}_t. Their openings become links in the NeXus output rather than fixed numbers.

Choppers

niess class emits calibration keys
DiscChopper one DiskChopper per opening radius, angle or windows, frequency or velocity, delay, width, height, zero_angle, beam_angle

A disc chopper's position is its spindle; the emitted AT is the point the beam crosses the disc. zero_angle and beam_angle say where that point is — measured counter-clockwise about +z, the first from the local +y axis to the disc's zero mark and the second from the mark to the beam — and the vector between the two follows from them, along with the rotation the emitted component carries so the disc ends up on the correct side of the beam. Both default to zero, which puts the beam at the top of the disc; a disc hanging above the beam has beam_angle = 180.

There is no offset to give. It was a field until the angles replaced it, and a calibration that still sets one is refused rather than ignored — reading a placement instruction as though it were absent would move the disc off the beam, where it absorbs every neutron without saying so. disc_beam_offset is the one formula, and calibration code that knows where the beam runs and needs the spindle negates what the chopper derives going the other way.

DiscChopper declares {name}speed and {name}delay as instrument parameters.

McStas' DiskChopper describes nslit identical, evenly spaced openings and nothing else, so a disc whose openings are neither is emitted as one DiskChopper per opening — sharing one speed and delay, placed in a McStas GROUP so a neutron passes if it clears any opening, and tagged so that anything reading the emitted file can tell they are one disc. niess.nexus writes a single NXdisk_chopper without needing the tags: the tree never split the disc. A disc with one opening is one component under its own name, which is what a disc chopper has always been. See composites.

windows (slit_edges) are measured from the zero mark: an even number of increasing values, two per opening, positive counter-clockwise facing +z, with a final edge beyond 360 where the last opening straddles the mark. Giving angle instead is shorthand for one opening centred on the beam, beam_angle ± angle/2 — so angle = 170 on a disc hanging above the beam is [95, 265], whose centre is the beam and whose emitted delay is therefore the disc's own.

Edges are not required to be positive, and one may be written a turn late: an opening centred on a beam at beam_angle = 0 straddles the mark, and [-85, 85] says so more plainly than [275, 445]. NXdisk_chopper is stricter — positive, increasing, opening edge first, and only the final edge past 360, which happens exactly when the last slit crosses the mark — so the NeXus writer puts them in that order, and writes beam_position alongside them so the frame is recorded. The mark itself needs no field: NXdisk_chopper measures the edges from it, so it is the origin of that frame rather than a number in it. When the mark passes is a separate question, answered by the top_dead_center log.

That reordering rotates which slit comes first rather than shifting the list: the wrap belongs to one slit, so adding 360 to every edge would carry the others out past it. [-10, 10, 60, 90] is written [60, 90, 350, 370], not [350, 370, 420, 450].

The emitted AT and ROTATED are not the disc's placement

A McStas DiskChopper expects its component origin on the beam, and always draws its disc below that origin. So the emitted component is both moved — by beam_offset(), from the spindle onto the beam — and turned, by zero_angle + beam_angle about z, for the disc to land on the right side. Both are properties of the target, not of the chopper.

Both are recorded in the component's provenance, as mccode_frame_offset (metres) and mccode_frame_rotation (a rotation vector in degrees), for anything outside niess reading the emitted file. niess.nexus needs neither: it has the disc. An NXdisk_chopper is therefore centred on the spindle, carrying the disc's own orientation — the placement the calibration gave — while McStas still gets the beam crossing it needs:

McStas NeXus
origin beam crossing spindle
orientation orientation * Rz(zero_angle + beam_angle) orientation

Without this the file would state the turn twice, once as a real rotation of the disc and once as beam_position, and a reader combining them would place the mark beam_position degrees from where it is; and the disc would sit radius - height/2 off its own axis.

Any component may do this. Component.__mccode_offset__ and Component.__mccode_frame_rotation__ report the difference between what is emitted and what the object is, and are zero unless a subclass moves or turns its own emission.

delay is a time, not an angle: it is when an opening's centre is at the beam, which is what McStas' DiskChopper acts on and what a real chopper is set with. The component also accepts a phase in degrees, but only converts it to a delay and then ignores it (McCode#2347 covers the two not being exact inverses), so niess emits delay and never phase.

Monitors

All monitors emit Frame_monitor and attach a da00 histogram-stream configuration as METADATA, published to {instrument}_beam_monitor unless told otherwise.

niess class calibration keys
FissionChamber width, height, thickness
He3Monitor radius, length, pressure
BeamCurrentMonitor width, height, thickness, sample_rate
GEM2D width, height, thickness, x_strips, y_strips

BeamCurrentMonitor's sample_rate sets its time binning. GEM2D's strip counts are accepted but not yet used in the emitted component.

Filters and attenuators

niess class emits calibration keys
NCrystalFilter Filter_sample width, height, length, composition, temperature
Attenuator Filter_sample as NCrystalFilter
OrderedFilter Filter_sample as NCrystalFilter, plus tau
RadialFilterCollimator Radial_col_filter radii, composition, temperature

Attenuator adds an int {name}_in = 0 instrument parameter and a matching WHEN, so it can be moved in and out of the beam at run time. RadialFilterCollimator needs the mcdotstar/mcstas-radial-filter-collimator@main component registry.

Unmodelled components

niess class emits calibration keys
Opaque whatever it was given mccode_type, mccode_parameters, and optionally when, group, extend, split, removable

A McStas component niess has no class for, carried through verbatim. It holds a component type name and the arguments to pass it, and emits exactly that.

It exists for converting a .instr, where most components of a real instrument have no niess class. Without somewhere to put them a converter has to drop them or guess, and the default below makes the guess silently — so Opaque is the alternative that says what it is. Its role is unmodelled-component rather than physical-component, which is what lets a target dispatch on "niess does not know what this is".

It is a placeholder, not a shortcut. Emitting McStas is all it can do: it carries no dimensions, so there is nothing for niess.brep to draw and nothing for niess.nexus to write beyond a placement. Counting them in a converted module measures how much of the instrument is still McStas-shaped. A component that is genuinely part of an instrument niess models needs a class — three methods, described in Writing a component.

Note that mccode_antlr writes only when, group and extend back out. SPLIT and REMOVABLE are set on the emitted instance and are visible to anything reading the Instr object, but Instance.to_file prints neither, so they do not survive being written as .instr text.

Declared but not implemented

These emit an Arm

The classes below have no __mccode__, so they inherit Component's, which emits an Arm. Some are abstract bases you are meant to subclass; the rest are simply unfinished. Either way, placing one in an instrument silently gives you a bare coordinate frame where you expected a component.

niess class why
Aperture, Chopper, Guide, Filter, Moderator abstract bases — use a concrete subclass
Collimator, SollerCollimator, RadialCollimator declared, not implemented
FermiChopper declared, not implemented

If you need one of the unimplemented ones, writing it is three methods — see Build a new instrument submodule.

Not placed directly

Wire, DiscreteWire, DiscreteTube, He3Tube, IdealCrystal and Crystal are building blocks used inside composites such as niess.bifrost's detector triplets, rather than placed in a beamline themselves. DirectSecondary and IndirectSecondary are data-reduction views produced by Tank.to_secondary(), not conversion targets.