niess.tof¶
A ready-to-run tof chopper-cascade model, read off the
discs. See Simulate a chopper cascade.
tof
¶
Fly neutrons through a chopper cascade, from a niess instrument.
tof (from the scipp developers) is a lightweight straight-line Monte Carlo for chopper
cascade diagrams. It wants the same description of a chopper train niess.chopcalc
extracts, with one difference: chopcalc emits parameter names so a band recomputes at
run time, while tof configures one specific machine and needs numbers.
Reading the tree, they already are numbers -- a disc's speed and delay are quantities on the disc:
from niess.instrument import Instrument, Mount
from niess.teaching import Primary
from niess.tof import to_tof_model
teaching = Instrument(name='teaching', origin='sample_origin', parts=(
Mount(name='primary', content=Primary.from_calibration()),
))
setup = to_tof_model(teaching)
setup # in a notebook: what it used, and what you may override
setup.model.run().plot()
Modelling an instrument niess did not build meant reading an emitted one and parsing those numbers back out of the C. That route is gone: niess models niess instruments.
Install with pip install 'niess[tof]'.
Modules:
-
mapping–The numbers a
tof.Chopperwants, from the way niess describes a disc. -
model–Chopper specs for
tof, read off the tree. -
parameters–Instrument parameters as numbers, and a record of which ones were used.
-
setup–The
tofpackage itself, and the result both routes hand back.
Classes:
-
ChopperSpec–One
tof.Chopper, as plain numbers, before anytofobject exists. -
Use–One instrument parameter the model depended on.
-
TofSetup–A ready-to-run
tof.Model, and what went into it.
Functions:
-
delay_to_phase–A disc's delay in seconds, as the phase angle in degrees
tofwants. -
spec_from_windows–A spec from the windows
niess.chopcalcextracts. -
chopper_specs–Every disc in
instrument, astofwants it. -
to_tof_model–A ready-to-run
tof.Modelforinstrument, and what went into it. -
as_declared–A supplied value as a plain number, in the unit whatever reads it declares.
ChopperSpec
dataclass
¶
ChopperSpec(name: str, frequency: float, anticlockwise: bool, open: tuple[float, ...], close: tuple[float, ...], phase: float, distance: float)
One tof.Chopper, as plain numbers, before any tof object exists.
Methods:
-
to_tof–The
tof.Chopperitself.
Attributes:
-
frequency(float) –Hz, never negative --
tofcarries the direction separately. -
open(tuple[float, ...]) –Degrees from the beam, one per opening, in
tof's sense. -
phase(float) –Degrees.
-
distance(float) –Metres along the beam.
tofmeasures from the same zero as its source, so this
frequency
instance-attribute
¶
frequency: float
Hz, never negative -- tof carries the direction separately.
open
instance-attribute
¶
Degrees from the beam, one per opening, in tof's sense.
distance
instance-attribute
¶
distance: float
Metres along the beam. tof measures from the same zero as its source, so this
is the source's own distance plus the path walked to the disc.
to_tof
¶
The tof.Chopper itself.
Source code in src/niess/tof/mapping.py
Use
dataclass
¶
Use(name: str, value: float, default: float | None, unit: str | None, overridden: bool, used_by: tuple[str, ...])
One instrument parameter the model depended on.
Attributes:
-
overridden(bool) –Whether the caller supplied it, rather than the instrument's own default.
-
used_by(tuple[str, ...]) –Where it was read, as
component.parameter, for the report.
TofSetup
dataclass
¶
TofSetup(model: Any, source: Any, choppers: tuple[ChopperSpec, ...], detectors: tuple[str, ...], parameters: tuple[Use, ...], excluded: tuple[Any, ...] = (), notes: tuple[str, ...] = (), _rebuild: Any = None)
A ready-to-run tof.Model, and what went into it.
Displaying this in a notebook answers "what do I need to provide?" -- which, for an instrument niess built, is usually nothing: every chopper knob is declared with the calibration's own value as its default. The knobs are listed anyway, because knowing which ones exist is the point of asking.
Methods:
-
with_values–The same instrument again, with these instrument parameters replaced.
with_values
¶
The same instrument again, with these instrument parameters replaced.
Source code in src/niess/tof/setup.py
delay_to_phase
¶
A disc's delay in seconds, as the phase angle in degrees tof wants.
tof has no notion of a delay. An opening is placed by its angle and the whole disc
is shifted by phase, which open_close_times adds to every angle before dividing
by the angular speed -- so a delay of d seconds is 360 * |speed| * d degrees.
The sign does not flip with the direction of rotation, which is the one thing here
worth being suspicious of. A NeXus phase is an angle in the disc's own rotating frame,
so its sign does flip: tof.Chopper.from_nexus writes phase = -phase for a
negative rotation speed for exactly that reason. A niess delay is a time, and a later
time is later whichever way the disc turns.
Checked against Chopper.open_close_times for both directions on an asymmetric
three-opening disc: negating this puts every opening somewhere else.
Source code in src/niess/tof/mapping.py
spec_from_windows
¶
spec_from_windows(*, name: str, windows, delay: float, speed: float, distance: float) -> ChopperSpec
A spec from the windows niess.chopcalc extracts.
Those windows are already in the frame where an edge at angle a is on the beam at
delay + a / (360 * speed) -- that is, a is measured from the beam towards the
mark, which is the opposite sense to tof, where an opening at angle a is
reached after turning through it. So the two swap sign, and a (minimum, maximum)
pair becomes (-maximum, -minimum): still increasing, which tof requires.
Source code in src/niess/tof/mapping.py
chopper_specs
¶
chopper_specs(instrument, values: dict | None = None, origin: float = 0.0, skip=(), path_lengths=None) -> tuple[ChopperSpec, ...]
Every disc in instrument, as tof wants it.
values overrides a knob by name -- {'chopper_rotation_speed': 20} -- which is what
running the same instrument at a different speed means. The disc names its knobs, so
the override needs no convention repeated here. A value that carries its own unit, a
scipp scalar, is converted to the one the disc declares the knob in, so a speed worked
out in kHz or a delay in ms can be handed over as it comes; a name that is not a knob
of this instrument is refused rather than quietly changing nothing.
origin is where the tof.Source these are paired with sits, and it belongs to
the model rather than to the instrument: an ESS source in tof carries its own
0.05 m facility offset, while the niess moderator is at the instrument origin. Pass
source.distance to have the two agree.
Source code in src/niess/tof/model.py
to_tof_model
¶
to_tof_model(instrument, *, source=None, values: dict | None = None, neutrons: int = 1000000, pulses: int | None = None, seed: int | None = None, sample: str | None = None, skip=(), path_lengths=None)
A ready-to-run tof.Model for instrument, and what went into it.
The instrument-reading version of this asks niess.chopcalc for a chopper train,
which is C text, and parses the numbers back out. Here every number is on an object:
a disc's speed and delay, a monitor's position, the source's wavelength band.
values overrides a knob by name, which is what running the same instrument at a
different chopper speed means -- a chopper's speed or delay, or one of the source's
wavelength bounds. A scipp scalar is converted to the unit that knob is declared in,
and a name that is not a knob is refused. See chopper_specs.
source is a tof.Source to use instead of building one. Building one fetches
the facility's pulse profile, so passing your own is how this runs offline -- which
is what the test suite does, and the reason this is an argument rather than
something read off the instrument.
sample names the component to put the last detector on. The instrument's own
origin is used when it is not given, which is right for an instrument that
declares one.
Source code in src/niess/tof/model.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 | |
as_declared
¶
A supplied value as a plain number, in the unit whatever reads it declares.
A chopper speed calculated elsewhere arrives as a scipp scalar carrying its own unit -- and a calculator is as entitled to hand back a period in milliseconds or a speed in rpm as it is to match what the instrument happens to say. The thing being set knows what unit it wants, so convert rather than making the caller strip the unit and hope the two agree.
unit of None means nothing declared one, so there is nothing to convert to and
the number is taken as it comes. Shared with the tree-reading route, which knows the
unit from the disc rather than from a DEFINE line, so that a value carrying its unit
is accepted the same way whichever way the instrument was read.
Source code in src/niess/tof/parameters.py
mapping
¶
The numbers a tof.Chopper wants, from the way niess describes a disc.
Deliberately free of tof itself, so the arithmetic can be tested -- and printed --
without the optional dependency installed, and so the one place the conversion is
derived is one place to read.
Classes:
-
ChopperSpec–One
tof.Chopper, as plain numbers, before anytofobject exists.
Functions:
-
delay_to_phase–A disc's delay in seconds, as the phase angle in degrees
tofwants. -
spec_from_windows–A spec from the windows
niess.chopcalcextracts.
ChopperSpec
dataclass
¶
ChopperSpec(name: str, frequency: float, anticlockwise: bool, open: tuple[float, ...], close: tuple[float, ...], phase: float, distance: float)
One tof.Chopper, as plain numbers, before any tof object exists.
Methods:
-
to_tof–The
tof.Chopperitself.
Attributes:
-
frequency(float) –Hz, never negative --
tofcarries the direction separately. -
open(tuple[float, ...]) –Degrees from the beam, one per opening, in
tof's sense. -
phase(float) –Degrees.
-
distance(float) –Metres along the beam.
tofmeasures from the same zero as its source, so this
frequency
instance-attribute
¶
frequency: float
Hz, never negative -- tof carries the direction separately.
open
instance-attribute
¶
Degrees from the beam, one per opening, in tof's sense.
distance
instance-attribute
¶
distance: float
Metres along the beam. tof measures from the same zero as its source, so this
is the source's own distance plus the path walked to the disc.
to_tof
¶
The tof.Chopper itself.
Source code in src/niess/tof/mapping.py
delay_to_phase
¶
A disc's delay in seconds, as the phase angle in degrees tof wants.
tof has no notion of a delay. An opening is placed by its angle and the whole disc
is shifted by phase, which open_close_times adds to every angle before dividing
by the angular speed -- so a delay of d seconds is 360 * |speed| * d degrees.
The sign does not flip with the direction of rotation, which is the one thing here
worth being suspicious of. A NeXus phase is an angle in the disc's own rotating frame,
so its sign does flip: tof.Chopper.from_nexus writes phase = -phase for a
negative rotation speed for exactly that reason. A niess delay is a time, and a later
time is later whichever way the disc turns.
Checked against Chopper.open_close_times for both directions on an asymmetric
three-opening disc: negating this puts every opening somewhere else.
Source code in src/niess/tof/mapping.py
spec_from_windows
¶
spec_from_windows(*, name: str, windows, delay: float, speed: float, distance: float) -> ChopperSpec
A spec from the windows niess.chopcalc extracts.
Those windows are already in the frame where an edge at angle a is on the beam at
delay + a / (360 * speed) -- that is, a is measured from the beam towards the
mark, which is the opposite sense to tof, where an opening at angle a is
reached after turning through it. So the two swap sign, and a (minimum, maximum)
pair becomes (-maximum, -minimum): still increasing, which tof requires.
Source code in src/niess/tof/mapping.py
parameters
¶
Instrument parameters as numbers, and a record of which ones were used.
niess.chopcalc emits parameter names on purpose, so a band recomputes at run time.
tof configures one specific machine, so it needs values. Everything here is about
getting from one to the other, and about being able to tell a notebook user afterwards
which knobs the model actually turned on.
Classes:
-
Use–One instrument parameter the model depended on.
-
ParameterValues–The values to evaluate expressions against, and a note of what got used.
Functions:
-
instrument_defaults–Every DEFINE INSTRUMENT parameter that has a value, by name.
-
instrument_units–Parameter units, unquoted -- a DEFINE line carries them as
name/"Hz" = 14. -
as_declared–A supplied value as a plain number, in the unit whatever reads it declares.
Use
dataclass
¶
Use(name: str, value: float, default: float | None, unit: str | None, overridden: bool, used_by: tuple[str, ...])
One instrument parameter the model depended on.
Attributes:
-
overridden(bool) –Whether the caller supplied it, rather than the instrument's own default.
-
used_by(tuple[str, ...]) –Where it was read, as
component.parameter, for the report.
ParameterValues
¶
The values to evaluate expressions against, and a note of what got used.
Methods:
-
evaluate–An expression as a number, or
Nonewhen it does not fold to one. -
evaluate_text–A
chopcalcfield -- C text naming instrument parameters -- as a number. -
number–A component parameter as a number, recording what it depended on.
-
uses–What the model read, in the order the instrument declares it.
Source code in src/niess/tof/parameters.py
evaluate
¶
An expression as a number, or None when it does not fold to one.
Source code in src/niess/tof/parameters.py
evaluate_text
¶
A chopcalc field -- C text naming instrument parameters -- as a number.
chopcalc writes C so a band recomputes at run time, and everything it writes happens to parse as a McCode expression, the conditional it emits for a run-time phase included. So the train it extracts can be reused whole rather than the instrument walked a second time.
Source code in src/niess/tof/parameters.py
number
¶
A component parameter as a number, recording what it depended on.
Source code in src/niess/tof/parameters.py
uses
¶
What the model read, in the order the instrument declares it.
Source code in src/niess/tof/parameters.py
instrument_defaults
¶
Every DEFINE INSTRUMENT parameter that has a value, by name.
niess writes a chopper's speed and delay with the calibration's own numbers as the defaults, so an instrument it built is usually complete on its own -- which is why the report below can normally say that nothing needs supplying.
Source code in src/niess/tof/parameters.py
instrument_units
¶
Parameter units, unquoted -- a DEFINE line carries them as name/"Hz" = 14.
as_declared
¶
A supplied value as a plain number, in the unit whatever reads it declares.
A chopper speed calculated elsewhere arrives as a scipp scalar carrying its own unit -- and a calculator is as entitled to hand back a period in milliseconds or a speed in rpm as it is to match what the instrument happens to say. The thing being set knows what unit it wants, so convert rather than making the caller strip the unit and hope the two agree.
unit of None means nothing declared one, so there is nothing to convert to and
the number is taken as it comes. Shared with the tree-reading route, which knows the
unit from the disc rather than from a DEFINE line, so that a value carrying its unit
is accepted the same way whichever way the instrument was read.