Vector objects#
First, install and import Vector.
[1]:
import vector
Making a vector#
If you only need a few vectors or performance is not a concern, you can make vectors as Python objects. The basic constructor for that is vector.obj, and the type of vector (2D/3D/4D, coordinate system, geometric or momentum) depends on the pattern of keyword arguments provided.
Below is a 2D, Cartesian, geometric vector:
[2]:
vector.obj(x=1.1, y=2.2)
[2]:
VectorObject2D(x=1.1, y=2.2)
Below is a 3D, Cartesian, momentum vector:
[3]:
vector.obj(px=1.1, py=2.2, pz=3.3)
[3]:
MomentumObject3D(px=1.1, py=2.2, pz=3.3)
Below is a 4D geometric vector that has Cartesian azimuthal components (x and y), the longitudinal component is expressed in pseudorapidity, and the temporal component is expressed using proper time:
[4]:
vector.obj(x=1.1, y=2.2, eta=3.3, tau=4.4)
[4]:
VectorObject4D(x=1.1, y=2.2, eta=3.3, tau=4.4)
The allowed keyword arguments for 2D vectors are:
xandyfor Cartesian azimuthal coordinates,px(\(p_x\)) andpy(\(p_y\)) for momentum,rho(\(\rho\)) andphi(\(\phi\)) for polar azimuthal coordinates,pt(\(p_T\)) andphi(\(\phi\)) for momentum.
For 3D vectors, you need the above and:
zfor the Cartesian longitudinal coordinate,pz(\(p_z\)) for momentum,theta(\(\theta\)) for the spherical polar angle (from \(0\) to \(\pi\), inclusive),eta(\(\eta\)) for pseudorapidity, which is a kind of spherical polar angle: \(\eta = -\ln \left[ \tan \left( \frac{\theta}{2} \right) \right]\).
For 4D vectors, you need the above and:
tfor the Cartesian temporal coordinate,e,E, orenergyto get four-momentum,tau(\(\tau\)) for the “proper time” (temporal coordinate in the vector’s rest coordinate system),m,M, ormassto get four-momentum.
Since momentum vectors have momentum-synonyms in addition to the geometrical names, any momentum-synonym will make the whole vector a momentum vector. The meanings of the geometric components are illustrated below:
This one constructor, vector.obj, can output a variety of data types. If you want to control the type more explicitly, you can use vector.VectorObject2D, vector.MomentumObject2D, vector.VectorObject3D, vector.MomentumObject3D, vector.VectorObject4D, and
vector.MomentumObject4D to construct or check the type explicitly. These classes also have from_* methods to construct vectors from positional arguments, rather than keyword arguments.
Using a vector#
Vector objects have a suite of properties and methods appropriate to their type (2D/3D/4D, geometric or momentum). For example, to compute the cross-product of two vectors, you would use cross:
[5]:
a = vector.obj(x=2, y=3, z=4)
b = vector.obj(x=1, y=0, z=2)
a.cross(b)
[5]:
VectorObject3D(x=6, y=0, z=-3)
or to compute the angle between them, you would use deltaangle:
[6]:
a.deltaangle(b)
[6]:
0.590872750145419
or to compute their sum, you would use +:
[7]:
a + b
[7]:
VectorObject3D(x=3, y=3, z=6)
In this last example, the + operator overloads the add method. Similarly, multiplication between a vector and a scalar number overloads scale, etc. Since they overload standard operators, vectors can be used in Python built-in functions like sum, as long as you provide a start:
[8]:
vs = [vector.obj(x=x, y=x / 10) for x in range(10)]
sum(vs, start=vector.obj(x=0, y=0))
[8]:
VectorObject2D(x=45, y=4.5)
The same applies to abs for the vector’s magnitude, but note that this depends on the number of dimensions:
[9]:
abs(vector.obj(x=3, y=4)) # sqrt(3**2 + 4**2)
[9]:
5.0
[10]:
abs(vector.obj(x=1, y=2, z=2)) # sqrt(1**2 + 2**2 + 2**2)
[10]:
3.0
[11]:
abs(vector.obj(x=3, y=3, z=3, t=6)) # sqrt(6**2 - 3**2 - 3**2 - 3**2)
[11]:
3.0
Equality (equal) and inequality (not_equal) are defined:
[12]:
vector.obj(x=3, y=4) == vector.obj(x=3, y=4)
[12]:
True
But you’ll probably want to use isclose (and possibly specify tolerances):
[13]:
vector.obj(x=3, y=4) == vector.obj(rho=5, phi=0.9272952180016122)
[13]:
False
[14]:
vector.obj(x=3, y=4).isclose(vector.obj(rho=5, phi=0.9272952180016122))
[14]:
True
The full set of properties and methods available to each type of vector (2D/3D/4D, geometric or momentum) is described in
Using coordinate systems#
A vector can be constructed using any combination of coordinate systems and computations will be performed using whatever coordinate system it has. Thus, after creating vectors, you can write code that does not depend on the coordinate system—it becomes a hidden implementation detail.
[15]:
a = vector.obj(x=2, y=1)
b = vector.obj(rho=3, phi=0)
a + b
[15]:
VectorObject2D(x=5.0, y=1.0)
Some of the properties of a vector are coordinates, so you can use Vector to convert coordinates.
[16]:
a.rho, a.phi
[16]:
(2.23606797749979, 0.4636476090008061)
Since the way that you access the original coordinates is the same as the way that you access converted coordinates,
[17]:
a.x, a.y
[17]:
(2, 1)
these conversions are part of the coordinate-abstraction.
For reasons of numerical precision, you might want to open this black box and explicitly change the coordinate system. These methods start with to_*:
[18]:
a.to_rhophi()
[18]:
VectorObject2D(rho=2.23606797749979, phi=0.4636476090008061)
[19]:
b.to_xy()
[19]:
VectorObject2D(x=3.0, y=0.0)
Geometric versus momentum vectors#
Vectors come in two flavors:
geometric: only one name for each property or method
momentum: same property or method can be accessed with several synonyms (which assume that the vector is a momentum vector).
[20]:
v = vector.obj(x=1, y=2, z=3)
v
[20]:
VectorObject3D(x=1, y=2, z=3)
[21]:
p = vector.obj(px=1, py=2, pz=3)
p
[21]:
MomentumObject3D(px=1, py=2, pz=3)
Calculations are the same in both cases:
[22]:
abs(v)
[22]:
3.7416573867739413
[23]:
abs(p)
[23]:
3.7416573867739413
but there are more ways to express some operations:
[24]:
v.rho
[24]:
2.23606797749979
[25]:
p.rho
[25]:
2.23606797749979
[26]:
p.pt
[26]:
2.23606797749979
The geometric vector satisfies the Zen of Python stipulation that
There should be one– and preferably only one –obvious way to do it.
and code that uses, for example, “pt” to specify “distance from the beamline” is obfuscated code. However, the most common use for these vectors in High Energy Physics (HEP) is to represent the momentum of particles. For that purpose, using “rho” for \(p_T\) is not self-documenting.
Momentum vectors have all of the same properties and methods as geometric vectors as well as momentum synonyms. In some cases, there are multiple momentum synonyms for adherence to different conventions. For example, energy and mass (the temporal component of momentum, as Cartesian and proper time, respectively) have four different spellings:
energy spelling |
mass spelling |
rationale |
|---|---|---|
|
|
geometric coordinates; \(\tau\) for proper time is conventional |
|
|
full names are more self-documenting in the code |
|
|
all other coordinates are lower-case single letters (sometimes Greek letters) |
|
|
capital E and M (only!) are used in other HEP vector libraries |
If any momentum components are used to construct a vector (or if vector.MomentumObject2D, vector.MomentumObject3D, or vector.MomentumObject4D are used explicitly), then the vector is momentum and all synonyms become available.
Numeric data types and numerical error#
Vector does not require any specific numeric data type, such as np.float32 or np.float64, it only requires that vector components are some kind of number, including integers.
[27]:
v = vector.obj(x=1, y=2.2)
[28]:
type(v.x)
[28]:
int
[29]:
type(v.y)
[29]:
float
[30]:
import numpy as np
[31]:
v = vector.obj(x=np.float32(1.1), y=np.float64(2.2))
[32]:
type(v.x)
[32]:
numpy.float32
[33]:
type(v.y)
[33]:
numpy.float64
The same formulas are applied, regardless of the numeric type, so if the numerical error is larger than you expect it to be, check your types (and coordinate systems).
Application to other backends#
Everything stated above about vector objects (except their methods of construction) apply equally to all other backends. Arrays of vectors and symbolic vector expressions in SymPy have the same properties and methods as vector objects, they can hide choice of coordinate system as an abstraction, and the set of synonyms can be minimal for geometric vectors and maximal for momentum vectors. Therefore, it can be convenient to use vector objects as a quick way to debug issues in large arrays. However, note that different backends can use different libraries for computations, and results might differ in numerical error.
In particular, note that SymPy vector expressions have a different sign convention for operations on space-like and negative time-like 4D vectors. For all other backends, Vector’s conventions were chosen to agree with popular HEP libraries, particularly ROOT, but for the SymPy backend, those conventions would insert piecewise if-then branches, which would complicate symbolic expressions.