diff --git a/examples/radio_shower.cpp b/examples/radio_shower.cpp index f38aa4f3ce68116fa8eb0ebd9c72d8ab30f0625e..9745cdbba2b32cd9107284bea282929edd1a2d4f 100644 --- a/examples/radio_shower.cpp +++ b/examples/radio_shower.cpp @@ -121,7 +121,9 @@ int main(int argc, char** argv) { registerRandomStreams(seed); // setup environment - using EnvType = setup::Environment; + using EnvironmentInterface = + IRefractiveIndexModel<IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>>; + using EnvType = Environment<EnvironmentInterface>; EnvType env; CoordinateSystemPtr const& rootCS = env.getCoordinateSystem(); Point const center{rootCS, 0_m, 0_m, 0_m}; @@ -132,12 +134,12 @@ int main(int argc, char** argv) { TimeDomainAntenna ant1("antenna1", point1, 0_s, 100_s, 1/1e-8_s); TimeDomainAntenna ant2("antenna2", point2, 0_s, 100_s, 1/1e-8_s); // the detector - std::vector<TimeDomainAntenna> detector; - detector.push_back(ant1); - detector.push_back(ant2); + AntennaCollection<TimeDomainAntenna> detector; + detector.addAntenna(ant1); + detector.addAntenna(ant2); auto builder = make_layered_spherical_atmosphere_builder< - setup::EnvironmentInterface, MyExtraEnv>::create(center, - constants::EarthRadius::Mean, 1., + EnvironmentInterface, MyExtraEnv>::create(center, + constants::EarthRadius::Mean, 1.000327, Medium::AirDry1Atm, MagneticFieldVector{rootCS, 0_T, 50_uT, 0_T}); @@ -248,9 +250,10 @@ int main(int argc, char** argv) { corsika::proposal::Interaction emCascade(env); corsika::proposal::ContinuousProcess emContinuous(env); InteractionCounter emCascadeCounted(emCascade); - // put radio here - RadioProcess<decltype(detector), CoREAS<decltype(detector), decltype(StraightPropagator(envCoREAS))>, decltype(StraightPropagator(envCoREAS))> - coreas(detector, envCoREAS); + // put radio process here + RadioProcess<decltype(detector), CoREAS<decltype(detector), + decltype(StraightPropagator(env))>, decltype(StraightPropagator(env))> + coreas(detector, env); OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false); TrackWriter trackWriter("tracks.dat"); @@ -305,7 +308,7 @@ int main(int argc, char** argv) { cut.reset(); emContinuous.reset(); - // get radio pulse + // get radio output coreas.writeOutput(); auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() + diff --git a/examples/radio_shower2.cpp b/examples/radio_shower2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7620cc88901ea785685dae8df88eccd77d6be3f --- /dev/null +++ b/examples/radio_shower2.cpp @@ -0,0 +1,318 @@ +/* + * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +/* 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/geometry/Plane.hpp> +#include <corsika/framework/geometry/Sphere.hpp> +#include <corsika/framework/core/Logging.hpp> +#include <corsika/framework/utility/SaveBoostHistogram.hpp> +#include <corsika/framework/process/ProcessSequence.hpp> +#include <corsika/framework/process/SwitchProcessSequence.hpp> +#include <corsika/framework/process/InteractionCounter.hpp> +#include <corsika/framework/random/RNGManager.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> +#include <corsika/framework/utility/CorsikaFenv.hpp> +#include <corsika/framework/core/Cascade.hpp> +#include <corsika/framework/geometry/PhysicalGeometry.hpp> + +#include <corsika/media/Environment.hpp> +#include <corsika/media/FlatExponential.hpp> +#include <corsika/media/HomogeneousMedium.hpp> +#include <corsika/media/IMagneticFieldModel.hpp> +#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp> +#include <corsika/media/NuclearComposition.hpp> +#include <corsika/media/MediumPropertyModel.hpp> +#include <corsika/media/UniformMagneticField.hpp> +#include <corsika/media/UniformRefractiveIndex.hpp> +#include <corsika/media/ShowerAxis.hpp> +#include <corsika/media/SlidingPlanarExponential.hpp> + +#include <corsika/modules/BetheBlochPDG.hpp> +#include <corsika/modules/LongitudinalProfile.hpp> +#include <corsika/modules/ObservationPlane.hpp> +#include <corsika/modules/OnShellCheck.hpp> +#include <corsika/modules/StackInspector.hpp> +#include <corsika/modules/TrackWriter.hpp> +#include <corsika/modules/ParticleCut.hpp> +#include <corsika/modules/Pythia8.hpp> +#include <corsika/modules/Sibyll.hpp> +#include <corsika/modules/UrQMD.hpp> +#include <corsika/modules/PROPOSAL.hpp> + +#include <corsika/modules/radio/RadioProcess.hpp> +#include <corsika/modules/radio/CoREAS.hpp> +#include <corsika/modules/radio/antennas/Antenna.hpp> +#include <corsika/modules/radio/antennas/TimeDomainAntenna.hpp> +#include <corsika/modules/radio/detectors/RadioDetector.hpp> +#include <corsika/modules/radio/propagators/StraightPropagator.hpp> +#include <corsika/modules/radio/propagators/SignalPath.hpp> +#include <corsika/modules/radio/propagators/RadioPropagator.hpp> + + +#include <corsika/setup/SetupStack.hpp> +#include <corsika/setup/SetupTrajectory.hpp> + +#include <iomanip> +#include <iostream> +#include <limits> +#include <string> + +/* + NOTE, WARNING, ATTENTION + + The .../Random.hpppp implement the hooks of external modules to the C8 random + number generator. It has to occur excatly ONCE per linked + executable. If you include the header below multiple times and + link this togehter, it will fail. + */ +#include <corsika/modules/sibyll/Random.hpp> +#include <corsika/modules/urqmd/Random.hpp> + +using namespace corsika; +using namespace std; + +using Particle = setup::Stack::particle_type; + +void registerRandomStreams(const int seed) { + RNGManager::getInstance().registerRandomStream("cascade"); + RNGManager::getInstance().registerRandomStream("qgsjet"); + RNGManager::getInstance().registerRandomStream("sibyll"); + RNGManager::getInstance().registerRandomStream("pythia"); + RNGManager::getInstance().registerRandomStream("urqmd"); + RNGManager::getInstance().registerRandomStream("proposal"); + + if (seed == 0) + RNGManager::getInstance().seedAll(); + else + RNGManager::getInstance().seedAll(seed); +} + + +int main(int argc, char** argv) { + + corsika_logger->set_pattern("[%n:%^%-8l%$] %s:%#: %v"); + logging::set_level(logging::level::trace); + + CORSIKA_LOG_INFO("vertical_EAS"); + + if (argc < 4) { + std::cerr << "usage: vertical_EAS <A> <Z> <energy/GeV> [seed]" << std::endl; + std::cerr << " if no seed is given, a random seed is chosen" << std::endl; + return 1; + } + feenableexcept(FE_INVALID); + + int seed = 0; + if (argc > 4) seed = std::stoi(std::string(argv[4])); + // initialize random number sequence(s) + registerRandomStreams(seed); + + // setup environment + using IModelInterface = IRefractiveIndexModel<IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>>; + using AtmModel = UniformRefractiveIndex<MediumPropertyModel<UniformMagneticField<HomogeneousMedium + <IModelInterface>>>>; + using EnvType = Environment<AtmModel>; + EnvType env; + CoordinateSystemPtr const& rootCS = env.getCoordinateSystem(); + Point const center{rootCS, 0_m, 0_m, 0_m}; + // the antenna location + const auto point1{Point(env.getCoordinateSystem(), 50_m, 50_m, 50_m)}; + const auto point2{Point(env.getCoordinateSystem(), 25_m, 25_m, 25_m)}; + // the antennas + TimeDomainAntenna ant1("antenna1", point1, 0_s, 100_s, 1/1e-8_s); + TimeDomainAntenna ant2("antenna2", point2, 0_s, 100_s, 1/1e-8_s); + // the detector + AntennaCollection<TimeDomainAntenna> detector; + detector.addAntenna(ant1); + detector.addAntenna(ant2); + // a refractive index + const double ri_{1.000327}; + // the constant density + const auto density{19.2_g / cube(1_cm)}; + // the composition we use for the homogeneous medium + NuclearComposition const protonComposition(std::vector<Code>{Code::Proton}, + std::vector<float>{1.f}); + // create magnetic field vector + Vector B0(rootCS, 0_T,50_uT, 0_T); + // create the medium + auto Medium = EnvType::createNode<Sphere>( + center, 1_km * std::numeric_limits<double>::infinity()); + // set the properties + auto const props = Medium->setModelProperties<AtmModel>(ri_, + Medium::AirDry1Atm, B0, density, protonComposition); + env.getUniverse()->addChild(std::move(Medium)); + + // setup particle stack, and add primary particle + setup::Stack stack; + stack.clear(); + const Code beamCode = Code::Nucleus; + unsigned short const A = std::stoi(std::string(argv[1])); + unsigned short Z = std::stoi(std::string(argv[2])); + auto const mass = get_nucleus_mass(A, Z); + const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[3])); + double theta = 0.; + auto const thetaRad = theta / 180. * M_PI; + + auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { + return sqrt((Elab - m) * (Elab + m)); + }; + HEPMomentumType P0 = elab2plab(E0, mass); + auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) { + return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad)); + }; + + auto const [px, py, pz] = momentumComponents(thetaRad, P0); + auto plab = MomentumVector(rootCS, {px, py, pz}); + cout << "input particle: " << beamCode << endl; + cout << "input angles: theta=" << theta << endl; + cout << "input momentum: " << plab.getComponents() / 1_GeV + << ", norm = " << plab.getNorm() << endl; + + auto const observationHeight = 0_km + builder.getEarthRadius(); + auto const injectionHeight = 112.75_km + builder.getEarthRadius(); + auto const t = -observationHeight * cos(thetaRad) + + sqrt(-static_pow<2>(sin(thetaRad) * observationHeight) + + static_pow<2>(injectionHeight)); + Point const showerCore{rootCS, 0_m, 0_m, observationHeight}; + Point const injectionPos = + showerCore + DirectionVector{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t; + + std::cout << "point of injection: " << injectionPos.getCoordinates() << std::endl; + + if (A != 1) { + stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, A, Z)); + + } else { + if (Z == 1) { + stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns)); + } else if (Z == 0) { + stack.addParticle(std::make_tuple(Code::Neutron, E0, plab, injectionPos, 0_ns)); + } else { + std::cerr << "illegal parameters" << std::endl; + return EXIT_FAILURE; + } + } + + // we make the axis much longer than the inj-core distance since the + // profile will go beyond the core, depending on zenith angle + std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.5 + << std::endl; + + ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.5, env}; + + // setup processes, decays and interactions + + corsika::sibyll::Interaction sibyll; + InteractionCounter sibyllCounted(sibyll); + + corsika::sibyll::NuclearInteraction sibyllNuc(sibyll, env); + InteractionCounter sibyllNucCounted(sibyllNuc); + + corsika::pythia8::Decay decayPythia; + + // use sibyll decay routine for decays of particles unknown to pythia + 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, + }}; + + decaySibyll.printDecayConfig(); + + ParticleCut cut{60_GeV, 60_GeV, 60_GeV, 60_GeV, true}; + corsika::proposal::Interaction emCascade(env); + corsika::proposal::ContinuousProcess emContinuous(env); + InteractionCounter emCascadeCounted(emCascade); + // put radio process here + RadioProcess<decltype(detector), CoREAS<decltype(detector), + decltype(StraightPropagator(env))>, decltype(StraightPropagator(env))> + coreas(detector, env); + + OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false); + TrackWriter trackWriter("tracks.dat"); + + LongitudinalProfile longprof{showerAxis}; + + Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.})); + ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}), + "particles.dat"); + + corsika::urqmd::UrQMD urqmd; + InteractionCounter urqmdCounted{urqmd}; + StackInspector<setup::Stack> stackInspect(1000, false, E0); + + // assemble all processes into an ordered process list + struct EnergySwitch { + HEPEnergyType cutE_; + EnergySwitch(HEPEnergyType cutE) + : cutE_(cutE) {} + SwitchResult operator()(const Particle& p) { + if (p.getEnergy() < cutE_) + return SwitchResult::First; + else + return SwitchResult::Second; + } + }; + auto hadronSequence = make_select( + urqmdCounted, make_sequence(sibyllNucCounted, sibyllCounted), EnergySwitch(55_GeV)); + auto decaySequence = make_sequence(decayPythia, decaySibyll); + auto sequence = + make_sequence(stackInspect, hadronSequence, reset_particle_mass, decaySequence, + emContinuous, cut, coreas, trackWriter, observationLevel, longprof); + + // define air shower object, run simulation + setup::Tracking tracking; + Cascade EAS(env, tracking, sequence, stack); + + // to fix the point of first interaction, uncomment the following two lines: + // EAS.forceInteraction(); + + EAS.run(); + + cut.showResults(); + emContinuous.showResults(); + observationLevel.showResults(); + const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() + + cut.getEmEnergy() + emContinuous.getEnergyLost() + + observationLevel.getEnergyGround(); + cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl + << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl; + observationLevel.reset(); + cut.reset(); + emContinuous.reset(); + + // get radio output + coreas.writeOutput(); + + auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() + + urqmdCounted.getHistogram(); + + save_hist(hists.labHist(), "inthist_lab_verticalEAS.npz", true); + save_hist(hists.CMSHist(), "inthist_cms_verticalEAS.npz", true); + longprof.save("longprof_verticalEAS.txt"); + +} \ No newline at end of file