Working with Distributions
In rapyds a distribution is a field of value(s) over the (physical) phase space. They are used to store output from responses, and is also used to specify external sources. This section gives a general overview of the distribution interface, as well as the various distributions available in rapyds.
Instantiating Distributions
In general, a distribution simply models a functional \(f:R^m \to R\). Which components of the underlying phase
space the distribution covers is specified using the distribution.variables
parameter.
- Distribution.variables
The phase space variable(s) this distribution covers. It is specified by giving one or more (a list) of the following tokens:
Phase space variables. Symbol
Phase Space Component
x
The \(x\) coordinate in cartesian geometry.
y
The \(y\) coordinate in cartesian geometry.
z
The \(z\) coordinate in cartesian geometry.
r
The radial coordinate is cylindrical geometry.
theta
The polar angle coordinate in cylindrical geometry.
e
The energy component.
t
The time component.
The number of variables also determines the phase space dimension of the distribution.
-
Distribution.aggregate
: sum, mean, min, or max
= sum Determines how ranges, and variables not explicitly specified when inserting or retrieving data should be handled. For example, if
sum
is specified, the total over all dimensions perpendicular to specified variables will be used. The options can be described mathematically as follows:Let \(f\) denote the distribution of interest, and suppose its phase space is decomposed as \(\mathbf{A}\times\mathbf{B}\). Then, a query specifying only variable values in \(\mathbf{A}\) will yield the following:
If
Distribution.aggregate
issum
:\[q(\mathbf{a}=\mathbf{a}_0) = \int_{\mathbf{B}}d\mathbf{b}\, f(\mathbf{a}=\mathbf{a}_0, \mathbf{b})\]If
Distribution.aggregate
isaverage
:\[q(\mathbf{a}=\mathbf{a}_0) =\frac{1}{|\mathbf{B}|} \int_{\mathbf{B}}d\mathbf{b}\, f(\mathbf{a}=\mathbf{a}_0, \mathbf{b})\]If
Distribution.aggregate
ismin
:\[q(\mathbf{a}=\mathbf{a}_0) = \min_{\mathbf{b}\in\mathbf{B}}f(\mathbf{a}=\mathbf{a}_0, \mathbf{b})\]If
Distribution.aggregate
ismax
:\[q(\mathbf{a}=\mathbf{a}_0) = \max_{\mathbf{b}\in\mathbf{B}}f(\mathbf{a}=\mathbf{a}_0, \mathbf{b})\]
Accessing Distribution Values
- Distribution.get_value(**query)
Retrieve a value from the distribution.
- Parameters:
query –
The phase space coordinates at which a value from the distribution should be retrieved. Takes the form of keyword value pairs, with keys listed in phase space tokens, followed by the corresponding phase space value.
Instead of a value, a range can also be specified, by passing the keywords
<v>_min
andor<v>_max
(here,<v>
must be replaced by one of the phase space tokens).
For example, if
dist
is a distribution covering the \(x\) coordinate and energy domains, then, assumingdist.aggregate='sum'
:Query examples. Syntax
Description
dist.get_value(x=5 * units.cm, e=10 * units.eV)
Retrieve the value at \(x\) equal to 5 cm and energy 10 eV
dist.get_value(x=5 * units.cm, e_max=100 * units.eV)
Retrieve the value at \(x\) equal to 5 cm, integrated over all energies less than 100 eV
dist.get_value(x=5 * units.cm, e_min=10 * units.eV, e_max=100 * units.eV)
Retrieve the value at \(x\) equal to 5 cm, integrated over all energies less than 100 eV, and greater than 10 eV
dist.get_value(x=5 * units.cm)
Retrieve the value at \(x\) equal to 5 cm, integrated over the energy domain
dist.get_value()
The integral of the distribution over its entire domain
Note
How range and unspecified values are treated, is determined by the
Distribution.aggregate
parameter.
- Distribution.set_value(value, **at)
Insert a value into the distribution at a particular point.
- Parameters:
value – Value to insert (or score).
at – The phase space coordinates at which a value should be inserted. Takes the form of keyword value pairs, with keys listed in phase space tokens, followed by the corresponding phase space value.
When the phase space point values are integers, this call will set the nodes or moments defining the distribution. How the insertion at not discrete points are handled, depends on the distribution type, and/or the interpolation rules used.
Attention
Unlike
Distribution.get_value()
, all phase space variables must be specified, and ranges are not allowed.
- Distribution.slice(coord, **query)
Produces a slice through the distribution along a specified coordinate in phase space.
- Parameters:
coord – The phase space token along which the slice should be taken.
query – Specify how the other variables should be treated. Similar syntax as used in
Distribution.get_value()
.
- Returns:
Two lists, the first a set op points in the variable coord, and the second the distribution values along these points, as determined by query and
Distribution.aggregate
.
Attention
The variable specified with coord can not also appear in query.
Note
The return value is suitable for use as the data value when using the plotting directive.
Distribution Containers
This section lists the distribution types available in the distributions
module.
- class distributions.Flat(**kwargs)
Represents a trivial distribution (e.g. without any shape), over the phase space region.
- class distributions.Discrete(**kwargs)
Represents a piece-wise constant one dimensional distribution. Its domain is represented by a sequence of adjacent (but disjoint) intervals or bins.
- Parameters:
kwargs – Can be used to (optionally) initiate any of the distribution’s parameters (e.g.
Distribution.variables
,Distribution.aggregate
orDiscrete.bins
).
Apart from the standard distribution parameters, distribution.Discrete
has
the following extra parameters:
- Discrete.bins
The bin structure. Can be specified as a monotonically increasing list of bin bounds, or, for equal sized bins, in the form
(v_min, v_max, n_bins)
, wherev_min, v_max
is the minimum and maximum value respectively, andn_bins
the number of bins.Attention
When specifying the bin bounds explicitly, the upper and lower bounds must also be given. Thus, for \(n\) bins, there should be \(n+1\) bounds.
For example, the following will create a discrete energy distribution with two bins:
d = distributions.Discrete(variables='e') d.bins = [1.0E-11 * units.MeV, 6.25E-7 * units.MeV, 20.0 * units.MeV]
When calling
Distribution.get_value()
orDistribution.set_value()
on a Discrete distribution, bin index values can also be passed. For example, in the above distributiond.get_value(e=19 * units.MeV) == d.get_value(e=1) True
Note
For equal sized bins, it is recommend that the
(v_min, v_max, n_bins)
is used, as certain plugins may only support this option.
- class distributions.Interpolated(**kwargs)
Represents a continuous one dimensional distribution, anchored at a discrete set of nodes in phase space, and using a specified interpolation rule.
Apart from the standard distribution parameters, distribution.Interpolated
has
the following extra parameters:
- Interpolated.nodes
The points in phase space at which the distribution is anchored. Similar to
Discrete.bins
, with the same syntax.
-
Interpolated.interpolation
: linear, quadratic or cubic
How values in between
Interpolated.nodes
are deduced. Either piecewise linear, quadratic or cubic splines.
- class distributions.Tensor(*dist, **kwargs)
Create a distribution over a multi-dimensional phase space by combining separate distributions, using an outer (or tensor) product approach.
- Parameters:
dist – A sequence of Distributions that will be added to the outer product.
kwargs – Specify the distributions to combine using their phase space tokens as keys. This pattern can only be used if each distribution is one dimensional (see the examples below).
When using discrete (bin) distributions (such as
distributions.Discrete
), then the tensor effectively subdivides each bin with another bin structure from a different portion of phase space.The domain of the tensor can be described mathematically as follows:
If \(f_i:A_i\to R\), \(i=1,\ldots,n\) are distributions on phase space \(A_i\), then the tensor \(T\) is a distribution on the direct sum \(A_1\oplus\ldots\oplus A_n\). In particular, the individual phase spaces \(A_i\) must be perpendicular, that is, the
Distribution.variables
of each of the distributions specified using dist (or kwargs) must be disjoint.For example, the following will create three energy bins, and further subdivide each energy bin into 10 spatial bins:
d = Tensor(Discrete(variables='e', bins=[1.0E-11 * units.MeV, 6.25E-7 * units.MeV, 20.0 * units.MeV]), Discrete(variables='x', bins=(-1.0 * units.cm, 1.0 * units.cm, 10)))
The above can also be produced using the following equivalent keyword based construction:
d = Tensor(e=Discrete(bins=[1.0E-11 * units.MeV, 6.25E-7 * units.MeV, 20.0 * units.MeV]), x=Discrete(bins=(-1.0 * units.cm, 1.0 * units.cm, 10)))
Attention
The
Distribution.variables
in this case is deduced automatically, and should not be set explicitly.
- distributions.cartesian_mesh(x=None, y=None, z=None, transformation=None)
Create a rectangular mesh distribution. This is a convenience function that creates a special
distributions.Tensor
instance usingdistributions.Discrete
instances.- Parameters:
All the ranges (x, y or z), can be specified as an explicit list of bounds, or a simpler notation for equal sized bins (see
Discrete.bins
). If bounds are not specified along a particular coordinate direction, the mesh will be unbounded in this coordinate.For example, the following,
m = distributions.cartesian_mesh(x=(-1.0 * units.cm, 1.0 * units.cm, 10), y=[0.0 * units.cm, 1.0 * units.cm, 2.0 * units.cm], z=(-10 * units.cm, 0 * units.cm, 10))
is equivalent to
m = distribution.Tensor(x=distributions.Discrete(bins=(-1.0 * units.cm, 1.0 * units.cm, 10)), y=distributions.Discrete(bins=[0.0 * units.cm, 1.0 * units.cm, 2.0 * units.cm]), z=distributions.Discrete(bins=(-10 * units.cm, 0 * units.cm, 10)))
- distributions.cylindrical_mesh(r, theta=None, axial=None, alignment='z', center=(0, 0, 0), transformation=None)
Create a cylindrical mesh distribution.
- Parameters:
theta (list or tuple) – The polar angle bin structure. If not specified, no angular bins will be created.
axial (list or tuple) – The bin structure along the coordinate axis perpendicular to the radial plane. This coordinate is determined by the alignment parameter.
alignment (string) –
Determines how the cylindrical mesh is aligned in the cartesian coordinate system. It can be specified by either giving the coordinate along which the axial mesh extend, or the radial plane:
Cylindrical mesh alignment options. Option
Description
’z’ or ‘xy’
The axial mesh extends along the \(z\)-axis, and the radial plane is \(xy\).
’x’ or ‘yz’
The axial mesh extends along the \(x\)-axis, and the radial plane is \(yz\).
’y’ or ‘xz’
The axial mesh extends along the \(y\)-axis, and the radial plane is \(xz\).
center (point) – Where the cylindrical mesh is centered in the radial plane.
transformation (affine_transformation) – Optional transformation that should be applied to the mesh.
All the ranges (r, theta or axial), can be specified as an explicit list of bounds, or a simpler notation for equal sized bins (see
Discrete.bins
).Note
Although center contains all three coordinate, only the values for the coordinates on the radial plane is used. For example, if alignment is z, then only the first two (\(x\) and \(y\) coordinates of center is used).
In the cylindrical mesh, a call to
Distribution.get_value()
can use either the polar r and theta keywords to specify radial mesh positions, or the standard cartesian coordinates x, y, and z.
- class distributions.Distributed(dist=None, sub_tag='bin')
Models a distribution with components spread over discrete categories. Typical examples are distributions in different in-core channels, or ex-core positions.
Note
This distribution is created automatically when using certain targets (e.g.
attached.InCoreChannels
). It is rarely necessary to instantiate, or modify this distribution type.- Parameters:
dist – The distribution that will be used in sub categories. If not specified, the distribution might differ from one category to the next.
sub_tag – The tag (or key) that will be used to specify the sub category in queries, e.g.
Distribution.get_value()
.
For example, if the categories are core loading positions, a typical query from this distribution has the following form:
v = dist.get_value(bin='CN', x=9.0 * units.cm, e=0.6 * units.eV)
Attaching Distributions
Distributions are usually attached to responses, in which case they are expected to be calculated, or sources, where
they describe how particles are produced. In both cases, the distribution can be considered overlay (that is, all model
elements are affected), or attached to particular model element(s), effectively restricting the response or source to
these element(s). The attached
module contains some objects which can be used to dynamically find targets
based on certain rules. These are summarized below.
- class attached.InCore(predicate=None)
Restricted to geometric elements (cells) in the
model.reactor_core
.- Parameters:
predicate (predicate or None) – Function object used to extract cells. Must take a cell instance as argument, and return True or False. If not specified, it is assumed that all cells in
model.reactor_core
are targeted.
For example, to target all cells with a particular part name:
tg = InCore(predicate=lambda c: c.part=='TargetPart')
Note
If
model.pool
is set and predicate is None, it will targetmodel.pool.core_cell
.
- class attached.InCoreChannel(channel, predicate=None)
Restricted to geometric elements (cells) within a specified position in the
model.core_map
.- Parameters:
channel – Position in the
model.core_map
to target.predicate (predicate or None) – Function object used to extract cells. Must take a cell instance as argument, and return True or False. If not specified, the entire
component
loaded in channel is assumed.
- class attached.InCoreChannels(predicate=None)
Targets all channels (or positions) within the
model.core_map
.- Parameters:
predicate – Function object used to extract cells. Must take a cell instance as argument, and return True or False. If not specified, the entire
component
loaded in each channel is assumed.
When this target is used, a
distributions.Distributed
is created automatically, with the bin values equal to the position labels.Note
If predicate is set, only the positions in
model.core_map
for which predicate return a non empty list of cells, will be present in the createddistributions.Distributed
.
Creating Responses
This section describes how to attach a distribution to a response function, which is then used to trigger the calculation of these distributions in certain application modes (e.g. critical_case). A response is created by instantiating a response.ResponseDistribution object:
from core.response import ResponseDistribution
resp = ResponseDistribution()
- ResponseDistribution.particle = neutron
The incident particle type this response is bound to.
- ResponseDistribution.to
Object used to deduce the model target(s) this response is attached to. Any of the objects listed in Attaching Distributions.
Note
If not specified, the
ResponseDistribution.distribution
will be estimated over the entire model (as an overlay).
-
ResponseDistribution.distribution
: Distribution
The function shape of this response. Can be any of the response containers listed in Distribution Containers.
Note
If not specified, a
distributions.Flat
distribution is assumed.
Creating Sources
A distribution can also be attached to a source, which generates particles for the