From ac89e78f06709cd3cbc4a4a6a2fe35d2ab81579c Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues Date: Fri, 30 Jun 2023 18:25:43 +0200 Subject: [PATCH] DecayChain.from_dict only supports single decay chains (#339) * DecayChain.from_dict only supports single decay chains * Ensure this fact is properly tested * style: pre-commit fixes * Fix indentation * Fix for doctests --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/decaylanguage/decay/decay.py | 72 ++++++++++++++++++++++++++++++-- tests/decay/test_decay.py | 21 ++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/decaylanguage/decay/decay.py b/src/decaylanguage/decay/decay.py index 99388b7c..f643de2a 100644 --- a/src/decaylanguage/decay/decay.py +++ b/src/decaylanguage/decay/decay.py @@ -477,17 +477,81 @@ def _build_decay_modes( Given the input dict representation of a `DecayChain` it returns a dict of mother particles and their final states as `DecayMode` instances. + + Parameters + ---------- + decay_modes: dict + A dict to be populated with the decay modes `DecayMode` + built from the input decay chain dictionary. + dc_dict: dict + The input decay chain dictionary. + + Note + ---- + Only single chains are supported, meaning every decaying particle + can only define a single decay mode. + + Examples + -------- + The simple example with no sub-decays: + >>> dc_dict = { + ... "anti-D0": [ + ... { + ... "bf": 1.0, + ... "fs": ["K+", "pi-"], + ... "model": "PHSP", + ... "model_params": "" + ... } + ... ] + ... } + >>> # It provides + >>> decay_modes = {} + >>> _build_decay_modes(decay_modes, dc_dict) + >>> decay_modes + {'anti-D0': } + + A more complicated example with a sub-decay and more than one mode + { + "anti-D*0": [ + { + "bf": 0.619, + "fs": [ + { + "anti-D0": [ + { + "bf": 1.0, + "fs": ["K+", "pi-"], + "model": "PHSP", + "model_params": "" + } + ] + }, + "pi0" + ], + "model": "VSS", + "model_params": "" + } + ] + } + provides + {'anti-D0': , + 'anti-D*0': } """ mother = list(dc_dict.keys())[0] dms = dc_dict[mother] for dm in dms: + # Single decay chains are allowed, which means a particle cannot have 2 decay modes + if mother in decay_modes: + raise RuntimeError("Input is not a single decay chain!") from None + try: fs = dm["fs"] except Exception as e: raise RuntimeError( "Internal dict representation not in the expected format - no 'fs' key is present!" ) from e + assert isinstance(fs, list) if _has_no_subdecay(fs): decay_modes[mother] = DecayMode.from_dict(dm) @@ -663,17 +727,19 @@ def _get_fs(decay: DecayModeDict) -> list[Any]: class DecayChain: """ - Class holding a particle decay chain, which is typically a top-level decay + Class holding a particle (single) decay chain, which is typically a top-level decay (mother particle, branching fraction and final-state particles) and a set of sub-decays for any non-stable particle in the top-level decay. - The whole chain can be seen as a mother particle and a list of decay modes. + The whole chain can be seen as a mother particle and a list of chained decay modes. This class is the main building block for the digital representation of full decay chains. Note ---- - This class does not assume any kind of particle names (EvtGen, PDG). + 1) Only single chains are supported, meaning every decaying particle + can only define a single decay mode. + 2) This class does not assume any kind of particle names (EvtGen, PDG). It is nevertheless advised to default use EvtGen names for consistency with the defaults used in the related classes `DecayMode` and `DaughtersDict`, unless there is a good motivation not to. diff --git a/tests/decay/test_decay.py b/tests/decay/test_decay.py index 123d664b..98b0444c 100644 --- a/tests/decay/test_decay.py +++ b/tests/decay/test_decay.py @@ -314,6 +314,27 @@ def test_DecayChain_constructor_from_dict(): assert DecayChain.from_dict(dc_dict).to_dict() == dc_dict +def test_DecayChain_constructor_from_dict_multiple_final_states(): + dc_dict = { + "MyD-": [ + { + "bf": 0.905, + "fs": ["K+", "pi-", "pi-"], + "model": "D_DALITZ", + "model_params": "", + }, + { + "bf": 0.095, + "fs": ["K+", "K-", "pi-"], + "model": "D_DALITZ", + "model_params": "", + }, + ] + } + with pytest.raises(RuntimeError): + _ = DecayChain.from_dict(dc_dict) + + def test_DecayChain_constructor_from_dict_RuntimeError(dc2): # For the sake of example remove some parts of a valid dict bad_dict_repr = dc2.to_dict()["D*+"][0]