Helicity versus canonical¶
import logging
import graphviz
import qrules
import sympy as sp
from IPython.display import HTML, Math
from rich.table import Table
import ampform
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.ERROR)
In this notebook, we have a look at the decay
in order to see the difference between a HelicityModel
formulated in the canonical basis and one formulated in the helicity basis. To simplify things, we only look at spin projection \(+1\) for \(D_1(2420)^0\), because the intensities for each of the spin projections of \(D_1(2420)^0\) are incoherent, no matter which spin formalism we choose.
Tip
For more information about the helicity formalism, see Chung 2014, Richman 1984, and Kutschke 1996.
First, we use qrules.generate_transitions()
to generate a ReactionInfo
instance for both formalisms:
def generate_transitions(formalism: str):
reaction = qrules.generate_transitions(
initial_state=("D(1)(2420)0", [+1]),
final_state=["K+", "K-", "K~0"],
allowed_intermediate_particles=["a(1)(1260)+"],
formalism=formalism,
)
builder = ampform.get_builder(reaction)
return builder.formulate()
cano_model = generate_transitions("canonical-helicity")
heli_model = generate_transitions("helicity")
From components
and parameter_defaults
, we can see that the canonical formalism has a larger number of amplitudes.
table = Table(show_edge=False)
table.add_column("Formalism")
table.add_column("Coefficients", justify="right")
table.add_column("Amplitudes", justify="right")
table.add_row(
"Canonical",
str(len(cano_model.parameter_defaults)),
str(len(cano_model.components) - 1),
)
table.add_row(
"Helicity",
str(len(heli_model.parameter_defaults)),
str(len(heli_model.components) - 1),
)
table
Formalism ┃ Coefficients ┃ Amplitudes ━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━ Canonical │ 3 │ 8 Helicity │ 3 │ 3
The reason for this is that canonical basis distinguishes amplitudes over their \(LS\)-combinations. This becomes clear if we define \(a\) to be the amplitude without coefficient (\(A = C a\)), and consider what the full, coherent intensity looks like.
If we write the full intensity as \(I = \left|\sum_i A_i\right|^2\), then we have, in the case of the canonical basis:
def extract_amplitude_substitutions(model):
amplitude_to_symbol = {}
for name, expr in model.components.items():
if not name.startswith("A"):
continue
for par in model.parameter_defaults:
if par in expr.args:
expr /= par
name = "a" + name[1:]
symbol = sp.Symbol(name)
amplitude_to_symbol[expr] = symbol
return amplitude_to_symbol
cano_amplitude_to_symbol = extract_amplitude_substitutions(cano_model)
heli_amplitude_to_symbol = extract_amplitude_substitutions(heli_model)
def render_amplitude_summation(model):
amplitude_to_symbol = extract_amplitude_substitutions(model)
collected_expr = sp.collect(
model.expression.subs(amplitude_to_symbol).args[0].args[0],
tuple(model.parameter_defaults),
)
terms = collected_expr.args
latex = ""
latex += R"\begin{align}"
latex += fR"\sum_i A_i & = {sp.latex(terms[0])}\\"
for term in terms[1:]:
latex += fR"& + {sp.latex(term)} \\"
latex += R"\end{align}"
return Math(latex)
render_amplitude_summation(cano_model)
In the helicity basis, the \(LS\)-combinations have been summed over already and we can only see an amplitude for each helicity:
render_amplitude_summation(heli_model)
Amplitudes in the canonical basis are formulated with regard to their \(LS\)-couplings. As such, they contain additional Clebsch-Gordan coefficients that serve as expansion coefficients.
def extract_amplitudes(model):
return {
expr: sp.Symbol(name)
for name, expr in model.components.items()
if name.startswith("A")
}
cano_amplitudes = extract_amplitudes(cano_model)
heli_amplitudes = extract_amplitudes(heli_model)
expression, symbol = next(iter(cano_amplitude_to_symbol.items()))
display(symbol, Math(fR"\quad = {sp.latex(expression)}"))
In the helicity basis, these Clebsch-Gordan coefficients and Wigner-\(D\) functions have been summed up, leaving only a Wigner-\(D\) for each node in the decay chain (two in this case):
expression, symbol = next(iter(heli_amplitude_to_symbol.items()))
display(symbol, Math(fR"\quad = {sp.latex(expression)}"))
We can see this also from the original ReactionInfo
objects. Let’s select only the transitions
where the \(a_1(1260)^+\) resonance has spin projection \(-1\) (taken to be helicity \(-1\) in the helicity formalism). We then see just one StateTransition
in the helicity basis and three transitions in the canonical basis:
def render_selection(model):
transitions = model.adapter.reaction_info.transitions
selection = filter(
lambda s: s.states[3].spin_projection == -1, transitions
)
dot = qrules.io.asdot(
selection, render_node=True, render_final_state_id=False
)
return graphviz.Source(dot)
display(
HTML("<b>Helicity</b> basis:"),
render_selection(heli_model),
HTML("<b>Canonical</b> basis:"),
render_selection(cano_model),
)