Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
/* clang-format off */
// InteractionCounter used boost/histogram, which
// fails if boost/type_traits have been included before. Thus, we have
// to include it first...
#include <corsika/framework/process/InteractionCounter.hpp>
/* clang-format on */
#include <corsika/framework/core/Cascade.hpp>
#include <corsika/framework/geometry/Box.hpp>
#include <corsika/framework/geometry/Cylinder.hpp>
#include <corsika/framework/process/ProcessSequence.hpp>
#include <corsika/framework/process/SwitchProcessSequence.hpp>
#include <corsika/framework/random/RNGManager.hpp>
#include <corsika/framework/stack/CombinedStack.hpp>
#include <corsika/framework/stack/Stack.hpp>
#include <corsika/media/Environment.hpp>
#include <corsika/media/HomogeneousMedium.hpp>
#include <corsika/media/IMediumModel.hpp>
#include <corsika/media/MediumProperties.hpp>
#include <corsika/media/MediumPropertyModel.hpp>
#include <corsika/media/ShowerAxis.hpp>
#include <corsika/modules/BetheBlochPDG.hpp>
#include <corsika/modules/ObservationPlane.hpp>
#include <corsika/modules/LongitudinalProfile.hpp>
#include <corsika/modules/writers/SubWriter.hpp>
#include <corsika/modules/writers/LongitudinalWriter.hpp>
#include <corsika/modules/writers/EnergyLossWriter.hpp>
#include <corsika/modules/PROPOSAL.hpp>
#include <corsika/modules/ParticleCut.hpp>
#include <corsika/modules/Pythia8.hpp>
#include <corsika/modules/Random.hpp>
#include <corsika/modules/Sibyll.hpp>
#include <corsika/modules/UrQMD.hpp>
#include <corsika/modules/tracking/TrackingStraight.hpp>
#include <corsika/output/OutputManager.hpp>
#include <corsika/stack/GeometryNodeStackExtension.hpp>
#include <corsika/stack/WeightStackExtension.hpp>
#include <corsika/stack/VectorStack.hpp>
#include <corsika/setup/SetupStack.hpp>
#include <CLI/App.hpp>
#include <CLI/Config.hpp>
#include <CLI/Formatter.hpp>
using namespace corsika;
using IMediumType = IMediumPropertyModel<IMediumModel>;
using EnvType = Environment<IMediumType>;
using StackActive = setup::Stack<EnvType>;
using StackView = StackActive::stack_view_type;
using Particle = StackActive::particle_type;
void registerRandomStreams(int seed) {
RNGManager<>::getInstance().registerRandomStream("cascade");
RNGManager<>::getInstance().registerRandomStream("qgsjet");
RNGManager<>::getInstance().registerRandomStream("sibyll");
RNGManager<>::getInstance().registerRandomStream("epos");
RNGManager<>::getInstance().registerRandomStream("pythia");
RNGManager<>::getInstance().registerRandomStream("urqmd");
RNGManager<>::getInstance().registerRandomStream("proposal");
if (seed == 0) {
std::random_device rd;
seed = rd();
CORSIKA_LOG_INFO("random seed (auto) {} ", seed);
} else {
CORSIKA_LOG_INFO("random seed {} ", seed);
}
RNGManager<>::getInstance().setSeed(seed);
}
int main(int argc, char** argv) {
// * process input
Code primaryType;
HEPEnergyType e0, eCut;
int A, Z, n_event;
int randomSeed;
std::string output_dir;
CLI::App app{"Neutrino event generator"};
// we start by definining a sub-group for the primary ID
auto opt_Z = app.add_option("-Z", Z, "Atomic number for primary")
->check(CLI::Range(0, 26))
->group("Primary");
auto opt_A = app.add_option("-A", A, "Atomic mass number for primary")
->needs(opt_Z)
->check(CLI::Range(1, 58))
->group("Primary");
app.add_option("-p,--pdg", "PDG code for primary.")
->excludes(opt_A)
->excludes(opt_Z)
->group("Primary");
app.add_option("--e0", "Minimum energy [GeV]")
->check(CLI::Range(50.0, 1e8))
->default_val(1e5)
->group("Primary");
app.add_option("--eCut", "Cut energy [GeV]")->default_val(1.);
app.add_option("-N,--nevent", n_event, "The number of events/showers to run.")
->default_val(1)
->check(CLI::PositiveNumber);
app.add_option("-f,--filename", output_dir, "Filename for output library")
->check(CLI::NonexistentPath)
->default_val("output");
app.add_option("-v", "Verbosity level: warn, info, debug, trace.")
->default_val("info")
->check(CLI::IsMember({"warn", "info", "debug", "trace"}));
app.add_option("-s", randomSeed, "Seed for random number")
->check(CLI::NonNegativeNumber)
->default_val(0);
CLI11_PARSE(app, argc, argv);
// check that we got either PDG or A/Z
// this can be done with option_groups but the ordering
// gets all messed up
if (app.count("--pdg") == 0) {
if ((app.count("-A") == 0) || (app.count("-Z") == 0)) {
CORSIKA_LOG_ERROR("If --pdg is not provided, then both -A and -Z are required.");
return 1;
}
}
// check if we want to use a PDG code instead
if (app.count("--pdg") > 0) {
primaryType = convert_from_PDG(PDGCode(app["--pdg"]->as<int>()));
} else {
// check manually for proton and neutrons
if ((A == 1) && (Z == 1))
primaryType = Code::Proton;
else if ((A == 1) && (Z == 0))
primaryType = Code::Neutron;
else
primaryType = get_nucleus_code(A, Z);
}
e0 = app["--e0"]->as<double>() * 1_GeV;
eCut = app["--eCut"]->as<double>() * 1_GeV;
std::string_view const loglevel = app["-v"]->as<std::string_view>();
if (loglevel == "warn") {
logging::set_level(logging::level::warn);
} else if (loglevel == "info") {
logging::set_level(logging::level::info);
} else if (loglevel == "debug") {
logging::set_level(logging::level::debug);
} else if (loglevel == "trace") {
#ifndef _C8_DEBUG_
CORSIKA_LOG_ERROR("trace log level requires a Debug build.");
return 1;
#endif
logging::set_level(logging::level::trace);
}
registerRandomStreams(randomSeed);
// * environment and universe
EnvType env;
auto& universe = env.getUniverse();
auto const& rootCS = env.getCoordinateSystem();
// * Water geometry
{
Point const center{rootCS, 0_m, 0_m, 0_m};
auto sphere = std::make_unique<Sphere>(center, 100_m);
auto node = std::make_unique<VolumeTreeNode<IMediumType>>(std::move(sphere));
auto comp = NuclearComposition({{Code::Oxygen}, {1.0}});
auto density = 1.02_g / (1_cm * 1_cm * 1_cm);
auto earth_medium =
std::make_shared<MediumPropertyModel<HomogeneousMedium<IMediumType>>>(
Medium::StandardRock, density, comp);
node->setModelProperties(earth_medium);
universe->addChild(std::move(node));
}
// * detector geometry
auto injectorLength = 50_m;
Point const injectorPos = Point(rootCS, {0_m, 0_m, injectorLength});
auto const& injectCS = make_translation(rootCS, injectorPos.getCoordinates());
DirectionVector upVec(rootCS, {0., 0., 1.});
DirectionVector leftVec(rootCS, {1., 0., 0.});
DirectionVector downVec(rootCS, {0., 0., -1.});
// * observation plane
std::vector<ObservationPlane<tracking_line::Tracking>> obsPlanes;
const int nPlane = 5;
for (int i = 0; i < nPlane - 1; i++) {
Point planeCenter{injectCS, {0_m, 0_m, -(i + 1) * 3_m}};
obsPlanes.push_back({Plane(planeCenter, upVec), leftVec, false});
}
obsPlanes.push_back({Plane(Point(injectCS, {0_m, 0_m, -50_m}), upVec), leftVec, true});
auto& obsPlaneFinal = obsPlanes[nPlane - 1];
// * longitutional profile
ShowerAxis const showerAxis{injectorPos, 1.2 * injectorLength * downVec, env};
LongitudinalWriter longiWriter{showerAxis, 5500, 1_g / square(1_cm)};
LongitudinalProfile<SubWriter<decltype(longiWriter)>> longprof{longiWriter};
// * energy loss profile
EnergyLossWriter dEdX{showerAxis, 1_g / square(1_cm), 5500};
// * physical process list
// particle production threshold
HEPEnergyType const emcut = eCut;
HEPEnergyType const hadcut = eCut;
ParticleCut<SubWriter<decltype(dEdX)>> cut(emcut, emcut, hadcut, hadcut, true, dEdX);
// hadronic interactions
HEPEnergyType heHadronModelThreshold = 63.1_GeV;
corsika::sibyll::Interaction sibyll(env);
// UrQMD not support our nucleon yet. See #456
corsika::urqmd::UrQMD urqmd;
InteractionCounter urqmdCounted(urqmd);
struct EnergySwitch {
HEPEnergyType cutE_;
EnergySwitch(HEPEnergyType cutE)
: cutE_(cutE) {}
bool operator()(const Particle& p) const { return (p.getKineticEnergy() < cutE_); }
};
// auto lowModel = make_sequence(urqmd);
auto hadronSequence =
make_select(EnergySwitch(heHadronModelThreshold), urqmdCounted, sibyll);
// decay process
corsika::pythia8::Decay decayPythia; // ? will double initialize be a problem?
corsika::sibyll::Decay decaySibyll{{
Code::N1440Plus,
Code::N1440MinusBar,
Code::N1440_0,
Code::N1440_0Bar,
Code::N1710Plus,
Code::N1710MinusBar,
Code::N1710_0,
Code::N1710_0Bar,
Code::Pi1300Plus,
Code::Pi1300Minus,
Code::Pi1300_0,
Code::KStar0_1430_0,
Code::KStar0_1430_0Bar,
Code::KStar0_1430_Plus,
Code::KStar0_1430_MinusBar,
}};
auto decaySequence = make_sequence(decayPythia, decaySibyll);
// EM process
corsika::proposal::Interaction emCascade(env, sibyll.getHadronInteractionModel(),
heHadronModelThreshold);
corsika::proposal::ContinuousProcess<SubWriter<decltype(dEdX)>> emContinuous(env, dEdX);
// BetheBlochPDG emContinuous;
// total physics list
auto physics_sequence =
make_sequence(emCascade, emContinuous, hadronSequence, decaySequence);
// * output module
OutputManager output(output_dir);
for (int i = 0; i < nPlane; i++) {
output.add(fmt::format("particles_{:}", i), obsPlanes[i]);
}
// hard coded
auto obsPlaneSequence =
make_sequence(obsPlanes[0], obsPlanes[1], obsPlanes[2], obsPlanes[3], obsPlanes[4]);
output.add("longi_profile", longiWriter);
output.add("energy_loss", dEdX);
// * the final process sequence
auto sequence = make_sequence(physics_sequence, cut, obsPlaneSequence, longprof);
// * tracking and stack
tracking_line::Tracking tracking;
StackActive stack;
// * cascade manager
Cascade EAS(env, tracking, sequence, output, stack);
// * main loop
output.startOfLibrary();
for (int i_shower = 0; i_shower < n_event; i_shower++) {
stack.clear();
CORSIKA_LOG_INFO("Event: {} / {}", i_shower, n_event);
// * inject primary
auto primary = stack.addParticle(std::make_tuple(
primaryType, e0 - get_mass(primaryType), downVec, injectorPos, 0_ns));
EAS.run();
// * report energy loss result
HEPEnergyType const Efinal = dEdX.getEnergyLost() + obsPlaneFinal.getEnergyGround();
CORSIKA_LOG_INFO(
"total energy budget (TeV): {:.2f} (dEdX={:.2f} ground={:.2f}), "
"relative difference (%): {:.3f}",
e0 / 1_TeV, dEdX.getEnergyLost() / 1_TeV, obsPlaneFinal.getEnergyGround() / 1_TeV,
(Efinal / e0 - 1.) * 100.);
}
output.endOfLibrary();
return 0;
}