diff --git a/corsika/detail/modules/proposal/InteractionModel.inl b/corsika/detail/modules/proposal/InteractionModel.inl index 7a34c1365dc66f5bd3aa019ebf1ed732c5ff772f..b5fb6cc8f6e8a2d62580dcf8ab9da10fe413cb49 100644 --- a/corsika/detail/modules/proposal/InteractionModel.inl +++ b/corsika/detail/modules/proposal/InteractionModel.inl @@ -15,6 +15,7 @@ #include <memory> #include <random> #include <tuple> +#include <PROPOSAL/particle/Particle.h> namespace corsika::proposal { @@ -50,7 +51,8 @@ namespace corsika::proposal { auto inter_types = PROPOSAL::CrossSectionVector::GetInteractionTypes(c); calc_[std::make_pair(comp.getHash(), code)] = std::make_tuple( PROPOSAL::make_secondaries(inter_types, particle[code], media.at(comp.getHash())), - PROPOSAL::make_interaction(c, true, true)); + PROPOSAL::make_interaction(c, true, true), + std::make_unique<LPM_calculator>(media.at(comp.getHash()), code, inter_types)); } template <typename THadronicLEModel, typename THadronicHEModel> @@ -110,6 +112,23 @@ namespace corsika::proposal { auto sec = std::get<eSECONDARIES>(c->second)->CalculateSecondaries(loss, target, rnd); + + // Check for LPM effect suppression! + + if (std::get<eLPM_SUPPRESSION>(c->second)) { + auto lpm_suppression = CheckForLPM( + *std::get<eLPM_SUPPRESSION>(c->second), projectile.getEnergy(), type, sec, + projectile.getNode()->getModelProperties().getMassDensity(place), target, v); + if (lpm_suppression) { + // Discard interaction - put initial particle back on stack + CORSIKA_LOG_DEBUG("LPM suppression detected!"); + view.addSecondary(std::make_tuple( + projectileId, projectile.getEnergy() - get_mass(projectileId), + projectile.getDirection())); + return ProcessReturn::Ok; + } + } + for (auto& s : sec) { auto E = s.energy * 1_MeV; auto vecProposal = s.direction; @@ -169,4 +188,64 @@ namespace corsika::proposal { return CrossSectionType::zero(); } + + template <typename THadronicLEModel, typename THadronicHEModel> + bool InteractionModel<THadronicLEModel, THadronicHEModel>::CheckForLPM( + const LPM_calculator& lpm_calculator, const HEPEnergyType projectile_energy, + const PROPOSAL::InteractionType type, + const std::vector<PROPOSAL::ParticleState>& sec, const MassDensityType mass_density, + const PROPOSAL::Component& target, const double v) { + + double suppression_factor; + double current_density = mass_density * ((1_cm * 1_cm * 1_cm) / 1_g); + + if (type == PROPOSAL::InteractionType::Photopair && lpm_calculator.photo_pair_lpm_) { + if (sec.size() != 2) + throw std::runtime_error( + "Invalid number of secondaries for Photopair in CheckForLPM"); + auto x = + sec[0].energy / (sec[0].energy + sec[1].energy); // particle energy asymmetry + double density_correction = current_density / lpm_calculator.mass_density_baseline_; + suppression_factor = lpm_calculator.photo_pair_lpm_->suppression_factor( + projectile_energy / 1_MeV, x, target, density_correction); + } else if (type == PROPOSAL::InteractionType::Brems && lpm_calculator.brems_lpm_) { + double density_correction = current_density / lpm_calculator.mass_density_baseline_; + suppression_factor = lpm_calculator.brems_lpm_->suppression_factor( + projectile_energy / 1_MeV, v, target, density_correction); + } else if (type == PROPOSAL::InteractionType::Epair && lpm_calculator.epair_lpm_) { + if (sec.size() != 3) + throw std::runtime_error( + "Invalid number of secondaries for EPair in CheckForLPM"); + double rho = (sec[1].energy - sec[2].energy) / + (sec[1].energy + sec[2].energy); // todo: check + // it would be better if these variables are calculated within PROPOSAL + double beta = (v * v) / (2 * (1 - v)); + double xi = (lpm_calculator.particle_mass_ * lpm_calculator.particle_mass_) / + (PROPOSAL::ME * PROPOSAL::ME) * v * v / 4 * (1 - rho * rho) / (1 - v); + double density_correction = current_density / lpm_calculator.mass_density_baseline_; + suppression_factor = lpm_calculator.epair_lpm_->suppression_factor( + projectile_energy / 1_MeV, v, rho * rho, beta, xi, density_correction); + } else { + return false; + } + + // rejection sampling + std::uniform_real_distribution<double> distr(0., 1.); + auto rnd_num = distr(RNG_); + + CORSIKA_LOGGER_TRACE(logger_, + "Checking for LPM suppression! energy: {}, v: {}, type: {}, " + "target: {}, mass_density: {}, mass_density_baseline: {}, " + "suppression_factor: {}, rnd: {}, result: {}, pmass: {} ", + projectile_energy, v, type, target.GetName(), + mass_density / (1_g / (1_cm * 1_cm * 1_cm)), + lpm_calculator.mass_density_baseline_, suppression_factor, + rnd_num, (rnd_num > suppression_factor), + lpm_calculator.particle_mass_); + + if (rnd_num > suppression_factor) + return true; + else + return false; + } } // namespace corsika::proposal diff --git a/corsika/modules/proposal/InteractionModel.hpp b/corsika/modules/proposal/InteractionModel.hpp index ae9d3b7e75f15d587644218abd303c290de4c516..aed4ddb26ef4500f9c27adc96f69b405b74a6ea3 100644 --- a/corsika/modules/proposal/InteractionModel.hpp +++ b/corsika/modules/proposal/InteractionModel.hpp @@ -38,9 +38,11 @@ namespace corsika::proposal { : public ProposalProcessBase, public HadronicPhotonModel<THadronicLEModel, THadronicHEModel> { - enum { eSECONDARIES, eINTERACTION }; + enum { eSECONDARIES, eINTERACTION, eLPM_SUPPRESSION }; + struct LPM_calculator; using calculator_t = std::tuple<std::unique_ptr<PROPOSAL::SecondariesCalculator>, - std::unique_ptr<PROPOSAL::Interaction>>; + std::unique_ptr<PROPOSAL::Interaction>, + std::unique_ptr<LPM_calculator>>; std::unordered_map<calc_key_t, calculator_t, hash> calc_; //!< Stores the secondaries and interaction calculators. @@ -52,6 +54,45 @@ namespace corsika::proposal { inline static auto logger_{get_logger("corsika_proposal_InteractionModel")}; + // Calculators for the LPM effect + struct LPM_calculator { + std::unique_ptr<PROPOSAL::crosssection::PhotoPairLPM> photo_pair_lpm_ = nullptr; + std::unique_ptr<PROPOSAL::crosssection::BremsLPM> brems_lpm_ = nullptr; + std::unique_ptr<PROPOSAL::crosssection::EpairLPM> epair_lpm_ = nullptr; + + const double mass_density_baseline_; // base mass density, note PROPOSAL units + const double particle_mass_; // particle mass, note PROPOSAL units + + LPM_calculator(const PROPOSAL::Medium& medium, const Code code, + std::vector<PROPOSAL::InteractionType> inter_types) + : mass_density_baseline_(medium.GetMassDensity()) + , particle_mass_(particle[code].mass) { + // at the moment, we always calculate the LPM effect based on the default cross + // sections. one could check for the parametrizations used in the other + // calculators. + if (std::find(inter_types.begin(), inter_types.end(), + PROPOSAL::InteractionType::Photopair) != inter_types.end()) + photo_pair_lpm_ = std::make_unique<PROPOSAL::crosssection::PhotoPairLPM>( + particle[code], medium, PROPOSAL::crosssection::PhotoPairKochMotz()); + if (std::find(inter_types.begin(), inter_types.end(), + PROPOSAL::InteractionType::Brems) != inter_types.end()) + brems_lpm_ = std::make_unique<PROPOSAL::crosssection::BremsLPM>( + particle[code], medium, PROPOSAL::crosssection::BremsElectronScreening()); + if (std::find(inter_types.begin(), inter_types.end(), + PROPOSAL::InteractionType::Epair) != inter_types.end()) + epair_lpm_ = + std::make_unique<PROPOSAL::crosssection::EpairLPM>(particle[code], medium); + } + }; + + //! + //! Checks whether the interaction is suppressed by the LPM effect + //! + bool CheckForLPM(const LPM_calculator&, const HEPEnergyType, + const PROPOSAL::InteractionType, + const std::vector<PROPOSAL::ParticleState>&, const MassDensityType, + const PROPOSAL::Component&, const double v); + public: //! //! Produces the stoachastic loss calculator for leptons based on nuclear