MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
marley_kinematics.cc
1
4//
5// This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6//
7// MARLEY is free software: you can redistribute it and/or modify it under the
8// terms of version 3 of the GNU General Public License as published by the
9// Free Software Foundation.
10//
11// For the full text of the license please see COPYING or
12// visit http://opensource.org/licenses/GPL-3.0
13//
14// Please respect the MCnet academic usage guidelines. See GUIDELINES
15// or visit https://www.montecarlonet.org/GUIDELINES for details.
16
17// HepMC3 includes
18#include "HepMC3/FourVector.h"
19#include "HepMC3/GenParticle.h"
20
21// MARLEY includes
22#include "marley/marley_utils.hh"
23#include "marley/marley_kinematics.hh"
24
25namespace {
26
27 double get_beta2(double beta_x, double beta_y, double beta_z)
28 {
29 double beta2 = std::pow(beta_x, 2) + std::pow(beta_y, 2)
30 + std::pow(beta_z, 2);
31 if (beta2 == 1) throw marley::Error(std::string("Cannot perform")
32 + " Lorentz boost because \u03B2^2 = 1 and therefore the Lorentz factor"
33 + "\u03B3 is infinite.");
34 else if (beta2 > 1) throw marley::Error(std::string("Cannot perform")
35 + " Lorentz boost because \u03B2^2 = " + std::to_string(beta2) + " > 1,"
36 + " which is unphysical.");
37 return beta2;
38 }
39
40}
41
42// Rotates a particle's 3-momentum so that it points in the (x, y, z) direction
43void marley_kinematics::rotate_momentum_vector( double x, double y, double z,
44 HepMC3::GenParticle& particle_to_rotate )
45{
46 // Get the magnitude of the vector pointing in the desired direction
47 double r = std::sqrt( std::pow(x, 2) + std::pow(y, 2) + std::pow(z, 2) );
48
49 // Get magnitude of the particle's 3-momentum
50 HepMC3::FourVector mom4 = particle_to_rotate.momentum();
51 double rp = mom4.p3mod();
52
53 // Rotate the particle's momentum vector into the desired direction
54 double ratio = rp / r;
55
56 double new_px = x * ratio;
57 double new_py = y * ratio;
58 double new_pz = z * ratio;
59
60 mom4.set_px( new_px );
61 mom4.set_py( new_py );
62 mom4.set_pz( new_pz );
63
64 particle_to_rotate.set_momentum( mom4 );
65}
66
67// Lorentz boost a particle, replacing its energy and momentum with the boosted
68// versions
69void marley_kinematics::lorentz_boost( double beta_x, double beta_y,
70 double beta_z, HepMC3::GenParticle& particle_to_boost )
71{
72 double beta2 = get_beta2(beta_x, beta_y, beta_z);
73
74 // If beta is zero in all directions, then we don't need to do the boost at
75 // all
76 if ( beta2 == 0. ) return;
77
78 // Calculate the Lorentz factor based on the boost velocity
79 double gamma = 1. / std::sqrt( 1. - beta2 );
80
81 HepMC3::FourVector mom4 = particle_to_boost.momentum();
82
83 double E = mom4.e();
84 double px = mom4.px();
85 double py = mom4.py();
86 double pz = mom4.pz();
87 double m = particle_to_boost.generated_mass();
88
89 // Compute the boosted energy and 3-momentum for the particle. The
90 // expressions we use here are based on
91 // https://en.wikipedia.org/wiki/Lorentz_transformation#Boost_in_any_direction
92 double beta_dot_p = beta_x * px + beta_y * py + beta_z * pz;
93 double factor = ( gamma - 1. ) * beta_dot_p / beta2;
94
95 double new_E = gamma * ( E - beta_dot_p );
96
97 // The new energy could conceivably dip slightly below the mass
98 // of the particle due to roundoff errors. If this is the case,
99 // set it to the particle mass.
100 if (new_E < m) new_E = m;
101
102 double new_px = ( -gamma * E + factor ) * beta_x + px;
103 double new_py = ( -gamma * E + factor ) * beta_y + py;
104 double new_pz = ( -gamma * E + factor ) * beta_z + pz;
105
106 mom4.set_e( new_E );
107 mom4.set_px( new_px );
108 mom4.set_py( new_py );
109 mom4.set_pz( new_pz );
110
111 particle_to_boost.set_momentum( mom4 );
112}
113
114// Right now, this function assumes that the coordinate axes in the lab and
115// initial particle rest frames are coincident (i.e., the coordinate axes
116// aren't rotated between the two frames at all). Since all interactions in the
117// current version of the code are isotropic (except for the initial 2-body
118// neutrino scattering reaction, which occurs for an incident neutrino
119// traveling in the positive z direction in the lab frame [target nucleus's
120// rest frame]), this is all we need for now.
121// TODO: Expand this to allow the two frames to be rotated with respect to each
122// other.
123void marley_kinematics::two_body_decay(
124 const std::shared_ptr< HepMC3::GenParticle >& initial_particle,
125 std::shared_ptr< HepMC3::GenParticle >& first_product,
126 std::shared_ptr< HepMC3::GenParticle >& second_product,
127 double cos_theta_first, double phi_first)
128{
129 // Get the masses of all three particles
130 double M = initial_particle->generated_mass();
131 double mfirst = first_product->generated_mass();
132 double msecond = second_product->generated_mass();
133
134 // Check to make sure the decay is kinematically allowed
135 if ( M < mfirst + msecond ) throw marley::Error( "A two-body decay was"
136 " requested that is not kinematically allowed." );
137
138 double M2 = std::pow( M, 2 );
139 double mfirst2 = std::pow( mfirst, 2 );
140 double msecond2 = std::pow( msecond, 2 );
141
142 // Compute the energies for the decay products in the rest
143 // frame of the initial particle
144 double Efirst = ( M2 - msecond2 + mfirst2 ) / ( 2 * M );
145 // M - Efirst == (M2 - mfirst2 + msecond2) / (2 * M)
146 double Esecond = M - Efirst;
147
148 // Avoid roundoff issues by not allowing the energies to dip below
149 // the particle masses
150 if ( Efirst < mfirst ) Efirst = mfirst;
151 if ( Esecond < msecond ) Esecond = msecond;
152
153 // Compute the 3-momenta for the decay products, still in the initial
154 // particle's rest frame
155 double pfirst = marley_utils::real_sqrt( std::pow(Efirst, 2) - mfirst2 );
156 double sin_theta_first = marley_utils::real_sqrt( 1.
157 - std::pow(cos_theta_first, 2) );
158 double p1x = pfirst * sin_theta_first * std::cos( phi_first );
159 double p1y = pfirst * sin_theta_first * std::sin( phi_first );
160 double p1z = pfirst * cos_theta_first;
161
162 // Conservation of 3-momenta tells us that, in the rest frame of the
163 // initial particle, p1 = -p2. We can use this as a shortcut.
164 double p2x = -p1x;
165 double p2y = -p1y;
166 double p2z = -p1z;
167
168 // Now that we have this information, load it into the product
169 // particles.
170 HepMC3::FourVector first_mom4( p1x, p1y, p1z, Efirst );
171 first_product->set_momentum( first_mom4 );
172
173 HepMC3::FourVector second_mom4( p2x, p2y, p2z, Esecond );
174 second_product->set_momentum( second_mom4 );
175
176 // Compute the parameters needed to boost these particles
177 // from the initial particle's rest frame into the lab frame
178 const HepMC3::FourVector& initial_mom4 = initial_particle->momentum();
179 double E_i = initial_mom4.e();
180 double px_i = initial_mom4.px();
181 double py_i = initial_mom4.py();
182 double pz_i = initial_mom4.pz();
183
184 // Boost in the opposite direction (this gives us the minus signs below) from
185 // the 3-momentum of rest_frame_particle. This takes the null 3-vector to the
186 // initial particle's 3-momentum.
187 double beta_x = -px_i / E_i;
188 double beta_y = -py_i / E_i;
189 double beta_z = -pz_i / E_i;
190
191 // Boost both products to the lab frame by replacing their
192 // energies and momenta with the boosted versions
193 lorentz_boost( beta_x, beta_y, beta_z, *first_product );
194 lorentz_boost( beta_x, beta_y, beta_z, *second_product );
195}
196
197// Get the square of the total energy of two particles in their center of
198// momentum frame
199double marley_kinematics::get_mandelstam_s(
200 const HepMC3::GenParticle& p1, const HepMC3::GenParticle& p2)
201{
202 const HepMC3::FourVector p1_mom4 = p1.momentum();
203 const HepMC3::FourVector p2_mom4 = p2.momentum();
204
205 double E1 = p1_mom4.e();
206 double m1 = p1.generated_mass();
207
208 double E2 = p2_mom4.e();
209 double m2 = p2.generated_mass();
210
211 // If one of the particles is at rest, use a shortcut. Otherwise,
212 // Lorentz transform to the center of momentum frame to determine
213 // the total cm frame energy
214 if (E1 == m1) return std::pow(m1, 2) + std::pow(m2, 2) + 2 * m1 * E2;
215 else if (E2 == m2) return std::pow(m1, 2) + std::pow(m2, 2) + 2 * m2 * E1;
216 else {
217 // Get total energy, momentum, and mass values for the two particles
218 double E_tot = E1 + E2;
219 double px_tot = p1_mom4.px() + p2_mom4.px();
220 double py_tot = p1_mom4.py() + p2_mom4.py();
221 double pz_tot = p1_mom4.pz() + p2_mom4.pz();
222 double m_tot = m1 + m2;
223
224 // Get boost parameters for a Lorentz transform to the center of momentum
225 // frame
226 double beta_x = px_tot / E_tot;
227 double beta_y = py_tot / E_tot;
228 double beta_z = pz_tot / E_tot;
229
230 // Calculate the Lorentz factor based on the boost velocity
231 double beta2 = get_beta2( beta_x, beta_y, beta_z );
232 double gamma = 1.0 / std::sqrt( 1.0 - beta2 );
233
234 // Compute the total boosted energy in the cm frame
235 double beta_dot_p_tot = beta_x * px_tot + beta_y * py_tot + beta_z * pz_tot;
236 double E_tot_cm = gamma * ( E_tot - beta_dot_p_tot );
237
238 // The new energy could conceivably dip slightly below the total rest mass
239 // of the particles due to roundoff errors. If this is the case,
240 // set it to the total rest mass.
241 if ( E_tot_cm < m_tot ) E_tot_cm = m_tot;
242
243 return std::pow( E_tot_cm, 2 );
244 }
245
246}
247
248
249// Boost two particles into their mutual CM frame
250void marley_kinematics::boost_to_cm_frame( HepMC3::GenParticle& p1,
252{
253 // Get parameters for a Lorentz boost to the center of momentum frame
254 const HepMC3::FourVector& p1_mom4 = p1.momentum();
255 const HepMC3::FourVector& p2_mom4 = p2.momentum();
256
257 double E_tot = p1_mom4.e() + p2_mom4.e();
258 double px_tot = p1_mom4.px() + p2_mom4.px();
259 double py_tot = p1_mom4.py() + p2_mom4.py();
260 double pz_tot = p1_mom4.pz() + p2_mom4.pz();
261
262 double beta_x = px_tot / E_tot;
263 double beta_y = py_tot / E_tot;
264 double beta_z = pz_tot / E_tot;
265
266 lorentz_boost( beta_x, beta_y, beta_z, p1 );
267 lorentz_boost( beta_x, beta_y, beta_z, p2 );
268}
Generic 4-vector.
Definition FourVector.h:36
void set_py(double pyy)
Set y-component of momentum.
Definition FourVector.h:123
void set_px(double pxx)
Set x-component of momentum.
Definition FourVector.h:116
double px() const
x-component of momentum
Definition FourVector.h:114
double py() const
y-component of momentum
Definition FourVector.h:121
double p3mod() const
Magnitude of p3 = (px, py, pz) vector.
Definition FourVector.h:163
double pz() const
z-component of momentum
Definition FourVector.h:128
void set_pz(double pzz)
Set z-component of momentum.
Definition FourVector.h:130
double e() const
Energy component of momentum.
Definition FourVector.h:135
void set_e(double ee)
Set energy component of momentum.
Definition FourVector.h:137
Stores particle-related information.
Definition GenParticle.h:34
void set_momentum(const FourVector &momentum)
Set momentum.
const FourVector & momentum() const
Get momentum.
Definition GenParticle.h:97
double generated_mass() const
Get generated mass.
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26