Package: phasespace
PhaseSpacePython implementation of the Raubold and Lynch method forn-body events using
TensorFlow as a backend.The code is based on the GENBOD function (W515 from CERNLIB), documented in [1]
and tries to follow it as closely as possible.Detailed documentation, including the API, can be found inhttps://phasespace.readthedocs.io.
Don’t hesitate to join ourgitterchannel for questions and comments.If you use phasespace in a scientific publication we would appreciate citations to theJOSSpublication:@article{puig_eschle_phasespace-2019,title={phasespace: n-body phase space generation in Python},doi={10.21105/joss.01570},url={https://doi.org/10.21105/joss.01570},year={2019},month={oct},publisher={The Open Journal},author={Albert Puig and Jonas Eschle},journal={Journal of Open Source Software}}Free software: BSD-3-Clause.[1]  F. James, Monte Carlo Phase Space, CERN 68-15 (1968)Why?Lately, data analysis in High Energy Physics (HEP), traditionally performed within theROOTecosystem,
has been moving more and more towards Python.
The possibility of carrying out purely Python-based analyses has become real thanks to the
development of many open source Python packages,
which have allowed to replace most ROOT functionality with Python-based packages.One of the aspects where this is still not possible is in the random generation ofn-body phase space events,
which are widely used in the field, for example to study kinematics
of the particle decays of interest, or to perform importance sampling in the case of complex amplitude models.
This has been traditionally done with theTGenPhaseSpaceclass, which is based of the GENBOD function of the
CERNLIB FORTRAN libraries and which requires a full working ROOT installation.This package aims to address this issue by providing a TensorFlow-based implementation of such a function
to generaten-body decays without requiring a ROOT installation.
Additionally, an oft-needed functionality to generate complex decay chains, not included inTGenPhaseSpace,
is also offered, leaving room for decaying resonances (which don’t have a fixed mass, but can be seen as a
broad peak).InstallingTo install with pip:$pipinstallphasespaceThis is the preferred method to installphasespace, as it will always install the most recent stable release.
To install the necessary dependencies to be used withDecayLanguage, use$pipinstall"phasespace[fromdecay]"How to usePhasespace can directly be used to generate from a DecayChain using theDecayLanguagepackage asexplained in the tutorial.The generation of simplen-body decays can be done using thenbody_decayshortcut to create a decay chain
with a very simple interface: one needs to pass the mass of the top particle and the masses of the children particle as
a list, optionally giving the names of the particles. Then, thegeneratemethod can be used to produce the desired sample.
For example, to generateB^0to Kpi, we would do:importphasespaceB0_MASS=5279.65PION_MASS=139.57018KAON_MASS=493.677weights,particles=phasespace.nbody_decay(B0_MASS,[PION_MASS,KAON_MASS]).generate(n_events=1000)Behind the scenes, this function runs the TensorFlow graph. It returnstf.Tensor, which, as TensorFlow 2.x is in eager mode,
is basically a numpy array. Anytf.Tensorcan be explicitly converted to a numpy array by callingtf.Tensor.numpy()on it.
Thegeneratefunction returns atf.Tensorof 1000 elements in the case ofweightsand a list ofn particles(2) arrays of (1000, 4) shape,
where each of the 4-dimensions corresponds to one of the components of the generated Lorentz 4-vector.
All particles are generated in the rest frame of the top particle; boosting to a certain momentum (or list of momenta) can be
achieved by passing the momenta to theboost_toargument.Sequential decays can be handled with theGenParticleclass (used internally bygenerate) and itsset_childrenmethod.
As an example, to build theB^{0}to K^{*}gammadecay in whichK^*to Kpi, we would write:fromphasespaceimportGenParticleB0_MASS=5279.65KSTARZ_MASS=895.55PION_MASS=139.57018KAON_MASS=493.677kaon=GenParticle('K+',KAON_MASS)pion=GenParticle('pi-',PION_MASS)kstar=GenParticle('K*',KSTARZ_MASS).set_children(kaon,pion)gamma=GenParticle('gamma',0)bz=GenParticle('B0',B0_MASS).set_children(kstar,gamma)weights,particles=bz.generate(n_events=1000)Where we have used the fact thatset_childrenreturns the parent particle.
In this case,particlesis adictwith the particle names as keys:>>>particles{'K*': array([[ 1732.79325872, -1632.88873127,   950.85807735,  2715.78804872],
       [-1633.95329448,   239.88921123, -1961.0402768 ,  2715.78804872],
       [  407.15613764, -2236.6569286 , -1185.16616251,  2715.78804872],
       ...,
       [ 1091.64603395, -1301.78721269,  1920.07503991,  2715.78804872],
       [ -517.3125083 ,  1901.39296899,  1640.15905194,  2715.78804872],
       [  656.56413668,  -804.76922982,  2343.99214816,  2715.78804872]]),
 'K+': array([[  750.08077976,  -547.22569019,   224.6920906 ,  1075.30490935],
       [-1499.90049089,   289.19714633, -1935.27960292,  2514.43047106],
       [   97.64746732, -1236.68112923,  -381.09526192,  1388.47607911],
       ...,
       [  508.66157459,  -917.93523639,  1474.7064148 ,  1876.11771642],
       [ -212.28646168,   540.26381432,   610.86656669,   976.63988936],
       [  177.16656666,  -535.98777569,   946.12636904,  1207.28744488]]),
 'gamma': array([[-1732.79325872,  1632.88873127,  -950.85807735,  2563.79195128],
       [ 1633.95329448,  -239.88921123,  1961.0402768 ,  2563.79195128],
       [ -407.15613764,  2236.6569286 ,  1185.16616251,  2563.79195128],
       ...,
       [-1091.64603395,  1301.78721269, -1920.07503991,  2563.79195128],
       [  517.3125083 , -1901.39296899, -1640.15905194,  2563.79195128],
       [ -656.56413668,   804.76922982, -2343.99214816,  2563.79195128]]),
 'pi-': array([[  982.71247896, -1085.66304109,   726.16598675,  1640.48313937],
       [ -134.0528036 ,   -49.3079351 ,   -25.76067389,   201.35757766],
       [  309.50867032,  -999.97579937,  -804.0709006 ,  1327.31196961],
       ...,
       [  582.98445936,  -383.85197629,   445.36862511,   839.6703323 ],
       [ -305.02604662,  1361.12915468,  1029.29248526,  1739.14815935],
       [  479.39757002,  -268.78145413,  1397.86577911,  1508.50060384]])}TheGenParticleclass is able to cache the graphs so it is possible to generate in a loop
without overhead:for i in range(10):
    weights, particles = bz.generate(n_events=1000)
    ...
    (do something with weights and particles)
    ...This way of generating is recommended in the case of large samples, as it allows to benefit from
parallelisation while at the same time keep the memory usage low.If we want to operate with the TensorFlow graph instead, we can use thegenerate_tensormethod
ofGenParticle, which has the same signature asgenerate.More examples can be found in thetestsfolder and in thedocumentation.Physics validationPhysics validation is performed continuously in the included tests (tests/test_physics.py), run through GitHub Actions.
This validation is performed at two levels:In simplen-body decays, the results ofphasespaceare checked againstTGenPhaseSpace.For sequential decays, the results ofphasespaceare checked againstRapidSim, a “fast Monte Carlo generator
for simulation of heavy-quark hadron decays”.
In the case of resonances, differences are expected because our tests don’t include proper modelling of their
mass shape, as it would require the introduction of
further dependencies. However, the results of the comparison can be expected visually.The results of all physics validation performed by thetests_physics.pytest are written intests/plots.ContributingContributions are always welcome, please have a look at theContributing guide.ChangelogDevelopMajor Features and ImprovementsBehavioral changesBug fixes and small changesRequirement changesThanks1.9.0 (20 Jul 2023)Add support for Python 3.11, drop support for 3.71.8.0 (27 Jan 2023)Requirement changesupgrade to zfit >= 0.10.0 and zfit-physics >= 0.3.0pinning uproot and awkward to ~4 and ~1, respectively1.7.0 (1. Sep 2022)Upgraded Python and TensorFlow version.Addedtfandtensorflowextra to requirements. If you intend to use
phasespace with TensorFlow in the future (and not another backend like numpy or JAX),
make sure to always install withphasespace[tf].Requirement changesupgrade to TensorFlow >= 2.7Python from 3.7 to 3.10 is now supported1.6.0 (14 Apr 2022)Major Features and ImprovementsImproved GenMultiDecay to have better control on the decay mass of non-stable particles.Added aparticle_model_mapargument to theGenMultiDecayclass. This is a
dict where the key is a particle name and the value is a mass function name.
The feature can be seen in theGenMultiDecay Tutorial.1.5.0 (27 Nov 2021)Major Features and Improvementsadd support to generate from a DecayChain usingthe decaylanguagepackage from Scikit-HEP.
This is in the new subpackage “fromdecay” and can be used by installing the extra withpip install phasespace[fromdecay].Requirement changesdrop Python 3.6 supportThanksto Simon Thor for contributing thefromdecaysubpackage.1.4.2 (5.11.2021)Requirement changesLosen restriction on TensorFlow, allow version 2.7 (and 2.5, 2.6)1.4.1 (27.08.2021)Requirement changesLosen restriction on TensorFlow, allow version 2.6 (and 2.5)1.4.0 (11.06.2021)Requirement changesrequire TensorFlow 2.5 as 2.4 breaks some functionality1.3.0 (28.05.2021)Major Features and ImprovementsSupport Python 3.9Support TensorFlow 2.5improved compilation in tf.functions, use of XLA where applicabledeveloper: modernization of setup, CI and moreThanksRemco de Boer for many commits and cleanups1.2.0 (17.12.20)Major Features and ImprovementsPython 3.8 supportAllow eager execution by setting withtf.config.run_functions_eagerly(True)or the environment variable “PHASESPACE_EAGER”Deterministic random number generation via seed
ortf.random.GeneratorinstanceBehavioral changesBug fixes and small changesRequirement changestighten TensorFlow to 2.3/2.4tighten TensorFlow Probability to 0.11/0.12ThanksRemco de Boer and Stefan Pflüger for discussions on random number genration1.1.0 (27.1.2020)This release switched to TensorFlow 2.0 eager mode. Please upgrade your TensorFlow installation if possible and change
your code (minimal changes) as described under “Behavioral changes”.
In case this is currently impossible to do, please downgrade to < 1.1.0.Major Features and Improvementsfull TF2 compatibilityBehavioral changesgeneratenow returns an eager Tensor. This is basically a numpy array wrapped by TensorFlow.
To explicitly convert it to a numpy array, use thenumpy()method of the eager Tensor.generate_tensoris now depreceated,generatecan directly be used instead.Bug fixes and small changesRequirement changesrequires now TensorFlow >= 2.0.0Thanks1.0.4 (13-10-2019)Major Features and ImprovementsRelease to conda-forge, thanks to Chris Burr