API reference#
phasespace#
phasespace.phasespace
#
Implementation of the Raubold and Lynch method to generate n-body events.
The code is based on the GENBOD function (W515 from CERNLIB), documented in:
F. James, Monte Carlo Phase Space, CERN 68-15 (1968)
GenParticle
#
Representation of a particle.
Instances of this class can be combined with each other to build decay chains,
which can then be used to generate phase space events through the generate
method.
A GenParticle must have
- a name, which is ensured not to clash with any others in
the decay chain.
- a mass, which can be either a number or a function to generate it according to
a certain distribution. The returned jax.Array needs to have shape (nevents,).
In this case, the particle is not considered as having a
fixed mass and the has_fixed_mass method will return False.
It may also have:
- Children, ie, decay products, which are also ``GenParticle`` instances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of the particle. |
required |
mass
|
(float, array - like, callable)
|
Mass of the particle. If it's a float, it gets
converted to array-like. If it is a callable, it is called as
|
required |
has_fixed_mass
property
#
bool: Is the mass a callable function?
has_children
property
#
bool: Does the particle have children?
has_grandchildren
property
#
bool: Does the particle have grandchildren?
get_mass(min_mass=None, max_mass=None, n_events=None, key=None)
#
Get the particle mass.
If the particle is resonant, the mass function is called as
mass(min_mass, max_mass, n_events, key).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
min_mass
|
array
|
Lower mass range. Defaults to None, which is only valid in the case of fixed mass. |
None
|
max_mass
|
array
|
Upper mass range. Defaults to None, which is only valid in the case of fixed mass. |
None
|
n_events
|
int
|
Number of events to produce. Has to be specified if the particle is resonant. |
None
|
key
|
``jax.Array``
|
JAX PRNG key, handed to the mass function. Has to be specified if the particle is resonant. |
None
|
Returns:
| Type | Description |
|---|---|
Array
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the mass is requested and has not been set. |
set_children(*children)
#
Assign children.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
children
|
GenParticle
|
Two or more children to assign to the current particle. |
()
|
Returns:
| Type | Description |
|---|---|
|
self |
Raises:
| Type | Description |
|---|---|
ValueError
|
If there is an inconsistency in the parent/children relationship, ie, |
KeyError
|
If there is a particle name clash. |
RuntimeError
|
If |
generate(n_events, boost_to=None, normalize_weights=True, key=None, *, as_vectors=None, chunk_size=None)
#
Generate normalized n-body phase space as JAX arrays.
Events are generated in the rest frame of the particle, unless boost_to is given.
Notes
In this method, the event weights are returned normalized to their maximum.
The generation is jit-compiled with n_events as a static argument: calling this
with a new value of n_events triggers a recompilation, while repeated calls with
the same value reuse the compiled function.
Chunking changes which events are drawn, as every chunk consumes its own split of
key: generate(n, key=k) and generate(n, key=k, chunk_size=c) give different
but equally valid samples, each of them reproducible. It bounds the memory of the
generation itself, not of the returned arrays, and keeps the number of compilations at
two (a full chunk and the remainder) whatever n_events is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_events
|
int
|
Number of events to generate. |
required |
boost_to
|
optional
|
Momentum vector of shape |
None
|
normalize_weights
|
bool
|
Normalize the event weight to its max? |
True
|
key
|
``KeyLike``
|
Either an integer seed, a JAX PRNG key or None, in which case a new key is created from OS entropy (and the generation is not reproducible). |
None
|
as_vectors
|
bool
|
If True, the output momenta are returned as |
None
|
chunk_size
|
int
|
Generate the events in chunks of at most this many rather than all at once, which bounds the peak memory of the generation. Defaults to None, which generates everything in one go. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
tuple[Array, dict[str, Array]] | tuple[Array, Array, dict[str, Array]]
|
Result of the generation, which varies with the value of
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the decay is kinematically forbidden, if |
nbody_decay(mass_top, masses, top_name='', names=None)
#
Shortcut to build an n-body decay of a GenParticle.
If the particle names are not given, the top particle is called 'top' and the
children 'p_{i}', where i corresponds to their position in the masses sequence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass_top
|
(array, list)
|
Mass of the top particle. Can be a list of 4-vectors. |
required |
masses
|
list
|
Masses of the child particles. |
required |
top_name
|
str
|
Name of the top particle. If not given, the top particle is named top. |
''
|
names
|
list
|
Names of the child particles. If not given, they are build as
'p_{i}', where i is given by their ordering in the |
None
|
Returns:
| Type | Description |
|---|---|
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the length of |
to_vectors(particles)
#
Convert a dictionary of particles to a dictionary of vector.Momentum instances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
particles
|
dict
|
Dictionary of particles, with the keys being the particle names and the values being the momenta. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Momentum]
|
Dictionary of |
phasespace.kinematics#
phasespace.kinematics
#
Basic kinematics.
scalar_product(vec1, vec2)
#
Calculate scalar product of two 3-vectors.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vec1
|
First vector. |
required | |
vec2
|
Second vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Scalar product of the two vectors. |
spatial_component(vector)
#
Extract spatial components of the input Lorentz vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input Lorentz vector (where indexes 0-2 are space, index 3 is time). |
required |
Returns:
| Type | Description |
|---|---|
|
Spatial components (3-vector) of the input Lorentz vector. |
time_component(vector)
#
Extract time component of the input Lorentz vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input Lorentz vector (where indexes 0-2 are space, index 3 is time). |
required |
Returns:
| Type | Description |
|---|---|
|
Time component of the input Lorentz vector. |
x_component(vector)
#
Extract spatial X component of the input Lorentz or 3-vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input vector. |
required |
Returns:
| Type | Description |
|---|---|
|
X component of the input vector. |
y_component(vector)
#
Extract spatial Y component of the input Lorentz or 3-vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Y component of the input vector. |
z_component(vector)
#
Extract spatial Z component of the input Lorentz or 3-vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Z component of the input vector. |
mass(vector)
#
Calculate mass scalar for Lorentz 4-momentum.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input Lorentz momentum vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Mass of the Lorentz 4-momentum vector. |
lorentz_vector(space, time)
#
Make a Lorentz vector from spatial and time components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
space
|
3-vector of spatial components. |
required | |
time
|
Time component. |
required |
Returns:
| Type | Description |
|---|---|
|
Lorentz 4-vector combining spatial and time components. |
lorentz_boost(vector, boostvector)
#
Perform Lorentz boost.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
4-vector to be boosted |
required | |
boostvector
|
Boost vector. Can be either 3-vector or 4-vector, since only spatial components are used. |
required |
Returns:
| Type | Description |
|---|---|
|
Boosted 4-vector. |
beta(vector)
#
Calculate beta of a given 4-vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input Lorentz momentum vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Beta (v/c) of the Lorentz momentum vector. |
boost_components(vector)
#
Get the boost components of a given 4-vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Input Lorentz momentum vector. |
required |
Returns:
| Type | Description |
|---|---|
|
Boost components (3-vector) of the Lorentz momentum vector. |
metric_tensor()
#
Metric tensor for Lorentz space (constant).
Returns:
| Type | Description |
|---|---|
|
Metric tensor for Lorentz space with signature (-1, -1, -1, 1). |
phasespace.random#
phasespace.random
#
Random number generation.
JAX random number generation is purely functional: every draw is an explicit function of a PRNG key. This module only normalizes what users may pass as a key.
ensure_key(key=None)
#
Normalize a user-supplied key into a JAX PRNG key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
KeyLike
|
This can be
- |
None
|
Returns:
| Type | Description |
|---|---|
Array
|
A JAX PRNG key. |
Notes
Never call this with None inside a jitted function: the key would be created once at
trace time and every call would then reuse the very same random numbers.
phasespace.precision#
phasespace.precision
#
Double precision handling.
The phase space computation asks for float64 explicitly everywhere, but JAX only honours that
while its x64 mode is enabled: with the mode off, every request is truncated to float32 and
warns once per call. Single precision is not an option here, as pdk suffers catastrophic
cancellation close to threshold, which degrades energy-momentum conservation from ~1e-15 to ~1e-6
relative.
The mode is therefore enabled per call rather than at import time, which keeps the dtype defaults of the calling program untouched.
with_float64(func)
#
Run func with JAX's double precision mode enabled.
Enabling the mode is scoped to the call, so it does not change the dtype defaults of the
calling program. The returned arrays are float64.
phasespace.fromdecay#
phasespace.fromdecay.genmultidecay
#
GenMultiDecay
#
__init__(gen_particles)
#
A GenParticle-type container that can handle multiple decays.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gen_particles
|
list[tuple[float, GenParticle]]
|
All the GenParticles and their corresponding probabilities. The list must be of the format [[probability, GenParticle instance], [probability, ... |
required |
from_dict(dec_dict, mass_converter=None, tolerance=None, particle_model_map=None)
classmethod
#
Create a GenMultiDecay instance from a dict in the DecayLanguage package format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dec_dict
|
dict
|
The input dict from which the GenMultiDecay object will be created from. A dec_dict has the same structure as the dicts used in DecayLanguage, see the examples below. |
required |
mass_converter
|
dict[str, Callable] | None
|
A dict with mass function names and their corresponding mass functions. These functions should take the particle mass and the mass width as inputs and return a mass function that phasespace can understand. This dict will be combined with the predefined mass functions in this package. See the Example below or the tutorial for how to use this parameter. |
None
|
tolerance
|
float | None
|
Minimum mass width of the particle to use a mass function instead of
assuming the mass to be constant. If None, the default value, defined by the class variable
|
None
|
particle_model_map
|
dict[str, str] | None
|
A dict where the key is a particle name and the value is a mass function name. If a particle is specified in the particle_model_map, then all appearances of this particle in dec_dict will get the same mass function. This way, one doesn't have to manually add the zfit parameter to every place where this particle appears in dec_dict. If the zfit parameter is specified for a particle which is also included in particle_model_map, the zfit parameter mass function name will be prioritized. |
None
|
Returns:
| Type | Description |
|---|---|
|
The created GenMultiDecay object. |
Examples:
Basic Usage
DecayLanguage is usually used to create a dict that can be understood by GenMultiDecay:
.. code-block:: python
from decaylanguage import DecFileParser
from phasespace.fromdecay import GenMultiDecay
# Parse a .dec file to create a DecayLanguage dict describing a D*+ particle
# that can decay in 2 different ways: D*+ -> D0(->K- pi+) pi+ or D*+ -> D+ gamma
parser = DecFileParser('example_decays.dec')
parser.parse()
dst_chain = parser.build_decay_chains("D*+")
# dst_chain will be:
# {'D*+': [{'bf': 0.984,
# 'fs': [{'D0': [{'bf': 1.0,
# 'fs': ['K-', 'pi+']}]},
# 'pi+']},
# {'bf': 0.016,
# 'fs': ['D+', 'gamma']}]}
Using particle_model_map
If the D0 particle should have a mass distribution of a gaussian when it decays, one can pass
the particle_model_map parameter to from_dict:
.. code-block:: python
dst_gen = GenMultiDecay.from_dict(dst_chain, particle_model_map={"D0": "gauss"})
This will then set the mass function of D0 to a gaussian for all its decays.
Using the zfit parameter
If more custom control is required, e.g., if D0 can decay in multiple ways and one of the decays
should have a specific mass function, a zfit parameter can be added to its decay dict:
.. code-block:: python
dst_chain["D*+"][0]["fs"][0]["D0"][0]["zfit"] = "gauss"
dst_gen = GenMultiDecay.from_dict(dst_chain)
This will now make the D0 particle have a gaussian mass function, only when it decays into
K- and pi+. In this case, there are no other ways that D0 can decay, so using
particle_model_map is a cleaner and easier option.
Custom Mass Functions
If the decay of the D0 particle should be modelled by a mass distribution that does not come with the package, a custom distribution can be created:
.. code-block:: python
def custom_gauss(mass, width):
def mass_func(min_mass, max_mass, n_events, key):
# a normal distribution truncated to the kinematic limits
standard = jax.random.truncated_normal(
key,
lower=(min_mass - mass) / width,
upper=(max_mass - mass) / width,
shape=(n_events,),
dtype=jnp.float64,
)
return mass + width * standard
return mass_func
# Change the distribution in the dst_chain dict
dst_chain["D*+"][0]["fs"][0]["D0"][0]["zfit"] = "custom_gauss"
# Pass the custom_gauss function to from_dict
dst_gen = GenMultiDecay.from_dict(dst_chain, mass_converter={"custom_gauss": custom_gauss})
Notes
For a more in-depth tutorial, see the tutorial on GenMultiDecay in the
documentation <https://cirkiters.github.io/phasespace-jax/GenMultiDecay_Tutorial/>__.
generate(n_events, normalize_weights=True, key=None, **kwargs)
#
Generate four-momentum vectors from the decay(s).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_events
|
int
|
Total number of events combined, for all the decays. |
required |
normalize_weights
|
bool
|
Normalize weights according to all events generated. This also changes the return values. See the phasespace documentation for more details. |
True
|
key
|
KeyLike
|
Either an integer seed, a JAX PRNG key or None, in which case a new key is created from OS entropy (and the generation is not reproducible). |
None
|
kwargs
|
Additional parameters passed to all calls of |
{}
|
Returns:
| Type | Description |
|---|---|
tuple[list[Array], list[Array]] | tuple[list[Array], list[Array], list[Array]]
|
The arguments returned by |
tuple[list[Array], list[Array]] | tuple[list[Array], list[Array], list[Array]]
|
details. However, instead of being 2 or 3 arrays, it is 2 or 3 lists of arrays, |
tuple[list[Array], list[Array]] | tuple[list[Array], list[Array], list[Array]]
|
each entry in the lists corresponding to the return arguments from the corresponding GenParticle |
tuple[list[Array], list[Array]] | tuple[list[Array], list[Array], list[Array]]
|
instances in |
tuple[list[Array], list[Array]] | tuple[list[Array], list[Array], list[Array]]
|
the weights are normalized to the maximum of all returned events. |
Notes
The number of events per decay mode is drawn at random, so each call generally requires a
recompilation of the underlying GenParticle.generate calls.
phasespace.fromdecay.mass_functions
#
Mass distribution functions for resonant particles.
This module provides factory functions that create mass distribution functions for resonant
particles. Each factory returns a callable with the signature
(min_mass, max_mass, n_events, key) that samples masses truncated to [min_mass, max_mass]
and is usable inside jitted code.
Sampling is done by inverse transform sampling (see e.g. L. Devroye, Non-Uniform Random Variate Generation, Springer 1986, Ch. II), analytically where a closed-form quantile function exists and on a precomputed grid for the relativistic Breit-Wigner, which has none.
gauss_factory(mass, width)
#
Create a Gaussian mass distribution function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass
|
Mean mass of the particle. |
required | |
width
|
Width (sigma) of the Gaussian distribution. |
required |
Returns:
| Type | Description |
|---|---|
|
Callable that generates masses from a Gaussian distribution truncated to |
|
|
|
|
|
returning an array of shape |
breitwigner_factory(mass, width)
#
Create a Breit-Wigner (Cauchy) mass distribution function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass
|
Central mass (m) of the particle. |
required | |
width
|
Width (gamma) of the Breit-Wigner distribution. |
required |
Returns:
| Type | Description |
|---|---|
|
Callable that generates masses from a Breit-Wigner distribution truncated to |
|
|
|
|
|
returning an array of shape |
Notes
The Cauchy CDF is :math:F(x) = 1/2 + \arctan((x - m) / \gamma) / \pi, which is inverted
analytically to sample within the limits.
relativistic_breitwigner_factory(mass, width)
#
Create a relativistic Breit-Wigner mass distribution function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mass
|
Central mass (m) of the particle. |
required | |
width
|
Width (gamma) of the relativistic Breit-Wigner distribution. |
required |
Returns:
| Type | Description |
|---|---|
|
Callable that generates masses from a relativistic Breit-Wigner distribution truncated to |
|
|
|
|
|
returning an array of shape |
Notes
The density is the constant-width relativistic Breit-Wigner
:math:f(m) \propto 1 / ((m^2 - m_0^2)^2 + m_0^2 \Gamma^2) (PDG, Review of Particle
Physics, resonance section), matching zfit_physics.pdf.RelativisticBreitWigner.
It has no closed-form quantile function, so the CDF is tabulated once here and inverted by
interpolation. The grid is placed at the quantiles of the Cauchy distribution that the
density takes in :math:s = m^2, which makes it dense across the peak while still reaching
far into the tails.