/* * (c) Copyright 2020 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. */ #include <PROPOSAL/PROPOSAL.h> #include <corsika/environment/IMediumModel.h> #include <corsika/process/proposal/ContinuousProcess.h> #include <corsika/process/proposal/Interaction.h> #include <corsika/setup/SetupEnvironment.h> #include <corsika/setup/SetupStack.h> #include <corsika/setup/SetupTrajectory.h> #include <corsika/units/PhysicalUnits.h> #include <corsika/utl/COMBoost.h> #include <corsika/logging/Logging.h> #include <iostream> namespace corsika::process::proposal { void ContinuousProcess::BuildCalculator(particles::Code code, environment::NuclearComposition const& comp) { // search crosssection builder for given particle auto p_cross = cross.find(code); if (p_cross == cross.end()) throw std::runtime_error("PROPOSAL could not find corresponding builder"); // interpolate the crosssection for given media and energy cut. These may // take some minutes if you have to build the tables and cannot read the // from disk auto c = p_cross->second(media.at(comp.hash()), emCut_); // Build displacement integral and scattering object and interpolate them too and // saved in the calc map by a key build out of a hash of composed of the component and // particle code. auto disp = PROPOSAL::make_displacement(c, true); auto scatter = PROPOSAL::make_scattering("highland", particle[code], media.at(comp.hash())); calc[std::make_pair(comp.hash(), code)] = std::make_tuple(std::move(disp), std::move(scatter)); } template <> ContinuousProcess::ContinuousProcess(setup::Environment const& _env, corsika::units::si::HEPEnergyType _emCut) : ProposalProcessBase(_env, _emCut) {} template <> void ContinuousProcess::Scatter(setup::Stack::ParticleType& vP, corsika::units::si::HEPEnergyType const& loss, corsika::units::si::GrammageType const& grammage) { using namespace corsika::units::si; // required for operator::_MeV // Get or build corresponding calculators auto c = GetCalculator(vP, calc); // Cast corsika vector to proposal vector auto vP_dir = vP.GetDirection(); auto d = vP_dir.GetComponents(); auto direction = PROPOSAL::Vector3D(d.GetX().magnitude(), d.GetY().magnitude(), d.GetZ().magnitude()); auto E_f = vP.GetEnergy() - loss; // draw random numbers required for scattering process std::uniform_real_distribution<double> distr(0., 1.); auto rnd = array<double, 4>(); for (auto& it : rnd) it = distr(fRNG); // calculate deflection based on particle energy, loss auto [mean_dir, final_dir] = get<eSCATTERING>(c->second)->Scatter( grammage / 1_g * square(1_cm), vP.GetEnergy() / 1_MeV, E_f / 1_MeV, direction, rnd); // TODO: neglect mean direction deflection because Trajectory is a const ref (void)mean_dir; // update particle direction after continuous loss caused by multiple // scattering auto vec = corsika::geometry::QuantityVector( final_dir.GetX() * E_f, final_dir.GetY() * E_f, final_dir.GetZ() * E_f); vP.SetMomentum(corsika::stack::MomentumVector(vP_dir.GetCoordinateSystem(), vec)); } template <> EProcessReturn ContinuousProcess::DoContinuous(setup::Stack::ParticleType& vP, setup::Trajectory const& vT) { using namespace corsika::units::si; // required for operator::_MeV if (!CanInteract(vP.GetPID())) return process::EProcessReturn::eOk; if (vT.GetLength() == 0_m) return process::EProcessReturn::eOk; // calculate passed grammage auto dX = vP.GetNode()->GetModelProperties().IntegratedGrammage(vT, vT.GetLength()); // Get or build corresponding track integral calculator and solve the // integral auto c = GetCalculator(vP, calc); auto final_energy = get<eDISPLACEMENT>(c->second)->UpperLimitTrackIntegral( vP.GetEnergy() / 1_MeV, dX / 1_g * 1_cm * 1_cm) * 1_MeV; auto dE = vP.GetEnergy() - final_energy; energy_lost_ += dE; // if the particle has a charge take multiple scattering into account if (vP.GetChargeNumber() != 0) Scatter(vP, dE, dX); vP.SetEnergy(final_energy); vP.SetMomentum(vP.GetMomentum() * vP.GetEnergy() / vP.GetMomentum().GetNorm()); return process::EProcessReturn::eOk; } template <> units::si::LengthType ContinuousProcess::MaxStepLength( setup::Stack::ParticleType const& vP, setup::Trajectory const& vT) { using namespace corsika::units::si; // required for operator::_MeV if (!CanInteract(vP.GetPID())) return units::si::meter * std::numeric_limits<double>::infinity(); // Limit the step size of a conitnuous loss. The maximal continuous loss seems to be a // hyper parameter which must be adjusted. // // in any case: never go below 0.99*emCut_ This needs to be // slightly smaller than emCut_ since, either this Step is limited // by energy_lim, then the particle is stopped in a very short // range (before doing anythin else) and is then removed // instantly. The exact position where it reaches emCut is not // important, the important fact is that its E_kin is zero // afterwards. // auto energy_lim = std::max(0.9 * vP.GetEnergy(), 0.99 * emCut_); // solving the track integral for giving energy lim auto c = GetCalculator(vP, calc); auto grammage = get<eDISPLACEMENT>(c->second)->SolveTrackIntegral( vP.GetEnergy() / 1_MeV, energy_lim / 1_MeV) * 1_g / square(1_cm); // return it in distance aequivalent auto dist = vP.GetNode()->GetModelProperties().ArclengthFromGrammage(vT, grammage); C8LOG_TRACE("PROPOSAL::MaxStepLength X={} g/cm2, l={} m ", grammage / 1_g * square(1_cm), dist / 1_m); return dist; } void ContinuousProcess::showResults() const { using namespace corsika::units::si; // required for operator::_MeV std::cout << " ******************************" << std::endl << " PROCESS::ContinuousProcess: " << std::endl; std::cout << " energy lost dE (GeV) : " << energy_lost_ / 1_GeV << std::endl; } void ContinuousProcess::reset() { using namespace corsika::units::si; // required for operator::_MeV energy_lost_ = 0_GeV; } } // namespace corsika::process::proposal