| name | vector |
| description | Use when the user needs to compute Lorentz-vector / 4-vector quantities in Python — invariant mass, transverse momentum, pseudorapidity, ΔR, rapidity, boosts — from arrays of particle kinematics. Backed by the Scikit-HEP `vector` library (works with NumPy and Awkward arrays). Does NOT cover reading the kinematics from a file (use `physlite-basics` for PHYSLITE branches), clustering jets from constituents (use `fastjet`), or open-ended analysis-code authoring (use the `analysis` agent). Disambiguator phrase: Scikit-HEP vector library 4-vector math. |
Vector
Overview
The vector library provides Lorentz vector arithmetic for NumPy arrays,
awkward-array records, and scalar objects. The key design: register behaviors on
ak.Array records once, then use physics methods (.deltaR(), .mass,
.boost(...)) directly without manual kinematic math.
When to Use
- Computing invariant mass or transverse mass of particle combinations
- Computing deltaR between two objects for overlap removal or matching
- Boosting to the rest frame of a parent particle
- Any operation that would otherwise require manual
px = pt * cos(phi) etc.
Key Concepts
| Concept | Notes |
|---|
vector.register_awkward() | One-time call; mutates global behavior dict — call at module level |
| Field name conventions | pt/phi/eta/mass OR px/py/pz/energy — vector auto-detects |
Momentum4D | The most common type for HEP 4-vectors |
.deltaR(other) | ΔR = √(Δη² + Δφ²) — available as method after behavior registration |
.mass property | Invariant mass from E²-p² = m² |
vector.obj(...) | Single scalar Python object — fast in Numba, slow in plain Python loops |
vector.array(...) | NumPy structured-array subclass — vectorized, good for fixed-shape collections |
vector.zip(...) | Like ak.zip but auto-sets with_name; no need to specify record type |
.to_*() | Explicit coordinate conversion (to_xyzt, to_rhophithetatau, etc.) |
VectorSympy*D | Symbolic vector types (SymPy backend); all operations return SymPy expressions |
vector.register_pytree() | Returns a pytree interface (flatten/unflatten); requires optree package |
Canonical Patterns
Register behaviors (do once at module top):
import vector
vector.register_awkward()
Alternatively, install behaviors only in a single array without touching global
ak.behavior:
import awkward as ak
arr = ak.Array([...], with_name="Momentum4D",
behavior=vector.backends.awkward.behavior)
Build a Momentum4D record array from NTuple columns:
import awkward as ak
jets = vector.zip(
{"pt": events["jet_pt"], "phi": events["jet_phi"],
"eta": events["jet_eta"], "mass": events["jet_m"]},
)
Invariant mass of all jet pairs:
combos = ak.combinations(jets, 2, axis=1)
j1, j2 = ak.unzip(combos)
mjj = (j1 + j2).mass / 1000
deltaR between every electron–jet pair (for overlap removal):
pairs = ak.cartesian({"e": electrons, "j": jets}, axis=1)
dr = pairs.e.deltaR(pairs.j)
non_overlapping = jets[~ak.any(dr < 0.4, axis=1)]
Boost to rest frame of a parent:
boosted = daughter.boost(-parent.to_beta3())
Explicit coordinate conversion (numerical precision):
v = vector.obj(px=3.0, py=4.0, pz=0.0, energy=5.0)
v.pt
v2 = v.to_rhophithetatau()
Scalar Python object (vector.obj) — single vector or Numba use:
v = vector.obj(pt=30.0, phi=0.5, eta=1.2, mass=0.105)
print(v.px, v.py, v.pz, v.energy)
vector.obj returns a plain Python object, not a NumPy array. It is slow in
Python loops but compiles efficiently under @nb.njit. However, there is no
performance advantage (and a likely disadvantage) when compiling a calculation
on just a few vectors.
NumPy structured array (vector.array) — fixed-shape collections:
import numpy as np
muons = vector.array(
{"pt": np.array([30.0, 45.0]), "phi": np.array([0.5, -1.2]),
"eta": np.array([1.2, -0.8]), "mass": np.full(2, 0.105)}
)
print(muons.px, muons.energy)
Numba-compiled loop over awkward arrays — best for large ragged collections:
import numba as nb
import numpy as np
@nb.njit
def sum_mass(array):
out = np.empty(len(array), np.float64)
for i, event in enumerate(array):
total = vector.obj(px=0.0, py=0.0, pz=0.0, E=0.0)
for vec in event:
total = total + vec
out[i] = total.mass
return out
masses = sum_mass(array)
JIT compilation has a cold-start cost but can be significantly faster on large
arrays; actual speedups depend on workload, hardware, and versions.
Symbolic vectors with SymPy — derive formulas or generate code:
import sympy
x, y, z, t = sympy.symbols("x y z t", real=True)
v = vector.VectorSympy4D(x=x, y=y, z=z, t=t)
v.rho
v.is_timelike()
expr = v.boost(v.to_beta3()).t
expr.simplify()
expr.subs({x: 3, y: 2, z: 1, t: 10})
import sympy.printing.fortran
print(sympy.printing.fortran.fcode(expr.simplify()))
PyTree integration — flatten vector state for scipy/optree algorithms:
pytree = vector.register_pytree()
state = {
"position": vector.obj(x=1.0, y=2.0, z=3.0, t=0.0),
"momentum": vector.obj(x=0.0, y=10.0, z=0.0, t=14.0),
}
flat, treedef = pytree.flatten(state)
reconstructed = pytree.unflatten(treedef, flat)
def wrapped_solve(fun, t_span, y0, t_eval):
flat_y0, treedef = pytree.flatten(y0)
def flat_fun(t, flat_y):
state = pytree.unflatten(treedef, flat_y)
dstate_dt = fun(t, state)
flat_dstate_dt, _ = pytree.flatten(dstate_dt)
return flat_dstate_dt
from scipy.integrate import solve_ivp
sol = solve_ivp(flat_fun, t_span, flat_y0, t_eval=t_eval)
return pytree.unflatten(treedef, sol.y)
Gotchas
-
register_awkward() before any vector access: Calling .deltaR() on an
ak.Array without prior registration raises AttributeError. If you see
this, you forgot the registration call.
-
vector.obj is a scalar, not an array: It represents one vector. For
arrays of vectors use vector.array(...) (NumPy backend) or
ak.zip(..., with_name="Momentum4D") (awkward backend). Lists of vector.obj
are slow in plain Python — use vector.array or Numba instead.
-
NumPy backend Numba support is incomplete: @nb.njit works with
vector.obj and vector.Array (awkward), but NumPy array (vector.array)
support inside Numba is incomplete (upstream issue [#43]).
-
Multiple spellings are valid for energy and mass — all four rows below are
recognized by every backend (objects, NumPy, awkward):
| temporal coord | synonyms | notes |
|---|
| Cartesian time | t, e, E, energy | four-momentum energy component |
| proper time | tau, m, M, mass | invariant mass / proper time |
The real constraint is don't mix coordinate systems: pt/phi/eta/energy
is fine; pt with px is not — vector picks the convention from the full set
of field names and raises if they conflict.
-
SymPy sign convention differs for space-like/negative time-like 4-vectors:
The SymPy backend avoids piecewise if-then branches in symbolic expressions,
so its conventions for such vectors differ from other backends (which follow
ROOT). For physical momentum vectors (positive time-like), all backends agree.
-
register_pytree() requires optree: optree is not a default vector
dependency. Install it separately; register_pytree() raises ImportError
without it. Call it once at module level before any flatten/unflatten use.
-
Units are your responsibility: Vector does no unit conversion. If pT is in
MeV, masses and energies are in MeV throughout. Divide by 1000 explicitly
before presenting in GeV.
-
with_name is required for ak.zip to get behavior:
ak.zip({...}, with_name="Momentum4D"). Without it, records are plain dicts.
-
Addition of 4-vectors: j1 + j2 returns a new Momentum4D vector —
invariant mass is then (j1 + j2).mass.
Interop
- awkward: Required for jagged/variable-length collections;
register_awkward() enables methods on ak.Array records
- uproot: Fields read from ROOT files usually need renaming to match vector
conventions
- hist: Compute quantities with vector, then flatten and fill
Hist
- numpy:
vector.array(...) creates NumPy-backed vectors for fixed-shape
data; vectorized but not suitable for variable-length/jagged structure
- numba:
@nb.njit works with vector.obj and vector.Array (awkward);
best for custom loops over large ragged arrays where awkward vectorization
cannot express the logic
- sympy:
VectorSympy2D/3D/4D and MomentumSympy2D/3D/4D give fully
symbolic coordinate transforms; useful for deriving kinematic formulas and
code-generating Fortran/C/LaTeX output
- optree:
vector.register_pytree() enables flatten/unflatten of vector
objects and nested structures containing them; integrates with
scipy.integrate, optimizers, and any algorithm expecting a 1D numeric array
Docs
https://vector.readthedocs.io/en/latest/