Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
test_event.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file test_event.cxx
1 // TRENTO: Reduced Thickness Event-by-event Nuclear Topology
2 // Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
3 // MIT License
4 
5 #include "../src/event.h"
6 
7 #include "catch.hpp"
8 #include "util.h"
9 
10 #include "../src/nucleus.h"
11 #include "../src/random.h"
12 
13 using namespace trento;
14 
15 TEST_CASE( "event" ) {
16  // Check Event class results against equivalent (but slower) methods.
17 
18  // Repeat for p == 0, p < 0, p > 0.
19  auto pplus = .5 + .49*random::canonical<>();
20  auto pminus = -.5 + .49*random::canonical<>();
21  for (auto p : {0., pplus, pminus }) {
22  // Random physical params.
23  auto norm = 1. + .5*random::canonical<>();
24  auto xsec = 4. + 3.*random::canonical<>();
25  auto nucleon_width = .5 + .2*random::canonical<>();
26 
27  // Effectively disable fluctuations to deterministically compute thickness.
28  auto fluct = 1e12;
29 
30  // Coarse-ish grid.
31  auto grid_max = 9.;
32  auto grid_step = 0.3;
33  auto grid_nsteps = 60;
34 
35  auto var_map = make_var_map({
36  {"normalization", norm},
37  {"reduced-thickness", p},
38  {"grid-max", grid_max},
39  {"grid-step", grid_step},
40  {"fluctuation", fluct},
41  {"cross-section", xsec},
42  {"nucleon-width", nucleon_width},
43  });
44 
45  Event event{var_map};
46  NucleonProfile profile{var_map};
47 
48  CHECK( event.reduced_thickness_grid().num_dimensions() == 2 );
49  CHECK( static_cast<int>(event.reduced_thickness_grid().shape()[0]) == grid_nsteps );
50  CHECK( static_cast<int>(event.reduced_thickness_grid().shape()[1]) == grid_nsteps );
51 
52  auto nucleusA = Nucleus::create("Pb");
53  auto nucleusB = Nucleus::create("Pb");
54 
55  // Sample impact param, nucleons, and participants.
56  auto b = 4.*std::sqrt(random::canonical<>());
57  nucleusA->sample_nucleons(+.5*b);
58  nucleusB->sample_nucleons(-.5*b);
59 
60  for (auto&& A : *nucleusA)
61  for (auto&& B : *nucleusB)
62  profile.participate(A, B);
63 
64  // Run a normal Event.
65  event.compute(*nucleusA, *nucleusB, profile);
66 
67  // Verify npart.
68  auto count_part = [](const Nucleus& nucleus) {
69  auto npart = 0;
70  for (const auto& n : nucleus)
71  if (n.is_participant())
72  ++npart;
73  return npart;
74  };
75  auto npart = count_part(*nucleusA) + count_part(*nucleusB);
76  CHECK( npart == event.npart() );
77 
78  // Compute TR grid the slow way -- switch the order of grid and nucleon loops.
79  boost::multi_array<double, 2> TR{boost::extents[grid_nsteps][grid_nsteps]};
80 
81  auto thickness = [&profile](const Nucleus& nucleus, double x, double y) {
82  auto t = 0.;
83  for (const auto& n : nucleus) {
84  if (n.is_participant()) {
85  auto dx = n.x() - x;
86  auto dy = n.y() - y;
87  t += profile.thickness(dx*dx + dy*dy);
88  }
89  }
90  return t;
91  };
92 
93  auto gen_mean = [p](double a, double b) {
94  if (std::abs(p) < 1e-12)
95  return std::sqrt(a*b);
96  else if (p < 0. && (a < 1e-12 || b < 1e-12))
97  return 0.;
98  else
99  return std::pow(.5*(std::pow(a, p) + std::pow(b, p)), 1./p);
100  };
101 
102  for (auto iy = 0; iy < grid_nsteps; ++iy) {
103  for (auto ix = 0; ix < grid_nsteps; ++ix) {
104  auto x = (ix + .5) * 2 * grid_max/grid_nsteps - grid_max;
105  auto y = (iy + .5) * 2 * grid_max/grid_nsteps - grid_max;
106  auto TA = thickness(*nucleusA, x, y);
107  auto TB = thickness(*nucleusB, x, y);
108  TR[iy][ix] = norm * gen_mean(TA, TB);
109  }
110  }
111 
112  // Verify multiplicity.
113  auto mult = std::pow(2*grid_max/grid_nsteps, 2) *
114  std::accumulate(TR.origin(), TR.origin() + TR.num_elements(), 0.);
115  CHECK( mult == Approx(event.multiplicity()) );
116 
117  // Verify each grid element.
118  auto all_correct = true;
119  for (const auto* t1 = TR.origin(),
120  * t2 = event.reduced_thickness_grid().origin();
121  t1 != TR.origin() + TR.num_elements();
122  ++t1, ++t2) {
123  if (*t1 != Approx(*t2).epsilon(1e-5)) {
124  all_correct = false;
125  WARN( "TR mismatch: " << *t1 << " != " << *t2 );
126  break;
127  }
128  }
129  CHECK( all_correct );
130 
131  // Verify eccentricity.
132  auto sum = 0., xcm = 0., ycm = 0.;
133  for (auto iy = 0; iy < grid_nsteps; ++iy) {
134  for (auto ix = 0; ix < grid_nsteps; ++ix) {
135  auto& t = TR[iy][ix];
136  sum += t;
137  xcm += t*ix;
138  ycm += t*iy;
139  }
140  }
141  xcm /= sum;
142  ycm /= sum;
143 
144  for (auto n = 2; n <= 5; ++n) {
145  auto real = 0., imag = 0., weight = 0.;
146  for (auto iy = 0; iy < grid_nsteps; ++iy) {
147  for (auto ix = 0; ix < grid_nsteps; ++ix) {
148  auto x = ix - xcm;
149  auto y = iy - ycm;
150  auto w = TR[iy][ix] * std::pow(x*x + y*y, .5*n);
151  // compute exp(i*n*phi) the naive way
152  auto phi = std::atan2(y, x);
153  real += w*std::cos(n*phi);
154  imag += w*std::sin(n*phi);
155  weight += w;
156  }
157  }
158  auto ecc = std::sqrt(real*real + imag*imag) / weight;
159  CHECK( ecc == Approx(event.eccentricity().at(n)).epsilon(1e-6).margin(1e-6) );
160  }
161  }
162 
163  // test grid size when step size does not evenly divide width
164  auto var_map = make_var_map({
165  {"normalization", 1.},
166  {"reduced-thickness", 0.},
167  {"grid-max", 10.},
168  {"grid-step", 0.3}
169  });
170 
171  Event event{var_map};
172 
173  CHECK( event.reduced_thickness_grid().num_dimensions() == 2 );
174  CHECK( event.reduced_thickness_grid().shape()[0] == 67 );
175  CHECK( event.reduced_thickness_grid().shape()[1] == 67 );
176 }