Kinematics#
Lorentz vectors#
AmpForm provides classes for formulating symbolic expressions for boosting and rotating Lorentz vectors. Usually, when building an amplitude model, you don’t have to use these classes, but sometimes you want to boost some four-momenta yourself (for instance to boost into the center-of-mass frame of your experiment. Here, we boost a four-momentum \(q\) from the lab frame of the BESIII detector into the center-of-mass frame \(p\) of the \(e^-e^+\) collision. Symbolically, this looks like this:
from ampform.kinematics.lorentz import (
ArrayMultiplication,
ArraySize,
BoostZMatrix,
Energy,
FourMomentumSymbol,
three_momentum_norm,
)
p = FourMomentumSymbol("p", shape=[])
q = FourMomentumSymbol("q", shape=[])
beta = three_momentum_norm(p) / Energy(p)
Bz = BoostZMatrix(beta, n_events=ArraySize(beta))
Bz_expr = ArrayMultiplication(Bz, q)
Bz_expr
We now use SymPy to create a numerical function. (Of course, you can use TensorWaves instead to use other numerical backends.)
Bz_func = sp.lambdify([p, q], Bz_expr.doit(), cse=True)
Finally, plugging in some numbers that represent data, we get the \(q\) in the rest frame of \(p\):
array([[ 2.39991886e+00, 3.00000000e-01, -1.50000000e+00,
-3.24770653e-03],
[ 3.38950389e+00, -4.50000000e-02, 6.00000000e-01,
1.06711603e+00]])
Four-vector array format
Lambdified expressions that involve Lorentz vector computations, expect the format \(p = \left(E, p_x, p_y, p_z\right)\). In addition, the shape of input arrays should be (n, 4) with n the number of events.
As a cross-check, notice how boosting the original boost momentum into its own rest frame, results in \(B_z(p) p = \left(m_{J/\psi}, 0, 0, 0\right)\):
Bz_func(pz_array, pz_array)
array([[3.0969547, 0. , 0. , 0. ]])
Note that in this case, boost vector \(p\) was in the \(z\) direction, so we were able to just boost with BoostZMatrix. In the more general case, we can use:
from ampform.kinematics.lorentz import BoostMatrix
B = BoostMatrix(p)
B_expr = ArrayMultiplication(B, q)
B_expr
B_func = sp.lambdify([p, q], B_expr.doit(), cse=True)
px_array = np.array([[3.0971, 30e-3, 0, 0]]) # x direction!
B_func(px_array, q_array)
array([[ 2.39720652, 0.27676543, -1.5 , 0.02 ],
[ 3.40059543, -0.07793769, 0.6 , 1.1 ]])
And again, \(B(p) p = \left(m_{J/\psi}, 0, 0, 0\right)\):
B_func(px_array, px_array)
array([[3.0969547, 0. , 0. , 0. ]])
Phase space#
Kinematics for a three-body decay \(0 \to 123\) can be fully described by two Mandelstam variables \(\sigma_1, \sigma_2\), because the third variable \(\sigma_3\) can be expressed in terms \(\sigma_1, \sigma_2\), the mass \(m_0\) of the initial state, and the masses \(m_1, m_2, m_3\) of the final state. As can be seen, the roles of \(\sigma_1, \sigma_2, \sigma_3\) are interchangeable.
The phase space is defined by the closed area that satisfies the condition \(\phi(\sigma_1,\sigma_2) \leq 0\), where \(\phi\) is a Kibble function:
and \(\lambda\) is the Källén function:
Any distribution over the phase space can now be defined using a two-dimensional grid over a Mandelstam pair \(\sigma_1,\sigma_2\) of choice, with the condition \(\phi(\sigma_1,\sigma_2)<0\) selecting the values that are physically allowed.
See Phase space for a three-body decay for an interactive visualization of the phase space region and an analytic expression for the phase space boundary.