Version: SMASH-3.4
density.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2013-2022,2024,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/density.h"
11 
12 #include "smash/constants.h"
13 #include "smash/logging.h"
14 
15 namespace smash {
16 
17 double density_factor(const ParticleType &type, DensityType dens_type) {
18  switch (dens_type) {
20  return type.is_hadron() ? 1. : 0.;
22  return static_cast<double>(type.baryon_number());
24  return type.is_baryon() || type.is_nucleus() ? type.isospin3_rel() : 0.;
25  case DensityType::Pion:
26  return type.pdgcode().is_pion() ? 1. : 0.;
28  return type.is_hadron() ? type.isospin3() : 0.;
30  return static_cast<double>(type.charge());
32  return static_cast<double>(type.strangeness());
33  default:
34  return 0.;
35  }
36 }
37 
38 std::pair<double, ThreeVector> unnormalized_smearing_factor(
39  const ThreeVector &r, const FourVector &p, const double m_inv,
40  const DensityParameters &dens_par, const bool compute_gradient) {
41  const double r_sqr = r.sqr();
42  // Distance from particle to point of interest > r_cut
43  if (r_sqr > dens_par.r_cut_sqr()) {
44  return std::make_pair(0.0, ThreeVector(0.0, 0.0, 0.0));
45  }
46 
47  const FourVector u = p * m_inv;
48  const double u_r_scalar = r * u.threevec();
49  const double r_rest_sqr = r_sqr + u_r_scalar * u_r_scalar;
50 
51  // Lorentz contracted distance from particle to point of interest > r_cut
52  if (r_rest_sqr > dens_par.r_cut_sqr()) {
53  return std::make_pair(0.0, ThreeVector(0.0, 0.0, 0.0));
54  }
55  const double sf = std::exp(-r_rest_sqr * dens_par.two_sig_sqr_inv()) * u.x0();
56  const ThreeVector sf_grad = compute_gradient
57  ? sf * (r + u.threevec() * u_r_scalar) *
58  dens_par.two_sig_sqr_inv() * 2.0
59  : ThreeVector(0.0, 0.0, 0.0);
60 
61  return std::make_pair(sf, sf_grad);
62 }
63 
64 /// \copydoc smash::current_eckart
65 template <typename /*ParticlesContainer*/ T>
66 std::tuple<double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector,
67  FourVector, FourVector>
68 current_eckart_impl(const ThreeVector &r, const T &plist,
69  const DensityParameters &par, DensityType dens_type,
70  bool compute_gradient, bool smearing) {
71  /* The current density of the positively and negatively charged particles.
72  * Division into positive and negative charges is necessary to avoid
73  * problems with the Eckart frame definition. Example of problem:
74  * get Eckart frame for two identical oppositely flying bunches of
75  * electrons and positrons. For this case jmu = (0, 0, 0, non-zero),
76  * so jmu.abs does not exist and Eckart frame is not defined.
77  * If one takes rho = jmu_pos.abs - jmu_neg.abs, it is still Lorentz-
78  * invariant and gives the right limit in non-relativistic case, but
79  * it gives no such problem. */
80  FourVector jmu_pos, jmu_neg;
81  /* The array of the derivatives of the current density.
82  * The zeroth component is the time derivative,
83  * while the next 3 ones are spacial derivatives. */
84  std::array<FourVector, 4> djmu_dxnu;
85 
86  for (const auto &p : plist) {
87  if (par.only_participants()) {
88  // if this conditions holds, the hadron is a spectator
89  if (p.get_history().collisions_per_particle == 0) {
90  continue;
91  }
92  if (par.ignore_unformed()) {
93  if (p.xsec_scaling_factor() < 1) {
94  continue;
95  }
96  }
97  }
98  const double dens_factor = density_factor(p.type(), dens_type);
99  if (std::fabs(dens_factor) < really_small) {
100  continue;
101  }
102  const FourVector mom = p.momentum();
103  const double m = mom.abs();
104  if (m < really_small) {
105  continue;
106  }
107  const double m_inv = 1.0 / m;
108  const auto sf_and_grad = unnormalized_smearing_factor(
109  p.position().threevec() - r, mom, m_inv, par, compute_gradient);
110  const FourVector tmp = mom * (dens_factor / mom.x0());
111  if (smearing) {
112  if (dens_factor > 0.) {
113  jmu_pos += tmp * sf_and_grad.first;
114  } else {
115  jmu_neg += tmp * sf_and_grad.first;
116  }
117  } else {
118  if (dens_factor > 0.) {
119  jmu_pos += tmp;
120  } else {
121  jmu_neg += tmp;
122  }
123  }
124  if (compute_gradient) {
125  for (int k = 1; k <= 3; k++) {
126  djmu_dxnu[k] += tmp * sf_and_grad.second[k - 1];
127  djmu_dxnu[0] -= tmp * sf_and_grad.second[k - 1] *
128  tmp.threevec()[k - 1] / dens_factor;
129  }
130  }
131  }
132 
133  // Eckart density (rest frame density)
134  const double rho_eck = (jmu_pos.abs() - jmu_neg.abs()) * par.norm_factor_sf();
135 
136  // $\partial_t j^{\mu}$
137  const FourVector djmu_dt = compute_gradient
138  ? djmu_dxnu[0] * par.norm_factor_sf()
139  : FourVector(0.0, 0.0, 0.0, 0.0);
140  // $\partial_x j^{\mu}$
141  const FourVector djmu_dx = compute_gradient
142  ? djmu_dxnu[1] * par.norm_factor_sf()
143  : FourVector(0.0, 0.0, 0.0, 0.0);
144  // $\partial_y j^{\mu}$
145  const FourVector djmu_dy = compute_gradient
146  ? djmu_dxnu[2] * par.norm_factor_sf()
147  : FourVector(0.0, 0.0, 0.0, 0.0);
148  // $\partial_z j^{\mu}$
149  const FourVector djmu_dz = compute_gradient
150  ? djmu_dxnu[3] * par.norm_factor_sf()
151  : FourVector(0.0, 0.0, 0.0, 0.0);
152 
153  // Gradient of j0
154  ThreeVector grad_j0 = ThreeVector(0.0, 0.0, 0.0);
155  // Curl of the 3-current density
156  ThreeVector curl_vecj = ThreeVector(0.0, 0.0, 0.0);
157  if (compute_gradient) {
158  curl_vecj.set_x1(djmu_dxnu[2].x3() - djmu_dxnu[3].x2());
159  curl_vecj.set_x2(djmu_dxnu[3].x1() - djmu_dxnu[1].x3());
160  curl_vecj.set_x3(djmu_dxnu[1].x2() - djmu_dxnu[2].x1());
161  curl_vecj *= par.norm_factor_sf();
162  for (int i = 1; i < 4; i++) {
163  grad_j0[i - 1] += djmu_dxnu[i].x0() * par.norm_factor_sf();
164  }
165  }
166  if (smearing) {
167  jmu_pos *= par.norm_factor_sf();
168  jmu_neg *= par.norm_factor_sf();
169  }
170  return std::make_tuple(rho_eck, jmu_pos + jmu_neg, grad_j0, curl_vecj,
171  djmu_dt, djmu_dx, djmu_dy, djmu_dz);
172 }
173 
174 std::tuple<double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector,
175  FourVector, FourVector>
176 current_eckart(const ThreeVector &r, const ParticleList &plist,
177  const DensityParameters &par, DensityType dens_type,
178  bool compute_gradient, bool smearing) {
179  return current_eckart_impl(r, plist, par, dens_type, compute_gradient,
180  smearing);
181 }
182 std::tuple<double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector,
183  FourVector, FourVector>
184 current_eckart(const ThreeVector &r, const Particles &plist,
185  const DensityParameters &par, DensityType dens_type,
186  bool compute_gradient, bool smearing) {
187  return current_eckart_impl(r, plist, par, dens_type, compute_gradient,
188  smearing);
189 }
190 
195  RectangularLattice<std::array<FourVector, 4>> *four_grad_lattice,
196  const LatticeUpdate update, const DensityType dens_type,
197  const DensityParameters &par, const std::vector<Particles> &ensembles,
198  const double time_step, const bool compute_gradient) {
199  // Do not proceed if lattice does not exists/update not required
200  if (lat == nullptr || lat->when_update() != update) {
201  return;
202  }
203  const std::array<int, 3> lattice_n_cells = lat->n_cells();
204  const int number_of_nodes =
205  lattice_n_cells[0] * lattice_n_cells[1] * lattice_n_cells[2];
206 
207  /*
208  * Take the provided DensityOnLattice lattice and use the information about
209  * the current to create a lattice of current FourVectors. Because the lattice
210  * hasn't been updated at this point yet, it provides the t_0 time step
211  * information on the currents.
212  */
213  // copy values of jmu at t_0 onto old_jmu;
214  // proceed only if finite difference gradients are calculated
216  for (int i = 0; i < number_of_nodes; i++) {
217  old_jmu->assign_value(i, ((*lat)[i]).jmu_net());
218  }
219  }
220 
221  update_lattice_accumulating_ensembles(lat, update, dens_type, par, ensembles,
222  compute_gradient);
223 
224  // calculate the gradients for finite difference derivatives
226  // copy values of jmu FourVectors at t_0 + time_step onto new_jmu
227  for (int i = 0; i < number_of_nodes; i++) {
228  new_jmu->assign_value(i, ((*lat)[i]).jmu_net());
229  }
230 
231  // compute time derivatives and gradients of all components of jmu
232  new_jmu->compute_four_gradient_lattice(*old_jmu, time_step,
233  *four_grad_lattice);
234 
235  // substitute new derivatives
236  int node_number = 0;
237  for (auto &node : *lat) {
238  auto tmp = (*four_grad_lattice)[node_number];
239  node.overwrite_djmu_dxnu(tmp[0], tmp[1], tmp[2], tmp[3]);
240  node_number++;
241  }
242  } // if (par.derivatives() == DerivativesMode::FiniteDifference)
243 
244  // calculate gradients of rest frame density
246  for (auto &node : *lat) {
247  // the rest frame density
248  double rho = node.rho();
249  const int sgn = rho > 0 ? 1 : -1;
250  if (std::abs(rho) < very_small_double) {
251  rho = sgn * very_small_double;
252  }
253 
254  // the computational frame j^mu
255  const FourVector jmu = node.jmu_net();
256  // computational frame array of derivatives of j^mu
257  const std::array<FourVector, 4> djmu_dxnu = node.djmu_dxnu();
258 
259  const double drho_dt =
260  (1 / rho) *
261  (jmu.x0() * djmu_dxnu[0].x0() - jmu.x1() * djmu_dxnu[0].x1() -
262  jmu.x2() * djmu_dxnu[0].x2() - jmu.x3() * djmu_dxnu[0].x3());
263 
264  const double drho_dx =
265  (1 / rho) *
266  (jmu.x0() * djmu_dxnu[1].x0() - jmu.x1() * djmu_dxnu[1].x1() -
267  jmu.x2() * djmu_dxnu[1].x2() - jmu.x3() * djmu_dxnu[1].x3());
268 
269  const double drho_dy =
270  (1 / rho) *
271  (jmu.x0() * djmu_dxnu[2].x0() - jmu.x1() * djmu_dxnu[2].x1() -
272  jmu.x2() * djmu_dxnu[2].x2() - jmu.x3() * djmu_dxnu[2].x3());
273 
274  const double drho_dz =
275  (1 / rho) *
276  (jmu.x0() * djmu_dxnu[3].x0() - jmu.x1() * djmu_dxnu[3].x1() -
277  jmu.x2() * djmu_dxnu[3].x2() - jmu.x3() * djmu_dxnu[3].x3());
278 
279  const FourVector drho_dxnu = {drho_dt, drho_dx, drho_dy, drho_dz};
280 
281  node.overwrite_drho_dxnu(drho_dxnu);
282  }
283  } // if (par.rho_derivatives() == RestFrameDensityDerivatives::On){
284 } // void update_lattice()
285 
286 std::ostream &operator<<(std::ostream &os, DensityType dens_type) {
287  switch (dens_type) {
288  case DensityType::Hadron:
289  os << "hadron density";
290  break;
291  case DensityType::Baryon:
292  os << "baryon density";
293  break;
295  os << "baryonic isospin density";
296  break;
297  case DensityType::Pion:
298  os << "pion density";
299  break;
301  os << "total isospin3 density";
302  break;
303  case DensityType::None:
304  os << "none";
305  break;
306  default:
307  os.setstate(std::ios_base::failbit);
308  }
309  return os;
310 }
311 
312 } // namespace smash
A class to pre-calculate and store parameters relevant for density calculation.
Definition: density.h:92
bool ignore_unformed() const
Definition: density.h:156
RestFrameDensityDerivativesMode rho_derivatives() const
Definition: density.h:132
bool only_participants() const
Definition: density.h:154
DerivativesMode derivatives() const
Definition: density.h:130
double two_sig_sqr_inv() const
Definition: density.h:146
double norm_factor_sf() const
Definition: density.h:152
double r_cut_sqr() const
Definition: density.h:144
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
double x3() const
Definition: fourvector.h:325
double x2() const
Definition: fourvector.h:321
double abs() const
calculate the lorentz invariant absolute value
Definition: fourvector.h:464
ThreeVector threevec() const
Definition: fourvector.h:329
double x0() const
Definition: fourvector.h:313
double x1() const
Definition: fourvector.h:317
Particle type contains the static properties of a particle species.
Definition: particletype.h:100
bool is_baryon() const
Definition: particletype.h:206
int isospin3() const
Definition: particletype.h:179
int strangeness() const
Definition: particletype.h:215
bool is_nucleus() const
Definition: particletype.h:254
PdgCode pdgcode() const
Definition: particletype.h:159
int32_t charge() const
The charge of the particle.
Definition: particletype.h:191
bool is_hadron() const
Definition: particletype.h:200
double isospin3_rel() const
Definition: particletype.h:182
int baryon_number() const
Definition: particletype.h:212
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
bool is_pion() const
Definition: pdgcode.h:471
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:49
LatticeUpdate when_update() const
Definition: lattice.h:171
void compute_four_gradient_lattice(RectangularLattice< FourVector > &old_lat, double time_step, RectangularLattice< std::array< FourVector, 4 >> &grad_lat) const
Compute a fourgradient on a lattice of FourVectors jmu via the finite difference method.
Definition: lattice.h:420
const std::array< int, 3 > & n_cells() const
Definition: lattice.h:159
void assign_value(int lattice_index, T value)
Overwrite with a template value T at a given node.
Definition: lattice.h:195
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
void set_x1(double x)
set first component
Definition: threevector.h:188
double sqr() const
Definition: threevector.h:275
void set_x3(double z)
set third component
Definition: threevector.h:196
void set_x2(double y)
set second component
Definition: threevector.h:192
Collection of useful constants that are known at compile time.
DensityType
Allows to choose which kind of density to calculate.
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Convenience: dereferences the ActionPtr to Action.
Definition: action.h:546
constexpr int p
Proton.
int sgn(T val)
Signum function.
Definition: random.h:207
Definition: action.h:24
std::tuple< double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector, FourVector, FourVector > current_eckart(const ThreeVector &r, const ParticleList &plist, const DensityParameters &par, DensityType dens_type, bool compute_gradient, bool smearing)
Calculates Eckart rest frame density and 4-current of a given density type and optionally the gradien...
Definition: density.cc:176
constexpr double very_small_double
A very small double, used to avoid division by zero.
Definition: constants.h:44
void update_lattice_accumulating_ensembles(RectangularLattice< T > *lat, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const bool compute_gradient)
Updates the contents on the lattice when ensembles are used.
Definition: density.h:654
std::tuple< double, FourVector, ThreeVector, ThreeVector, FourVector, FourVector, FourVector, FourVector > current_eckart_impl(const ThreeVector &r, const T &plist, const DensityParameters &par, DensityType dens_type, bool compute_gradient, bool smearing)
Calculates Eckart rest frame density and 4-current of a given density type and optionally the gradien...
Definition: density.cc:68
void update_lattice(RectangularLattice< DensityOnLattice > *lat, RectangularLattice< FourVector > *old_jmu, RectangularLattice< FourVector > *new_jmu, RectangularLattice< std::array< FourVector, 4 >> *four_grad_lattice, const LatticeUpdate update, const DensityType dens_type, const DensityParameters &par, const std::vector< Particles > &ensembles, const double time_step, const bool compute_gradient)
Updates the contents on the lattice of DensityOnLattice type.
Definition: density.cc:191
LatticeUpdate
Enumerator option for lattice updates.
Definition: lattice.h:38
std::pair< double, ThreeVector > unnormalized_smearing_factor(const ThreeVector &r, const FourVector &p, const double m_inv, const DensityParameters &dens_par, const bool compute_gradient=false)
Implements gaussian smearing for any quantity.
Definition: density.cc:38
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:41
double density_factor(const ParticleType &type, DensityType dens_type)
Get the factor that determines how much a particle contributes to the density type that is computed.
Definition: density.cc:17