File indexing completed on 2025-08-05 08:19:07
0001
0002
0003
0004
0005
0006 #include "event.h"
0007
0008 #include <algorithm>
0009 #include <cmath>
0010
0011 #include <boost/program_options/variables_map.hpp>
0012 #include "nucleus.h"
0013 #include <iostream>
0014 #include <stdexcept>
0015
0016 namespace trento {
0017
0018 namespace {
0019
0020 constexpr double TINY = 1e-12;
0021
0022
0023
0024 inline double positive_pmean(double p, double a, double b) {
0025 return std::pow(.5*(std::pow(a, p) + std::pow(b, p)), 1./p);
0026 }
0027
0028
0029
0030 inline double negative_pmean(double p, double a, double b) {
0031 if (a < TINY || b < TINY)
0032 return 0.;
0033 return positive_pmean(p, a, b);
0034 }
0035
0036
0037 inline double geometric_mean(double a, double b) {
0038 return std::sqrt(a*b);
0039 }
0040
0041
0042 double beam_rapidity(double beam_energy) {
0043
0044 constexpr double mp = 0.938;
0045 return 0.5 * (beam_energy/mp + std::sqrt(std::pow(beam_energy/mp, 2) - 4.));
0046 }
0047
0048 }
0049
0050
0051
0052
0053
0054
0055
0056
0057 Event::Event(const VarMap& var_map)
0058 : norm_(var_map["normalization"].as<double>()),
0059 beam_energy_(var_map["beam-energy"].as<double>()),
0060 exp_ybeam_(beam_rapidity(beam_energy_)),
0061 mean_coeff_(var_map["mean-coeff"].as<double>()),
0062 std_coeff_(var_map["std-coeff"].as<double>()),
0063 skew_coeff_(var_map["skew-coeff"].as<double>()),
0064 skew_type_(var_map["skew-type"].as<int>()),
0065 dxy_(var_map["xy-step"].as<double>()),
0066 deta_(var_map["eta-step"].as<double>()),
0067 nsteps_(std::ceil(2.*var_map["xy-max"].as<double>()/dxy_)),
0068 neta_(std::ceil(2.*var_map["eta-max"].as<double>()/(deta_+1e-15))),
0069 xymax_(.5*nsteps_*dxy_),
0070 etamax_(var_map["eta-max"].as<double>()),
0071 eta2y_(var_map["jacobian"].as<double>(), etamax_, deta_),
0072 cgf_(),
0073 TA_(boost::extents[nsteps_][nsteps_]),
0074 TB_(boost::extents[nsteps_][nsteps_]),
0075 TR_(boost::extents[nsteps_][nsteps_][1]),
0076 TAB_(boost::extents[nsteps_][nsteps_]),
0077 with_ncoll_(var_map["ncoll"].as<bool>()),
0078 density_(boost::extents[nsteps_][nsteps_][neta_]) {
0079
0080
0081
0082 try {
0083 if ((skew_type_ == 1 && skew_coeff_ > 10.) ||
0084 (skew_type_ == 2 && skew_coeff_ > 3.) ){
0085 auto info = std::string("Error: skew coefficent too large to be stable.\n")
0086 + std::string("Requirements: (1) relative skew, skew_coeff < 10\n")
0087 + std::string(" (2) absolute skew, skew_coeff < 3");
0088 throw std::invalid_argument(info);
0089 }
0090 }
0091 catch (const std::invalid_argument& error){
0092 std::cerr << error.what() << std::endl;
0093 exit(1);
0094 }
0095
0096
0097
0098
0099 auto p = var_map["reduced-thickness"].as<double>();
0100 if (std::fabs(p) < TINY) {
0101 compute_reduced_thickness_ = [this]() {
0102 compute_reduced_thickness(geometric_mean);
0103 };
0104 } else if (p > 0.) {
0105 compute_reduced_thickness_ = [this, p]() {
0106 compute_reduced_thickness(
0107 [p](double a, double b) { return positive_pmean(p, a, b); });
0108 };
0109 } else {
0110 compute_reduced_thickness_ = [this, p]() {
0111 compute_reduced_thickness(
0112 [p](double a, double b) { return negative_pmean(p, a, b); });
0113 };
0114 }
0115 }
0116
0117 void Event::compute(const Nucleus& nucleusA, const Nucleus& nucleusB,
0118 NucleonProfile& profile) {
0119
0120 npart_ = 0;
0121 compute_nuclear_thickness(nucleusA, profile, TA_);
0122 compute_nuclear_thickness(nucleusB, profile, TB_);
0123 compute_reduced_thickness_();
0124 compute_observables();
0125 }
0126
0127 bool Event::is3D() const {
0128 return etamax_ > TINY;
0129 }
0130
0131 namespace {
0132
0133
0134
0135 template <typename T>
0136 inline const T& clip(const T& value, const T& min, const T& max) {
0137 if (value < min)
0138 return min;
0139 if (value > max)
0140 return max;
0141 return value;
0142 }
0143
0144 }
0145
0146
0147 void Event::clear_TAB(void){
0148 ncoll_ = 0;
0149 for (int iy = 0; iy < nsteps_; ++iy) {
0150 for (int ix = 0; ix < nsteps_; ++ix) {
0151 TAB_[iy][ix] = 0.;
0152 }
0153 }
0154 }
0155
0156
0157 void Event::accumulate_TAB(Nucleon& A, Nucleon& B, NucleonProfile& profile){
0158 ncoll_ ++;
0159
0160 double xA = A.x() + xymax_, yA = A.y() + xymax_;
0161 double xB = B.x() + xymax_, yB = B.y() + xymax_;
0162
0163 double bpp_sq = std::pow(xA - xB, 2) + std::pow(yA - yB, 2);
0164
0165 double x = (xA+xB)/2.;
0166 double y = (yA+yB)/2.;
0167
0168 const double r = profile.radius();
0169 int ixmin = clip(static_cast<int>((x-r)/dxy_), 0, nsteps_-1);
0170 int iymin = clip(static_cast<int>((y-r)/dxy_), 0, nsteps_-1);
0171 int ixmax = clip(static_cast<int>((x+r)/dxy_), 0, nsteps_-1);
0172 int iymax = clip(static_cast<int>((y+r)/dxy_), 0, nsteps_-1);
0173
0174
0175 auto norm_Tpp = profile.norm_Tpp(bpp_sq);
0176 for (auto iy = iymin; iy <= iymax; ++iy) {
0177 double dysqA = std::pow(yA - (static_cast<double>(iy)+.5)*dxy_, 2);
0178 double dysqB = std::pow(yB - (static_cast<double>(iy)+.5)*dxy_, 2);
0179 for (auto ix = ixmin; ix <= ixmax; ++ix) {
0180 double dxsqA = std::pow(xA - (static_cast<double>(ix)+.5)*dxy_, 2);
0181 double dxsqB = std::pow(xB - (static_cast<double>(ix)+.5)*dxy_, 2);
0182
0183
0184
0185
0186
0187 TAB_[iy][ix] += profile.deterministic_thickness(dxsqA + dysqA)
0188 * profile.deterministic_thickness(dxsqB + dysqB)
0189 / norm_Tpp;
0190 }
0191 }
0192 }
0193
0194 void Event::compute_nuclear_thickness(
0195 const Nucleus& nucleus, NucleonProfile& profile, Grid& TX) {
0196
0197
0198
0199
0200
0201
0202
0203
0204 std::fill(TX.origin(), TX.origin() + TX.num_elements(), 0.);
0205
0206 const double r = profile.radius();
0207
0208
0209 for (const auto& nucleon : nucleus) {
0210 if (!nucleon.is_participant())
0211 continue;
0212
0213 ++npart_;
0214
0215
0216 double x = nucleon.x() + xymax_;
0217 double y = nucleon.y() + xymax_;
0218
0219
0220 int ixmin = clip(static_cast<int>((x-r)/dxy_), 0, nsteps_-1);
0221 int iymin = clip(static_cast<int>((y-r)/dxy_), 0, nsteps_-1);
0222 int ixmax = clip(static_cast<int>((x+r)/dxy_), 0, nsteps_-1);
0223 int iymax = clip(static_cast<int>((y+r)/dxy_), 0, nsteps_-1);
0224
0225
0226 profile.fluctuate();
0227
0228
0229 for (auto iy = iymin; iy <= iymax; ++iy) {
0230 double dysq = std::pow(y - (static_cast<double>(iy)+.5)*dxy_, 2);
0231 for (auto ix = ixmin; ix <= ixmax; ++ix) {
0232 double dxsq = std::pow(x - (static_cast<double>(ix)+.5)*dxy_, 2);
0233 TX[iy][ix] += profile.thickness(dxsq + dysq);
0234 }
0235 }
0236 }
0237 }
0238
0239 template <typename GenMean>
0240 void Event::compute_reduced_thickness(GenMean gen_mean) {
0241 double sum = 0.;
0242 double ixcm = 0.;
0243 double iycm = 0.;
0244
0245 for (int iy = 0; iy < nsteps_; ++iy) {
0246 for (int ix = 0; ix < nsteps_; ++ix) {
0247 auto ta = TA_[iy][ix];
0248 auto tb = TB_[iy][ix];
0249 auto t = norm_ * gen_mean(ta, tb);
0250
0251 TR_[iy][ix][0] = t;
0252
0253
0254 if (is3D()) {
0255 auto mean = mean_coeff_ * mean_function(ta, tb, exp_ybeam_);
0256 auto std = std_coeff_ * std_function(ta, tb);
0257 auto skew = skew_coeff_ * skew_function(ta, tb, skew_type_);
0258 cgf_.calculate_dsdy(mean, std, skew);
0259 auto mid_norm = cgf_.interp_dsdy(0.)*eta2y_.Jacobian(0.);
0260 for (int ieta = 0; ieta < neta_; ++ieta) {
0261 auto eta = -etamax_ + ieta*deta_;
0262 auto rapidity = eta2y_.rapidity(eta);
0263 auto jacobian = eta2y_.Jacobian(eta);
0264 auto rapidity_dist = cgf_.interp_dsdy(rapidity);
0265 density_[iy][ix][ieta] = t * rapidity_dist / mid_norm * jacobian;
0266 }
0267 }
0268
0269 sum += t;
0270
0271
0272 ixcm += t * static_cast<double>(ix);
0273 iycm += t * static_cast<double>(iy);
0274 }
0275 }
0276
0277 multiplicity_ = dxy_ * dxy_ * sum;
0278 ixcm_ = ixcm / sum;
0279 iycm_ = iycm / sum;
0280 }
0281
0282 void Event::compute_observables() {
0283
0284
0285
0286 struct EccentricityAccumulator {
0287 double re = 0.;
0288 double im = 0.;
0289 double wt = 0.;
0290 double finish() const
0291 { return std::sqrt(re*re + im*im) / std::fmax(wt, TINY); }
0292 double angle() const
0293 { return atan2(im, re); }
0294 } e2, e3, e4, e5;
0295
0296 for (int iy = 0; iy < nsteps_; ++iy) {
0297 for (int ix = 0; ix < nsteps_; ++ix) {
0298 const auto& t = TR_[iy][ix][0];
0299 if (t < TINY)
0300 continue;
0301
0302
0303 auto x = static_cast<double>(ix) - ixcm_;
0304 auto x2 = x*x;
0305 auto x3 = x2*x;
0306 auto x4 = x2*x2;
0307
0308 auto y = static_cast<double>(iy) - iycm_;
0309 auto y2 = y*y;
0310 auto y3 = y2*y;
0311 auto y4 = y2*y2;
0312
0313 auto r2 = x2 + y2;
0314 auto r = std::sqrt(r2);
0315 auto r4 = r2*r2;
0316
0317 auto xy = x*y;
0318 auto x2y2 = x2*y2;
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346 e2.re += t * (x2 - y2);
0347 e2.im += t * 2.*xy;
0348 e2.wt += t * r2;
0349
0350 e3.re += t * (x3 - 3.*x*y2);
0351 e3.im += t * y*(3. - 4.*y2);
0352 e3.wt += t * r2*r;
0353
0354 e4.re += t * (x4 + y4 - 6.*x2y2);
0355 e4.im += t * 4.*xy*(1. - 2.*y2);
0356 e4.wt += t * r4;
0357
0358 e5.re += t * x*(x4 - 10.*x2y2 - 5.*y4);
0359 e5.im += t * y*(1. - 12.*x2 + 16.*x4);
0360 e5.wt += t * r4*r;
0361 }
0362 }
0363
0364 eccentricity_[2] = e2.finish();
0365 eccentricity_[3] = e3.finish();
0366 eccentricity_[4] = e4.finish();
0367 eccentricity_[5] = e5.finish();
0368
0369 psi_[2] = e2.angle()/2.;
0370 psi_[3] = e3.angle()/3.;
0371 psi_[4] = e4.angle()/4.;
0372 psi_[5] = e5.angle()/5.;
0373 }
0374
0375 }