Version: SMASH-1.6
nucleus.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2019
3  * SMASH Team
4  *
5  * GNU General Public License (GPLv3 or later)
6  */
7 #include "smash/nucleus.h"
8 
9 #include <fstream>
10 #include <iostream>
11 #include <limits>
12 #include <map>
13 #include <string>
14 
15 #include "smash/angles.h"
16 #include "smash/constants.h"
17 #include "smash/fourvector.h"
18 #include "smash/inputfunctions.h"
19 #include "smash/logging.h"
20 #include "smash/numerics.h"
21 #include "smash/particles.h"
22 #include "smash/pdgcode.h"
23 #include "smash/random.h"
24 #include "smash/threevector.h"
25 
26 namespace smash {
27 
28 Nucleus::Nucleus(const std::map<PdgCode, int> &particle_list, int nTest) {
29  fill_from_list(particle_list, nTest);
31 }
32 
33 Nucleus::Nucleus(Configuration &config, int nTest) {
34  // Fill nuclei with particles.
35  std::map<PdgCode, int> part = config.take({"Particles"});
36  fill_from_list(part, nTest);
37  // Look for user-defined values or take the default parameters.
38  if (config.has_value({"Diffusiveness"}) && config.has_value({"Radius"}) &&
39  config.has_value({"Saturation_Density"})) {
41  } else if (!config.has_value({"Diffusiveness"}) &&
42  !config.has_value({"Radius"}) &&
43  !config.has_value({"Saturation_Density"})) {
45  } else {
46  throw std::invalid_argument(
47  "Diffussiveness, Radius and Saturation_Density "
48  "required to manually configure the Woods-Saxon"
49  " distribution. Only one/two were provided. \n"
50  "Providing none of the above mentioned "
51  "parameters automatically configures the "
52  "distribution based on the atomic number.");
53  }
54 }
55 
56 double Nucleus::mass() const {
57  double total_mass = 0.;
58  for (auto i = cbegin(); i != cend(); i++) {
59  total_mass += i->momentum().abs();
60  }
61  return total_mass / (testparticles_ + 0.0);
62 }
63 
217  // Get the solid angle of the nucleon.
218  Angles dir;
220  // diffusiveness_ zero or negative? Use hard sphere.
221  if (almost_equal(diffusiveness_, 0.)) {
222  return dir.threevec() * nuclear_radius_ * std::cbrt(random::canonical());
223  }
224  if (almost_equal(nuclear_radius_, 0.)) {
225  return smash::ThreeVector();
226  }
227  double radius_scaled = nuclear_radius_ / diffusiveness_;
228  double prob_range1 = 1.0;
229  double prob_range2 = 3. / radius_scaled;
230  double prob_range3 = 2. * prob_range2 / radius_scaled;
231  double prob_range4 = 1. * prob_range3 / radius_scaled;
232  double ranges234 = prob_range2 + prob_range3 + prob_range4;
233  double t;
235  do {
236  double which_range = random::uniform(-prob_range1, ranges234);
237  if (which_range < 0.0) {
238  t = radius_scaled * (std::cbrt(random::canonical()) - 1.);
239  } else {
240  t = -std::log(random::canonical());
241  if (which_range >= prob_range2) {
242  t -= std::log(random::canonical());
243  if (which_range >= prob_range2 + prob_range3) {
244  t -= std::log(random::canonical());
245  }
246  }
247  }
255  } while (random::canonical() > 1. / (1. + std::exp(-std::abs(t))));
257  double position_scaled = t + radius_scaled;
258  double position = position_scaled * diffusiveness_;
259  return dir.threevec() * position;
260 }
261 
262 double Nucleus::woods_saxon(double r) {
263  return r * r / (std::exp((r - nuclear_radius_) / diffusiveness_) + 1);
264 }
265 
267  for (auto i = begin(); i != end(); i++) {
268  // Initialize momentum
269  i->set_4momentum(i->pole_mass(), 0.0, 0.0, 0.0);
270  /* Sampling the Woods-Saxon, get the radial
271  * position and solid angle for the nucleon. */
273 
274  // Set the position of the nucleon.
275  i->set_4position(FourVector(0.0, pos));
276  }
277 
278  // Recenter and rotate
279  align_center();
280  rotate();
281 }
282 
285  switch (A) {
286  case 1: // single particle
287  /* In case of testparticles, an infinite reaction loop will be
288  * avoided by a small finite spread according to a single particles
289  * 'nucleus'. The proper solution will be to introduce parallel
290  * ensembles. */
292  ? 0.
293  : 1. - std::exp(-(testparticles_ - 1.) * 0.1));
294  set_diffusiveness(testparticles_ == 1 ? -1. : 0.02);
295  break;
296  case 238: // Uranium
297  // Default values.
298  set_diffusiveness(0.556);
299  set_nuclear_radius(6.86);
300  set_saturation_density(0.166);
301  break;
302  case 208: // Lead
303  // Default values.
304  set_diffusiveness(0.54);
305  set_nuclear_radius(6.67);
306  set_saturation_density(0.161);
307  break;
308  case 197: // Gold
309  // Default values.
310  set_diffusiveness(0.535);
311  set_nuclear_radius(6.38);
312  set_saturation_density(0.1695);
313  break;
314  case 63: // Copper
315  // Default values.
316  set_diffusiveness(0.5977);
317  set_nuclear_radius(4.20641);
318  set_saturation_density(0.1686);
319  break;
320  default:
321  if (A <= 16) {
322  // radius: rough guess for all nuclei not listed explicitly with A <= 16
323  // saturation density already has reasonable default
324  set_nuclear_radius(1.2 * std::cbrt(A));
325  set_diffusiveness(0.545);
326  } else {
327  // radius and diffusiveness taken from \iref{Rybczynski:2013yba}
328  set_diffusiveness(0.54);
329  set_nuclear_radius(1.12 * std::pow(A, 1.0 / 3.0) -
330  0.86 * std::pow(A, -1.0 / 3.0));
331  }
332  }
333 }
334 
336  set_diffusiveness(static_cast<double>(config.take({"Diffusiveness"})));
337  set_nuclear_radius(static_cast<double>(config.take({"Radius"})));
338  // Saturation density (normalization for accept/reject sampling)
340  static_cast<double>(config.take({"Saturation_Density"})));
341 }
342 
344  const int N_n = std::count_if(begin(), end(), [](const ParticleData i) {
345  return i.pdgcode() == pdg::n;
346  });
347  const int N_p = std::count_if(begin(), end(), [](const ParticleData i) {
348  return i.pdgcode() == pdg::p;
349  });
350  const FourVector nucleus_center = center();
351  const int A = N_n + N_p;
352  constexpr double pi2_3 = 3.0 * M_PI * M_PI;
353  const auto &log = logger<LogArea::Nucleus>();
354 
355  log.debug() << N_n << " neutrons, " << N_p << " protons.";
356 
357  ThreeVector ptot = ThreeVector(0.0, 0.0, 0.0);
358  for (auto i = begin(); i != end(); i++) {
359  // Only protons and neutrons get Fermi momenta
360  if (i->pdgcode() != pdg::p && i->pdgcode() != pdg::n) {
361  if (i->is_baryon()) {
362  log.warn() << "No rule to calculate Fermi momentum "
363  << "for particle " << i->pdgcode();
364  }
365  continue;
366  }
367  const double r = (i->position() - nucleus_center).abs3();
368  double rho = nuclear_density /
369  (std::exp((r - nuclear_radius_) / diffusiveness_) + 1.);
370  if (i->pdgcode() == pdg::p) {
371  rho = rho * N_p / A;
372  }
373  if (i->pdgcode() == pdg::n) {
374  rho = rho * N_n / A;
375  }
376  const double p =
377  hbarc * std::pow(pi2_3 * rho * random::uniform(0.0, 1.0), 1.0 / 3.0);
378  Angles phitheta;
379  phitheta.distribute_isotropically();
380  const ThreeVector ith_3momentum = phitheta.threevec() * p;
381  ptot += ith_3momentum;
382  i->set_3momentum(ith_3momentum);
383  log.debug() << "Particle: " << *i
384  << ", pF[GeV]: " << hbarc * std::pow(pi2_3 * rho, 1.0 / 3.0)
385  << " r[fm]: " << r
386  << " Nuclear radius[fm]: " << nuclear_radius_;
387  }
388  if (A == 0) {
389  // No Fermi momenta should be assigned
390  assert(ptot.x1() == 0.0 && ptot.x2() == 0.0 && ptot.x3() == 0.0);
391  } else {
392  /* Ensure zero total momentum of nucleus - redistribute ptot equally
393  * among protons and neutrons */
394  const ThreeVector centralizer = ptot / A;
395  for (auto i = begin(); i != end(); i++) {
396  if (i->pdgcode() == pdg::p || i->pdgcode() == pdg::n) {
397  i->set_4momentum(i->pole_mass(),
398  i->momentum().threevec() - centralizer);
399  }
400  }
401  }
402 }
403 
404 void Nucleus::boost(double beta_scalar) {
405  double beta_squared = beta_scalar * beta_scalar;
406  double one_over_gamma = std::sqrt(1.0 - beta_squared);
407  double gamma = 1.0 / one_over_gamma;
408  /* We are talking about a /passive/ lorentz transformation here, as
409  * far as I can see, so we need to boost in the direction opposite to
410  * where we want to go
411  * ( The vector we transform - p - stays unchanged, but we go into
412  * a system that moves with -beta. Now in this frame, it seems
413  * like p has been accelerated with +beta.
414  * ) */
415  for (auto i = begin(); i != end(); i++) {
416  /* a real Lorentz Transformation would leave the particles at
417  * different times here, which we would then have to propagate back
418  * to equal times. Since we know the result, we can simply multiply
419  * the z-value with 1/gamma. */
420  FourVector this_position = i->position();
421  this_position.set_x3(this_position.x3() * one_over_gamma);
422  i->set_4position(this_position);
423  /* The simple Lorentz transformation of momenta does not take into account
424  * that nucleus has binding energy. Here we apply the method used
425  * in the JAM code \iref{Nara:1999dz}: p' = p_beam + gamma*p_F.
426  * This formula is derived under assumption that all nucleons have
427  * the same binding energy. */
428  FourVector mom_i = i->momentum();
429  i->set_4momentum(i->pole_mass(), mom_i.x1(), mom_i.x2(),
430  gamma * (beta_scalar * mom_i.x0() + mom_i.x3()));
431  }
432 }
433 
434 void Nucleus::fill_from_list(const std::map<PdgCode, int> &particle_list,
435  int testparticles) {
436  testparticles_ = testparticles;
437  for (auto n = particle_list.cbegin(); n != particle_list.cend(); ++n) {
438  const ParticleType &current_type = ParticleType::find(n->first);
439  double current_mass = current_type.mass();
440  for (unsigned int i = 0; i < n->second * testparticles_; i++) {
441  // append particle to list and set its PDG code.
442  particles_.emplace_back(current_type);
443  particles_.back().set_4momentum(current_mass, 0.0, 0.0, 0.0);
444  }
445  }
446 }
447 
448 void Nucleus::shift(double z_offset, double x_offset, double simulation_time) {
449  // Move the nucleus in z and x directions, and set the time.
450  for (auto i = begin(); i != end(); i++) {
451  FourVector this_position = i->position();
452  this_position.set_x3(this_position.x3() + z_offset);
453  this_position.set_x1(this_position.x1() + x_offset);
454  this_position.set_x0(simulation_time);
455  i->set_4position(this_position);
456  i->set_formation_time(simulation_time);
457  }
458 }
459 
460 void Nucleus::copy_particles(Particles *external_particles) {
461  for (auto p = begin(); p != end(); p++) {
462  external_particles->insert(*p);
463  }
464 }
465 
467  FourVector centerpoint(0.0, 0.0, 0.0, 0.0);
468  for (auto p = cbegin(); p != cend(); p++) {
469  centerpoint += p->position();
470  }
471  centerpoint /= size();
472  return centerpoint;
473 }
474 
475 std::ostream &operator<<(std::ostream &out, const Nucleus &n) {
476  return out << " #particles #testparticles mass [GeV] "
477  "radius [fm] diffusiveness [fm]\n"
478  << format(n.number_of_particles(), nullptr, 12)
479  << format(n.size(), nullptr, 17) << format(n.mass(), nullptr, 13)
480  << format(n.get_nuclear_radius(), nullptr, 14)
481  << format(n.get_diffusiveness(), nullptr, 20);
482 }
483 
484 } // namespace smash
FormattingHelper< T > format(const T &value, const char *unit, int width=-1, int precision=-1)
Acts as a stream modifier for std::ostream to output an object with an optional suffix string and wit...
Definition: logging.h:310
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:81
void set_diffusiveness(double diffuse)
Sets the diffusiveness of the nucleus.
Definition: nucleus.h:231
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:30
virtual ThreeVector distribute_nucleon()
The distribution of return values from this function is according to a spherically symmetric Woods-Sa...
Definition: nucleus.cc:216
void shift(double z_offset, double x_offset, double simulation_time)
Shifts the nucleus to correct impact parameter and z displacement.
Definition: nucleus.cc:448
double get_nuclear_radius() const
Definition: nucleus.h:266
bool almost_equal(const N x, const N y)
Checks two numbers for relative approximate equality.
Definition: numerics.h:42
void set_nuclear_radius(double rad)
Sets the nuclear radius.
Definition: nucleus.h:261
const FourVector & position() const
Get the particle&#39;s position in Minkowski space.
Definition: particledata.h:185
void set_3momentum(const ThreeVector &mom)
Set the momentum of the particle without modifying the energy.
Definition: particledata.h:177
Collection of useful constants that are known at compile time.
double x3() const
Definition: threevector.h:163
friend std::ostream & operator<<(std::ostream &, const Nucleus &)
Writes the state of the Nucleus object to the output stream.
Definition: nucleus.cc:475
A nucleus is a collection of particles that are initialized, before the beginning of the simulation a...
Definition: nucleus.h:27
FourVector center() const
Calculate geometrical center of the nucleus.
Definition: nucleus.cc:466
virtual void rotate()
Rotates the nucleus.
Definition: nucleus.h:137
ThreeVector threevec() const
Definition: fourvector.h:306
Interface to the SMASH configuration files.
constexpr double hbarc
GeV <-> fm conversion factor.
Definition: constants.h:25
bool has_value(std::initializer_list< const char * > keys) const
Returns whether there is a non-empty value behind the requested keys.
std::vector< ParticleData >::const_iterator cbegin() const
For const iterators over the particle list:
Definition: nucleus.h:220
Generic numerical functions.
void boost(double beta_scalar)
Boosts the nuclei into the computational frame, such that the nucleons have the appropriate momentum ...
Definition: nucleus.cc:404
T canonical()
Definition: random.h:113
void set_x1(double x)
Definition: fourvector.h:296
double x0() const
Definition: fourvector.h:290
void set_saturation_density(double density)
Sets the saturation density of the nucleus.
Definition: nucleus.h:241
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
bool is_baryon() const
Definition: particledata.h:88
void set_x3(double z)
Definition: fourvector.h:304
ThreeVector threevec() const
Definition: angles.h:268
size_t size() const
Number of numerical (=test-)particles in the nucleus:
Definition: nucleus.h:147
double mass() const
Definition: particletype.h:144
void set_x0(double t)
Definition: fourvector.h:292
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Nucleus()=default
default constructor
double x3() const
Definition: fourvector.h:302
Particle type contains the static properties of a particle species.
Definition: particletype.h:97
void set_4momentum(const FourVector &momentum_vector)
Set the particle&#39;s 4-momentum directly.
Definition: particledata.h:145
virtual void generate_fermi_momenta()
Generates momenta according to Fermi motion for the nucleons.
Definition: nucleus.cc:343
double get_diffusiveness() const
Definition: nucleus.h:236
double woods_saxon(double x)
Woods-Saxon distribution.
Definition: nucleus.cc:262
const ParticleData & insert(const ParticleData &p)
Inserts the particle into the list of particles.
Definition: particles.cc:50
std::vector< ParticleData >::const_iterator cend() const
For const iterators over the particle list:
Definition: nucleus.h:224
double mass() const
Definition: nucleus.cc:56
T uniform(T min, T max)
Definition: random.h:88
double x1() const
Definition: threevector.h:155
double nuclear_radius_
Nuclear radius of this nucleus.
Definition: nucleus.h:199
double diffusiveness_
Diffusiveness of Woods-Saxon distribution of this nucleus in fm (for diffusiveness_ == 0...
Definition: nucleus.h:195
double pole_mass() const
Get the particle&#39;s pole mass ("on-shell").
Definition: particledata.h:96
void fill_from_list(const std::map< PdgCode, int > &particle_list, int testparticles)
Adds particles from a map PDG code => Number_of_particles_with_that_PDG_code to the nucleus...
Definition: nucleus.cc:434
constexpr int p
Proton.
void arrange_nucleons()
Sets the positions of the nucleons inside a nucleus.
Definition: nucleus.cc:266
constexpr double nuclear_density
Ground state density of symmetric nuclear matter [fm^-3].
Definition: constants.h:42
double x2() const
Definition: threevector.h:159
void copy_particles(Particles *particles)
Copies the particles from this nucleus into the particle list.
Definition: nucleus.cc:460
std::vector< ParticleData > particles_
Particles associated with this nucleus.
Definition: nucleus.h:210
std::vector< ParticleData >::iterator begin()
For iterators over the particle list:
Definition: nucleus.h:214
Angles provides a common interface for generating directions: i.e., two angles that should be interpr...
Definition: angles.h:59
double x2() const
Definition: fourvector.h:298
size_t number_of_particles() const
Number of physical particles in the nucleus:
Definition: nucleus.h:155
The Particles class abstracts the storage and manipulation of particles.
Definition: particles.h:33
void distribute_isotropically()
Populate the object with a new direction.
Definition: angles.h:188
constexpr int n
Neutron.
virtual void set_parameters_automatic()
Sets the deformation parameters of the Woods-Saxon distribution according to the current mass number...
Definition: nucleus.cc:283
size_t testparticles_
Number of testparticles per physical particle.
Definition: nucleus.h:206
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:32
double x1() const
Definition: fourvector.h:294
void align_center()
Shifts the nucleus so that its center is at (0,0,0)
Definition: nucleus.h:178
ParticleData contains the dynamic information of a certain particle.
Definition: particledata.h:52
Definition: action.h:24
std::vector< ParticleData >::iterator end()
For iterators over the particle list:
Definition: nucleus.h:218
const FourVector & momentum() const
Get the particle&#39;s 4-momentum.
Definition: particledata.h:139
virtual void set_parameters_from_config(Configuration &config)
Sets the parameters of the Woods-Saxon according to manually added values in the configuration file...
Definition: nucleus.cc:335