Release notes¶
What changed in each version, and what to do about it. This page is the repository's
CHANGELOG.md, included here
rather than written twice.
Each release on GitHub carries the same notes, and installing a version pins you to what it
says: pip install 'niess==0.9.0'.
0.12.0¶
A disc chopper's timing has the knobs an ESS chopper controller has, with the same meaning, so a simulation can be driven the way NICOS drives the real instrument. This changes names and the default timing.
| was | now |
|---|---|
{disc}_delay /"ns", when the mark passes the sensor |
{disc}_experiment_delay /"ns" (ESS ChopDly-S), the one NICOS sets |
| — | pulse_delay /"ns" (ESS BeamPosDly-S), one for the instrument, default 0 |
| — | {disc}_mechanical_delay /"degrees" (ESS MechDly-S), default from the geometry |
calibration delay |
calibration experiment_delay, and optionally pulse_delay; delay is refused |
- The first opening edge, NeXus's
slit_edges[0], is at the beampulse_delay + experiment_delayafter proton-on-target (t=0 in McStas). With every knob at its default that is t=0, where before the disc's reference orientation was. - The McStas
delayis the controller's total, formed as ESS'sevr_seq_calcformsTotDly:mechanical_delay / (360 |f|) + pulse_delay + experiment_delay.niess.tofforms the same total. mechanical_delayis(slit_edges[0] - beam_position), signed by the calibrated direction of rotation, modulo a turn.niess.components.chopper.mechanical_delayandexperiment_delaycompute it, and convert a total back to an experiment delay.- A simulated
NXdisk_chopperwritesexperiment_delay,mechanical_delayandpulse_delay, each fed by its knob, as well asdelay.delaynames nosimulation_parameter, because it is computed. Serving it needs a mccode-plumber that forms the total from the three. - The scaffold turns a McStas
delayinto the experiment delay that reproduces it. - BIFROST's second bandwidth disc is calibrated at -14 Hz, the way it turns, so its mechanical delay is right for it.
- The
examplesextra needs chopcal 0.6.1, whosechopcal.nicos.experiment_delaygives the experiment delay the chopcal notebook sets.
0.11.1¶
Swap the orentation of the BIFROST triplets when output to McStas. The real detectors have their 'A' connector at smallest scattering angle and smallest final energy. This is a 180 degree rotation around the local x-axis for each triplet with respect to their earlier layout. In this orientation x=A/(A+B) ~ 1 is the smallest pixel in each triplet as identified by the EFU, which matches expectations.
0.11.0¶
The CAD (BREP) export becomes usable on its own, and is documented. Nothing is removed or renamed; the CAD output itself changes, which is why this is a minor release.
to_assembly and save_step take parameters, the values of the run-time knobs frames turn
by, in degrees -- {'detector_tank_angle': 60, 'sample_rotation': 30} for BIFROST -- which were
always drawn at their defaults. A name no frame turns by is refused.
The assembly follows the instrument's tree, with one child per drawn component named as it is
in McStas and NeXus (tank > channel_3 > channel_3_1 > channel_3_1_analyzer > blade_0),
where it used to be a single unlabelled compound. A viewer can show, hide and pick each part, and
a STEP file keeps the names.
The ESS moderator is drawn as ESS_butterfly's own outline for its sector and beamline, as tall as
the moderator, in place of a box reaching to its focus. Disc choppers (DiscChopper,
NXDiskChopper), which were not drawn, are discs about their spindle with their openings cut as
NXdisk_chopper describes them. Apertures and filters are coloured.
New documentation: a how-to for exporting an instrument to STEP, a CAD gallery with an
interactive model of each kind of solid niess draws, and an API page for niess.brep.
0.10.1¶
Corrections to BIFROST's secondary spectrometer in the CAD (BREP) output, and agreement between the analyzer geometry niess describes and the one McStas simulates. Nothing is removed or renamed.
BIFROST's analyzers and triplets are now drawn in CAD; they had no builders, so a STEP export showed neither. Each triplet sits the analyzer-detector distance from its analyzer, along the detector-angle frame, as in McStas and NeXus.
The radial collimators were drawn on the beam axis at half their radii, pointing sideways. They now stand on a vertical axis at their own radii, centred on the beam.
Each analyzer is drawn, in NeXus and CAD, from the blades niess builds: on the Rowland circle
through the sample, the analyzer centre and the detector centre, each turned to focus, in place
of a flat row. Arm.local_blades gives them in the analyzer's frame.
The angle_h passed to Monochromator_Rowland is now the design coverage the blades were built
with (Analyzer.rowland_angle_h), so McStas places its slabs on niess's blades; the previous
value put the outer slabs up to 0.8 mm away. Simulations of BIFROST therefore change slightly.
CAD and NeXus are checked against each other for every analyzer and triplet position, and the McStas slab placement against niess's blades.
0.10.0¶
Many corrections and improvements, mostly geared toward / enabled by producing simulated BIFROST output which can be read by ess.reduce / ess.spectroscopy.
Generally, the niess refactor previously did not correctly place components defined relative to another for NeXus Stucture JSON output nor BREP output.
The source component inserted into NeXus was configured to specify a beam current parameter but the reduction routines expect beam charge depositited per pulse.
The NeXus origin was not set properly previously; the standard origin location is the sample position.
The BIFROST detector tubes exported to NeXus appeared with 10 pixels per tube while, presently, the EFU pixelates each into 100 pixels.
0.9.2¶
Added — split points for restage¶
Mount(split_before=True) says a simulation may be split into two stages just before
that piece, and McStas gets an Arm named split_before_<mount> there, at the frame the
piece hangs from. Pass the names to restage's --split-at (0.15.1 or later), which splits at the latest one
a scan's points still share a primary at. It is the author's statement because only the
author knows it is safe: the MCPL file between stages keeps no USERVARS. BIFROST declares
one, before the sample -- which makes the normalization monitor part of the shared primary
in a sample-rotation scan -- and none before the tank, whose readout reads the
event_time_zero the sample sets.
Added — monitor efficiencies for replay¶
A monitor's calibration may give its efficiency, the fraction of the neutrons crossing
it that the real monitor counts. It becomes FrameMonitor.efficiency and the collector's
efficiency, which scales every recorded weight, so a replay that samples counts by
weight (readout-replay --counting-time) sends the EFU what the real monitor would see.
It defaults to 1; the McStas histogram is unchanged either way. Thinning stays where it
was, Collect(keep_probability=...), one value or one per monitor; both are now checked
when the instrument is emitted rather than when the simulation starts.
BIFROST's monitors get their design efficiencies (niess.bifrost.parameters.monitor_efficiencies):
psc 1e-7, overlap and bandwidth 1e-5, normalization 1e-5, and 0.23 for the elastic monitor's
0.2 atm 3He tube, an estimate until it is measured.
0.9.1¶
Added — beam monitors read out through the cbm EFU¶
A monitor can say which cbm Event Formation Unit channel the real one is read through:
FrameMonitor.readout, a niess.cbm.CbmChannel with the FEN, channel, readout type and
Kafka source. BIFROST's five monitors carry ECDC's (niess.bifrost.parameters.cbm_channels),
every one histogrammed and aggregated over 14 pulses.
On its own a channel changes nothing. It is used three ways:
to_mccode(..., collect=Collect(...))follows each such monitor with a mcstas-readout-masterCollectorBM0orCollectorBMI, recording the rays it counted forreadout-replayto send to the EFU.Collectnames the file, the monitor ring, an unbiased per-monitorkeep_probability, and optionally where the collectors are found.niess.cbm.efu_config(instrument)writes the cbm EFU's JSON configuration.to_nexus_structure(..., efu_monitors=True)describes each monitor by the da00 stream its EFU publishes rather than by the histogram McStas produces.
0.9.0¶
A moderator that only emits what the choppers pass, and the moderator called by its name.
chopper-lib's Polygon_ESS_butterfly arrives as PolygonESSModerator, opt-in: given the
same chopper train it reaches the same answer as the ordinary moderator, but no longer spends
rays the first disc would absorb. To make room for it, ESSource becomes ESSModerator:
at ESS the source is the accelerator, and NeXus already says so. The teaching instrument
now runs, and the tests now run on macOS and Windows as well as Linux.
Added — PolygonESSModerator, opt-in¶
chopper-lib's Polygon_ESS_butterfly as a niess moderator. It works out the exact region
of (inverse velocity, emission time) the chopper train transmits and emits only inside
it. With resample it draws every ray from inside the region instead, so a run's whole
ncount reaches the sample. It is an ESSModerator with the component's options added,
and the BIFROST and teaching instruments do not use it unless asked:
parameters['moderator'] = PolygonESSModerator.from_calibration(
parameters['moderator'] | {'resample': True})
The component reads the train, so narrow_source_wavelengths now publishes it under the
names the moderator asks for, without export_choppers. Naming different ones is refused,
and failing to publish raises even without strict.
Changed — ESSource is ESSModerator¶
The component an instrument's neutrons start from is its moderator. At ESS the
source is the proton accelerator that drives the spallation target, and NeXus already
uses the word that way: /entry/instrument/source describes the accelerator. The classes
are renamed to match.
| was | is |
|---|---|
niess.components.ESSource |
niess.components.ESSModerator |
niess.components.source.Source |
niess.components.Moderator |
niess.components.source |
niess.components.moderator |
default instance name ESS_source |
ESS_moderator |
Moderator replaces the empty stub of the same name and is now the base class that the
McStas, NeXus, tof and chopcalc targets look for.
The old names still import, with a DeprecationWarning, and will be removed in a later
release. Older files keep working too:
- JSON tagged
ESSourcereads as anESSModerator. - An
.instrwhose provenance recordsniess.components.source.ESSourceresolves to theESSModeratortranslators.
New emissions record niess.components.moderator.ESSModerator in each moderator's
niess_provenance block. That one line is the only change to the emitted instruments,
and it is why the three frozen .instr files were re-minted.
Changed — chopper-lib 4.2.2¶
The registry is pinned at mcdotstar/mcstas-chopper-lib@v4.2.2, which ships
Polygon_ESS_butterfly to McCode under a BSD-3-Clause licence. The version guard stays
at 4.2.1, because nothing niess calls changed.
Changed — the tests run on macOS and Windows too¶
CI now tests on Linux, macOS and Windows (#103). The regression tests — those that
compare floats against files frozen under tests/data/baseline — are marked
@pytest.mark.regression and run on Linux only, where the files were minted: another
platform may round the last bit of a computed rotation differently, and an exact
comparison cannot tell that from a real change. Everywhere else they are skipped, and
pytest --regression runs them anyway. Frozen comparisons with no floats in them — flow
graphs, field order, counts — stay unmarked and run everywhere.
Fixed¶
- The teaching instrument runs. Its calibration gave the moderator nowhere to send
its rays, which
ESS_butterflyneeds, so the compiled instrument stopped in INITIALIZE with "Please choose to set either the dist parameter or specify a target_index". The moderator is now aimed at the first guide's opening, as BIFROST's is, with the distance, width and height taken from that guide's own parameters:dist=1.5, focus_xw=0.04, focus_yh=0.06. The teaching goldens are re-minted for those three parameters. niess-scaffoldwrites the modules it generates as UTF-8. On Windows they were written in the locale's encoding, which Python does not read source in.niess.nexus.load_bindingsreads the bindings file as UTF-8 on every platform.
Migrating from 0.8.0¶
| 0.8.0 | 0.9.0 |
|---|---|
from niess.components import ESSource |
from niess.components import ESSModerator |
niess.components.source.Source |
niess.components.Moderator |
ESSource.from_calibration({...}) with no name |
named ESS_moderator, not ESS_source |
| chopper-lib fetched at 4.2.1 | fetched at 4.2.2; 4.2.1 is still accepted |
Nothing has to change straight away. The old names import with a
DeprecationWarning, and JSON or .instr files written by 0.8.0 still load. The one
behavioural difference is the default instance name, which matters only to a moderator
built without a name and then referred to by it. restage and mccode-plumber need no
release for this one: neither uses the renamed classes.
0.8.0¶
BIFROST's NeXus file, named the way the real instrument's is. ECDC keeps the bindings that say which topic and source fill each log of the real file; niess now carries a copy, takes ECDC's names for BIFROST's components, and can write a simulation that uses the real topics and PV names while never being mistaken for the real thing. To make the number a run sets the number its file records, every chopper and jaw knob is now named and declared as ESS publishes it.
Added — streams from a facility bindings file¶
niess.nexus.BoundStreams reads each log's topic and source from a bindings file, such
as ECDC's bindings.yaml (niess.nexus.load_bindings), instead of building them from a
PV root. BIFROST carries a verbatim copy of ECDC's file in niess/bifrost/ecdc/, with
niess.bifrost.ecdc.bifrost_streams() to use it:
- By default it writes a simulation made to look like the real instrument. Only the
logs the simulation has are written. Every EPICS source is prefixed
mcstas:, so a simulated PV is never the real one. Each simulated log names its knob in asimulation_parameterattribute. simulated=Falsewrites the real instrument's file, with every bound log.
tests/test_bifrost_ecdc_bindings.py requires every binding to be written or excused
with a reason. src/niess/bifrost/ecdc/UPSTREAM.md says how to refresh the file.
Changed — BIFROST takes ECDC's names¶
ECDC controls what the real file contains, so niess adopts its names rather than mapping between the two:
| was | is |
|---|---|
jaw_3, jaw_2, jaw_1 |
divergence_slit_1, divergence_slit_2, divergence_slit_3 — numbered along the beam, so the order is reversed |
slit |
sample_jaws |
a3, a4 (degree) |
sample_rotation, detector_tank_angle (degrees), with positioners of the same names |
a3_source, a4_source, a3_pv_root, a4_pv_root |
sample_rotation_source, … — the old keys raise, naming the new ones |
motor topic bifrost_motors |
bifrost_motion |
source (the moderator) |
moderator |
Changed — knobs are named and declared as ESS publishes them¶
A knob is named for the log it is published as and declared in that log's unit. The conversion to what McStas wants happens where the knob is inserted into a component, so the number a run sets is the number the file records. This applies to every instrument:
| was | is |
|---|---|
{disc}speed /"Hz" |
{disc}_rotation_speed /"Hz" |
{disc}delay /"s" |
{disc}_delay /"ns"; McStas gets 1e-9 * {disc}_delay |
{disc}park, {disc}park_angle /"deg" |
{disc}_park_angle /"degrees" |
{jaw}_l, _r, _b, _t /"m" |
{jaw}_left, _right, _bottom, _top /"mm"; McStas gets 0.001 * … |
A DiscChopper opening's start-up variable is now {name}_opening_delay, since for a
single opening {name}_delay is the knob.
Changed — NeXus names¶
- The accelerator's
NXsourceissource, notneutron_prod_info. Its per-pulse log iscurrent(in mA), notcurrent_log. - A simulated disc writes its delay as
delay, in ns, not asmark_delayin s. A simulated disc has no electronics adding to its delay, so the one knob it has is its total delay. - A motorised frame's positioner is named for its knob (
detector_tank_angle), not{frame}_{knob}(tank_mounting_a4). The frame name is kept only where two frames share a knob. - A component emitted under a name already used directly under
NXinstrumentnow raises. - Every simulated NXlog fed by a parameter names it in a
simulation_parameterattribute, whichever binder wrote the file: the chopper speed, delay and park angle, and a driven axis'svalue. Real files carry none.
Added — a dependency¶
pyyaml, to read a bindings file. mccode-antlr already required it, so nothing new is installed.
Migrating from 0.7.0¶
| 0.7.0 | 0.8.0 |
|---|---|
{disc}speed=14 |
{disc}_rotation_speed=14 |
{disc}delay=0.0123 (s) |
{disc}_delay=12300000 (ns) |
{jaw}_l=-0.015 (m) |
{jaw}_left=-15 (mm) |
BIFROST a3=…, a4=… |
sample_rotation=…, detector_tank_angle=… (degrees) |
instrument(a3_source=…, a4_pv_root=…) |
instrument(sample_rotation_source=…, detector_tank_angle_pv_root=…) |
BIFROST jaw_3, jaw_2, jaw_1 |
divergence_slit_1, _2, _3: slit 1 is the old jaw_3 |
BIFROST slit |
sample_jaws |
primary.source, RELATIVE source |
primary.moderator, RELATIVE moderator |
/entry/instrument/neutron_prod_info/current_log |
/entry/instrument/source/current |
…/{disc}/mark_delay (s) |
…/{disc}/delay (ns) |
/entry/instrument/tank_mounting_a4 |
/entry/instrument/detector_tank_angle |
Scripts and scans need their parameter names and units changed, not just their names: a chopper delay is a million times larger, a jaw edge a thousand. A name that no longer exists is refused by McStas; a value in the old unit is not, so check the numbers.
The tools around niess move with it. restage sets {disc}_rotation_speed and
{disc}_delay for an instrument that declares them, and mccode-plumber reads delay in
ns, source/current, and each log's simulation_parameter. Both still accept an
instrument or structure from 0.7.0. mccode-plumber now publishes every parameter to EPICS
before each scan point, so a simulated instrument no longer needs an UpdateEPICS
component of its own.
Regenerate any reference NeXus files and McStas instruments. Placements are unchanged, but every renamed knob and group above is visible in both.
0.7.0¶
Added — niess.scaffold, and a .instr migration path¶
Removing the instrument-reading routes (below) left no way in from a McStas file at all.
This is the way back in, and it is a different kind of tool: not a second front-end into
every target, but a one-way source generator. It reads a .instr and writes a niess
submodule you own and edit, so what runs afterwards is ordinary niess source and there is
still exactly one path into each target.
It resolves every placement, maps the components whose McStas type determines a niess
class, wraps the rest in Opaque, and refuses to write anything unless the module it
generates places every component where the .instr does.
Be clear about the scale of what it does: for a real instrument it maps a small fraction
of the components — 8 of 50 for ESS_IN5_reprate. The rest become Opaque and the report
lists them, most frequent first. That list is the point of the tool as much as the code
is.
docs/how-to/translate-an-instr.md now documents the tool and, more usefully, the four
things it cannot do.
Added — niess.components.Opaque¶
A McStas component niess has no class for, carried through verbatim: it holds a component
type name and arguments and emits exactly that, with role unmodelled-component.
It exists because the alternative was worse. A Component subclass with no __mccode__
inherits one that emits a bare Arm, so an unmodelled component silently became a
coordinate frame and the instrument quietly changed meaning.
Fixed — a run-time parameter passed to a component by its declaration¶
Component.to_mccode substituted str(InstrumentParameter) for an
InstrumentParameter-valued entry in a component's parameter dictionary. That is the
parameter's declaration — slit_width/"m"=0.03 — which the assembler then re-parsed as
arithmetic, giving slit_width*1.0/"m". It now substitutes the name.
No shipped component reached this: Aperture and the rest pass .name themselves. It was
Opaque that first handed over the object.
Changed — BIFROST's discs are chopper-lib NXdisk_chopper components¶
A disc is now emitted as one NXdisk_chopper from
mcstas-chopper-lib rather than one
McStas DiskChopper per opening, and its openings go into the instrument's DECLARE as
an array the component and chopper-lib's band arithmetic both read. Each disc gains a
{disc}park_angle run-time knob alongside {disc}speed and {disc}delay; nothing is
removed. NXDiskChopper is the class, DiscChopper remains for the DiskChopper route.
The beam crossing moves. disc_beam_offset now takes the aperture's width into account:
the reach is the chord sqrt(radius² - (width/2)²), not the radius, so the far corners of
the aperture sit on the disc circumference instead of overhanging it. BIFROST's four
30.4 mm discs move 0.330 mm and its two 60 mm bandwidth discs 1.288 mm. It is degenerate
with the old rule at zero width. Everything after radius is keyword-only now, because
inserting width ahead of height silently changed what a positional call meant.
chopper-lib 4.2.1 or newer is required, raised from 4.1.0. niess.chopcalc calls
chopper_wavelength_limits, which goes through the range_set_sort that until 4.2.1
"gave different answers on different platforms, which is how a chopper train's admitted
band came out 0.098 Å wide on Windows and 1.906 Å on Linux for the same discs". The struct
layout has been stable since 4.0.0, so an older library links and runs — it just answers
the question wrong, and differently per host. The emitted #error guard says so.
Fixed — every target sees a disc again¶
NXDiskChopper was added as a sibling of DiscChopper rather than a subclass, and
niess.tof and niess.nexus both selected discs with isinstance(x, DiscChopper). An
unregistered type falls through rather than failing, so both went quiet instead of loud:
chopper_specsreturned(). A BIFROSTtofmodel had no choppers at all, and the instrument reported that it "declares['source_lambda_max', 'source_lambda_min']" when it declares eighteen chopper knobs besides.- Every disc was written to NeXus as a bare reference frame carrying a description — no
slits,slit_edges,rotation_speed,delayorradius. BIFROST's file contained zeroNXdisk_choppergroups, which is whatmccode-plumberlooks for to wire a chopper's PVs to the forwarder.
The knobs, the openings and the controller now live on Chopper, so there is one of each:
speed_parameter/delay_parameter/park_parameter all return an InstrumentParameter
(one class returned a bare name, the other a parameter), and DISC_CHOPPERS names the two
disc classes so no caller has to remember there are two.
Fixed — three defects the migration left behind¶
- A disc's edges are two per opening.
NXDiskChopperpaired consecutive edges, so the gap between two openings became an opening of its own: a three-slit disc reported five slits and emitted ten edge values where six were given. Invisible until now because the two pairings agree for a single opening, and every BIFROST disc has one. niess.chopcalcemitted invalid C —free(train[0].edges;, with no closing paren. Only instruments whose rows own their edges emit that line, so BIFROST, whose rows areDECLAREarrays, never showed it. The emitted C is now checked for balanced delimiters.pv_rootandtdc_channelare back on a disc, having been dropped in the retyping. Without them a file can only be written in simulated mode, so real-mode NeXus had quietly become unavailable for all of BIFROST.
Changed — a NeXus structure the ESS checker accepts¶
The written file now passes the ESS structure checker, in either of two modes.
to_nexus_structure(instrument, streams=...) says where a driven axis's numbers come
from: SIMULATED (the default) writes the simulation's own parameter names, REAL writes
the EPICS positioners the components declare. One tree, two files.
Two fields go, and neither is a rename:
zero_positionon anNXdisk_chopper.NXdisk_choppermeasuresslit_edgesfrom the top-dead-centre mark, so the mark is the origin of that frame rather than a number in it — writing the angle again alongside the edges it is the zero of says nothing the file does not already say. When the mark passes is a different question, and thetop_dead_centerlog answers it.x_gap/y_gapon an aperture. They areNXslitfields; a jaw is anNXaperture. Only the driven edges are recorded, so a jaw's fixed height is now not in the file. If it is wanted it needs a home chosen on purpose.
Aperture.__mccode_extra__ goes with them, for the same reason on the McStas side: a
jaw's edges are run-time parameters, so provenance repeating their width duplicated what
the instance already carries. The fixed height went unrecorded there too.
Changed — BIFROST tags the channel at the cassette, not at a ring of slits¶
The nine radial filter-collimators move from one-per-channel up into the tank, share a
single McStas GROUP with the elastic monitor, and write secondary_cassette from their
own EXTEND. A neutron scatters in at most one of them, which is the tagging
RadialSlitBank existed to do — so the slit bank is gone, along with slit_angles,
slit_width, slit_radius and SLIT_BOUNDARY_MARGIN. The beam's branch point is the
sample now rather than a component inside the tank, which is where it always was
physically.
Two flow bugs went with it: Tank.__niess_flow__ reported the monitor's path without ever
calling the monitor's own __niess_flow__, so the node existed only if something above
happened to draw an edge to it — Tank.to_graph() alone gave nine disconnected channels
and no monitor.
Added — a composite may GROUP and EXTEND what it encloses¶
A McStas GROUP makes its members alternatives: a neutron is offered each in turn until
one does not absorb it. So it spans components that are separate objects and no one of them
can name it, and the same goes for the EXTEND recording which of them took the ray.
McCodeContext gains groups and extends beside the whens it already had — set by a
composite's __mccode_enter__ against a descendant's path, applied by the leaf emitter.
This is not the disc chopper's pattern. A disc emits several instances of itself, so it
owns them and calls GROUP in its own loop. Here each part is a component in its own right
and has to be emitted by the registry, with the name, frame, placement and provenance the
walk supplies — a parent emitting its own children would bypass all of it.
Fixed — an elliptic guide's widest point was in the wrong place¶
EllipticGuide evaluated its half-width at offset - minor + z where
Elliptic_guide_gravity.comp traces neutrons against z - offset: a sign error on the
offset, and a spurious minor. Both terms are small next to the major axis, so the result
stayed a plausible guide and read as more-or-less right — but the widest point of the
channel belongs at z == offset, and the sign put it at -offset, which for most of
BIFROST's guides is outside the guide entirely. Across the 58 faces of the primary the
median error is 10% and the worst is 100%.
TaperedGuide and EllipticGuide now have __off__ and __nexus_leaf__ alongside
StraightGuide's, and the m-values move onto Guide so all three wind their faces the
same way — [top, right, bottom, left] per segment, one m-value each. The generic
@translator(Guide) is gone: it wrote length and scalar m_left/m_right/m_top/
m_bottom, none of which NXguide has.
Changed — provenance schema 3 drops source_name¶
It could only ever repeat instance.name. Every writer passed the same string it had just
handed assembler.component, and Instance keeps that name verbatim since add_component
raises on a collision rather than renaming around it. Nothing read it —
dispatch._resolve_local resolves on source_type and role. What it carried was risk:
two copies of one name can disagree, and the copy in the metadata is the one that would
look authoritative when they did. NiessProvenance.source_name stays as an attribute, read
off the instance.
Added — niess.components.Motor, a driven axis that says where its numbers come from¶
A Motor carries a name, a unit, a default, and the Kafka source/topic its values
arrive on. Motor.parameter() gives the InstrumentParameter a run sets, and the NeXus
target writes it as the NXlog an NXpositioner value or an NXtransformations entry
needs — attributes for depends_on, transformation_type and vector on the group, and
the stream module carrying the unit twice (value_units for the f144 contract, a units
attribute for a reader resolving the chain).
Fixed — a dataset module the ESS file-writer can read¶
Dataset modules were written with type where the ESS file-writer schema requires
dtype. One key, every static dataset in every file.
Changed — BIFROST's radial slit bank is no longer written to NeXus¶
It was a single NXslit reporting ten slits, with an angular width recorded in x_gap,
which is a length. Splitting it would not have helped: ten NXslits would report ten
apertures that are not there, still in the wrong units, and a reduction would have to
learn to ignore them. An absent thing says nothing; ten fabricated ones say something
false.
There is no ring of slits at BIFROST's sample. It is how a neutron leaving the sample gets
tagged with the channel it entered — the emitted McStas component reports which opening it
passed, and everything downstream is gated on that. slitDistance being drivable, with a
default chosen to clear everything further out, is what a surface that is not there can
afford to do, and no CAD builder draws it.
It remains a niess object and McStas still emits it unchanged — the .instr goldens do
not move — because the beam really does divide there, and the particle flow has to say so.
The flow record now steps over unwritten nodes rather than naming only written ones, which
is what kept nine channels and a monitor from being left with nothing feeding them.
Removed — the instrument-reading routes¶
Every target read the niess object tree and had a second implementation that read an
assembled mccode_antlr instrument, recovering what the tree states. The second one is
gone, in niess.chopcalc, niess.tof, niess.brep and niess.nexus alike — some 4000
lines. Converting an instrument niess did not build is no longer supported.
Reading a .instr still is: niess.io.mccode.load_instr parses one so its placements can
be inspected, which is what checking a niess submodule against the hand-written file it
replaces needs.
What this changes for a caller:
narrow_source_wavelengthsno longer finds its own chopper train.chopper_trainis a required argument fromtrain_from_instrument, andsource,skip,path_lengths,latest_emissionandgraphmoved with the searching.niess.brep.via_instr.instrument_to_assembly,niess.tof.via_instr.to_tof_model,niess.nexus.via_instr.to_nexus_structureand theniess.nexus.via_instrsubpackage no longer exist.Subject.extrais gone: a guide states itssubstrateandresolutionas fields, so a builder no longer reads them off a provenance tag.
Fixed — two things only the removed route was doing¶
Both were bugs the parallel implementations had been hiding, and both are in the tree route now:
@inputs/@outputswere not written at all. McCode cannot say that a beam branches, so the removed route had to be handed the real flow asgraph=; a niess instrument states it, and BIFROST's radial slits now record all ten paths leaving them.slit_edgeswere written as niess holds them, which is deliberately looser thanNXdisk_chopperallows —[-85, 85]rather than[275, 445]. The conversion to the standard's order lived in the removed route. It isDiscChopper.nexus_slit_edgesnow, andslit_angleis written again alongside it.
Every conversion target now lives under its own subject name and reads the niess object
tree. The route that reads an assembled McStas instrument instead is kept, one
via_instr per package, and is what goes when converting a foreign .instr stops being
served. niess.targets is gone.
Added¶
- niess objects display themselves. msgspec generates a repr from the fields, and a
composite's fields are its whole subtree — a BIFROST
Instrumentcame out at nearly 300 000 characters, so typing its name flooded the terminal.Instrument,Mount, and everyBaseandSectionnow say what they are, what they contain and how big that is, in columns aligned across each set of siblings. A node small enough to say so also lists its own fields — one chopper gives its radius, speed and position, where a whole instrument lists its parts;niess.display.PARAMETERSoverrides that judgement andniess.display.show()takes it per call. In a notebook_repr_html_renders the same tree as nested<details>, collapsed below the top level, so a thousand-node instrument opens where you want it.
Changed — import paths¶
| was | is |
|---|---|
niess.targets.mccode.to_mccode |
niess.mccode.to_mccode |
niess.targets.nexus.to_nexus_structure |
niess.nexus.to_nexus_structure |
niess.targets.nexus.BIFROST_REGISTRY |
niess.nexus.bifrost.BIFROST_REGISTRY |
niess.targets.brep.to_assembly |
niess.brep.to_assembly |
niess.tof.tree.to_tof_model |
niess.tof.to_tof_model |
niess.chopcalc.tree.train_from_instrument |
niess.chopcalc.train_from_instrument |
niess.nexus.load_instr |
niess.io.mccode.load_instr |
niess.mccode (Assembler helpers) |
niess.assembler |
niess.mccode (provenance metadata) |
niess.provenance |
niess.nexus.{instrument,translators,registry,bifrost,expression,orientation,variables} |
niess.nexus.via_instr.* |
niess.brep.components |
niess.brep.builders (shapes), niess.brep.via_instr (the old walk) |
niess.tof.components |
niess.tof.setup (shared), niess.tof.via_instr (the old walk) |
niess.chopcalc.discovery |
niess.chopcalc.paths (shared), niess.chopcalc.via_instr |
Changed — three names now mean the tree route¶
niess.nexus.to_nexus_structure, niess.tof.to_tof_model and niess.brep.save_step
each used to resolve to the instrument-reading implementation and now resolve to the
tree-reading one. They take an Instrument where they took an Assembler or an Instr.
The previous behaviour is niess.nexus.via_instr, niess.tof.via_instr and
niess.brep.via_instr respectively.
Removed¶
niess.__init__re-exports.import niessis a namespace; name the subject you want (from niess.components import Crystal,from niess.instrument import Instrument). This also stopsimport niessfrom loading the CAD target and every component class.niess.brep.registryandDEFAULT_BREP_REGISTRY— it had become a two-name alias of the registry it imported. The registry isniess.brep.BREP_REGISTRY.niess.tof.registryas a module:NiessTofRegistryandDEFAULT_TOF_REGISTRYare inniess.tof.via_instr, with the decorators that populate them.- Dead code carried no further:
_provenance_value,_loft_rectangles,_loft_ellipsesand_ellipse_spanin the BREP builders;_importand_lazyin the NeXus target.
Fixed¶
- Importing the tree NeXus target no longer executes the whole instrument-reading one
(
niess.nexus.structurereached..nexus.nodes, which ran the old package__init__). - Importing generic NeXus no longer imports
niess.bifrost: the tree target calledregister_bifrost()at module scope, so one instrument's translators loaded whether or not anything asked. They register on import ofniess.nexus.bifrost, as the instrument-reading route always did. niess.tofandniess.chopcalcno longer reach into the demoted route for shared arithmetic —niess.chopcalc.pathsholds what both use, including the beam-path walk andglobal_position, whichniess.tofhad been importing as a private.
Migrating from 0.6.0¶
| 0.6.0 | 0.7.0 |
|---|---|
to_nexus_structure(instr), to_tof_model(assembler), save_step(instr) |
build a niess Instrument and pass that; see Changed — three names now mean the tree route |
niess.targets.* |
see the import paths table above |
import niess; niess.Crystal |
from niess.components import Crystal |
RadialSlitBank |
gone; the cassette's EXTEND writes secondary_cassette |
MultiSlitChopper-era DiscChopper for BIFROST |
NXDiskChopper, one chopper-lib NXdisk_chopper per disc |
provenance source_name |
read it off the instance; schema is 3 |
| chopper-lib 4.1.0 | 4.2.1 or newer, enforced by an #error guard |
Converting an instrument niess did not build is no longer supported. That is the
headline removal and there is no drop-in replacement: the Instr-reading route behind each
target is gone. Reading a .instr still works — niess.io.mccode.load_instr — and
niess-scaffold turns one into a niess submodule you own and edit, once, so what runs
afterwards is ordinary niess source. See Added — niess.scaffold.
Regenerate any reference NeXus files. More moved this time than in 0.6.0:
- disc choppers are chopper-lib
NXdisk_choppergroups, withslit_edgesin aDECLAREarray and a{disc}park_angleknob - the beam crossing moved by up to 1.288 mm, because the reach is now the chord
sqrt(radius² - (width/2)²)rather than the radius zero_positionis gone fromNXdisk_chopper, andx_gap/y_gapfrom apertures- BIFROST's radial-slit
NXslitis gone — it described ten slits that are not there - dataset modules carry
dtype, nottype, which older files got wrong
Regenerate cached CAD or OFF geometry too. An elliptic guide's half-width was evaluated at the wrong place; across BIFROST's primary the median error was 10% and the worst 100%.
If you build with an older chopper-lib the instrument will not compile, which is deliberate: until 4.2.1 the band arithmetic gave different answers on different platforms.
0.6.0¶
Choppers, twice over: described the way they are actually built and controlled, and then
put to work. A disc is now one class however many openings it has, set with a delay rather
than a phase, and placed by where its spindle is rather than by an offset someone worked
out by hand. Two new modules read that description — niess.chopcalc narrows a source to
the band its chopper train passes, and niess.tof flies neutrons through the train in a
notebook.
Added¶
niess.tof—to_tof_model()turns an emitted instrument into a ready-to-runtof.Model, with every disc chopper and a detector at each monitor and at the sample. It returns aTofSetuprather than a bare model, which reports in a notebook table which run-time parameters were used and at what value, andwith_values()turns one knob without rebuilding the rest.pulses=simulates more than one source pulse, which is what shows a disc running at half the source frequency doing its job. Install withpip install 'niess[tof]'.niess.chopcalc—narrow_source_wavelengths()emits a chopper-lib call into the instrument'sINITIALIZEso the source samples only the band the choppers pass, which is free simulation speed. The band is computed at run time from the chopper parameters, so changing a delay on the command line recomputes it without rebuilding anything.export_choppers=publishes the train toDECLAREfor a component that needs the chopper description itself rather than the band it implies.- A user-supplied particle-flow graph, accepted by
to_nexus_structure(),narrow_source_wavelengths(),build_train()andto_tof_model(). McCode has no way to say that a beam branches — its instruments are a list — so an instrument that splits at the sample, as BIFROST does, had every component past that point treated as fed by whichever happened to be declared before it. Passing the graph fixes both the@inputswritten into NeXus and the flight paths measured through it. Omitting it derives the flow from declaration order, as before. zero_angleandbeam_angleonDiscChopper, saying where the beam crosses the disc: counter-clockwise about +z, the first from local +y to the disc's zero mark, the second from the mark to the beam. A disc hanging above the beam isbeam_angle = 180.windowsonDiscChopper— slit edges from the zero mark, two per opening, for a disc whose openings are neither identical nor evenly spaced.- Extras:
niess[tof],niess[examples]for the documentation's notebooks, andniess[brep], which had been needed byniess.brepall along with no way to ask for it by name. - Documentation: how-to guides for
narrowing the wavelength band
and simulating a chopper cascade,
an API page for
niess.tof, and a notebook joining chopcal, niess and tof on BIFROST.
Changed¶
- Disc choppers are set with a delay in seconds, not a phase in degrees. A delay is
what a real chopper is set with, it is what McStas'
DiskChopperacts on, and unlike a phase it does not depend on which way the disc turns.Chopper.phaseis nowChopper.delay, and the emitted instrument parameter{name}phaseis{name}delay. - A disc chopper's
positionis its spindle. The emittedATis the point the beam crosses the disc, computed fromzero_angleandbeam_angle, along with the rotation that puts the disc on the correct side of the beam. Both are recorded in provenance and taken back out byniess.nexus. - NeXus output for disc choppers has changed, in three ways: an
NXdisk_chopperis centred on the spindle rather than on the beam crossing, it no longer carries the McStas frame twist as a real rotation of the disc, and itsslit_edgesare ordered as the standard asks. Compare files across this release rather than assuming they match. - chopper-lib 3.0.0 or newer is required by
niess.chopcalc, pinned asmcdotstar/mcstas-chopper-lib@v3.0.0, with a#errorin the emitted C for anyone who overrides the registry with something older.
Removed¶
MultiSlitChopper—DiscChoppertakeswindowsand emits oneDiskChopperper opening, grouped, exactly asMultiSlitChopperdid.DiscChopper.offset— givezero_angleandbeam_angleinstead. A calibration that still sets an offset 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.DiscChopper.chopper_lib_parameters—niess.chopcalcbuilds the chopper-lib description from the emitted instrument.
Fixed¶
- Rotations near ±90°.
mccode_ordered_anglesextracted McCode's three angles by hand, with a gimbal-lock guard written for a different Euler convention than the formulas it guarded. 7 of 20000 random orientations came back as a different rotation, and exactly ±90° was handled by neither branch. It uses scipy now. - Components that move. A component positioned by an instrument parameter raised out of
to_tof_model()instead of being skipped, because the check for "is this a number yet" caught onlyAttributeErrorwhileExpr.valueraisesNotImplementedError. Secondary spectrometers on a movable tank convert now. - Multi-opening discs narrowed the band too little. Such a disc was approximated by the single angular envelope spanning its first and last edges, so one whose openings reach right round admitted everything and was dropped. Describing each opening gets it back: for the three-slit disc in the test suite, 0.75–30 Å goes from not narrowed at all to 2.20–13.08 Å.
slit_edgesordering.NXdisk_chopperwants positive increasing edges starting with an opening edge, with only the final edge allowed past 360. That reordering rotates which slit comes first rather than shifting the list, so[-10, 10, 60, 90]is written[60, 90, 350, 370], not[350, 370, 420, 450].- The test suite passes without the optional extras, which is what optional was supposed to mean. The documentation examples and notebooks skip when an extra is absent, and only then — an example broken any other way still fails.
Migrating from 0.5.0¶
| 0.5.0 | 0.6.0 |
|---|---|
DiscChopper(..., phase=...) |
DiscChopper(..., delay=...), in seconds |
{name}phase run-time parameter |
{name}delay |
MultiSlitChopper(..., windows=...) |
DiscChopper(..., windows=...) |
DiscChopper(..., offset=...) |
DiscChopper(..., zero_angle=..., beam_angle=...) |
chopper.chopper_lib_parameters() |
niess.chopcalc.build_train(instrument) |
A calibration dictionary carrying offset is refused with a message naming the
replacement, so an instrument submodule that has not been converted says so on the first
build rather than quietly placing a disc in the wrong place.
If you keep reference NeXus files, regenerate them: disc chopper geometry moved, for the reasons under Changed.