Version: SMASH-1.8
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 static constexpr int LNucleus = LogArea::Nucleus::id;
28 
29 Nucleus::Nucleus(const std::map<PdgCode, int> &particle_list, int nTest) {
30  fill_from_list(particle_list, nTest);
32 }
33 
34 Nucleus::Nucleus(Configuration &config, int nTest) {
35  // Fill nuclei with particles.
36  std::map<PdgCode, int> part = config.take({"Particles"});
37  fill_from_list(part, nTest);
38  // Look for user-defined values or take the default parameters.
39  if (config.has_value({"Diffusiveness"}) && config.has_value({"Radius"}) &&
40  config.has_value({"Saturation_Density"})) {
42  } else if (!config.has_value({"Diffusiveness"}) &&
43  !config.has_value({"Radius"}) &&
44  !config.has_value({"Saturation_Density"})) {
46  } else {
47  throw std::invalid_argument(
48  "Diffussiveness, Radius and Saturation_Density "
49  "required to manually configure the Woods-Saxon"
50  " distribution. Only one/two were provided. \n"
51  "Providing none of the above mentioned "
52  "parameters automatically configures the "
53  "distribution based on the atomic number.");
54  }
55 }
56 
57 double Nucleus::mass() const {
58  double total_mass = 0.;
59  for (auto i = cbegin(); i != cend(); i++) {
60  total_mass += i->momentum().abs();
61  }
62  return total_mass / (testparticles_ + 0.0);
63 }
64 
218  // Get the solid angle of the nucleon.
219  Angles dir;
221  // diffusiveness_ zero or negative? Use hard sphere.
222  if (almost_equal(diffusiveness_, 0.)) {
223  return dir.threevec() * nuclear_radius_ * std::cbrt(random::canonical());
224  }
225  if (almost_equal(nuclear_radius_, 0.)) {
226  return smash::ThreeVector();
227  }
228  double radius_scaled = nuclear_radius_ / diffusiveness_;
229  double prob_range1 = 1.0;
230  double prob_range2 = 3. / radius_scaled;
231  double prob_range3 = 2. * prob_range2 / radius_scaled;
232  double prob_range4 = 1. * prob_range3 / radius_scaled;
233  double ranges234 = prob_range2 + prob_range3 + prob_range4;
234  double t;
236  do {
237  double which_range = random::uniform(-prob_range1, ranges234);
238  if (which_range < 0.0) {
239  t = radius_scaled * (std::cbrt(random::canonical()) - 1.);
240  } else {
241  t = -std::log(random::canonical());
242  if (which_range >= prob_range2) {
243  t -= std::log(random::canonical());
244  if (which_range >= prob_range2 + prob_range3) {
245  t -= std::log(random::canonical());
246  }
247  }
248  }
256  } while (random::canonical() > 1. / (1. + std::exp(-std::abs(t))));
258  double position_scaled = t + radius_scaled;
259  double position = position_scaled * diffusiveness_;
260  return dir.threevec() * position;
261 }
262 
263 double Nucleus::woods_saxon(double r) {
264  return r * r / (std::exp((r - nuclear_radius_) / diffusiveness_) + 1);
265 }
266 
268  for (auto i = begin(); i != end(); i++) {
269  // Initialize momentum
270  i->set_4momentum(i->pole_mass(), 0.0, 0.0, 0.0);
271  /* Sampling the Woods-Saxon, get the radial
272  * position and solid angle for the nucleon. */
274 
275  // Set the position of the nucleon.
276  i->set_4position(FourVector(0.0, pos));
277  }
278 
279  // Recenter and rotate
280  align_center();
281  rotate();
282 }
283 
286  int Z = Nucleus::number_of_protons();
287  switch (A) {
288  case 1: // single particle
289  /* In case of testparticles, an infinite reaction loop will be
290  * avoided by a small finite spread according to a single particles
291  * 'nucleus'. The proper solution will be to introduce parallel
292  * ensembles. */
294  ? 0.
295  : 1. - std::exp(-(testparticles_ - 1.) * 0.1));
296  set_diffusiveness(testparticles_ == 1 ? -1. : 0.02);
297  break;
298  case 238: // Uranium
299  // Default values.
300  if (Z == 92) {
301  set_diffusiveness(0.556);
302  set_nuclear_radius(6.86);
303  set_saturation_density(0.166);
304  }
305  break;
306  case 208: // Lead
307  // Default values.
308  if (Z == 82) {
309  set_diffusiveness(0.54);
310  set_nuclear_radius(6.67);
311  set_saturation_density(0.161);
312  }
313  break;
314  case 197: // Gold
315  // Default values.
316  if (Z == 79) {
317  set_diffusiveness(0.535);
318  set_nuclear_radius(6.38);
319  set_saturation_density(0.1695);
320  }
321  break;
322  case 63: // Copper
323  // Default values.
324  if (Z == 29) {
325  set_diffusiveness(0.5977);
326  set_nuclear_radius(4.20641);
327  set_saturation_density(0.1686);
328  }
329  break;
330  case 96:
331  if (Z == 40) { // Zirconium
332  // Default values.
333  set_diffusiveness(0.46);
334  set_nuclear_radius(5.02);
335  set_saturation_density(0.1673);
336  } else if (Z == 44) { // Ruthenium
337  // Default values.
338  set_diffusiveness(0.46);
339  set_nuclear_radius(5.085);
340  set_saturation_density(0.1604);
341  } else {
342  // radius and diffusiveness taken from \iref{Rybczynski:2013yba}
343  set_diffusiveness(0.54);
344  set_nuclear_radius(1.12 * std::pow(A, 1.0 / 3.0) -
345  0.86 * std::pow(A, -1.0 / 3.0));
346  }
347  break;
348 
349  default:
350  // saturation density already has reasonable default
352  if (A <= 16) {
353  set_diffusiveness(0.545);
354  } else {
355  // diffusiveness taken from \iref{Rybczynski:2013yba}
356  set_diffusiveness(0.54);
357  }
358  }
359 }
360 
362  set_diffusiveness(static_cast<double>(config.take({"Diffusiveness"})));
363  set_nuclear_radius(static_cast<double>(config.take({"Radius"})));
364  // Saturation density (normalization for accept/reject sampling)
366  static_cast<double>(config.take({"Saturation_Density"})));
367 }
368 
370  const int N_n = std::count_if(begin(), end(), [](const ParticleData i) {
371  return i.pdgcode() == pdg::n;
372  });
373  const int N_p = std::count_if(begin(), end(), [](const ParticleData i) {
374  return i.pdgcode() == pdg::p;
375  });
376  const FourVector nucleus_center = center();
377  const int A = N_n + N_p;
378  constexpr double pi2_3 = 3.0 * M_PI * M_PI;
379 
380  logg[LNucleus].debug() << N_n << " neutrons, " << N_p << " protons.";
381 
382  ThreeVector ptot = ThreeVector(0.0, 0.0, 0.0);
383  for (auto i = begin(); i != end(); i++) {
384  // Only protons and neutrons get Fermi momenta
385  if (i->pdgcode() != pdg::p && i->pdgcode() != pdg::n) {
386  if (i->is_baryon()) {
387  logg[LNucleus].warn() << "No rule to calculate Fermi momentum "
388  << "for particle " << i->pdgcode();
389  }
390  continue;
391  }
392  const double r = (i->position() - nucleus_center).abs3();
393  const double theta = (i->position().threevec().get_theta());
394  double rho = nucleon_density(r, cos(theta));
395 
396  if (i->pdgcode() == pdg::p) {
397  rho = rho * N_p / A;
398  }
399  if (i->pdgcode() == pdg::n) {
400  rho = rho * N_n / A;
401  }
402  const double p =
403  hbarc * std::pow(pi2_3 * rho * random::uniform(0.0, 1.0), 1.0 / 3.0);
404  Angles phitheta;
405  phitheta.distribute_isotropically();
406  const ThreeVector ith_3momentum = phitheta.threevec() * p;
407  ptot += ith_3momentum;
408  i->set_3momentum(ith_3momentum);
409  logg[LNucleus].debug() << "Particle: " << *i << ", pF[GeV]: "
410  << hbarc * std::pow(pi2_3 * rho, 1.0 / 3.0)
411  << " r[fm]: " << r
412  << " Nuclear radius[fm]: " << nuclear_radius_;
413  }
414  if (A == 0) {
415  // No Fermi momenta should be assigned
416  assert(ptot.x1() == 0.0 && ptot.x2() == 0.0 && ptot.x3() == 0.0);
417  } else {
418  /* Ensure zero total momentum of nucleus - redistribute ptot equally
419  * among protons and neutrons */
420  const ThreeVector centralizer = ptot / A;
421  for (auto i = begin(); i != end(); i++) {
422  if (i->pdgcode() == pdg::p || i->pdgcode() == pdg::n) {
423  i->set_4momentum(i->pole_mass(),
424  i->momentum().threevec() - centralizer);
425  }
426  }
427  }
428 }
429 
430 void Nucleus::boost(double beta_scalar) {
431  double beta_squared = beta_scalar * beta_scalar;
432  double one_over_gamma = std::sqrt(1.0 - beta_squared);
433  double gamma = 1.0 / one_over_gamma;
434  /* We are talking about a /passive/ lorentz transformation here, as
435  * far as I can see, so we need to boost in the direction opposite to
436  * where we want to go
437  * ( The vector we transform - p - stays unchanged, but we go into
438  * a system that moves with -beta. Now in this frame, it seems
439  * like p has been accelerated with +beta.
440  * ) */
441  for (auto i = begin(); i != end(); i++) {
442  /* a real Lorentz Transformation would leave the particles at
443  * different times here, which we would then have to propagate back
444  * to equal times. Since we know the result, we can simply multiply
445  * the z-value with 1/gamma. */
446  FourVector this_position = i->position();
447  this_position.set_x3(this_position.x3() * one_over_gamma);
448  i->set_4position(this_position);
449  /* The simple Lorentz transformation of momenta does not take into account
450  * that nucleus has binding energy. Here we apply the method used
451  * in the JAM code \iref{Nara:1999dz}: p' = p_beam + gamma*p_F.
452  * This formula is derived under assumption that all nucleons have
453  * the same binding energy. */
454  FourVector mom_i = i->momentum();
455  i->set_4momentum(i->pole_mass(), mom_i.x1(), mom_i.x2(),
456  gamma * (beta_scalar * mom_i.x0() + mom_i.x3()));
457  }
458 }
459 
460 void Nucleus::fill_from_list(const std::map<PdgCode, int> &particle_list,
461  int testparticles) {
462  testparticles_ = testparticles;
463  for (auto n = particle_list.cbegin(); n != particle_list.cend(); ++n) {
464  const ParticleType &current_type = ParticleType::find(n->first);
465  double current_mass = current_type.mass();
466  for (unsigned int i = 0; i < n->second * testparticles_; i++) {
467  // append particle to list and set its PDG code.
468  particles_.emplace_back(current_type);
469  particles_.back().set_4momentum(current_mass, 0.0, 0.0, 0.0);
470  }
471  }
472 }
473 
474 void Nucleus::shift(double z_offset, double x_offset, double simulation_time) {
475  // Move the nucleus in z and x directions, and set the time.
476  for (auto i = begin(); i != end(); i++) {
477  FourVector this_position = i->position();
478  this_position.set_x3(this_position.x3() + z_offset);
479  this_position.set_x1(this_position.x1() + x_offset);
480  this_position.set_x0(simulation_time);
481  i->set_4position(this_position);
482  i->set_formation_time(simulation_time);
483  }
484 }
485 
486 void Nucleus::copy_particles(Particles *external_particles) {
487  for (auto p = begin(); p != end(); p++) {
488  external_particles->insert(*p);
489  }
490 }
491 
493  FourVector centerpoint(0.0, 0.0, 0.0, 0.0);
494  for (auto p = cbegin(); p != cend(); p++) {
495  centerpoint += p->position();
496  }
497  centerpoint /= size();
498  return centerpoint;
499 }
500 
502  // Sample euler_theta_ such that cos(theta) is uniform
503  euler_phi_ = twopi * random::uniform(0., 1.);
504  euler_theta_ = std::acos(2 * random::uniform(0., 1.) - 1);
505  euler_psi_ = twopi * random::uniform(0., 1.);
506 }
507 
508 double Nucleus::nucleon_density(double r, double) {
509  return nuclear_density /
510  (std::exp((r - nuclear_radius_) / diffusiveness_) + 1.);
511 }
512 
513 std::ostream &operator<<(std::ostream &out, const Nucleus &n) {
514  return out << " #particles #testparticles mass [GeV] "
515  "radius [fm] diffusiveness [fm]\n"
516  << format(n.number_of_particles(), nullptr, 12)
517  << format(n.size(), nullptr, 17) << format(n.mass(), nullptr, 13)
518  << format(n.get_nuclear_radius(), nullptr, 14)
519  << format(n.get_diffusiveness(), nullptr, 20);
520 }
521 
522 } // namespace smash
smash::ParticleData::set_3momentum
void set_3momentum(const ThreeVector &mom)
Set the momentum of the particle without modifying the energy.
Definition: particledata.h:180
smash::Nucleus::random_euler_angles
void random_euler_angles()
Randomly generate Euler angles.
Definition: nucleus.cc:501
smash
Definition: action.h:24
smash::Nucleus
A nucleus is a collection of particles that are initialized, before the beginning of the simulation a...
Definition: nucleus.h:27
smash::FourVector::set_x1
void set_x1(double x)
Definition: fourvector.h:309
smash::Nucleus::diffusiveness_
double diffusiveness_
Diffusiveness of Woods-Saxon distribution of this nucleus in fm (for diffusiveness_ == 0,...
Definition: nucleus.h:241
smash::Nucleus::fill_from_list
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:460
smash::Nucleus::cbegin
std::vector< ParticleData >::const_iterator cbegin() const
For const iterators over the particle list:
Definition: nucleus.h:279
smash::Nucleus::Nucleus
Nucleus()=default
default constructor
smash::ParticleData::momentum
const FourVector & momentum() const
Get the particle's 4-momentum.
Definition: particledata.h:142
smash::Nucleus::generate_fermi_momenta
virtual void generate_fermi_momenta()
Generates momenta according to Fermi motion for the nucleons.
Definition: nucleus.cc:369
smash::ParticleData::pole_mass
double pole_mass() const
Get the particle's pole mass ("on-shell").
Definition: particledata.h:99
smash::Nucleus::number_of_protons
size_t number_of_protons() const
Number of physical protons in the nucleus:
Definition: nucleus.h:183
smash::ParticleData
Definition: particledata.h:52
smash::Nucleus::default_nuclear_radius
double default_nuclear_radius()
Default nuclear radius calculated as:
Definition: nucleus.h:315
smash::ThreeVector::x3
double x3() const
Definition: threevector.h:173
smash::Nucleus::begin
std::vector< ParticleData >::iterator begin()
For iterators over the particle list:
Definition: nucleus.h:273
smash::FourVector::x3
double x3() const
Definition: fourvector.h:315
smash::Nucleus::woods_saxon
double woods_saxon(double x)
Woods-Saxon distribution.
Definition: nucleus.cc:263
smash::Nucleus::nuclear_radius_
double nuclear_radius_
Nuclear radius of this nucleus.
Definition: nucleus.h:245
smash::operator<<
std::ostream & operator<<(std::ostream &out, const ActionPtr &action)
Definition: action.h:463
smash::nuclear_density
constexpr double nuclear_density
Ground state density of symmetric nuclear matter [fm^-3].
Definition: constants.h:45
smash::FourVector::set_x0
void set_x0(double t)
Definition: fourvector.h:305
smash::ParticleType::mass
double mass() const
Definition: particletype.h:144
smash::Angles::distribute_isotropically
void distribute_isotropically()
Populate the object with a new direction.
Definition: angles.h:188
smash::Nucleus::euler_theta_
double euler_theta_
Euler angel theta.
Definition: nucleus.h:267
smash::ParticleData::pdgcode
PdgCode pdgcode() const
Get the pdgcode of the particle.
Definition: particledata.h:81
smash::Nucleus::copy_particles
void copy_particles(Particles *particles)
Copies the particles from this nucleus into the particle list.
Definition: nucleus.cc:486
smash::Configuration::has_value
bool has_value(std::initializer_list< const char * > keys) const
Returns whether there is a non-empty value behind the requested keys.
Definition: configuration.cc:181
smash::Nucleus::align_center
void align_center()
Shifts the nucleus so that its center is at (0,0,0)
Definition: nucleus.h:215
smash::Nucleus::arrange_nucleons
virtual void arrange_nucleons()
Sets the positions of the nucleons inside a nucleus.
Definition: nucleus.cc:267
smash::LNucleus
static constexpr int LNucleus
Definition: nucleus.cc:27
smash::FourVector::set_x3
void set_x3(double z)
Definition: fourvector.h:317
fourvector.h
smash::hbarc
constexpr double hbarc
GeV <-> fm conversion factor.
Definition: constants.h:25
smash::Nucleus::number_of_particles
size_t number_of_particles() const
Number of physical particles in the nucleus:
Definition: nucleus.h:164
smash::FourVector::x1
double x1() const
Definition: fourvector.h:307
smash::ParticleData::set_4momentum
void set_4momentum(const FourVector &momentum_vector)
Set the particle's 4-momentum directly.
Definition: particledata.h:148
smash::logg
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
smash::Nucleus::set_parameters_automatic
virtual void set_parameters_automatic()
Sets the deformation parameters of the Woods-Saxon distribution according to the current mass number.
Definition: nucleus.cc:284
smash::Nucleus::particles_
std::vector< ParticleData > particles_
Particles associated with this nucleus.
Definition: nucleus.h:256
smash::ParticleType::find
static const ParticleType & find(PdgCode pdgcode)
Returns the ParticleType object for the given pdgcode.
Definition: particletype.cc:105
random.h
angles.h
smash::Configuration
Interface to the SMASH configuration files.
Definition: configuration.h:464
smash::ThreeVector
Definition: threevector.h:31
smash::Nucleus::testparticles_
size_t testparticles_
Number of testparticles per physical particle.
Definition: nucleus.h:252
smash::Nucleus::euler_psi_
double euler_psi_
Euler angel psi.
Definition: nucleus.h:269
smash::format
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:304
smash::Nucleus::shift
void shift(double z_offset, double x_offset, double simulation_time)
Shifts the nucleus to correct impact parameter and z displacement.
Definition: nucleus.cc:474
smash::twopi
constexpr double twopi
.
Definition: constants.h:42
smash::Nucleus::set_nuclear_radius
void set_nuclear_radius(double rad)
Sets the nuclear radius.
Definition: nucleus.h:330
smash::ParticleData::is_baryon
bool is_baryon() const
Definition: particledata.h:88
smash::FourVector::x0
double x0() const
Definition: fourvector.h:303
smash::ParticleData::position
const FourVector & position() const
Get the particle's position in Minkowski space.
Definition: particledata.h:188
smash::Nucleus::distribute_nucleon
virtual ThreeVector distribute_nucleon()
The distribution of return values from this function is according to a spherically symmetric Woods-Sa...
Definition: nucleus.cc:217
threevector.h
smash::Nucleus::end
std::vector< ParticleData >::iterator end()
For iterators over the particle list:
Definition: nucleus.h:277
smash::Nucleus::center
FourVector center() const
Calculate geometrical center of the nucleus.
Definition: nucleus.cc:492
smash::ParticleType
Definition: particletype.h:97
smash::FourVector::x2
double x2() const
Definition: fourvector.h:311
smash::almost_equal
bool almost_equal(const N x, const N y)
Checks two numbers for relative approximate equality.
Definition: numerics.h:42
smash::Nucleus::rotate
virtual void rotate()
Rotates the nucleus.
Definition: nucleus.h:146
smash::ThreeVector::x1
double x1() const
Definition: threevector.h:165
smash::Nucleus::set_parameters_from_config
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:361
nucleus.h
smash::Nucleus::mass
double mass() const
Definition: nucleus.cc:57
smash::Nucleus::set_diffusiveness
void set_diffusiveness(double diffuse)
Sets the diffusiveness of the nucleus.
Definition: nucleus.h:290
smash::Particles::insert
const ParticleData & insert(const ParticleData &p)
Inserts the particle into the list of particles.
Definition: particles.cc:50
smash::Nucleus::size
size_t size() const
Number of numerical (=test-)particles in the nucleus:
Definition: nucleus.h:156
particles.h
smash::Angles::threevec
ThreeVector threevec() const
Definition: angles.h:268
smash::Particles
Definition: particles.h:33
smash::ThreeVector::get_theta
double get_theta() const
Definition: threevector.h:271
numerics.h
constants.h
smash::Nucleus::euler_phi_
double euler_phi_
Euler angel phi.
Definition: nucleus.h:265
pdgcode.h
logging.h
smash::Configuration::take
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Definition: configuration.cc:140
smash::Nucleus::boost
void boost(double beta_scalar)
Boosts the nuclei into the computational frame, such that the nucleons have the appropriate momentum ...
Definition: nucleus.cc:430
smash::FourVector
Definition: fourvector.h:33
smash::pdg::p
constexpr int p
Proton.
Definition: pdgcode_constants.h:28
smash::Angles
Angles provides a common interface for generating directions: i.e., two angles that should be interpr...
Definition: angles.h:59
smash::random::uniform
T uniform(T min, T max)
Definition: random.h:88
smash::pdg::n
constexpr int n
Neutron.
Definition: pdgcode_constants.h:30
smash::Nucleus::nucleon_density
virtual double nucleon_density(double r, double)
Return the Woods-Saxon probability density for the given position.
Definition: nucleus.cc:508
smash::Nucleus::cend
std::vector< ParticleData >::const_iterator cend() const
For const iterators over the particle list:
Definition: nucleus.h:283
inputfunctions.h
smash::random::canonical
T canonical()
Definition: random.h:113
smash::FourVector::threevec
ThreeVector threevec() const
Definition: fourvector.h:319
smash::Nucleus::set_saturation_density
void set_saturation_density(double density)
Sets the saturation density of the nucleus.
Definition: nucleus.h:300
smash::ThreeVector::x2
double x2() const
Definition: threevector.h:169