Relativistic Kinematics¶
This note introduces the relativistic kinematics commonly used in nuclear and particle physics analysis. The emphasis is practical: four-momentum, invariant mass, Lorentz boosts, and the ROOT classes TVector3 and TLorentzVector.
Lorentz Transformation¶
In special relativity, the space-time coordinates of an event depend on the inertial frame in which they are measured. Consider two inertial frames: $S$, and $S'$, where $S'$ moves with velocity $v$ along the $+z$-axis relative to $S$. The Lorentz transformation relates the coordinates $(ct,x,y,z)$ and $(ct',x',y',z')$ by
$$ \begin{align} x' &= x, & x &= x' \\ y' &= y, & y &= y' \\ z' &= \gamma (z - \beta ct), & z &= \gamma (z' + \beta ct) \\ ct' &= \gamma (ct - \beta z), & ct &= \gamma (ct' + \beta z) \end{align} $$ where
- $\beta = v/c$,
- $\gamma = 1/\sqrt{1-\beta^2}$.
A few points are especially important in physics analysis.
First, the space-time interval
$$ s^2=(ct)^2-x^2-y^2-z^2 $$
is invariant under Lorentz transformation. This invariance is the prototype for the invariant norm of the energy-momentum 4-vector discussed below.
Second, the transverse coordinates are unchanged by a boost along the $z$-axis:
$$ x'=x,\qquad y'=y. $$
Third, the Lorentz transformation leads to two standard relativistic effects:
- length contraction along the boost direction,
- time dilation for moving clocks.
If a particle has proper lifetime $\tau$ in its rest frame, then in a frame where it moves with Lorentz factor $\gamma$, its average observed lifetime is $\gamma \tau$. This is directly relevant in detector-based reconstruction of unstable particles.
Relativistic Energy and Momentum¶
For a particle with rest mass $m$ and velocity $v$,
$$ \vec{p}=\gamma m \vec{v}, \qquad E=\gamma mc^2. $$
The total energy contains both rest energy and kinetic energy:
$$ E = T + mc^2, \qquad T = (\gamma-1)mc^2. $$
The energy and momentum transform under Lorentz boosts in the same way as the time and longitudinal spatial coordinates. For a boost along $z$,
$$ \begin{align} p'_x &= p_x, & p_x &= p'_x \\ p'_y &= p_y, & p_y &= p'_y \\ p'_z &= \gamma (p_z-\beta E/c), & p_z &= \gamma (p'_z+\beta E'/c) \\ E'/c &= \gamma(E/c-\beta p_z), & E/c &= \gamma(E'/c+\beta p'_z) \end{align} $$ The most important invariant relation is
$$ E^2 = (pc)^2 + (mc^2)^2. $$
This equation is central in all later analyses. It is often used in three equivalent ways:
$$ m^2c^4 = E^2 - p^2c^2, $$
$$ p = \gamma \beta mc, $$
and, if the kinetic energy $T$ is known instead of the total energy,
$$ (pc)^2 = T^2 + 2mc^2T. $$
The last form is especially useful in nuclear physics, where detector calibration often yields kinetic energy more directly than total energy.
For a massless particle such as a photon, $m=0$, so
$$ E = pc. $$
For highly relativistic particles with $E \gg mc^2$, the approximation $E \approx pc$ becomes accurate.
Natural Units¶
In nuclear and particle physics, it is standard to use natural units with $c=1$. In this convention, energy, mass, and momentum are all expressed in units such as MeV or GeV, and many formulas simplify:
$$ E^2 = p^2 + m^2, \qquad m^2 = E^2 - p^2, \qquad T = E-m, \qquad p^2 = T^2 + 2mT. $$
This convention will be used throughout the remainder of the chapter unless otherwise noted.
Published experimental papers still often write masses in GeV/$c^2$ and momenta in GeV/$c$ to make dimensions explicit. In analysis code, however, one usually works in natural units and omits the factors of $c$, as long as units are handled consistently.
Example¶
Consider a $K_S$ meson with rest mass $m_{K_S}=0.4977$ GeV moving along the $+z$-axis with $v=0.95c$. Then
$$ \gamma = \frac{1}{\sqrt{1-0.95^2}} = 3.2026, $$
$$ E = \gamma m_{K_S} = 3.2026 \times 0.4977 = 1.5939\ \text{GeV}, $$
$$ p_z = \gamma \beta m_{K_S} = 3.2026 \times 0.95 \times 0.4977 = 1.5142\ \text{GeV}. $$
Since $p_x=p_y=0$,
$$ m = \sqrt{E^2-p^2} = \sqrt{(1.5939)^2-(1.5142)^2} = 0.4977\ \text{GeV}, $$
which reproduces the rest mass.
This is a simple but important check: in practical analysis, whenever a 4-vector is constructed, one should be able to recover the particle mass from $E^2-p^2$, up to numerical precision and detector resolution.
Vectors and Lorentz Transformations¶
The 4-vector formalism provides the natural language for relativistic analysis. It unifies energy and momentum into one object and makes invariant quantities explicit.
Definition of 4-Vectors¶
A 4-vector combines one time-like component with three spatial components. Two examples are used repeatedly in relativistic mechanics:
space-time 4-vector: $$ X^\mu = (ct, x, y, z), $$
energy-momentum 4-vector: $$ P^\mu = (E/c, p_x, p_y, p_z). $$
In natural units, the energy-momentum 4-vector is usually written as
$$ P^\mu = (E,\vec{p}) = (E,p_x,p_y,p_z). $$
For a general 4-vector $A^\mu=(A_0,\vec{A})$, the Minkowski inner product is
$$ A\cdot B = A_0B_0 - \vec{A}\cdot\vec{B}, $$
and the invariant norm is
$$ A^2 = A\cdot A = A_0^2 - |\vec{A}|^2. $$
For the energy-momentum 4-vector,
$$ P^2 = E^2 - p^2 = m^2 $$
in natural units. This is the origin of invariant mass.
Lorentz Transformation of 4-Vectors¶
A Lorentz transformation maps one 4-vector to another in a different inertial frame while preserving the Minkowski norm. This is why the quantity $P^2=m^2$ is frame-independent.
In the particle rest frame,
$$ P^\mu = (m,0,0,0), $$
so
$$ P^2 = m^2. $$
In any boosted frame, the components $E$ and $\vec{p}$ change, but the invariant mass does not.
This fact is fundamental in data analysis. Peak structures in invariant-mass spectra are meaningful precisely because invariant mass is independent of the reference frame used to describe the event.
Multi-Particle Systems¶
For a system of particles, the total 4-momentum is
$$ P^\mu_{\text{tot}} = \sum_i P^\mu_i. $$
Its invariant norm defines the invariant mass of the system:
$$ M_{\text{sys}}^2 = P_{\text{tot}}^2 = \left(\sum_i E_i\right)^2 - \left|\sum_i \vec{p}_i\right|^2. $$
This formula is one of the most important working equations in experimental particle and nuclear physics. It is used to reconstruct short-lived resonances and composite systems from detected final-state particles.
For example, in
$$ \pi^0\rightarrow \gamma\gamma, $$
the invariant mass of the two photons should peak near the $\pi^0$ mass.
Conservation in Reactions¶
For decays and scattering reactions, 4-momentum conservation is written compactly as
$$ P^\mu_{\text{initial}} = P^\mu_{\text{final}}. $$
For example:
decay: $$ P \to P_1 + P_2 + \cdots $$
scattering: $$ P_1 + P_2 \to P_3 + P_4 + \cdots $$
Because each $P^\mu$ contains both energy and momentum, one equation automatically enforces both energy conservation and momentum conservation.
This compact notation will be used heavily in Section 7.2, especially when defining invariant mass and missing mass.

Physics Vectors¶
Vectors are used throughout physics analysis to describe positions, directions, momenta, and relativistic 4-momenta. In ROOT, dedicated vector classes make these quantities easy to construct, manipulate, and interpret in a way that matches standard analysis practice.
Coordinate Representations¶
A physical vector can be expressed in different coordinate systems depending on the problem. In collider physics, the variables $(p_T,\eta,\phi,m)$ are often convenient. In many nuclear-physics and fixed-target applications, however, the Cartesian form $(p_x,p_y,p_z,E)$ is usually the most transparent because it connects directly to momentum conservation and reaction kinematics.
For the present chapter, Cartesian 4-vectors are the most natural choice. They are especially convenient for writing conservation laws, constructing invariant masses, and performing explicit boosts between the laboratory and center-of-mass frames.
TVector3¶
TVector3 stores a three-vector
$$ \vec v=(x,y,z). $$
It is commonly used for:
- positions;
- directions;
- three-momenta;
- beam axes;
- detector directions;
- boost velocity vectors.
Useful functions:
X(), Y(), Z()
Mag(), Mag2()
Theta(), Phi()
Perp()
Unit()
Dot(v)
Cross(v)
Angle(v)
RotateX(a), RotateY(a), RotateZ(a)
SetXYZ(x,y,z)
SetMagThetaPhi(mag,theta,phi)
Angles are in radians.
TVector3 Example: Momentum Direction¶
Unit()gives the direction without changing the momentum magnitude.
TVector3 p(0.3, 0.4, 1.2); // GeV
std::cout << "px = " << p.X() << std::endl;
std::cout << "py = " << p.Y() << std::endl;
std::cout << "pz = " << p.Z() << std::endl;
std::cout << "|p| = " << p.Mag() << " GeV" << std::endl;
std::cout << "pT = " << p.Perp() << " GeV" << std::endl;
std::cout << "theta = "
<< p.Theta()*TMath::RadToDeg()
<< " deg" << std::endl;
std::cout << "phi = "
<< p.Phi()*TMath::RadToDeg()
<< " deg" << std::endl;
TVector3 dir = p.Unit();
std::cout << "|dir| = " << dir.Mag() << std::endl;
px = 0.3 py = 0.4 pz = 1.2 |p| = 1.3 GeV pT = 0.5 GeV theta = 22.6199 deg phi = 53.1301 deg |dir| = 1
TVector3 Example: Opening Angle¶
TVector3 p1(0.2, 0.1, 1.0);
TVector3 p2(-0.1, 0.3, 0.8);
double angle = p1.Angle(p2);
std::cout << "opening angle = "
<< angle*TMath::RadToDeg()
<< " deg" << std::endl;
std::cout << "p1 dot p2 = "
<< p1.Dot(p2)
<< std::endl;
opening angle = 23.2323 deg p1 dot p2 = 0.81
Mathematically,
$$ \cos\theta= \frac{\vec p_1\cdot \vec p_2} {|\vec p_1||\vec p_2|}. $$
TLorentzVector¶
TLorentzVector stores a four-vector in the form
$$ (p_x,p_y,p_z,E). $$
It is used for:
- invariant mass;
- total four-momentum;
- missing mass;
- Lorentz boosts;
- center-of-mass variables.
Useful functions:
Px(), Py(), Pz(), E()
P(), Pt()
Theta(), Phi()
M(), M2()
Beta(), Gamma()
Vect()
SetPxPyPzE(px,py,pz,E)
SetXYZM(px,py,pz,m)
BoostVector()
Boost(beta)
The function M() returns
$$ M=\sqrt{E^2-p^2}. $$
TLorentzVector Example: $K_S$¶
TLorentzVector ks;
ks.SetPxPyPzE(0.0, 0.0, 1.5142, 1.5939);
std::cout << "E = " << ks.E() << " GeV" << std::endl;
std::cout << "pz = " << ks.Pz() << " GeV" << std::endl;
std::cout << "M = " << ks.M() << " GeV" << std::endl;
std::cout << "beta = " << ks.Beta() << std::endl;
std::cout << "gamma = " << ks.Gamma() << std::endl;
E = 1.5939 GeV pz = 1.5142 GeV M = 0.49771 GeV beta = 0.949997 gamma = 3.20247
Constructing a Four-Vector from Momentum and Mass¶
If the momentum and particle mass are known, use
SetXYZM(px,py,pz,m)
ROOT calculates
$$ E=\sqrt{p_x^2+p_y^2+p_z^2+m^2}. $$
Example:
const double mpi = 0.13957; // GeV
TLorentzVector pion;
pion.SetXYZM(0.10, -0.05, 0.40, mpi);
std::cout << "E = " << pion.E() << " GeV" << std::endl;
std::cout << "M = " << pion.M() << " GeV" << std::endl;
E = 0.438155 GeV M = 0.13957 GeV
Constructing a Four-Vector from Kinetic Energy¶
If kinetic energy $T$, mass $m$, and direction $(\theta,\phi)$ are known,
$$ E=T+m, $$
$$ p=\sqrt{T^2+2mT}. $$
Example for an alpha particle:
const double mAlpha = 3727.38; // MeV
const double T = 5.0; // MeV
double theta = 40.0*TMath::DegToRad();
double phi = 30.0*TMath::DegToRad();
double E = T + mAlpha;
double p = std::sqrt(T*T + 2.0*mAlpha*T);
TVector3 pvec;
pvec.SetMagThetaPhi(p, theta, phi);
TLorentzVector alpha;
alpha.SetPxPyPzE(pvec.X(), pvec.Y(), pvec.Z(), E);
std::cout << "M = " << alpha.M() << " MeV" << std::endl;
M = 3727.38 MeV
Invariant Mass with TLorentzVector¶
For two particles,
TLorentzVector pair = p1 + p2;
double mass = pair.M();
This implements
$$ M^2=(E_1+E_2)^2 - |\vec p_1+\vec p_2|^2. $$
Example:
const double mpi = 0.13957; // GeV
TLorentzVector piPlus;
TLorentzVector piMinus;
piPlus.SetXYZM(0.20, 0.10, 0.50, mpi);
piMinus.SetXYZM(-0.15, -0.05, 0.30, mpi);
TLorentzVector pair_pi = piPlus + piMinus;
std::cout << "M(pi+ pi-) = "
<< pair_pi.M()
<< " GeV" << std::endl;
M(pi+ pi-) = 0.472771 GeV
17. Boosts in ROOT¶
Suppose a parent particle $A$ moves in the lab frame and a daughter particle $b$ is observed either in the parent rest frame or in the lab frame.
We store the parent four-momentum in the lab frame as
TLorentzVector parent_lab;
Then
TVector3 beta = parent_lab.BoostVector();
returns the velocity of the parent in the lab frame,
$$
\vec\beta_A = \frac{\vec p_A}{E_A}.
$$
This beta connects two frames:
- the parent rest frame, where $A$ is at rest;
- the lab frame, where $A$ is moving.
Now let daughter be the four-vector of a daughter particle.
If daughter is defined in the parent rest frame, then
daughter.Boost(beta);
transforms it to the lab frame.
If daughter is given in the lab frame, then
daughter.Boost(-beta);
transforms it back to the parent rest frame.
So the same beta is used in both directions:
Boost(beta): parent rest frame $\rightarrow$ lab frame;Boost(-beta): lab frame $\rightarrow$ parent rest frame.
18. Example¶
The parent particle has mass
$$ M = 1.0\ \text{GeV} $$
and moves in the lab along $+z$ with
$$ p_z = 2.0\ \text{GeV}. $$
A daughter particle is first defined in the parent rest frame. We then boost it to the lab frame, and finally boost it back to the parent rest frame.
// Parent four-vector in the lab frame
const double M = 1.0; // GeV
const double pz = 2.0; // GeV
TLorentzVector parent_lab;
parent_lab.SetPxPyPzE(0.0, 0.0, pz, std::sqrt(M*M + pz*pz));
// Velocity of the parent in the lab
TVector3 beta = parent_lab.BoostVector();
// Daughter four-vector in the parent rest frame
const double md = 0.2; // GeV
const double px = 0.3; // GeV
TLorentzVector daughter_rest;
daughter_rest.SetPxPyPzE(px, 0.0, 0.0, std::sqrt(md*md + px*px));
// Boost from parent rest frame to lab frame
TLorentzVector daughter_lab = daughter_rest;
daughter_lab.Boost(beta);
// Boost back from lab frame to parent rest frame
TLorentzVector daughter_back = daughter_lab;
daughter_back.Boost(-beta);
std::cout << "Daughter in parent rest frame:\n";
std::cout << " E = " << daughter_rest.E() << "\n";
std::cout << " px = " << daughter_rest.Px() << "\n";
std::cout << " py = " << daughter_rest.Py() << "\n";
std::cout << " pz = " << daughter_rest.Pz() << "\n";
std::cout << " M = " << daughter_rest.M() << "\n\n";
std::cout << "Daughter in lab frame:\n";
std::cout << " E = " << daughter_lab.E() << "\n";
std::cout << " px = " << daughter_lab.Px() << "\n";
std::cout << " py = " << daughter_lab.Py() << "\n";
std::cout << " pz = " << daughter_lab.Pz() << "\n";
std::cout << " M = " << daughter_lab.M() << "\n\n";
std::cout << "Boosted back to parent rest frame:\n";
std::cout << " E = " << daughter_back.E() << "\n";
std::cout << " px = " << daughter_back.Px() << "\n";
std::cout << " py = " << daughter_back.Py() << "\n";
std::cout << " pz = " << daughter_back.Pz() << "\n";
std::cout << " M = " << daughter_back.M() << "\n";
Daughter in parent rest frame: E = 0.360555 px = 0.3 py = 0 pz = 0 M = 0.2 Daughter in lab frame: E = 0.806226 px = 0.3 py = 0 pz = 0.72111 M = 0.2 Boosted back to parent rest frame: E = 0.360555 px = 0.3 py = 0 pz = 1.35992e-16 M = 0.2
// Parent four-vector in the lab frame
TLorentzVector parent_lab;
parent_lab.SetPxPyPzE(0.0, 0.0, 2.0, std::sqrt(5.0));
// Daughter four-vector measured in the lab frame
TLorentzVector daughter_lab;
daughter_lab.SetXYZM(0.3, 0.0, 0.8, 0.2);
// Velocity of the parent in the lab frame
TVector3 beta = parent_lab.BoostVector();
// Boost daughter from lab frame to parent rest frame
TLorentzVector daughter_rest = daughter_lab;
daughter_rest.Boost(-beta);
std::cout << "daughter mass = "
<< daughter_rest.M()
<< " GeV" << std::endl;
std::cout << "theta in parent rest frame = "
<< daughter_rest.Theta()*TMath::RadToDeg()
<< " deg" << std::endl;
daughter mass = 0.2 GeV theta in parent rest frame = 83.5602 deg