# WARNING: advised to install a specific version, e.g. qrules==0.1.2
%pip install -q qrules[doc,viz] IPython
import os
STATIC_WEB_PAGE = {"EXECUTE_NB", "READTHEDOCS"}.intersection(os.environ)
{autolink-concat}
import attrs
import graphviz
from IPython.display import display
import qrules
from qrules.conservation_rules import (
SpinEdgeInput,
SpinNodeInput,
parity_conservation,
spin_conservation,
spin_magnitude_conservation,
)
from qrules.quantum_numbers import Parity
QRules generates {class}.MutableTransition
s, populates them with quantum numbers (edge properties representing states and nodes properties representing interactions), then checks whether the generated {class}.MutableTransition
s comply with the rules formulated in the {mod}.conservation_rules
module.
The {mod}.conservation_rules
module can also be used separately. In this notebook, we will illustrate this by checking spin and parity conservation.
See {func}.parity_conservation
:
parity_conservation(
ingoing_edge_qns=[Parity(-1)],
outgoing_edge_qns=[Parity(+1), Parity(+1)],
l_magnitude=1,
)
:::{seealso}
{func}.spin_conservation
, tests/unit/conservation_rules/test_spin.py
, {pdg-review}Quark Model
, and these lecture notes by Curtis Meyer.
:::
{func}.spin_conservation
checks whether spin magnitude and spin projections are conserved. In addition, it checks whether the Clebsch-Gordan coefficients are non-zero, meaning that the coupled spins on the interaction nodes are valid as well.
spin_conservation(
ingoing_spins=[
SpinEdgeInput(0, 0),
],
outgoing_spins=[
SpinEdgeInput(0, 0),
SpinEdgeInput(0, 0),
],
interaction_qns=SpinNodeInput(
l_magnitude=0, # false if 1
l_projection=0,
s_magnitude=0,
s_projection=0,
),
)
spin_conservation(
ingoing_spins=[
SpinEdgeInput(1, 0),
],
outgoing_spins=[
SpinEdgeInput(1, +1),
SpinEdgeInput(1, -1),
],
interaction_qns=SpinNodeInput(
l_magnitude=1,
l_projection=0,
s_magnitude=2,
s_projection=0,
),
)
.StateTransition
¶First, generate some {obj}.StateTransition
s with {func}.generate_transitions
, then select one of them:
reaction = qrules.generate_transitions(
initial_state="J/psi(1S)",
final_state=["K0", "Sigma+", "p~"],
allowed_interaction_types="strong",
formalism="canonical",
)
transition = reaction.transitions[0]
Next, have a look at the edge and node properties, and use the underlying {class}.Topology
to extract one of the node {class}.InteractionProperties
with the surrounding states (these are {obj}tuple
s of a {class}.Particle
and a {obj}float
spin projection).
dot = qrules.io.asdot(transition, render_node=True)
display(graphviz.Source(dot))
dot = qrules.io.asdot(
transition.topology,
render_node=True,
render_resonance_id=True,
render_initial_state_id=True,
)
display(graphviz.Source(dot))
We select node $(0)$, which has incoming state ID $-1$ and outgoing state IDs $0$ and $3$:
topology = transition.topology
node_id = 0
in_id, *_ = topology.get_edge_ids_ingoing_to_node(node_id)
out_id1, out_id2, *_ = topology.get_edge_ids_outgoing_from_node(node_id)
incoming_state = transition.states[in_id]
outgoing_state1 = transition.states[out_id1]
outgoing_state2 = transition.states[out_id2]
interaction = transition.interactions[node_id]
spin_magnitude_conservation(
ingoing_spin_magnitudes=[incoming_state.particle.spin],
outgoing_spin_magnitudes=[
outgoing_state1.particle.spin,
outgoing_state2.particle.spin,
],
interaction_qns=interaction,
)
Contrary to expectations, this transition does not conserve spin projection and therefore {func}.spin_conservation
returns {obj}False
:
spin_conservation(
ingoing_spins=[
SpinEdgeInput(
spin_magnitude=incoming_state.particle.spin,
spin_projection=incoming_state.spin_projection,
)
],
outgoing_spins=[
SpinEdgeInput(
spin_magnitude=outgoing_state1.particle.spin,
spin_projection=outgoing_state1.spin_projection,
),
SpinEdgeInput(
spin_magnitude=outgoing_state2.particle.spin,
spin_projection=outgoing_state2.spin_projection,
),
],
interaction_qns=interaction,
)
The reason is that AmpForm formulates the {class}~ampform.helicity.HelicityModel
with the helicity formalism first and then uses a transformation to get the model in the canonical basis (see {func}~ampform.helicity.formulate_isobar_cg_coefficients
). The canonical basis does not conserve helicity (taken to be {attr}.State.spin_projection
).
.StateTransition
s¶When checking conservation rules, you may want to modify the properties on the {obj}.StateTransition
s. However, a {obj}.StateTransition
is a {class}.FrozenTransition
, so it is not possible to modify its {attr}~.FrozenTransition.interactions
and {attr}~.FrozenTransition.states
. The only way around this is to create a new instance with {func}attrs.evolve
.
First, we get the instance (in this case one of the {class}.InteractionProperties
) and substitute its {attr}.InteractionProperties.l_magnitude
:
new_interaction = attrs.evolve(transition.interactions[node_id], l_magnitude=2)
new_interaction
We then again use {func}attrs.evolve
to substitute the {attr}.Transition.interactions
of the original {obj}.StateTransition
:
new_interaction_dict = dict(transition.interactions) # make mutable
new_interaction_dict[node_id] = new_interaction
new_transition = attrs.evolve(transition, interactions=new_interaction_dict)
dot = qrules.io.asdot(new_transition, render_node=True)
graphviz.Source(dot)