Version: SMASH-3.1
propagation.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015-2023
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/propagation.h"
11 
12 #include "smash/boxmodus.h"
13 #include "smash/collidermodus.h"
14 #include "smash/listmodus.h"
15 #include "smash/logging.h"
16 #include "smash/spheremodus.h"
17 
18 namespace smash {
19 static constexpr int LPropagation = LogArea::Propagation::id;
20 
21 double calc_hubble(double time, const ExpansionProperties &metric) {
22  double h; // Hubble parameter
23 
24  switch (metric.mode_) {
26  h = 0.;
27  break;
29  h = metric.b_ / (2 * (metric.b_ * time + 1));
30  break;
32  h = 2 * metric.b_ / (3 * (metric.b_ * time + 1));
33  break;
35  h = metric.b_ * time;
36  break;
37  default:
38  h = 0.;
39  }
40 
41  return h;
42 }
43 
44 double propagate_straight_line(Particles *particles, double to_time,
45  const std::vector<FourVector> &beam_momentum) {
46  bool negative_dt_error = false;
47  double dt = 0.0;
48  for (ParticleData &data : *particles) {
49  const double t0 = data.position().x0();
50  dt = to_time - t0;
51  if (dt < 0.0 && !negative_dt_error) {
52  // Print error message once, not for every particle
53  negative_dt_error = true;
54  logg[LPropagation].error("propagate_straight_line - negative dt = ", dt);
55  }
56  assert(dt >= 0.0);
57  /* "Frozen Fermi motion": Fermi momenta are only used for collisions,
58  * but not for propagation. This is done to avoid nucleus flying apart
59  * even if potentials are off. Initial nucleons before the first collision
60  * are propagated only according to beam momentum.
61  * Initial nucleons are distinguished by data.id() < the size of
62  * beam_momentum, which is by default zero except for the collider modus
63  * with the fermi motion == frozen.
64  * todo(m. mayer): improve this condition (see comment #11 issue #4213)*/
65  assert(data.id() >= 0);
66  const bool avoid_fermi_motion =
67  (static_cast<uint64_t>(data.id()) <
68  static_cast<uint64_t>(beam_momentum.size())) &&
69  (data.get_history().collisions_per_particle == 0);
70  ThreeVector v;
71  if (avoid_fermi_motion) {
72  const FourVector vbeam = beam_momentum[data.id()];
73  v = vbeam.velocity();
74  } else {
75  v = data.velocity();
76  }
77  const FourVector distance = FourVector(0.0, v * dt);
78  logg[LPropagation].debug("Particle ", data, " motion: ", distance);
79  FourVector position = data.position() + distance;
80  position.set_x0(to_time);
81  data.set_4position(position);
82  }
83  return dt;
84 }
85 
86 void expand_space_time(Particles *particles,
87  const ExperimentParameters &parameters,
88  const ExpansionProperties &metric) {
89  const double dt = parameters.labclock->timestep_duration();
90  for (ParticleData &data : *particles) {
91  // Momentum and position modification to ensure appropriate expansion
92  const double h = calc_hubble(parameters.labclock->current_time(), metric);
93  FourVector delta_mom = FourVector(0.0, h * data.momentum().threevec() * dt);
94  FourVector expan_dist =
95  FourVector(0.0, h * data.position().threevec() * dt);
96 
97  logg[LPropagation].debug("Particle ", data,
98  " expansion motion: ", expan_dist);
99  // New position and momentum
100  FourVector position = data.position() + expan_dist;
101  FourVector momentum = data.momentum() - delta_mom;
102 
103  // set the new momentum and position variables
104  data.set_4position(position);
105  data.set_4momentum(momentum);
106  // force the on shell condition to ensure correct energy
107  data.set_4momentum(data.pole_mass(), data.momentum().threevec());
108  }
109 }
110 
112  std::vector<Particles> &ensembles, double dt, const Potentials &pot,
113  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *FB_lat,
114  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *FI3_lat,
115  RectangularLattice<std::pair<ThreeVector, ThreeVector>> *EM_lat,
116  DensityLattice *jB_lat) {
117  // Copy particles from ALL ensembles to a single list before propagation
118  // and calculate potentials from this list
119  ParticleList plist;
120  for (Particles &particles : ensembles) {
121  const ParticleList tmp = particles.copy_to_vector();
122  plist.insert(plist.end(), tmp.begin(), tmp.end());
123  }
124 
125  bool possibly_use_lattice =
126  (pot.use_skyrme() ? (FB_lat != nullptr) : true) &&
127  (pot.use_vdf() ? (FB_lat != nullptr) : true) &&
128  (pot.use_symmetry() ? (FI3_lat != nullptr) : true);
129  std::pair<ThreeVector, ThreeVector> FB, FI3, EM_fields;
130  double min_time_scale = std::numeric_limits<double>::infinity();
131 
132  for (Particles &particles : ensembles) {
133  for (ParticleData &data : particles) {
134  // Only baryons and nuclei will be affected by the potentials
135  if (!(data.is_baryon() || data.is_nucleus())) {
136  continue;
137  }
138  const auto scale = pot.force_scale(data.type());
139  const ThreeVector r = data.position().threevec();
140  /* Lattices can be used for calculation if 1-2 are fulfilled:
141  * 1) Required lattices are not nullptr - possibly_use_lattice
142  * 2) r is not out of required lattices */
143  const bool use_lattice =
144  possibly_use_lattice &&
145  (pot.use_skyrme() ? FB_lat->value_at(r, FB) : true) &&
146  (pot.use_vdf() ? FB_lat->value_at(r, FB) : true) &&
147  (pot.use_symmetry() ? FI3_lat->value_at(r, FI3) : true);
148  if (!use_lattice && !pot.use_potentials_outside_lattice()) {
149  continue;
150  }
151  if (!pot.use_skyrme() && !pot.use_vdf()) {
152  FB = std::make_pair(ThreeVector(0., 0., 0.), ThreeVector(0., 0., 0.));
153  }
154  if (!pot.use_symmetry()) {
155  FI3 = std::make_pair(ThreeVector(0., 0., 0.), ThreeVector(0., 0., 0.));
156  }
157  if (!use_lattice) {
158  const auto tmp = pot.all_forces(r, plist);
159  FB = std::make_pair(std::get<0>(tmp), std::get<1>(tmp));
160  FI3 = std::make_pair(std::get<2>(tmp), std::get<3>(tmp));
161  }
162  /* Floating point traps should be raised if the force is not overwritten
163  * with a meaningful value */
164  const auto sNaN = std::numeric_limits<double>::signaling_NaN();
165  ThreeVector force(sNaN, sNaN, sNaN);
166  if (pot.use_momentum_dependence()) {
168  jB_lat, data.position().threevec(), data.momentum().threevec(),
169  data.effective_mass(), plist);
170  force = -energy_grad * scale.first;
171  force +=
172  scale.second * data.type().isospin3_rel() *
173  (FI3.first + data.momentum().velocity().cross_product(FI3.second));
174  } else {
175  force = scale.first *
176  (FB.first +
177  data.momentum().velocity().cross_product(FB.second)) +
178  scale.second * data.type().isospin3_rel() *
179  (FI3.first +
180  data.momentum().velocity().cross_product(FI3.second));
181  }
182  // Potentially add Lorentz force
183  if (pot.use_coulomb() && EM_lat->value_at(r, EM_fields)) {
184  // factor hbar*c to convert fields from 1/fm^2 to GeV/fm
185  force += hbarc * data.type().charge() * elementary_charge *
186  (EM_fields.first +
187  data.momentum().velocity().cross_product(EM_fields.second));
188  }
189  logg[LPropagation].debug("Update momenta: F [GeV/fm] = ", force);
190  data.set_4momentum(data.effective_mass(),
191  data.momentum().threevec() + force * dt);
192 
193  // calculate the time scale of the change in momentum
194  const double Force_abs = force.abs();
195  if (Force_abs < really_small) {
196  continue;
197  }
198  const double time_scale = data.momentum().x0() / Force_abs;
199  if (time_scale < min_time_scale) {
200  min_time_scale = time_scale;
201  }
202  }
203  }
204  // warn if the time step is too big
205  constexpr double safety_factor = 0.1;
206  if (dt > safety_factor * min_time_scale) {
207  logg[LPropagation].warn()
208  << "The time step size is too large for an accurate propagation "
209  << "with potentials. Maximum safe value: "
210  << safety_factor * min_time_scale << " fm.\n"
211  << "In case of Triangular or Discrete smearing you may additionally "
212  << "need to increase the number of ensembles or testparticles.";
213  }
214 }
215 
216 } // namespace smash
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
ThreeVector velocity() const
Get the velocity (3-vector divided by zero component).
Definition: fourvector.h:333
void set_x0(double t)
Definition: fourvector.h:315
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:58
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
A class that stores parameters of potentials, calculates potentials and their gradients.
Definition: potentials.h:36
virtual bool use_symmetry() const
Definition: potentials.h:435
ThreeVector single_particle_energy_gradient(DensityLattice *jB_lattice, const ThreeVector &position, const ThreeVector &momentum, double mass, ParticleList &plist) const
Calculates the gradient of the single-particle energy (including potentials) in the calculation frame...
Definition: potentials.h:65
virtual bool use_skyrme() const
Definition: potentials.h:433
bool use_potentials_outside_lattice() const
Definition: potentials.h:470
virtual bool use_coulomb() const
Definition: potentials.h:437
static std::pair< double, int > force_scale(const ParticleType &data)
Evaluates the scaling factor of the forces acting on the particles.
Definition: potentials.cc:156
virtual std::tuple< ThreeVector, ThreeVector, ThreeVector, ThreeVector > all_forces(const ThreeVector &r, const ParticleList &plist) const
Evaluates the electric and magnetic components of the forces at point r.
Definition: potentials.cc:272
virtual bool use_vdf() const
Definition: potentials.h:453
virtual bool use_momentum_dependence() const
Definition: potentials.h:439
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:47
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
double abs() const
Definition: threevector.h:268
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
Definition: action.h:24
void update_momenta(std::vector< Particles > &particles, double dt, const Potentials &pot, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FB_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *FI3_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *EM_lat, DensityLattice *jB_lat)
Updates the momenta of all particles at the current time step according to the equations of motion:
Definition: propagation.cc:111
void expand_space_time(Particles *particles, const ExperimentParameters &parameters, const ExpansionProperties &metric)
Modifies positions and momentum of all particles to account for space-time deformation.
Definition: propagation.cc:86
double propagate_straight_line(Particles *particles, double to_time, const std::vector< FourVector > &beam_momentum)
Propagates the positions of all particles on a straight line to a given moment.
Definition: propagation.cc:44
constexpr double hbarc
GeV <-> fm conversion factor.
Definition: constants.h:25
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:37
double calc_hubble(double time, const ExpansionProperties &metric)
Calculate the Hubble parameter , which describes how large the expansion flow is.
Definition: propagation.cc:21
static constexpr int LPropagation
Definition: propagation.cc:19
const double elementary_charge
Elementary electric charge in natural units, approximately 0.3.
Definition: constants.h:98
Struct containing the type of the metric and the expansion parameter of the metric.
Definition: propagation.h:26
double b_
Expansion parameter in the metric (faster expansion for larger values)
Definition: propagation.h:30
ExpansionMode mode_
Type of metric used.
Definition: propagation.h:28
Helper structure for Experiment.
std::unique_ptr< Clock > labclock
System clock (for simulation time keeping in the computational frame)