File indexing completed on 2025-08-05 08:19:07
0001
0002
0003
0004
0005
0006 #include "nucleus.h"
0007
0008 #include <algorithm>
0009 #include <cmath>
0010 #include <memory>
0011 #include <stdexcept>
0012 #include <string>
0013 #include <utility>
0014
0015 #include <boost/math/constants/constants.hpp>
0016 #ifdef TRENTO_HDF5
0017
0018 #ifdef NDEBUG
0019 #define BOOST_DISABLE_ASSERTS
0020 #endif
0021 #include <boost/multi_array.hpp>
0022 #endif
0023
0024 #include "hdf5_utils.h"
0025 #include "random.h"
0026
0027 namespace trento {
0028
0029 double correct_a(double a, double w) {
0030 constexpr auto c = 0.61;
0031 constexpr auto a_min = 0.01;
0032 return std::sqrt(std::fmax(a*a - c*c*w*w, a_min*a_min));
0033 }
0034
0035 NucleusPtr Nucleus::create(const std::string& species, double nucleon_width, double nucleon_dmin) {
0036
0037
0038 if (species == "p")
0039 return NucleusPtr{new Proton{}};
0040 else if (species == "d")
0041 return NucleusPtr{new Deuteron{}};
0042 else if (species == "Cu")
0043 return NucleusPtr{new WoodsSaxonNucleus{
0044 63, 4.20, 0.596, nucleon_dmin
0045 }};
0046 else if (species == "Cu2")
0047 return NucleusPtr{new DeformedWoodsSaxonNucleus{
0048 63, 4.20, 0.596, 0.162, -0.006, nucleon_dmin
0049 }};
0050 else if (species == "Xe")
0051 return NucleusPtr{new WoodsSaxonNucleus{
0052 129, 5.36, 0.590, nucleon_dmin
0053 }};
0054 else if (species == "Au")
0055 return NucleusPtr{new WoodsSaxonNucleus{
0056 197, 6.38, 0.535, nucleon_dmin
0057 }};
0058 else if (species == "Au2")
0059 return NucleusPtr{new DeformedWoodsSaxonNucleus{
0060 197, 6.38, 0.535, -0.131, -0.031, nucleon_dmin
0061 }};
0062 else if (species == "Pb")
0063 return NucleusPtr{new WoodsSaxonNucleus{
0064 208, 6.62, 0.546, nucleon_dmin
0065 }};
0066 else if (species == "U")
0067 return NucleusPtr{new DeformedWoodsSaxonNucleus{
0068 238, 6.81, 0.600, 0.280, 0.093, nucleon_dmin
0069 }};
0070 else if (species == "U2")
0071 return NucleusPtr{new DeformedWoodsSaxonNucleus{
0072 238, 6.86, 0.420, 0.265, 0.000, nucleon_dmin
0073 }};
0074 else if (species == "U3")
0075 return NucleusPtr{new DeformedWoodsSaxonNucleus{
0076 238, 6.67, 0.440, 0.280, 0.093, nucleon_dmin
0077 }};
0078
0079 else if (hdf5::filename_is_hdf5(species)) {
0080 #ifdef TRENTO_HDF5
0081 return ManualNucleus::create(species);
0082 #else
0083 throw std::invalid_argument{"HDF5 output was not compiled"};
0084 #endif
0085 }
0086 else
0087 throw std::invalid_argument{"unknown projectile species: " + species};
0088 }
0089
0090 Nucleus::Nucleus(std::size_t A) : nucleons_(A), offset_(0) {}
0091
0092 void Nucleus::sample_nucleons(double offset) {
0093 offset_ = offset;
0094 sample_nucleons_impl();
0095 }
0096
0097 void Nucleus::set_nucleon_position(
0098 iterator nucleon, double x, double y, double z) {
0099 nucleon->set_position(x + offset_, y, z);
0100 }
0101
0102 Proton::Proton() : Nucleus(1) {}
0103
0104
0105 double Proton::radius() const {
0106 return 0.;
0107 }
0108
0109
0110 void Proton::sample_nucleons_impl() {
0111 set_nucleon_position(begin(), 0., 0., 0.);
0112 }
0113
0114
0115
0116 Deuteron::Deuteron(double a, double b)
0117 : Nucleus(2),
0118 a_(std::fmin(a, b)),
0119 b_(std::fmax(a, b))
0120 {}
0121
0122 double Deuteron::radius() const {
0123
0124
0125 return -std::log(.01)/(2*a_);
0126 }
0127
0128 void Deuteron::sample_nucleons_impl() {
0129
0130
0131
0132
0133
0134
0135
0136 double r, prob;
0137 do {
0138
0139 auto u = random::canonical<double>();
0140
0141 r = -std::log(u) / (2*a_);
0142
0143
0144
0145
0146 prob = std::pow(std::exp(-a_*r) - std::exp(-b_*r), 2) / u;
0147 } while (prob < random::canonical<double>());
0148
0149
0150 auto cos_theta = random::cos_theta<double>();
0151 auto phi = random::phi<double>();
0152
0153
0154 auto r_sin_theta = r * std::sqrt(1. - cos_theta*cos_theta);
0155 auto x = r_sin_theta * std::cos(phi);
0156 auto y = r_sin_theta * std::sin(phi);
0157 auto z = r * cos_theta;
0158
0159
0160 set_nucleon_position(begin(), x, y, z);
0161
0162 set_nucleon_position(std::next(begin()), -x, -y, -z);
0163 }
0164
0165 MinDistNucleus::MinDistNucleus(std::size_t A, double dmin)
0166 : Nucleus(A),
0167 dminsq_(dmin*dmin)
0168 {}
0169
0170 bool MinDistNucleus::is_too_close(const_iterator nucleon) const {
0171 if (dminsq_ < 1e-10)
0172 return false;
0173 for (const_iterator nucleon2 = begin(); nucleon2 != nucleon; ++nucleon2) {
0174 auto dx = nucleon->x() - nucleon2->x();
0175 auto dy = nucleon->y() - nucleon2->y();
0176 auto dz = nucleon->z() - nucleon2->z();
0177 if (dx*dx + dy*dy + dz*dz < dminsq_)
0178 return true;
0179 }
0180 return false;
0181 }
0182
0183
0184
0185 WoodsSaxonNucleus::WoodsSaxonNucleus(
0186 std::size_t A, double R, double a, double dmin)
0187 : MinDistNucleus(A, dmin),
0188 R_(R),
0189 a_(a),
0190 woods_saxon_dist_(1000, 0., R + 10.*a,
0191 [R, a](double r) { return r*r/(1.+std::exp((r-R)/a)); })
0192 {}
0193
0194
0195
0196
0197
0198 double WoodsSaxonNucleus::radius() const {
0199 return R_ + 3.*a_;
0200 }
0201
0202
0203 void WoodsSaxonNucleus::sample_nucleons_impl() {
0204
0205
0206
0207
0208
0209
0210
0211
0212 std::vector<double> radii(size());
0213 for (auto&& r : radii)
0214 r = woods_saxon_dist_(random::engine);
0215 std::sort(radii.begin(), radii.end());
0216
0217
0218 auto r_iter = radii.cbegin();
0219 for (iterator nucleon = begin(); nucleon != end(); ++nucleon) {
0220
0221 auto& r = *r_iter++;
0222
0223
0224 auto ntries = 0;
0225 do {
0226
0227 auto cos_theta = random::cos_theta<double>();
0228 auto phi = random::phi<double>();
0229
0230
0231 auto r_sin_theta = r * std::sqrt(1. - cos_theta*cos_theta);
0232 auto x = r_sin_theta * std::cos(phi);
0233 auto y = r_sin_theta * std::sin(phi);
0234 auto z = r * cos_theta;
0235
0236 set_nucleon_position(nucleon, x, y, z);
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246 } while (++ntries < 1000 && is_too_close(nucleon));
0247 }
0248
0249 }
0250
0251
0252
0253
0254 DeformedWoodsSaxonNucleus::DeformedWoodsSaxonNucleus(
0255 std::size_t A, double R, double a, double beta2, double beta4, double dmin)
0256 : MinDistNucleus(A, dmin),
0257 R_(R),
0258 a_(a),
0259 beta2_(beta2),
0260 beta4_(beta4),
0261 rmax_(R*(1. + .63*std::fabs(beta2) + .85*std::fabs(beta4)) + 10.*a)
0262 {}
0263
0264
0265
0266
0267
0268 double DeformedWoodsSaxonNucleus::radius() const {
0269 return rmax_ - 7.*a_;
0270 }
0271
0272 double DeformedWoodsSaxonNucleus::deformed_woods_saxon_dist(
0273 double r, double cos_theta) const {
0274 auto cos_theta_sq = cos_theta*cos_theta;
0275
0276
0277 using math::double_constants::one_div_root_pi;
0278 auto Y20 = std::sqrt(5)/4. * one_div_root_pi * (3.*cos_theta_sq - 1.);
0279 auto Y40 = 3./16. * one_div_root_pi *
0280 (35.*cos_theta_sq*cos_theta_sq - 30.*cos_theta_sq + 3.);
0281
0282
0283 auto Reff = R_ * (1. + beta2_*Y20 + beta4_*Y40);
0284
0285 return 1. / (1. + std::exp((r - Reff) / a_));
0286 }
0287
0288
0289 void DeformedWoodsSaxonNucleus::sample_nucleons_impl() {
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302 const auto cos_a = random::cos_theta<double>();
0303 const auto sin_a = std::sqrt(1. - cos_a*cos_a);
0304
0305
0306 const auto angle_b = random::phi<double>();
0307 const auto cos_b = std::cos(angle_b);
0308 const auto sin_b = std::sin(angle_b);
0309
0310
0311
0312 struct Sample {
0313 double r, cos_theta;
0314 };
0315
0316 std::vector<Sample> samples(size());
0317
0318 for (auto&& sample : samples) {
0319
0320
0321 do {
0322 sample.r = rmax_ * std::cbrt(random::canonical<double>());
0323 sample.cos_theta = random::cos_theta<double>();
0324 } while (
0325 random::canonical<double>() >
0326 deformed_woods_saxon_dist(sample.r, sample.cos_theta)
0327 );
0328 }
0329
0330
0331
0332
0333 std::sort(
0334 samples.begin(), samples.end(),
0335 [](const Sample& a, const Sample& b) {
0336 return a.r < b.r;
0337 }
0338 );
0339
0340
0341 auto sample = samples.cbegin();
0342 for (iterator nucleon = begin(); nucleon != end(); ++nucleon, ++sample) {
0343 auto& r = sample->r;
0344 auto& cos_theta = sample->cos_theta;
0345
0346 auto r_sin_theta = r * std::sqrt(1. - cos_theta*cos_theta);
0347 auto z = r * cos_theta;
0348
0349
0350 auto ntries = 0;
0351 do {
0352
0353 auto phi = random::phi<double>();
0354
0355
0356 auto x = r_sin_theta * std::cos(phi);
0357 auto y = r_sin_theta * std::sin(phi);
0358
0359
0360
0361
0362 auto x_rot = x*cos_b - y*cos_a*sin_b + z*sin_a*sin_b;
0363 auto y_rot = x*sin_b + y*cos_a*cos_b - z*sin_a*cos_b;
0364 auto z_rot = y*sin_a + z*cos_a;
0365
0366 set_nucleon_position(nucleon, x_rot, y_rot, z_rot);
0367
0368
0369
0370
0371 z *= -1;
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381 } while (++ntries < 1000 && is_too_close(nucleon));
0382 }
0383 }
0384
0385 #ifdef TRENTO_HDF5
0386
0387 namespace {
0388
0389
0390 template <typename T, std::size_t FileDims, std::size_t MemDims>
0391 boost::multi_array<T, MemDims>
0392 read_dataset(
0393 const H5::DataSet& dataset,
0394 const std::array<hsize_t, FileDims>& count,
0395 const std::array<hsize_t, FileDims>& start,
0396 const std::array<hsize_t, MemDims>& shape) {
0397 boost::multi_array<T, MemDims> array{shape};
0398 auto filespace = dataset.getSpace();
0399 filespace.selectHyperslab(H5S_SELECT_SET, count.data(), start.data());
0400 auto memspace = hdf5::make_dataspace(shape);
0401
0402 dataset.read(array.data(), hdf5::type<T>(), memspace, filespace);
0403
0404 return array;
0405 }
0406
0407 }
0408
0409 std::unique_ptr<ManualNucleus> ManualNucleus::create(const std::string& path) {
0410 auto file = hdf5::try_open_file(path);
0411
0412
0413
0414 if (file.getNumObjs() != 1)
0415 throw std::invalid_argument{
0416 "file '" + path + "' must contain exactly one object"
0417 };
0418
0419 auto name = file.getObjnameByIdx(0);
0420 #if H5_VERSION_GE(1, 8, 13)
0421 if (file.childObjType(name) != H5O_TYPE_DATASET)
0422 #else
0423 if (file.getObjTypeByIdx(0) != H5G_DATASET)
0424 #endif
0425 throw std::invalid_argument{
0426 "object '" + name + "' in file '" + path + "' is not a dataset"
0427 };
0428
0429
0430 auto dataset = std::unique_ptr<H5::DataSet>{
0431 new H5::DataSet{file.openDataSet(name)}
0432 };
0433
0434
0435 std::array<hsize_t, 3> shape;
0436 auto ndim = dataset->getSpace().getSimpleExtentDims(shape.data());
0437
0438 if (ndim != 3)
0439 throw std::invalid_argument{
0440 "dataset '" + name + "' in file '" + path + "' has " +
0441 std::to_string(ndim) + " dimensions (need 3)"
0442 };
0443
0444 if (shape[2] != 3)
0445 throw std::invalid_argument{
0446 "dataset '" + name + "' in file '" + path + "' has " +
0447 std::to_string(shape[2]) + " columns (need 3)"
0448 };
0449
0450
0451 const auto& nconfigs = shape[0];
0452 const auto& A = shape[1];
0453
0454
0455 auto n = std::min(500/A + 1, nconfigs);
0456 std::array<hsize_t, 3> count = {n, A, 3};
0457 std::array<hsize_t, 3> start = {0, 0, 0};
0458 std::array<hsize_t, 2> shape_n = {n*A, 3};
0459 auto positions = read_dataset<float>(*dataset, count, start, shape_n);
0460
0461 auto rmax_sq = 0.;
0462
0463 for (const auto& position : positions) {
0464 auto& x = position[0];
0465 auto& y = position[1];
0466 auto& z = position[2];
0467 auto r_sq = x*x + y*y + z*z;
0468 if (r_sq > rmax_sq)
0469 rmax_sq = r_sq;
0470 }
0471
0472 auto rmax = std::sqrt(rmax_sq);
0473
0474 return std::unique_ptr<ManualNucleus>{
0475 new ManualNucleus{std::move(dataset), nconfigs, A, rmax}
0476 };
0477 }
0478
0479 ManualNucleus::ManualNucleus(std::unique_ptr<H5::DataSet> dataset,
0480 std::size_t nconfigs, std::size_t A, double rmax)
0481 : Nucleus(A),
0482 dataset_(std::move(dataset)),
0483 rmax_(rmax),
0484 index_dist_(0, nconfigs - 1)
0485 {}
0486
0487 ManualNucleus::~ManualNucleus() = default;
0488
0489 double ManualNucleus::radius() const {
0490 return rmax_;
0491 }
0492
0493 void ManualNucleus::sample_nucleons_impl() {
0494
0495
0496 const auto angle_1 = random::phi<double>();
0497 const auto c1 = std::cos(angle_1);
0498 const auto s1 = std::sin(angle_1);
0499
0500 const auto c2 = random::cos_theta<double>();
0501 const auto s2 = std::sqrt(1. - c2*c2);
0502
0503 const auto angle_3 = random::phi<double>();
0504 const auto c3 = std::cos(angle_3);
0505 const auto s3 = std::sin(angle_3);
0506
0507
0508 std::array<hsize_t, 3> count = {1, size(), 3};
0509 std::array<hsize_t, 3> start = {index_dist_(random::engine), 0, 0};
0510 std::array<hsize_t, 2> shape = {size(), 3};
0511 const auto positions = read_dataset<float>(*dataset_, count, start, shape);
0512
0513
0514 auto positions_iter = positions.begin();
0515 for (iterator nucleon = begin(); nucleon != end(); ++nucleon) {
0516
0517 auto position = *positions_iter++;
0518 auto& x = position[0];
0519 auto& y = position[1];
0520 auto& z = position[2];
0521
0522
0523 auto x_rot = x*(c1*c3 - c2*s1*s3) - y*(c3*s1 + c1*c2*s3) + z*s2*s3;
0524 auto y_rot = x*(c1*s3 + c2*c3*s1) - y*(s1*s3 - c1*c2*c3) - z*c3*s2;
0525 auto z_rot = x*s1*s2 + y*c1*s2 + z*c2;
0526
0527 set_nucleon_position(nucleon, x_rot, y_rot, z_rot);
0528 }
0529 }
0530
0531 #endif
0532
0533 }