23 const std::filesystem::path &output_path) {
24 if (!std::filesystem::exists(output_path)) {
26 output_path.string() +
31 const std::string modus_chooser = config.
read({
"General",
"Modus"});
32 logg[
LExperiment].debug() <<
"Modus for this calculation: " << modus_chooser;
34 if (modus_chooser ==
"Box") {
35 return std::make_unique<Experiment<BoxModus>>(config, output_path);
36 }
else if (modus_chooser ==
"List") {
37 return std::make_unique<Experiment<ListModus>>(config, output_path);
38 }
else if (modus_chooser ==
"ListBox") {
39 return std::make_unique<Experiment<ListBoxModus>>(config, output_path);
40 }
else if (modus_chooser ==
"Collider") {
41 return std::make_unique<Experiment<ColliderModus>>(config, output_path);
42 }
else if (modus_chooser ==
"Sphere") {
43 return std::make_unique<Experiment<SphereModus>>(config, output_path);
46 ") requested from ExperimentBase::create.");
135 const int ntest = config.
take({
"General",
"Testparticles"}, 1);
137 throw std::invalid_argument(
"Testparticle number should be positive!");
141 const bool only_participants =
142 config.
take({
"Output",
"Thermodynamics",
"Only_Participants"},
false);
144 if (only_participants && config.
has_value({
"Potentials"})) {
145 throw std::invalid_argument(
146 "Only_Participants option cannot be "
147 "set to True when using Potentials.");
150 const std::string modus_chooser = config.
take({
"General",
"Modus"});
154 double box_length = -1.0;
155 if (config.
has_value({
"Modi",
"Box",
"Length"})) {
156 box_length = config.
read({
"Modi",
"Box",
"Length"});
159 if (config.
has_value({
"Modi",
"ListBox",
"Length"})) {
160 box_length = config.
read({
"Modi",
"ListBox",
"Length"});
166 const double dt = config.
take({
"General",
"Delta_Time"}, 1.);
168 throw std::invalid_argument(
"Delta_Time cannot be zero or negative.");
171 const double t_end = config.
read({
"General",
"End_Time"});
173 throw std::invalid_argument(
"End_Time cannot be zero or negative.");
177 if (box_length > 0.0 && dt > box_length / 10.0) {
178 throw std::invalid_argument(
179 "Please decrease the timestep size. "
180 "A value of (dt <= l_box / 10) is necessary in the box modus.");
184 std::unique_ptr<Clock> output_clock =
nullptr;
185 if (config.
has_value({
"Output",
"Output_Times"})) {
186 if (config.
has_value({
"Output",
"Output_Interval"})) {
187 throw std::invalid_argument(
188 "Please specify either Output_Interval or Output_Times");
190 std::vector<double> output_times = config.
take({
"Output",
"Output_Times"});
193 output_times.push_back(t_end + 1.);
194 output_clock = std::make_unique<CustomClock>(output_times);
196 const double output_dt = config.
take({
"Output",
"Output_Interval"}, t_end);
197 if (output_dt <= 0.) {
198 throw std::invalid_argument(
199 "Output_Interval cannot be zero or negative.");
201 output_clock = std::make_unique<UniformClock>(0.0, output_dt, t_end);
206 if (config.
has_value({
"Output",
"Photons"}) &&
207 (!config.
has_value({
"Collision_Term",
"Photons"}))) {
208 throw std::invalid_argument(
209 "Photon output is enabled although photon production is disabled. "
210 "Photon production can be configured in the \"Photon\" subsection "
211 "of the \"Collision_Term\".");
215 bool missing_output_2to2 =
false;
216 bool missing_output_brems =
false;
217 if (!(config.
has_value({
"Output",
"Photons"}))) {
218 if (config.
has_value({
"Collision_Term",
"Photons",
"2to2_Scatterings"})) {
219 missing_output_2to2 =
220 config.
read({
"Collision_Term",
"Photons",
"2to2_Scatterings"});
222 if (config.
has_value({
"Collision_Term",
"Photons",
"Bremsstrahlung"})) {
223 missing_output_brems =
224 config.
read({
"Collision_Term",
"Photons",
"Bremsstrahlung"});
227 if (missing_output_2to2 || missing_output_brems) {
228 throw std::invalid_argument(
229 "Photon output is disabled although photon production is enabled. "
230 "Please enable the photon output.");
236 if (config.
has_value({
"Output",
"Dileptons"}) &&
237 (!config.
has_value({
"Collision_Term",
"Dileptons"}))) {
238 throw std::invalid_argument(
239 "Dilepton output is enabled although dilepton production is disabled. "
240 "Dilepton production can be configured in the \"Dileptons\" subsection "
241 "of the \"Collision_Term\".");
245 bool missing_output_decays =
false;
246 if (!(config.
has_value({
"Output",
"Dileptons"}))) {
247 if (config.
has_value({
"Collision_Term",
"Dileptons",
"Decays"})) {
248 missing_output_decays =
249 config.
read({
"Collision_Term",
"Dileptons",
"Decays"});
252 if (missing_output_decays) {
253 throw std::invalid_argument(
254 "Dilepton output is disabled although dilepton production is "
256 "Please enable the dilepton output.");
261 const double low_snn_cut =
262 config.
take({
"Collision_Term",
"Elastic_NN_Cutoff_Sqrts"}, 1.98);
265 if (proton && pion &&
266 low_snn_cut > proton->mass() + proton->mass() + pion->mass()) {
267 logg[
LExperiment].warn(
"The cut-off should be below the threshold energy",
268 " of the process: NN to NNpi");
270 const bool potential_affect_threshold =
271 config.
take({
"Lattice",
"Potentials_Affect_Thresholds"},
false);
272 const double scale_xs =
273 config.
take({
"Collision_Term",
"Cross_Section_Scaling"}, 1.0);
275 const auto criterion = config.
take({
"Collision_Term",
"Collision_Criterion"},
278 if (config.
has_value({
"Collision_Term",
"Fixed_Min_Cell_Length"}) &&
280 throw std::invalid_argument(
281 "Only use a fixed minimal cell length with the stochastic collision "
284 if (config.
has_value({
"Collision_Term",
"Maximum_Cross_Section"}) &&
286 throw std::invalid_argument(
287 "Only use maximum cross section with the "
288 "geometric collision criterion. Use Fixed_Min_Cell_Length to change "
290 "size for the stochastic criterion.");
301 const double maximum_cross_section_default =
304 double maximum_cross_section =
305 config.
take({
"Collision_Term",
"Maximum_Cross_Section"},
306 maximum_cross_section_default);
307 maximum_cross_section *= scale_xs;
309 std::make_unique<UniformClock>(0.0, dt, t_end),
310 std::move(output_clock),
311 config.
take({
"General",
"Ensembles"}, 1),
313 config.
take({
"General",
"Derivatives_Mode"},
318 config.
take({
"General",
"Field_Derivatives_Mode"},
320 config.
take({
"General",
"Smearing_Mode"},
322 config.
take({
"General",
"Gaussian_Sigma"}, 1.),
323 config.
take({
"General",
"Gauss_Cutoff_In_Sigma"}, 4.),
324 config.
take({
"General",
"Discrete_Weight"}, 1. / 3.0),
325 config.
take({
"General",
"Triangular_Range"}, 2.0),
327 config.
take({
"Collision_Term",
"Two_to_One"},
true),
329 config.
take({
"Collision_Term",
"Multi_Particle_Reactions"},
331 config.
take({
"Collision_Term",
"Strings"}, modus_chooser !=
"Box"),
332 config.take({
"Collision_Term",
"Resonance_Lifetime_Modifier"}, 1.),
333 config.take({
"Collision_Term",
"NNbar_Treatment"},
336 potential_affect_threshold,
338 maximum_cross_section,
339 config.take({
"Collision_Term",
"Fixed_Min_Cell_Length"}, 2.5),
342 config.take({
"Collision_Term",
"Include_Weak_And_EM_Decays_At_The_End"},
344 config.take({
"Collision_Term",
"Decay_Initial_Particles"},
345 InputKeys::collTerm_decayInitial.default_value()),
350 uint64_t scatterings_this_interval,
354 double E_mean_field_initial) {
355 const SystemTimeSpan elapsed_seconds = SystemClock::now() - time_start;
358 const QuantumNumbers difference = current_values - conserved_initial;
359 int total_particles = 0;
360 for (
const Particles &particles : ensembles) {
361 total_particles += particles.size();
366 const double current_energy = current_values.
momentum().
x0();
367 const double energy_per_part =
368 (total_particles > 0) ? (current_energy + E_mean_field) / total_particles
371 std::ostringstream ss;
373 ss << field<7, 3> << time
375 << field<11, 3> << current_energy
377 << field<11, 3> << E_mean_field
379 << field<12, 3> << current_energy + E_mean_field
381 << field<12, 6> << energy_per_part;
383 if (total_particles == 0) {
384 ss << field<13, 6> <<
"N/A";
386 ss << field<13, 6> << (difference.
momentum().
x0()
387 + E_mean_field - E_mean_field_initial)
390 ss << field<14, 3> << scatterings_this_interval
391 << field<10, 3> << total_particles
392 << field<9, 3> << elapsed_seconds;
403 const double V_cell = (jmuB_lat.
cell_sizes())[0] *
406 double E_mean_field = 0.0;
407 double density_mean = 0.0;
408 double density_variance = 0.0;
423 <<
"\nSymmetry energy is not included in the mean field calculation."
439 double C1GeV = (potentials.
skyrme_a()) / 1000.0;
440 double C2GeV = (potentials.
skyrme_b()) / 1000.0;
449 int number_of_nodes = 0;
450 double lattice_mean_field_total = 0.0;
452 for (
auto &node : jmuB_lat) {
455 double rhoB = node.rho();
457 const double j0B = node.jmu_net().x0();
459 const double abs_rhoB = std::abs(rhoB);
464 density_variance += j0B * j0B;
474 double mean_field_contribution_1 = (C1GeV / b1) * std::pow(abs_rhoB, b1) /
476 double mean_field_contribution_2 = (C2GeV / b2) * std::pow(abs_rhoB, b2) /
479 lattice_mean_field_total +=
480 V_cell * (mean_field_contribution_1 + mean_field_contribution_2);
484 density_mean = density_mean / number_of_nodes;
485 density_variance = density_variance / number_of_nodes;
486 double density_scaled_variance =
487 std::sqrt(density_variance - density_mean * density_mean) /
491 <<
"\n\t\t\t\t\t density mean = " << density_mean;
493 <<
"\n\t\t\t\t\t density scaled variance = " << density_scaled_variance;
495 <<
"\n\t\t\t\t\t total mean_field = "
500 E_mean_field = lattice_mean_field_total;
512 <<
"\nSymmetry energy is not included in the VDF mean-field "
514 <<
"\nas VDF potentials haven't been fitted with symmetry energy."
534 int number_of_nodes = 0;
535 double lattice_mean_field_total = 0.0;
537 for (
auto &node : jmuB_lat) {
540 double rhoB = node.rho();
542 const double j0B = node.jmu_net().x0();
543 double abs_rhoB = std::abs(rhoB);
545 density_variance += j0B * j0B;
556 double mean_field_contribution = 0.0;
558 mean_field_contribution +=
560 std::pow(abs_rhoB, potentials.
powers()[i] - 2.0) *
562 ((potentials.
powers()[i] - 1.0) / potentials.
powers()[i]) *
563 abs_rhoB * abs_rhoB) /
564 std::pow(rhoB_0, potentials.
powers()[i] - 1.0);
566 lattice_mean_field_total += V_cell * mean_field_contribution;
570 density_mean = density_mean / number_of_nodes;
571 density_variance = density_variance / number_of_nodes;
572 double density_scaled_variance =
573 std::sqrt(density_variance - density_mean * density_mean) /
577 <<
"\n\t\t\t\t\t density mean = " << density_mean;
579 <<
"\n\t\t\t\t\t density scaled variance = " << density_scaled_variance;
581 <<
"\n\t\t\t\t\t total mean_field = "
586 E_mean_field = lattice_mean_field_total;
589 double electromagnetic_potential = 0.0;
593 double V_cell_em = em_lattice->cell_sizes()[0] *
594 em_lattice->cell_sizes()[1] *
595 em_lattice->cell_sizes()[2];
596 for (
auto &fields : *em_lattice) {
598 electromagnetic_potential +=
599 hbarc * 0.5 * V_cell_em * (fields.first.sqr() + fields.second.sqr());
602 logg[
LExperiment].debug() <<
"Total energy in electromagnetic field = "
603 << electromagnetic_potential;
604 E_mean_field += electromagnetic_potential;
618 double E_mean_field,
double modus_impact_parameter,
620 bool projectile_target_interact,
621 bool kinematic_cut_for_SMASH_IC) {
623 const double E_kinetic_total = current_values.
momentum().
x0();
624 const double E_total = E_kinetic_total + E_mean_field;
626 EventInfo event_info{modus_impact_parameter,
634 !projectile_target_interact,
635 kinematic_cut_for_SMASH_IC};
640 static bool warn_mass_discrepancy =
true;
641 static bool warn_off_shell_particle =
true;
642 for (
auto it = particle_list.begin(); it != particle_list.end();) {
643 auto &particle = *it;
644 auto pdgcode = particle.pdgcode();
647 if (pdgcode == 0x310 || pdgcode == 0x130) {
657 auto valid_smash_particle =
659 pdgcode, particle.effective_mass(), particle.position(),
660 particle.momentum(),
LExperiment, warn_mass_discrepancy,
661 warn_off_shell_particle);
662 particle.set_4position(valid_smash_particle.position());
663 particle.set_4momentum(valid_smash_particle.momentum());
664 particle.set_cross_section_scaling_factor(
665 valid_smash_particle.xsec_scaling_factor());
669 <<
"SMASH does not recognize pdg code " << pdgcode
670 <<
" obtained from hadron list. This particle will be ignored.\n";
671 it = particle_list.erase(it);
Interface to the SMASH configuration files.
bool has_value(std::initializer_list< const char * > keys) const
Return whether there is a non-empty value behind the requested keys.
void remove_all_entries_in_section_but_one(const std::string &key, std::initializer_list< const char * > section={})
Remove all entries in the given section except for key.
Value take(std::initializer_list< const char * > keys)
The default interface for SMASH to read configuration values.
Value read(std::initializer_list< const char * > keys) const
Additional interface for SMASH to read configuration values without removing them.
static std::unique_ptr< ExperimentBase > create(Configuration &config, const std::filesystem::path &output_path)
Factory method that creates and initializes a new Experiment<Modus>.
static const ParticleTypePtr try_find(PdgCode pdgcode)
Returns the ParticleTypePtr for the given pdgcode.
static bool exists(PdgCode pdgcode)
The Particles class abstracts the storage and manipulation of particles.
A class that stores parameters of potentials, calculates potentials and their gradients.
const std::vector< double > & powers() const
virtual bool use_symmetry() const
const std::vector< double > & coeffs() const
virtual bool use_skyrme() const
virtual bool use_coulomb() const
double skyrme_tau() const
int number_of_terms() const
virtual bool use_vdf() const
double saturation_density() const
A container for storing conserved values.
FourVector momentum() const
A container class to hold all the arrays on the lattice and access them.
const std::array< double, 3 > & cell_sizes() const
std::bitset< 10 > ReactionsBitSet
Container for the 2 to 2 reactions in the code.
@ Strings
Use string fragmentation.
std::bitset< 4 > MultiParticleReactionsBitSet
Container for the n to m reactions in the code.
@ Stochastic
Stochastic Criteiron.
@ Covariant
Covariant Criterion.
#define SMASH_SOURCE_LOCATION
Hackery that is required to output the location in the source code where the log statement occurs.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
T uniform_int(T min, T max)
EventInfo fill_event_info(const std::vector< Particles > &ensembles, double E_mean_field, double modus_impact_parameter, const ExperimentParameters ¶meters, bool projectile_target_interact, bool kinematic_cut_for_SMASH_IC)
Generate the EventInfo object which is passed to outputs_.
SystemClock::duration SystemTimeSpan
The time duration type (alias) used for measuring run times.
std::string format_measurements(const std::vector< Particles > &ensembles, uint64_t scatterings_this_interval, const QuantumNumbers &conserved_initial, SystemTimePoint time_start, double time, double E_mean_field, double E_mean_field_initial)
Generate a string which will be printed to the screen when SMASH is running.
ExperimentParameters create_experiment_parameters(Configuration &config)
Gathers all general Experiment parameters.
constexpr double very_small_double
A very small double, used to avoid division by zero.
double calculate_mean_field_energy(const Potentials &potentials, RectangularLattice< smash::DensityOnLattice > &jmu_B_lat, RectangularLattice< std::pair< ThreeVector, ThreeVector >> *em_lattice, const ExperimentParameters ¶meters)
Calculate the total mean field energy of the system; this will be printed to the screen when SMASH is...
static constexpr int LExperiment
void validate_and_adjust_particle_list(ParticleList &particle_list)
Validate a particle list adjusting each particle to be a valid SMASH particle.
constexpr double nuclear_density
Ground state density of symmetric nuclear matter [fm^-3].
ParticleData create_valid_smash_particle_matching_provided_quantities(PdgCode pdgcode, double mass, const FourVector &four_position, const FourVector &four_momentum, int log_area, bool &mass_warning, bool &on_shell_warning)
This function creates a SMASH particle validating the provided information.
constexpr double hbarc
GeV <-> fm conversion factor.
std::chrono::time_point< std::chrono::system_clock > SystemTimePoint
Type (alias) that is used to store the current time.
Structure to contain custom data for output.
Exception class that is thrown if an invalid modus is requested from the Experiment factory.
Exception class that is thrown if the requested output path in the Experiment factory is not existing...
Helper structure for Experiment.
double box_length
Length of the box in fm in case of box modus, otherwise -1.
int n_ensembles
Number of parallel ensembles.
std::unique_ptr< Clock > outputclock
Output clock to keep track of the next output time.
int testparticles
Number of test-particles.