API: chopcal.lib¶
Everything chopper-lib exposes: discs, the bands they pass, the grid mask, and the exact transmitted region.
The classes here are mostly nanobind bindings onto the C, so they are the same objects a McStas instrument works with rather than a Python reimplementation of them.
Describing a disc¶
Chopper
¶
One disk chopper: how fast it turns, where its openings are, and how far away.
The disk is described the way the NeXus NXdisk_chopper standard and McStas'
CollectorDiskChopper describe one. edges are the slit edges in degrees from
the disk's own zero mark, two per opening, strictly increasing, opening edge
first; beam is the angle from that mark to where the beam crosses the disk.
An edge at angle a is on the beam at
t(a) = delay + (beam - a) / (360 * speed)
and every 1/|speed| seconds after that. Only beam - a appears, so a caller can
hand over the disk's own numbers with no conversion to do. A negative speed
turns the disk the other way, which does not move delay -- a delay is a time,
so it means the same thing in either direction -- but does swap which side of it
an opening arrives on.
This replaces the angle of chopper-lib 2.x, which could only describe one
opening centred on the beam. Chopper.centred still writes one of those.
init(self, speed: float = 0, delay: float = 0, beam: float = 0, edges: collections.abc.Sequence[float] = [], path: float = 0, aperture: float = 0) -> None
Methods:
-
__new__–Create and return a new object. See help(type) for accurate signature.
-
__repr__–Unambiguous, and in the units the fields are actually stored in.
-
__str__–The same disk, said out loud.
-
centred–centred(speed: float = 0, delay: float = 0, width: float = 0, path: float = 0, aperture: float = 0) -> chopcal._chopper_lib_impl.Chopper
-
parked_is_open–parked_is_open(self) -> bool
-
validate–validate(self) -> None
Attributes:
-
__doc__–str(object='') -> str
-
__module__–str(object='') -> str
-
aperture–Angular width of the beam where it crosses the disk, in degrees.
-
beam–From the disk's zero mark to where the beam crosses it, in degrees
-
delay–When the disk point at angle
beamis on the beam path, in seconds -
edges–Slit edges in degrees from the zero mark, two per opening, increasing.
-
opening–Total opening angle in degrees, summed over the openings
-
openings–The (opening, closing) edge pairs, in degrees from the disk's zero mark.
-
path–Flight path from the source to the disk, in metres
-
quantities–The fields as scipp variables, each carrying its own unit.
-
speed–Rotation frequency in Hz; negative turns the disk the other way
__doc__
class-attribute
¶
__doc__ = "One disk chopper: how fast it turns, where its openings are, and how far away.\n\nThe disk is described the way the NeXus `NXdisk_chopper` standard and McStas'\n`CollectorDiskChopper` describe one. `edges` are the slit edges in degrees from\nthe disk's own zero mark, two per opening, strictly increasing, opening edge\nfirst; `beam` is the angle from that mark to where the beam crosses the disk.\nAn edge at angle `a` is on the beam at\n\n t(a) = delay + (beam - a) / (360 * speed)\n\nand every 1/|speed| seconds after that. Only `beam - a` appears, so a caller can\nhand over the disk's own numbers with no conversion to do. A negative `speed`\nturns the disk the other way, which does not move `delay` -- a delay is a time,\nso it means the same thing in either direction -- but does swap which side of it\nan opening arrives on.\n\nThis replaces the `angle` of chopper-lib 2.x, which could only describe one\nopening centred on the beam. `Chopper.centred` still writes one of those."
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__module__
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
aperture
property
¶
Angular width of the beam where it crosses the disk, in degrees. Widens every window by half of it at each end; 0 is a point beam.
edges
property
¶
Slit edges in degrees from the zero mark, two per opening, increasing. A slit across the mark is written past 360 -- [350, 370], not [350, 10].
openings
property
¶
The (opening, closing) edge pairs, in degrees from the disk's zero mark.
quantities
property
¶
The fields as scipp variables, each carrying its own unit.
The attributes are plain numbers in fixed units -- Hz, seconds, degrees, metres --
and this is the same values with the units attached, so that whatever reads them
can convert rather than assume. Worth reaching for whenever the numbers are going
somewhere else: the table above prints delays in milliseconds because that is the
scale they live on, while chopper.delay is in seconds, and a scalar cannot be
read the wrong way round.
edges is a list rather than a single number, so it comes back as an array
variable over an edge dimension.
__new__
builtin
¶
Create and return a new object. See help(type) for accurate signature.
centred
¶
centred(speed: float = 0, delay: float = 0, width: float = 0, path: float = 0, aperture: float = 0) -> chopcal._chopper_lib_impl.Chopper
A disk of one opening width degrees across, centred on the beam.
Equivalent to beam=0 and edges=[-width/2, +width/2], which is what
chopper-lib built internally from the angle of its 2.x structure.
parked_is_open
method descriptor
¶
parked_is_open(self) -> bool
Whether this disk stands open on the beam when it is not turning.
speed is not read: a turning disk stands open once a period whatever its
angles are. It matters for a parked one, because the window and mask
functions leave a disk parked open out of the calculation -- no period,
so it constrains nothing -- while one parked shut is a beam stop and
empties the answer. An empty result does not say which happened; this does.
validate
method descriptor
¶
validate(self) -> None
Raise if the edges are not an increasing, even-length list, or the
aperture is negative. Called on construction; call it again after
assigning to edges, which cannot check itself.
beam_aperture
¶
beam_aperture(radius: float, slit_height: float, window_width: float, window_height: float, at: str = 'inner') -> float
The angular width of a beam where it crosses a disk, in degrees.
This is what Chopper.aperture wants. An opening is angular and a beam is not, so
a neutron crossing to one side of the beam centre meets an edge early or late; the
largest such angle belongs to the inner corners of the beam window, nearest the
spindle, where a given width subtends the most angle.
slit_height is the radial extent of the opening cut in the disk -- McStas
DiskChopper's yheight, which puts the beam centre at radius - yheight/2.
window_width and window_height describe the beam inside it, which is smaller.
All four lengths in metres; the answer is in degrees.
at is where across the window the angle is taken. 'inner', the default, is
the corner nearest the spindle: the last part of the beam an edge clears, and so
the edge of the region Region.transmit builds. 'centre' is the beam centre:
while an edge crosses the window the fraction it has passed grows linearly at the
rate the centre sets, which is the ramp Region.transmit_weighted models. Use
'centre' for the choppers handed to transmit_weighted; against McStas with the
real window geometry it fits a BIFROST train to within counting error, where
'inner' leaves the tails of the arrival profile 6-18% low.
ChopperSet
¶
Bases: dict
The choppers of an instrument, by name, in beam order.
A dict in every respect -- set['ps1'], set.values(), **set all behave
as usual -- that prints itself as a table rather than as six memory addresses.
delay is when the disk point at beam is on the beam path and open how
long the openings spend there each turn, so a single-opening disk centred on the beam
passes neutrons from delay - open/2 to delay + open/2, and again every
1/speed after that. Both are shown in milliseconds because that is the scale they
live on; the attributes themselves are in seconds.
aperture is how wide the beam is on the disk, in degrees about its spindle. It
widens every window by half of it at each end, so open understates the time a
real beam spends in the openings; zero is a point beam.
Use :attr:quantities to get them as scipp variables instead, which is the safer
thing to hand to anything else -- the units come along and cannot be misread.
Attributes:
-
quantities–Every chopper's fields as scipp variables, by name.
quantities
property
¶
Every chopper's fields as scipp variables, by name.
Fenced rather than indented because ['ps1']['delay'] is also valid Markdown
for a reference link, and the documentation build reads these docstrings.
What a train passes¶
wavelength_windows
¶
wavelength_windows(choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], wavelength_min: float = 0.0001, wavelength_max: float = 100.0, latest_emission: float = 0.003) -> list[tuple[float, float]]
Every wavelength band the train passes, in angstrom.
The band list rather than the envelope wavelength_limits returns, and the one to
reach for when there is more than one: an envelope spanning two bands also spans
the gap between them, which the train does not pass.
See wavelength_limits on why a band here may be narrower than it looks.
wavelength_limits
¶
wavelength_limits(choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], wavelength_min: float = 0.0001, wavelength_max: float = 100.0, latest_emission: float = 0.003) -> tuple[int, tuple[float, float]]
The same envelope in angstrom, as (count, (low, high)).
A count above one means the envelope also spans wavelengths the train does not
pass; wavelength_windows gives the bands themselves.
This and wavelength_windows are an over-approximation. chopper-lib works out
each disk's admissible inverse velocities letting the emission time range over the
whole pulse independently, then intersects those ranges, so it can report a band
that no single emission time actually delivers -- an intersection of projections
is a superset of the projection of the intersection. It is tight where the disks
leave wide, overlapping emission windows, which is the usual case and includes
every band an instrument is set up to pass. inverse_velocity_time_mask keeps the
two coordinates coupled and does not make this error; where the two disagree, the
mask is right.
inverse_velocity_windows
¶
inverse_velocity_windows(choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], inv_v_min: float = 2.527784135457727e-08, inv_v_max: float = 0.02527784135457727, latest_emission: float = 0.003) -> list[tuple[float, float]]
Every inverse velocity band the train passes, in s/m.
A disk parked open is left out, having no period to constrain anything; one
parked shut empties the list -- see Chopper.parked_is_open.
inverse_velocity_limits
¶
inverse_velocity_limits(choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], inv_v_min: float = 2.527784135457727e-08, inv_v_max: float = 0.02527784135457727, latest_emission: float = 0.003) -> tuple[int, tuple[float, float]]
The envelope of those bands, as (count, (low, high)) in s/m.
A count above one means the envelope also contains inverse velocities the train
does not pass; use inverse_velocity_windows for the bands themselves.
The exact transmitted region¶
Region
¶
The region of (inverse velocity, emission time) a chopper train transmits.
A union of convex polygons, and exact: a neutron emitted at inverse velocity a
and time t reaches path L at t + L*a, so a disc open on [lower, upper]
accepts lower <= t + L*a <= upper -- a slab between two parallel lines. A
train's acceptance is an intersection of unions of such slabs, and intersection
distributes over union, so the region is a union of convex pieces: one per
choice of which opening and which turn of each disc a neutron goes through.
This is what inverse_velocity_windows and inverse_velocity_time_mask
approximate, each in its own direction. The window functions project each disc
separately and intersect the projections, which can report a band no single
emission time delivers. The mask quantises, losing channels thinner than a bin
and keeping partly covered bins whole. This does neither.
Start from Region.rectangle, the region a source draws from, and transmit it
through the train.
init(self) -> None
An empty region, transmitting nothing.
Methods:
-
__bool__–bool(self) -> bool
-
__len__–len(self) -> int
-
__new__–Create and return a new object. See help(type) for accurate signature.
-
arrival_moments–The amount reaching
distancein each arrival-time bin, with the mean and spread -
arrivals–What a gridded source puts through the region, by arrival time at
distance. -
contains–contains(self, inverse_velocity: float, time: float) -> bool
-
coverage–The fraction of each (time, inverse velocity) cell the region covers.
-
from_wavelengths–A source rectangle given in angstrom rather than in inverse velocity.
-
integrate–How much of a gridded source the region transmits.
-
inverse_velocity_ranges–inverse_velocity_ranges(self) -> list[tuple[float, float]]
-
rectangle–rectangle(inverse_velocity_minimum: float, inverse_velocity_range: float, time_minimum: float, time_range: float) -> chopcal._chopper_lib_impl.Region
-
sampler–sampler(self, sampled_area: float) -> chopcal._chopper_lib_impl.RegionSampler
-
to_dict–The region as plain data, in the schema :meth:
Region.write_jsonwrites. -
transmit–transmit(self, choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], path_spreads: collections.abc.Sequence[float] = []) -> chopcal._chopper_lib_impl.Region
-
transmit_weighted–transmit_weighted(self, choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], groups: collections.abc.Sequence[int] = []) -> chopcal._chopper_lib_impl.WeightedRegion
-
wavelength_ranges–wavelength_ranges(self) -> list[tuple[float, float]]
-
write_json–Write the region to
pathas JSON, byte for byte what the McStas component writes.
Attributes:
-
__doc__–str(object='') -> str
-
__module__–str(object='') -> str
-
area–Total area in s^2/m. A plain sum: the pieces are disjoint, because
-
polygons–The convex pieces, as a list
__doc__
class-attribute
¶
__doc__ = "The region of (inverse velocity, emission time) a chopper train transmits.\n\nA union of convex polygons, and exact: a neutron emitted at inverse velocity `a`\nand time `t` reaches path `L` at `t + L*a`, so a disc open on [lower, upper]\naccepts `lower <= t + L*a <= upper` -- a slab between two parallel lines. A\ntrain's acceptance is an intersection of unions of such slabs, and intersection\ndistributes over union, so the region *is* a union of convex pieces: one per\nchoice of which opening and which turn of each disc a neutron goes through.\n\nThis is what `inverse_velocity_windows` and `inverse_velocity_time_mask`\napproximate, each in its own direction. The window functions project each disc\nseparately and intersect the projections, which can report a band no single\nemission time delivers. The mask quantises, losing channels thinner than a bin\nand keeping partly covered bins whole. This does neither.\n\nStart from `Region.rectangle`, the region a source draws from, and `transmit` it\nthrough the train."
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__module__
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
area
property
¶
Total area in s^2/m. A plain sum: the pieces are disjoint, because two turns of one disc cannot both pass the same neutron.
__new__
builtin
¶
Create and return a new object. See help(type) for accurate signature.
arrival_moments
¶
The amount reaching distance in each arrival-time bin, with the mean and spread
of its wavelength -- the numbers a wavelength lookup table stores.
The same integration as :meth:arrivals, summed over inverse velocity, but each
piece of the region contributes its own exact first and second moments rather than
its cell's centre. So the mean and spread are exact for the gridded source however
coarse its grid: coarsening loses only how well the grid describes the source.
signal, the edges, distance and period are as :meth:arrivals takes them.
Returns an :class:ArrivalMoments of three numpy arrays, one value per arrival bin.
arrivals
¶
What a gridded source puts through the region, by arrival time at distance.
A neutron emitted at inverse velocity a and time t reaches distance (m) at
t + distance * a. That shear keeps areas, so each cell's share of the region is
carried into arrival_times (s, bin edges) exactly: the density times the area
between two lines of constant arrival time. distance 0 gives the emission times.
Shaped (arrival time, inverse velocity), since the shear leaves inverse velocity
alone: sum over the last axis for the time profile at a monitor or the sample, or keep
it for the wavelength as well. With period (s), arrival times are folded into
[0, period) first, as a detector folds them into its frame -- give arrival_times
in that range.
signal is read as :meth:integrate reads it. One flight path for every neutron: a
guide's spread of paths smears arrivals, and is not included.
contains
method descriptor
¶
contains(self, inverse_velocity: float, time: float) -> bool
Whether a neutron emitted at this (inverse velocity, time) gets through
coverage
¶
The fraction of each (time, inverse velocity) cell the region covers.
Exact: each polygon is clipped to each cell it reaches, where
:func:inverse_velocity_time_mask counts a cell whole or not at all. Shaped
(time, inverse velocity), as the masks are; inverse_velocities (s/m) and times
(s, at the source) are bin edges. Needs numpy.
from_wavelengths
¶
A source rectangle given in angstrom rather than in inverse velocity.
Converted the way chopper-lib converts, so the bands that come back out of
:meth:Region.wavelength_ranges are on the same footing as the rectangle that went in.
integrate
¶
How much of a gridded source the region transmits.
signal is the amount in each (time, inverse velocity) cell -- a histogram of the
source, shaped as :meth:coverage is -- spread evenly over its cell. That reading is
what makes this exact rather than approximate in the cells the region only partly
covers, which :func:unmasked_probability counts whole.
inverse_velocity_ranges
method descriptor
¶
inverse_velocity_ranges(self) -> list[tuple[float, float]]
The bands the region covers in inverse velocity, sorted and merged, s/m.
The projection of the intersection, where inverse_velocity_windows gives
the intersection of the projections -- a superset of this one.
rectangle
¶
rectangle(inverse_velocity_minimum: float, inverse_velocity_range: float, time_minimum: float, time_range: float) -> chopcal._chopper_lib_impl.Region
The rectangle a source draws from: inverse velocities in s/m by emission times in s. Both ranges must be positive.
sampler
method descriptor
¶
sampler(self, sampled_area: float) -> chopcal._chopper_lib_impl.RegionSampler
A sampler over this region, drawing uniformly and never rejecting.
sampled_area is the area of the region the caller draws from -- normally
the area of the source rectangle this was transmitted from -- and sets the
acceptance.
to_dict
¶
The region as plain data, in the schema :meth:Region.write_json writes.
sampled is the source :class:Region this was transmitted from, which sets the
acceptance; without it that and sampled are None, exactly as the file has them.
transmit
method descriptor
¶
transmit(self, choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], path_spreads: collections.abc.Sequence[float] = []) -> chopcal._chopper_lib_impl.Region
What a train leaves of this region, as a new Region; this one is unchanged.
path_spreads is one extra flight path per chopper, in metres, for a guide
that is not a straight line. The deviation is in path, so its effect on an
arrival time is deviation * inverse_velocity -- larger for a slow neutron,
and nothing at all to the inverse velocity -- so it does not grow the region
evenly, it opens each slab into a wedge. It gives the support under that
uncertainty rather than a distribution over it, the same bargain
Chopper.aperture makes for the width of the beam.
transmit_weighted
method descriptor
¶
transmit_weighted(self, choppers: collections.abc.Sequence[chopcal._chopper_lib_impl.Chopper], groups: collections.abc.Sequence[int] = []) -> chopcal._chopper_lib_impl.WeightedRegion
What a train leaves of this region, weighted by how likely each neutron is to
get through, as a WeightedRegion; see that class. Give the choppers the
aperture beam_aperture(..., at='centre') returns: that sets the ramp.
wavelength_ranges
method descriptor
¶
wavelength_ranges(self) -> list[tuple[float, float]]
The same bands in angstrom, converted as chopper-lib converts them.
write_json
¶
Write the region to path as JSON, byte for byte what the McStas component writes.
sampled is the source :class:Region, of which only its single polygon is passed
down. :meth:Region.to_dict is the same thing without a file.
Polygon
¶
One convex piece of a transmitted region, in (inverse velocity, time).
A value, not a handle: it owns nothing and copies freely. Its vertices are the disc edges that bound it, at the precision of a double -- nothing here is sampled onto a grid.
Initialize self. See help(type(self)) for accurate signature.
Methods:
-
__len__–len(self) -> int
-
__new__–Create and return a new object. See help(type) for accurate signature.
-
contains–contains(self, inverse_velocity: float, time: float) -> bool
Attributes:
-
__doc__–str(object='') -> str
-
__module__–str(object='') -> str
-
area–Area in s^2/m -- phase space, not a count
-
vertices–(inverse velocity, time) pairs in order, s/m and s
__doc__
class-attribute
¶
__doc__ = 'One convex piece of a transmitted region, in (inverse velocity, time).\n\nA value, not a handle: it owns nothing and copies freely. Its vertices are the\ndisc edges that bound it, at the precision of a double -- nothing here is sampled\nonto a grid.'
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__module__
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__new__
builtin
¶
Create and return a new object. See help(type) for accurate signature.
contains
method descriptor
¶
contains(self, inverse_velocity: float, time: float) -> bool
Whether a point lies inside, its boundary counting as inside
RegionSampler
¶
Draws (inverse velocity, time) pairs uniformly from a transmitted region.
The same job as MaskSampler and the same shape -- three uniform deviates, one
binary search, never rejects -- over the exact region rather than a grid
approximation of it. Each polygon is fanned into triangles from its first vertex,
so count here is triangles, not polygons.
init(self, region: chopcal._chopper_lib_impl.Region, sampled_area: float) -> None
Methods:
-
__new__–Create and return a new object. See help(type) for accurate signature.
-
draw–draw(self, triangle_deviate: float, first_deviate: float, second_deviate: float) -> tuple[float, float]
-
draw_many–draw_many(self, triangle_deviates: ndarray[dtype=float64, shape=(), order='C', writable=False], first_deviates: ndarray[dtype=float64, shape=(), order='C', writable=False], second_deviates: ndarray[dtype=float64, shape=(*), order='C', writable=False]) -> tuple[list[float], list[float]]
-
sample–count(inverse_velocity, time) pairs, as two numpy arrays.
Attributes:
-
__doc__–str(object='') -> str
-
__module__–str(object='') -> str
-
acceptance–The factor every drawn ray's weight must be multiplied by.
-
count–Triangles this draws from; 0 means nothing to draw
__doc__
class-attribute
¶
__doc__ = 'Draws (inverse velocity, time) pairs uniformly from a transmitted region.\n\nThe same job as `MaskSampler` and the same shape -- three uniform deviates, one\nbinary search, never rejects -- over the exact region rather than a grid\napproximation of it. Each polygon is fanned into triangles from its first vertex,\nso `count` here is triangles, not polygons.'
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__module__
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
acceptance
property
¶
The factor every drawn ray's weight must be multiplied by.
Exact: the transmitted area over the sampled area, both in closed form
rather than counted in cells. MaskSampler.acceptance estimates the same
number on a grid and can only over-estimate it, since a partly covered
cell is weighted whole.
Everything the note on MaskSampler.acceptance says about independence
still applies: the correction is right only while both coordinates are
drawn uniformly and independently of each other and of everything else.
__new__
builtin
¶
Create and return a new object. See help(type) for accurate signature.
draw
method descriptor
¶
draw(self, triangle_deviate: float, first_deviate: float, second_deviate: float) -> tuple[float, float]
One (inverse_velocity, time) pair from three uniform deviates on [0, 1).
draw_many
method descriptor
¶
draw_many(self, triangle_deviates: ndarray[dtype=float64, shape=(), order='C', writable=False], first_deviates: ndarray[dtype=float64, shape=(), order='C', writable=False], second_deviates: ndarray[dtype=float64, shape=(*), order='C', writable=False]) -> tuple[list[float], list[float]]
The same draw over three equal-length arrays, returning two lists.
sample
¶
count (inverse_velocity, time) pairs, as two numpy arrays.
The compiled draw and draw_many take their uniform deviates as arguments, so
that chopper-lib needs no generator of its own and a McStas TRACE can hand over
rand01(). This is the convenience for everything else: it draws the deviates from
generator -- numpy.random.default_rng() if none is given -- and calls
draw_many.
The caller still owes every drawn ray's weight a factor of sampler.acceptance.
WeightedRegion
¶
The region a chopper train transmits, weighted by how likely a neutron is to get through.
With a disc aperture, a Region's boundary is where transmission falls to zero.
Inside, an edge sweeping a uniformly lit beam ramps the transmission linearly, and
this carries that ramp: the same convex polygons, each with a product of affine
factors. Discs in one group -- a pair sharing a housing -- are crossed at the same
place across the beam, so the group passes the part of the beam open in all of
them: the smaller transmission for a co-rotating pair, and for a counter-rotating
pair a ramp starting at the half-way time and twice as steep. Different groups
multiply. Pieces may overlap where two openings of one disc ramp at once, and their
transmissions add.
Make one with Region.transmit_weighted.
Initialize self. See help(type(self)) for accurate signature.
Methods:
-
__bool__–bool(self) -> bool
-
__len__–len(self) -> int
-
__new__–Create and return a new object. See help(type) for accurate signature.
-
arrival_moments–The amount reaching
distancein each arrival-time bin, with the mean and spread -
arrivals–What a gridded source puts through the region, by arrival time at
distance. -
coverage–The fraction of each (time, inverse velocity) cell the region covers.
-
integrate–How much of a gridded source the region transmits.
-
transmission–transmission(self, inverse_velocity: float, time: float) -> float
Attributes:
-
__doc__–str(object='') -> str
-
__module__–str(object='') -> str
-
integral–The integral of the transmission, s^2/m: the area a uniformly drawn ray
-
pieces–Each piece as (Polygon, factors), a factor (a, b, c) being
__doc__
class-attribute
¶
__doc__ = "The region a chopper train transmits, weighted by how likely a neutron is to get\nthrough.\n\nWith a disc `aperture`, a Region's boundary is where transmission falls to zero.\nInside, an edge sweeping a uniformly lit beam ramps the transmission linearly, and\nthis carries that ramp: the same convex polygons, each with a product of affine\nfactors. Discs in one *group* -- a pair sharing a housing -- are crossed at the same\nplace across the beam, so the group passes the part of the beam open in all of\nthem: the smaller transmission for a co-rotating pair, and for a counter-rotating\npair a ramp starting at the half-way time and twice as steep. Different groups\nmultiply. Pieces may overlap where two openings of one disc ramp at once, and their\ntransmissions add.\n\nMake one with `Region.transmit_weighted`."
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
__module__
class-attribute
¶
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.
integral
property
¶
The integral of the transmission, s^2/m: the area a uniformly drawn ray budget effectively passes. Exact.
pieces
property
¶
Each piece as (Polygon, factors), a factor (a, b, c) being ainverse_velocity + btime + c; the transmission is their product.
__new__
builtin
¶
Create and return a new object. See help(type) for accurate signature.
arrival_moments
¶
The amount reaching distance in each arrival-time bin, with the mean and spread
of its wavelength -- the numbers a wavelength lookup table stores.
The same integration as :meth:arrivals, summed over inverse velocity, but each
piece of the region contributes its own exact first and second moments rather than
its cell's centre. So the mean and spread are exact for the gridded source however
coarse its grid: coarsening loses only how well the grid describes the source.
signal, the edges, distance and period are as :meth:arrivals takes them.
Returns an :class:ArrivalMoments of three numpy arrays, one value per arrival bin.
arrivals
¶
What a gridded source puts through the region, by arrival time at distance.
A neutron emitted at inverse velocity a and time t reaches distance (m) at
t + distance * a. That shear keeps areas, so each cell's share of the region is
carried into arrival_times (s, bin edges) exactly: the density times the area
between two lines of constant arrival time. distance 0 gives the emission times.
Shaped (arrival time, inverse velocity), since the shear leaves inverse velocity
alone: sum over the last axis for the time profile at a monitor or the sample, or keep
it for the wavelength as well. With period (s), arrival times are folded into
[0, period) first, as a detector folds them into its frame -- give arrival_times
in that range.
signal is read as :meth:integrate reads it. One flight path for every neutron: a
guide's spread of paths smears arrivals, and is not included.
coverage
¶
The fraction of each (time, inverse velocity) cell the region covers.
Exact: each polygon is clipped to each cell it reaches, where
:func:inverse_velocity_time_mask counts a cell whole or not at all. Shaped
(time, inverse velocity), as the masks are; inverse_velocities (s/m) and times
(s, at the source) are bin edges. Needs numpy.
integrate
¶
How much of a gridded source the region transmits.
signal is the amount in each (time, inverse velocity) cell -- a histogram of the
source, shaped as :meth:coverage is -- spread evenly over its cell. That reading is
what makes this exact rather than approximate in the cells the region only partly
covers, which :func:unmasked_probability counts whole.
transmission
method descriptor
¶
transmission(self, inverse_velocity: float, time: float) -> float
The probability a neutron emitted at this (inverse velocity, time) gets through
ArrivalMoments
¶
Bases: NamedTuple
What reaches a distance in each arrival-time bin: how much, and at what wavelength.
amount is in the units of the signal; wavelength and sigma are the mean and
standard deviation of wavelength over it, in angstrom, NaN where nothing arrives.
On a grid¶
inverse_velocity_time_mask
¶
Which (inverse velocity, time) bins a chopper train passes.
inverse_velocities (s/m) and times (s, at the source) are bin edges, so the
mask is one smaller in each direction. It comes back shaped
(time, inverse velocity) -- time is the slow axis, which is how the C stores it
and the opposite of the argument order.
grow expands each allowed region by that many bins in every direction. It is a
blunt instrument: it admits inverse velocities no disk ever passes. Prefer
:attr:Chopper.aperture for a beam of finite width, which opens the windows in time
only, where the width actually acts.
Returns (mask, allowed_bin_count). Needs numpy.
unmasked_probability
¶
The fraction of signal lying in the allowed bins of mask.
Both are shaped (time, inverse velocity), as inverse_velocity_time_mask returns.
This is the transmission an instrument sees -- a weighted fraction -- and is not
the weight correction a sampler needs; that is :attr:MaskSampler.acceptance, which
counts draws rather than intensity.
MaskSampler
¶
MaskSampler(mask, inverse_velocities, times, inverse_velocity_minimum, inverse_velocity_range, time_minimum, time_range)
Bases: MaskSampler
Draws (inverse velocity, time) pairs from the allowed cells of a finished mask.
Sampling the whole region and discarding what the mask excludes spends the ray budget
to keep the fraction the choppers pass, which for a real chopper train is a fraction
of a percent. Drawing from the allowed cells keeps all of it and is the same
distribution, provided every drawn weight is multiplied by :attr:acceptance.
mask is shaped (time, inverse velocity) and inverse_velocities/times are its
bin edges. The four bounds describe the region the caller samples uniformly, which is
not in general the region the grid covers -- a grid sized with ceil runs past it
in the last row and column, and those cells are clipped before they are weighted so
that they do not inflate :attr:acceptance.
MaskValue
¶
Bases: IntEnum
What a cell of an inverse_velocity_time_mask holds.
A finished mask only ever holds EXCLUDED or INCLUDED -- GROWN is the marker the growing pass leaves behind and folds back into INCLUDED before it returns -- but it is written to file as it stands, so a mask read back from one can carry it.
Conversions¶
wavelength_to_inverse_velocity
¶
wavelength_to_inverse_velocity(wavelength: float) -> float
Angstrom to s/m, the way chopper-lib does it.
inverse_velocity_to_wavelength
¶
inverse_velocity_to_wavelength(inverse_velocity: float) -> float
s/m to angstrom, the way chopper-lib does it.