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.