From 8b5eb265df88f6a0e03e74d7cde920b228bba8c6 Mon Sep 17 00:00:00 2001 From: MiaochenJin Date: Wed, 12 Jun 2024 15:21:20 -0400 Subject: [PATCH 01/11] finished implementing D meson DB, but lower energies decay still is a bit problematic --- projects/dataclasses/private/Particle.cxx | 11 + .../public/SIREN/dataclasses/Particle.h | 3 + .../SIREN/dataclasses/ParticleTypes.def | 6 + .../SecondaryBoundedVertexDistribution.cxx | 17 + .../SecondaryPhysicalVertexDistribution.cxx | 21 + .../SecondaryVertexPositionDistribution.cxx | 1 + projects/injection/private/Injector.cxx | 202 ++++-- projects/injection/private/Weighter.cxx | 6 + .../injection/ColumnDepthLeptonInjector.h | 2 + .../injection/CylinderVolumeLeptonInjector.h | 2 + .../injection/DecayRangeLeptonInjector.h | 2 + .../SIREN/injection/RangedLeptonInjector.h | 2 + projects/interactions/CMakeLists.txt | 5 + .../private/CharmDISFromSpline.cxx | 601 ++++++++++++++++++ .../private/CharmHadronization.cxx | 166 +++++ .../interactions/private/CharmMesonDecay.cxx | 505 +++++++++++++++ projects/interactions/private/DMesonELoss.cxx | 281 ++++++++ .../interactions/private/Hadronization.cxx | 17 + .../private/InteractionCollection.cxx | 19 +- .../private/pybindings/CharmDISFromSpline.h | 87 +++ .../private/pybindings/CharmHadronization.h | 38 ++ .../private/pybindings/CharmMesonDecay.h | 34 + .../private/pybindings/DMesonELoss.h | 37 ++ .../private/pybindings/Hadronization.h | 34 + .../pybindings/InteractionCollection.h | 7 + .../private/pybindings/interactions.cxx | 17 + .../SIREN/interactions/CharmDISFromSpline.h | 162 +++++ .../SIREN/interactions/CharmHadronization.h | 82 +++ .../SIREN/interactions/CharmMesonDecay.h | 89 +++ .../public/SIREN/interactions/DMesonELoss.h | 96 +++ .../public/SIREN/interactions/Hadronization.h | 58 ++ .../interactions/InteractionCollection.h | 17 +- .../public/SIREN/utilities/Constants.h | 6 + python/SIREN_Controller.py | 2 +- 34 files changed, 2567 insertions(+), 68 deletions(-) create mode 100644 projects/interactions/private/CharmDISFromSpline.cxx create mode 100644 projects/interactions/private/CharmHadronization.cxx create mode 100644 projects/interactions/private/CharmMesonDecay.cxx create mode 100644 projects/interactions/private/DMesonELoss.cxx create mode 100644 projects/interactions/private/Hadronization.cxx create mode 100644 projects/interactions/private/pybindings/CharmDISFromSpline.h create mode 100644 projects/interactions/private/pybindings/CharmHadronization.h create mode 100644 projects/interactions/private/pybindings/CharmMesonDecay.h create mode 100644 projects/interactions/private/pybindings/DMesonELoss.h create mode 100644 projects/interactions/private/pybindings/Hadronization.h create mode 100644 projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h create mode 100644 projects/interactions/public/SIREN/interactions/CharmHadronization.h create mode 100644 projects/interactions/public/SIREN/interactions/CharmMesonDecay.h create mode 100644 projects/interactions/public/SIREN/interactions/DMesonELoss.h create mode 100644 projects/interactions/public/SIREN/interactions/Hadronization.h diff --git a/projects/dataclasses/private/Particle.cxx b/projects/dataclasses/private/Particle.cxx index 60154e49d..7952adc1e 100644 --- a/projects/dataclasses/private/Particle.cxx +++ b/projects/dataclasses/private/Particle.cxx @@ -83,5 +83,16 @@ bool isCharged(ParticleType p){ p==ParticleType::Hadrons); } +bool isQuark(Particle::ParticleType p){ + return(p==Particle::ParticleType::Charm || p==Particle::ParticleType::CharmBar); + } + +bool isHadron(Particle::ParticleType p){ + return(p==Particle::ParticleType::Hadrons || + p==Particle::ParticleType::D0 || p==Particle::ParticleType::D0Bar || + p==Particle::ParticleType::DPlus || p==Particle::ParticleType::DMinus); + } + + } // namespace utilities } // namespace siren diff --git a/projects/dataclasses/public/SIREN/dataclasses/Particle.h b/projects/dataclasses/public/SIREN/dataclasses/Particle.h index 94a61693f..fff79c4a6 100644 --- a/projects/dataclasses/public/SIREN/dataclasses/Particle.h +++ b/projects/dataclasses/public/SIREN/dataclasses/Particle.h @@ -74,6 +74,9 @@ class Particle { bool isLepton(ParticleType p); bool isCharged(ParticleType p); bool isNeutrino(ParticleType p); +bool isQuark(Particle::ParticleType p); +bool isHadron(Particle::ParticleType p); + } // namespace dataclasses } // namespace siren diff --git a/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def b/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def index 8c0687c3c..73b578f97 100644 --- a/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def +++ b/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def @@ -56,6 +56,12 @@ X(TauPlus, -15) X(TauMinus, 15) X(NuTau, 16) X(NuTauBar, -16) +X(Charm, 4) +X(CharmBar, -4) +X(K0, 311) +X(K0Bar, -311) + + /* Nuclei */ X(HNucleus, 1000010010) diff --git a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx index 2a648aac5..ca3c407cd 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx @@ -53,10 +53,17 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { + std::cout << "in sample bounded vertex" << std::endl; + siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; siren::math::Vector3D endcap_0 = pos; + // skip computation of endpoint if interaction is hadronization + if (interactions->HasHadronizations()) { + record.SetLength(0); + return; + } siren::math::Vector3D endcap_1 = endcap_0 + max_length * dir; siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), max_length); @@ -116,11 +123,21 @@ void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { + std::cout << "in sample bounded vertex gen prob" << std::endl; + siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); siren::math::Vector3D vertex(record.interaction_vertex); siren::math::Vector3D endcap_0 = record.primary_initial_position; + // hadrnoization treated differently, but also check for misconducting + if (interactions->HasHadronizations()) { + if (vertex == endcap_0) { + return 1.0; + } else { + return 0.0; + } + } siren::math::Vector3D endcap_1 = endcap_0 + max_length * dir; siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), max_length); diff --git a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx index 9b58ce9e7..f9fb53b41 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx @@ -51,10 +51,21 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { + std::cout << "in sample physical vertex" << std::endl; siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; + // std::cout << "in sample physical vertex-1" << std::endl; siren::math::Vector3D endcap_0 = pos; + // treat hadronizations differntely + if (interactions->HasHadronizations()) { + std::cout << "in sample physical vertex-hadron" << std::endl; + + record.SetLength(0); + return; + } + // std::cout << "in sample physical vertex-shouldnm't be here" << std::endl; + siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), std::numeric_limits::infinity()); path.ClipToOuterBounds(); @@ -75,6 +86,7 @@ void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { + std::cout << "in sample physical vertex gen prob" << std::endl; + siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); siren::math::Vector3D vertex(record.interaction_vertex); siren::math::Vector3D endcap_0 = record.primary_initial_position; + if (interactions->HasHadronizations()) { + if (vertex == endcap_0) { + return 1.0; + } else { + return 0.0; + } + } siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), std::numeric_limits::infinity()); path.ClipToOuterBounds(); diff --git a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx index 87b44e63d..18e9c0b69 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx @@ -15,6 +15,7 @@ namespace distributions { // class SecondaryVertexPositionDistribution : InjectionDistribution //--------------- void SecondaryVertexPositionDistribution::Sample(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { + // std::cout << "sampling vertex" << std::endl; SampleVertex(rand, detector_model, interactions, record); } diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index 877a729e6..1725b3f64 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -12,6 +12,8 @@ #include "SIREN/interactions/DarkNewsCrossSection.h" #include "SIREN/interactions/InteractionCollection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + #include "SIREN/dataclasses/DecaySignature.h" #include "SIREN/dataclasses/InteractionSignature.h" #include "SIREN/dataclasses/Particle.h" @@ -180,109 +182,164 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record std::vector matching_signatures; std::vector> matching_cross_sections; std::vector> matching_decays; + std::vector> matching_hadronizations; + siren::dataclasses::InteractionRecord fake_record = record; double fake_prob; - if (interactions->HasCrossSections()) { - for(auto const target : available_targets) { - if(possible_targets.find(target) != possible_targets.end()) { - // Get target density - double target_density = detector_model->GetParticleDensity(intersections, DetectorPosition(interaction_vertex), target); - // Loop over cross sections that have this target - std::vector> const & target_cross_sections = interactions->GetCrossSectionsForTarget(target); - for(auto const & cross_section : target_cross_sections) { - // Loop over cross section signatures with the same target - std::vector signatures = cross_section->GetPossibleSignaturesFromParents(record.signature.primary_type, target); - for(auto const & signature : signatures) { - fake_record.signature = signature; - fake_record.target_mass = detector_model->GetTargetMass(target); - // Add total cross section times density to the total prob - fake_prob = target_density * cross_section->TotalCrossSection(fake_record); - total_prob += fake_prob; - xsec_prob += fake_prob; - // Add total prob to probs - probs.push_back(total_prob); - // Add target and cross section pointer to the lists - matching_targets.push_back(target); - matching_cross_sections.push_back(cross_section); - matching_signatures.push_back(signature); + // if contains hadronization, then perform only hadronization + if (interactions->HasHadronizations()) { + std::cout << "saw hadronization" << std::endl; + double total_frag_prob = 0; + std::vector frag_probs; + for(auto const & hadronization : interactions->GetHadronizations() ) { + for(auto const & signature : hadronization->GetPossibleSignaturesFromParent(record.signature.primary_type)) { + double frag_prob = 0; + + fake_record.signature = signature; + for (auto & secondary : fake_record.signature.secondary_types) { + frag_prob += hadronization->FragmentationFraction(secondary); + } + + total_frag_prob += frag_prob; + frag_probs.push_back(total_frag_prob); + // Add target and decay pointer to the lists + matching_targets.push_back(siren::dataclasses::Particle::ParticleType::Decay); + matching_hadronizations.push_back(hadronization); + matching_signatures.push_back(signature); + } + } + + std::cout << "Hadronization finished signatures" << std::endl; + + + // now choose the specific charmed hadron to fragment into + double r = random->Uniform(0, total_frag_prob); + unsigned int index = 0; + for(; (index < frag_probs.size()-1) and (r > frag_probs[index]); ++index) { + } // fixes the index of the chosen fragmentation + record.signature.target_type = matching_targets[index]; + record.signature = matching_signatures[index]; + record.target_mass = detector_model->GetTargetMass(record.signature.target_type); + siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); + + matching_hadronizations[index]->SampleFinalState(xsec_record, random); + xsec_record.Finalize(record); + + std::cout << "hadronization done!" << std::endl; + + } else { + if (interactions->HasCrossSections()) { + std::cout << "saw xsec" << std::endl; + for(auto const target : available_targets) { + if(possible_targets.find(target) != possible_targets.end()) { + // Get target density + double target_density = detector_model->GetParticleDensity(intersections, DetectorPosition(interaction_vertex), target); + // Loop over cross sections that have this target + std::vector> const & target_cross_sections = interactions->GetCrossSectionsForTarget(target); + for(auto const & cross_section : target_cross_sections) { + // Loop over cross section signatures with the same target + std::vector signatures = cross_section->GetPossibleSignaturesFromParents(record.signature.primary_type, target); + for(auto const & signature : signatures) { + fake_record.signature = signature; + fake_record.target_mass = detector_model->GetTargetMass(target); + // Add total cross section times density to the total prob + fake_prob = target_density * cross_section->TotalCrossSection(fake_record); + total_prob += fake_prob; + xsec_prob += fake_prob; + // Add total prob to probs + probs.push_back(total_prob); + // Add target and cross section pointer to the lists + matching_targets.push_back(target); + matching_cross_sections.push_back(cross_section); + matching_signatures.push_back(signature); + } } } } } - } - if (interactions->HasDecays()) { - for(auto const & decay : interactions->GetDecays() ) { - for(auto const & signature : decay->GetPossibleSignaturesFromParent(record.signature.primary_type)) { - fake_record.signature = signature; - // fake_prob has units of 1/cm to match cross section probabilities - fake_prob = 1./(decay->TotalDecayLengthForFinalState(fake_record)/siren::utilities::Constants::cm); - total_prob += fake_prob; - // Add total prob to probs - probs.push_back(total_prob); - // Add target and decay pointer to the lists - matching_targets.push_back(siren::dataclasses::ParticleType::Decay); - matching_decays.push_back(decay); - matching_signatures.push_back(signature); + if (interactions->HasDecays()) { + std::cout << "saw decay" << std::endl; + for(auto const & decay : interactions->GetDecays() ) { + for(auto const & signature : decay->GetPossibleSignaturesFromParent(record.signature.primary_type)) { + fake_record.signature = signature; + // fake_prob has units of 1/cm to match cross section probabilities + fake_prob = 1./(decay->TotalDecayLengthForFinalState(fake_record)/siren::utilities::Constants::cm); + total_prob += fake_prob; + // Add total prob to probs + probs.push_back(total_prob); + // Add target and decay pointer to the lists + matching_targets.push_back(siren::dataclasses::ParticleType::Decay); + matching_decays.push_back(decay); + matching_signatures.push_back(signature); + } } } - } - if(total_prob == 0) - throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); - // Throw a random number - double r = random->Uniform(0, total_prob); - // Choose the target and cross section - unsigned int index = 0; - for(; (index+1 < probs.size()) and (r > probs[index]); ++index) {} - record.signature.target_type = matching_targets[index]; - record.signature = matching_signatures[index]; - double selected_prob = 0.0; - for(unsigned int i=0; i 0 ? probs[i] - probs[i - 1] : probs[i]); + if(total_prob == 0) + throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); + // Throw a random number + double r = random->Uniform(0, total_prob); + // Choose the target and cross section + unsigned int index = 0; + for(; (index+1 < probs.size()) and (r > probs[index]); ++index) {} + record.signature.target_type = matching_targets[index]; + record.signature = matching_signatures[index]; + double selected_prob = 0.0; + for(unsigned int i=0; i 0 ? probs[i] - probs[i - 1] : probs[i]); + } } + if(selected_prob == 0) + throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); + record.target_mass = detector_model->GetTargetMass(record.signature.target_type); + siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); + if(r <= xsec_prob) { + matching_cross_sections[index]->SampleFinalState(xsec_record, random); + } else { + matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); + } + xsec_record.Finalize(record); } - if(selected_prob == 0) - throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); - record.target_mass = detector_model->GetTargetMass(record.signature.target_type); - siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); - if(r <= xsec_prob) { - matching_cross_sections[index]->SampleFinalState(xsec_record, random); - } else { - matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); - } - xsec_record.Finalize(record); } // Function to sample secondary processes // // Modifies an InteractionRecord with the new event siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::dataclasses::SecondaryDistributionRecord & secondary_record) const { + std::cout << "sampling secondary" << std::endl; + std::cout << "secondary record type is " << secondary_record.type << " " << secondary_record.id << std::endl; std::shared_ptr secondary_process = secondary_process_map.at(secondary_record.type); std::shared_ptr secondary_interactions = secondary_process->GetInteractions(); std::vector> secondary_distributions = secondary_process->GetSecondaryInjectionDistributions(); - size_t max_tries = 100; + size_t max_tries = 10; size_t tries = 0; size_t failed_tries = 0; while(true) { + // std::cout << "gotcha" << std::endl; try { for(auto & distribution : secondary_distributions) { + std::cout << "sample distribution" << std::endl; distribution->Sample(random, detector_model, secondary_process->GetInteractions(), secondary_record); } siren::dataclasses::InteractionRecord record; secondary_record.Finalize(record); + // std::cout << "sample distribution" << std::endl; + SampleCrossSection(record, secondary_interactions); return record; } catch(siren::utilities::InjectionFailure const & e) { + std::cout << "caught error" << std::endl; + failed_tries += 1; - if(tries > max_tries) { + if(failed_tries > max_tries) { throw(siren::utilities::InjectionFailure("Failed to generate secondary process!")); break; } continue; } - if(tries > max_tries) { + if(failed_tries > max_tries) { throw(siren::utilities::InjectionFailure("Failed to generate secondary process!")); break; } @@ -292,10 +349,11 @@ siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::da siren::dataclasses::InteractionTree Injector::GenerateEvent() { siren::dataclasses::InteractionRecord record; - size_t max_tries = 100; + size_t max_tries = 10; size_t tries = 0; size_t failed_tries = 0; // Initial Process + // std::cout << "Sampling primary interactions" << std::endl; while(true) { tries += 1; try { @@ -323,9 +381,12 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { std::shared_ptr parent = tree.add_entry(record); // Secondary Processes + // std::cout << "Sampling primary interactions" << std::endl; + std::deque, std::shared_ptr>> secondaries; std::function)> add_secondaries = [&](std::shared_ptr parent) { for(size_t i=0; irecord.signature.secondary_types.size(); ++i) { + // std::cout << "for loop 1" << std::endl; siren::dataclasses::ParticleType const & type = parent->record.signature.secondary_types[i]; std::map>::iterator it = secondary_process_map.find(type); if(it == secondary_process_map.end()) { @@ -342,16 +403,27 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { }; add_secondaries(parent); + // std::cout << "num secondaries: " << secondaries.size() << std::endl; while(secondaries.size() > 0) { + // std::cout << "while loop 1" << std::endl; + for(int i = secondaries.size() - 1; i >= 0; --i) { + // std::cout << "for loop 2" << std::endl; + std::shared_ptr parent = std::get<0>(secondaries[i]); std::shared_ptr secondary_dist = std::get<1>(secondaries[i]); + // std::cout << "for loop 2-1" << std::endl; + secondaries.erase(secondaries.begin() + i); siren::dataclasses::InteractionRecord secondary_record = SampleSecondaryProcess(*secondary_dist); std::shared_ptr secondary_datum = tree.add_entry(secondary_record, parent); + // std::cout << "for loop 2-2" << std::endl; + add_secondaries(secondary_datum); } + // std::cout << "while loop 1-2" << std::endl; + } injected_events += 1; return tree; diff --git a/projects/injection/private/Weighter.cxx b/projects/injection/private/Weighter.cxx index 81dbe6dfb..afd11cfb8 100644 --- a/projects/injection/private/Weighter.cxx +++ b/projects/injection/private/Weighter.cxx @@ -25,6 +25,8 @@ #include "SIREN/injection/Process.h" // for Phy... #include "SIREN/injection/WeightingUtils.h" // for Cro... #include "SIREN/math/Vector3D.h" // for Vec... +#include "SIREN/dataclasses/Particle.h" + #include "SIREN/injection/Injector.h" @@ -111,6 +113,10 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c double physical_probability = 1.0; double generation_probability = injectors[idx]->EventsToInject();//GenerationProbability(tree); for(auto const & datum : tree.tree) { + // skip weighting if record contains hadronization + if (isQuark(datum->record.signature.primary_type)) { + continue; + } std::tuple bounds; if(datum->depth() == 0) { bounds = injectors[idx]->PrimaryInjectionBounds(datum->record); diff --git a/projects/injection/public/SIREN/injection/ColumnDepthLeptonInjector.h b/projects/injection/public/SIREN/injection/ColumnDepthLeptonInjector.h index 73d541132..9caa13eb7 100644 --- a/projects/injection/public/SIREN/injection/ColumnDepthLeptonInjector.h +++ b/projects/injection/public/SIREN/injection/ColumnDepthLeptonInjector.h @@ -22,6 +22,8 @@ #include "SIREN/interactions/CrossSection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + #include "SIREN/detector/DetectorModel.h" #include "SIREN/distributions/primary/vertex/ColumnDepthPositionDistribution.h" #include "SIREN/distributions/primary/vertex/DepthFunction.h" diff --git a/projects/injection/public/SIREN/injection/CylinderVolumeLeptonInjector.h b/projects/injection/public/SIREN/injection/CylinderVolumeLeptonInjector.h index 4cb2bd3d5..1aabfd1f5 100644 --- a/projects/injection/public/SIREN/injection/CylinderVolumeLeptonInjector.h +++ b/projects/injection/public/SIREN/injection/CylinderVolumeLeptonInjector.h @@ -23,6 +23,8 @@ #include "SIREN/interactions/InteractionCollection.h" #include "SIREN/interactions/CrossSection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + #include "SIREN/detector/DetectorModel.h" #include "SIREN/distributions/primary/vertex/CylinderVolumePositionDistribution.h" #include "SIREN/geometry/Cylinder.h" // for Cylinder diff --git a/projects/injection/public/SIREN/injection/DecayRangeLeptonInjector.h b/projects/injection/public/SIREN/injection/DecayRangeLeptonInjector.h index 26a0c5ec4..d2eb44ded 100644 --- a/projects/injection/public/SIREN/injection/DecayRangeLeptonInjector.h +++ b/projects/injection/public/SIREN/injection/DecayRangeLeptonInjector.h @@ -23,6 +23,8 @@ #include "SIREN/interactions/InteractionCollection.h" #include "SIREN/interactions/CrossSection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + #include "SIREN/detector/DetectorModel.h" #include "SIREN/distributions/primary/vertex/DecayRangeFunction.h" #include "SIREN/distributions/primary/vertex/DecayRangePositionDistribution.h" diff --git a/projects/injection/public/SIREN/injection/RangedLeptonInjector.h b/projects/injection/public/SIREN/injection/RangedLeptonInjector.h index 581aa91ae..a835b9c45 100644 --- a/projects/injection/public/SIREN/injection/RangedLeptonInjector.h +++ b/projects/injection/public/SIREN/injection/RangedLeptonInjector.h @@ -21,6 +21,8 @@ #include "SIREN/interactions/InteractionCollection.h" #include "SIREN/interactions/CrossSection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + #include "SIREN/detector/DetectorModel.h" #include "SIREN/distributions/primary/vertex/RangeFunction.h" #include "SIREN/distributions/primary/vertex/RangePositionDistribution.h" diff --git a/projects/interactions/CMakeLists.txt b/projects/interactions/CMakeLists.txt index c6ee384d2..a8eec569d 100644 --- a/projects/interactions/CMakeLists.txt +++ b/projects/interactions/CMakeLists.txt @@ -16,6 +16,11 @@ LIST (APPEND interactions_SOURCES ${PROJECT_SOURCE_DIR}/projects/interactions/private/DummyCrossSection.cxx ${PROJECT_SOURCE_DIR}/projects/interactions/private/pyDarkNewsCrossSection.cxx ${PROJECT_SOURCE_DIR}/projects/interactions/private/pyDarkNewsDecay.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/CharmDISFromSpline.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/Hadronization.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/CharmHadronization.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/CharmMesonDecay.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/DMesonELoss.cxx ) add_library(SIREN_interactions OBJECT ${interactions_SOURCES}) set_property(TARGET SIREN_interactions PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/projects/interactions/private/CharmDISFromSpline.cxx b/projects/interactions/private/CharmDISFromSpline.cxx new file mode 100644 index 000000000..45f15b98e --- /dev/null +++ b/projects/interactions/private/CharmDISFromSpline.cxx @@ -0,0 +1,601 @@ +#include "SIREN/interactions/CharmDISFromSpline.h" + +#include // for map, opera... +#include // for set, opera... +#include // for array +#include // for pow, log10 +#include // for tie, opera... +#include // for allocator +#include // for basic_string +#include // for vector +#include // for assert +#include // for size_t + +#include // for P4, Boost +#include // for Vector3 + +#include // for splinetable +//#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSection +#include "SIREN/dataclasses/InteractionRecord.h" // for Interactio... +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/utilities/Constants.h" // for electronMass + +namespace siren { +namespace interactions { + +namespace { +///Check whether a given point in phase space is physically realizable. +///Based on equations 6-8 of http://dx.doi.org/10.1103/PhysRevD.66.113007 +///S. Kretzer and M. H. Reno +///"Tau neutrino deep inelastic charged current interactions" +///Phys. Rev. D 66, 113007 +///\param x Bjorken x of the interaction +///\param y Bjorken y of the interaction +///\param E Incoming neutrino in energy in the lab frame ($E_\nu$) +///\param M Mass of the target nucleon ($M_N$) +///\param m Mass of the secondary lepton ($m_\tau$) +bool kinematicallyAllowed(double x, double y, double E, double M, double m) { + if(x > 1) //Eq. 6 right inequality + return false; + if(x < ((m * m) / (2 * M * (E - m)))) //Eq. 6 left inequality + return false; + //denominator of a and b + double d = 2 * (1 + (M * x) / (2 * E)); + //the numerator of a (or a*d) + double ad = 1 - m * m * ((1 / (2 * M * E * x)) + (1 / (2 * E * E))); + double term = 1 - ((m * m) / (2 * M * E * x)); + //the numerator of b (or b*d) + double bd = sqrt(term * term - ((m * m) / (E * E))); + return (ad - bd) <= d * y and d * y <= (ad + bd); //Eq. 7 +} +} + +CharmDISFromSpline::CharmDISFromSpline() {} + +CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromFile(differential_filename, total_filename); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types) { + LoadFromFile(differential_filename, total_filename); + ReadParamsFromSplineTable(); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromFile(differential_filename, total_filename); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()) { + LoadFromFile(differential_filename, total_filename); + ReadParamsFromSplineTable(); + InitializeSignatures(); + SetUnits(units); +} + +void CharmDISFromSpline::SetUnits(std::string units) { + std::transform(units.begin(), units.end(), units.begin(), + [](unsigned char c){ return std::tolower(c); }); + if(units == "cm") { + unit = 1.0; + } else if(units == "m") { + unit = 10000.0; + } else { + throw std::runtime_error("Cross section units not supported!"); + } +} + +bool CharmDISFromSpline::equal(CrossSection const & other) const { + const CharmDISFromSpline* x = dynamic_cast(&other); + + if(!x) + return false; + else + return + std::tie( + interaction_type_, + target_mass_, + minimum_Q2_, + signatures_, + primary_types_, + target_types_, + differential_cross_section_, + total_cross_section_) + == + std::tie( + x->interaction_type_, + x->target_mass_, + x->minimum_Q2_, + x->signatures_, + x->primary_types_, + x->target_types_, + x->differential_cross_section_, + x->total_cross_section_); +} + +void CharmDISFromSpline::LoadFromFile(std::string dd_crossSectionFile, std::string total_crossSectionFile) { + + differential_cross_section_ = photospline::splinetable<>(dd_crossSectionFile.c_str()); + + if(differential_cross_section_.get_ndim()!=3 && differential_cross_section_.get_ndim()!=2) + throw std::runtime_error("cross section spline has " + std::to_string(differential_cross_section_.get_ndim()) + + " dimensions, should have either 3 (log10(E), log10(x), log10(y)) or 2 (log10(E), log10(y))"); + + total_cross_section_ = photospline::splinetable<>(total_crossSectionFile.c_str()); + + if(total_cross_section_.get_ndim() != 1) + throw std::runtime_error("Total cross section spline has " + std::to_string(total_cross_section_.get_ndim()) + + " dimensions, should have 1, log10(E)"); +} + +void CharmDISFromSpline::LoadFromMemory(std::vector & differential_data, std::vector & total_data) { + differential_cross_section_.read_fits_mem(differential_data.data(), differential_data.size()); + total_cross_section_.read_fits_mem(total_data.data(), total_data.size()); +} + +double CharmDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton_type) { + int32_t lepton_number = std::abs(static_cast(lepton_type)); + double lepton_mass; + switch(lepton_number) { + case 11: + lepton_mass = siren::utilities::Constants::electronMass; + break; + case 13: + lepton_mass = siren::utilities::Constants::muonMass; + break; + case 15: + lepton_mass = siren::utilities::Constants::tauMass; + break; + case 12: + lepton_mass = 0; + case 14: + lepton_mass = 0; + case 16: + lepton_mass = 0; + break; + default: + throw std::runtime_error("Unknown lepton type!"); + } + return lepton_mass; +} + +void CharmDISFromSpline::ReadParamsFromSplineTable() { + // returns true if successfully read target mass + bool mass_good = differential_cross_section_.read_key("TARGETMASS", target_mass_); + // returns true if successfully read interaction type + bool int_good = differential_cross_section_.read_key("INTERACTION", interaction_type_); + // returns true if successfully read minimum Q2 + bool q2_good = differential_cross_section_.read_key("Q2MIN", minimum_Q2_); + + if(!int_good) { + // assume DIS to preserve compatability with previous versions + interaction_type_ = 1; + } + + if(!q2_good) { + // assume 1 GeV^2 + minimum_Q2_ = 1; + } + + if(!mass_good) { + if(int_good) { + if(interaction_type_ == 1 or interaction_type_ == 2) { + target_mass_ = (siren::dataclasses::isLepton(siren::dataclasses::ParticleType::PPlus)+ + siren::dataclasses::isLepton(siren::dataclasses::ParticleType::Neutron))/2; + } else if(interaction_type_ == 3) { + target_mass_ = siren::dataclasses::isLepton(siren::dataclasses::ParticleType::EMinus); + } else { + throw std::runtime_error("Logic error. Interaction type is not 1, 2, or 3!"); + } + + } else { + if(differential_cross_section_.get_ndim() == 3) { + target_mass_ = (siren::dataclasses::isLepton(siren::dataclasses::ParticleType::PPlus)+ + siren::dataclasses::isLepton(siren::dataclasses::ParticleType::Neutron))/2; + } else if(differential_cross_section_.get_ndim() == 2) { + target_mass_ = siren::dataclasses::isLepton(siren::dataclasses::ParticleType::EMinus); + } else { + throw std::runtime_error("Logic error. Spline dimensionality is not 2, or 3!"); + } + } + } +} + +void CharmDISFromSpline::InitializeSignatures() { + signatures_.clear(); + for(auto primary_type : primary_types_) { + dataclasses::InteractionSignature signature; + signature.primary_type = primary_type; + + if(not isNeutrino(primary_type)) { + throw std::runtime_error("This DIS implementation only supports neutrinos as primaries!"); + } + + siren::dataclasses::ParticleType charged_lepton_product = siren::dataclasses::ParticleType::unknown; + siren::dataclasses::ParticleType neutral_lepton_product = primary_type; + + if(primary_type == siren::dataclasses::ParticleType::NuE) { + charged_lepton_product = siren::dataclasses::ParticleType::EMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuEBar) { + charged_lepton_product = siren::dataclasses::ParticleType::EPlus; + } else if(primary_type == siren::dataclasses::ParticleType::NuMu) { + charged_lepton_product = siren::dataclasses::ParticleType::MuMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuMuBar) { + charged_lepton_product = siren::dataclasses::ParticleType::MuPlus; + } else if(primary_type == siren::dataclasses::ParticleType::NuTau) { + charged_lepton_product = siren::dataclasses::ParticleType::TauMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuTauBar) { + charged_lepton_product = siren::dataclasses::ParticleType::TauPlus; + } else { + throw std::runtime_error("InitializeSignatures: Unkown parent neutrino type!"); + } + + if(interaction_type_ == 1) { + signature.secondary_types.push_back(charged_lepton_product); + } else if(interaction_type_ == 2) { + signature.secondary_types.push_back(neutral_lepton_product); + } else if(interaction_type_ == 3) { + signature.secondary_types.push_back(siren::dataclasses::ParticleType::Hadrons); + } else { + throw std::runtime_error("InitializeSignatures: Unkown interaction type!"); + } + + signature.secondary_types.push_back(siren::dataclasses::ParticleType::Charm); + for(auto target_type : target_types_) { + signature.target_type = target_type; + + signatures_.push_back(signature); + + std::pair key(primary_type, target_type); + signatures_by_parent_types_[key].push_back(signature); + } + } +} + +double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord const & interaction) const { + siren::dataclasses::ParticleType primary_type = interaction.signature.primary_type; + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + double primary_energy; + primary_energy = interaction.primary_momentum[0]; + // if we are below threshold, return 0 + if(primary_energy < InteractionThreshold(interaction)) + return 0; + return TotalCrossSection(primary_type, primary_energy); +} + +double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType primary_type, double primary_energy) const { + if(not primary_types_.count(primary_type)) { + throw std::runtime_error("Supplied primary not supported by cross section!"); + } + double log_energy = log10(primary_energy); + + if(log_energy < total_cross_section_.lower_extent(0) + or log_energy > total_cross_section_.upper_extent(0)) { + throw std::runtime_error("Interaction energy ("+ std::to_string(primary_energy) + + ") out of cross section table range: [" + + std::to_string(pow(10.,total_cross_section_.lower_extent(0))) + " GeV," + + std::to_string(pow(10.,total_cross_section_.upper_extent(0))) + " GeV]"); + } + + int center; + total_cross_section_.searchcenters(&log_energy, ¢er); + double log_xs = total_cross_section_.ndsplineeval(&log_energy, ¢er, 0); + + return unit * std::pow(10.0, log_xs); +} + +double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionRecord const & interaction) const { + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + rk::P4 p2(geom3::Vector3(0, 0, 0), interaction.target_mass); + double primary_energy; + primary_energy = interaction.primary_momentum[0]; + assert(interaction.signature.secondary_types.size() == 2); + unsigned int lepton_index = (isLepton(interaction.signature.secondary_types[0])) ? 0 : 1; + unsigned int other_index = 1 - lepton_index; + + std::array const & mom3 = interaction.secondary_momenta[lepton_index]; + std::array const & mom4 = interaction.secondary_momenta[other_index]; + rk::P4 p3(geom3::Vector3(mom3[1], mom3[2], mom3[3]), interaction.secondary_masses[lepton_index]); + rk::P4 p4(geom3::Vector3(mom4[1], mom4[2], mom4[3]), interaction.secondary_masses[other_index]); + + rk::P4 q = p1 - p3; + + double Q2 = -q.dot(q); + double y = 1.0 - p2.dot(p3) / p2.dot(p1); + double x = Q2 / (2.0 * p2.dot(q)); + double lepton_mass = GetLeptonMass(interaction.signature.secondary_types[lepton_index]); + + return DifferentialCrossSection(primary_energy, x, y, lepton_mass, Q2); +} + +double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { + double log_energy = log10(energy); + // check preconditions + if(log_energy < differential_cross_section_.lower_extent(0) + || log_energy>differential_cross_section_.upper_extent(0)) + return 0.0; + if(x <= 0 || x >= 1) + return 0.0; + if(y <= 0 || y >= 1) + return 0.0; + + // we assume that: + // the target is stationary so its energy is just its mass + // the incoming neutrino is massless, so its kinetic energy is its total energy + if(std::isnan(Q2)) { + Q2 = 2.0 * energy * target_mass_ * x * y; + } + if(Q2 < minimum_Q2_) // cross section not calculated, assumed to be zero + return 0; + + // cross section should be zero, but this check is missing from the original + // CSMS calculation, so we must add it here + if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) + return 0; + + std::array coordinates{{log_energy, log10(x), log10(y)}}; + std::array centers; + if(!differential_cross_section_.searchcenters(coordinates.data(), centers.data())) + return 0; + double result = pow(10., differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0)); + assert(result >= 0); + return unit * result; +} + +double CharmDISFromSpline::InteractionThreshold(dataclasses::InteractionRecord const & interaction) const { + // Consider implementing DIS thershold at some point + return 0; +} + +void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { + // Uses Metropolis-Hastings Algorithm! + // useful for cases where we don't know the supremum of our distribution, and the distribution is multi-dimensional + if (differential_cross_section_.get_ndim() != 3) { + throw std::runtime_error("I expected 3 dimensions in the cross section spline, but got " + std::to_string(differential_cross_section_.get_ndim()) +". Maybe your fits file doesn't have the right 'INTERACTION' key?"); + } + + rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); + rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); + + // we assume that: + // the target is stationary so its energy is just its mass + // the incoming neutrino is massless, so its kinetic energy is its total energy + // double s = target_mass_ * trecord.secondary_momentarget_mass_ + 2 * target_mass_ * primary_energy; + // double s = std::pow(rk::invMass(p1, p2), 2); + + double primary_energy; + rk::P4 p1_lab; + rk::P4 p2_lab; + p1_lab = p1; + p2_lab = p2; + primary_energy = p1_lab.e(); + + unsigned int lepton_index = (isLepton(record.signature.secondary_types[0])) ? 0 : 1; + unsigned int other_index = 1 - lepton_index; + double m = GetLeptonMass(record.signature.secondary_types[lepton_index]); + + double m1 = record.primary_mass; + double m3 = m; + double E1_lab = p1_lab.e(); + double E2_lab = p2_lab.e(); + + + // The out-going particle always gets at least enough energy for its rest mass + double yMax = 1 - m / primary_energy; + double logYMax = log10(yMax); + + // The minimum allowed value of y occurs when x = 1 and Q is minimized + double yMin = minimum_Q2_ / (2 * E1_lab * E2_lab); + double logYMin = log10(yMin); + // The minimum allowed value of x occurs when y = yMax and Q is minimized + // double xMin = minimum_Q2_ / ((s - target_mass_ * target_mass_) * yMax); + double xMin = minimum_Q2_ / (2 * E1_lab * E2_lab * yMax); + double logXMin = log10(xMin); + + bool accept; + + // kin_vars and its twin are 3-vectors containing [nu-energy, Bjorken X, Bjorken Y] + std::array kin_vars, test_kin_vars; + + // centers of the cross section spline tales. + std::array spline_table_center, test_spline_table_center; + + // values of cross_section from the splines. By * Bx * Spline(E,x,y) + double cross_section, test_cross_section; + + // No matter what, we're evaluating at this specific energy. + kin_vars[0] = test_kin_vars[0] = log10(primary_energy); + + // check preconditions + if(kin_vars[0] < differential_cross_section_.lower_extent(0) + || kin_vars[0] > differential_cross_section_.upper_extent(0)) + throw std::runtime_error("Interaction energy out of cross section table range: [" + + std::to_string(pow(10.,differential_cross_section_.lower_extent(0))) + " GeV," + + std::to_string(pow(10.,differential_cross_section_.upper_extent(0))) + " GeV]"); + + // sample an intial point + do { + // rejection sample a point which is kinematically allowed by calculation limits + double trialQ; + do { + kin_vars[1] = random->Uniform(logXMin,0); + kin_vars[2] = random->Uniform(logYMin,logYMax); + trialQ = (2 * E1_lab * E2_lab) * pow(10., kin_vars[1] + kin_vars[2]); + } while(trialQ differential_cross_section_.upper_extent(1)) { + accept = false; + } + if(kin_vars[2] < differential_cross_section_.lower_extent(2) + || kin_vars[2] > differential_cross_section_.upper_extent(2)) { + accept = false; + } + + if(accept) { + // finds the centers in the cross section spline table, returns true if it's successful + // also sets the centers + accept = differential_cross_section_.searchcenters(kin_vars.data(),spline_table_center.data()); + } + } while(!accept); + + //TODO: better proposal distribution? + double measure = pow(10., kin_vars[1] + kin_vars[2]); // Bx * By + + // Bx * By * xs(E, x, y) + // evalutates the differential spline at that point + cross_section = measure*pow(10., differential_cross_section_.ndsplineeval(kin_vars.data(), spline_table_center.data(), 0)); + + // this is the magic part. Metropolis Hastings Algorithm. + // MCMC method! + const size_t burnin = 40; // converges to the correct distribution over multiple samplings. + // big number means more accurate, but slower + for(size_t j = 0; j <= burnin; j++) { + // repeat the sampling from above to get a new valid point + double trialQ; + do { + test_kin_vars[1] = random->Uniform(logXMin, 0); + test_kin_vars[2] = random->Uniform(logYMin, logYMax); + trialQ = (2 * E1_lab * E2_lab) * pow(10., test_kin_vars[1] + test_kin_vars[2]); + } while(trialQ < minimum_Q2_ || !kinematicallyAllowed(pow(10., test_kin_vars[1]), pow(10., test_kin_vars[2]), primary_energy, target_mass_, m)); + + accept = true; + if(test_kin_vars[1] < differential_cross_section_.lower_extent(1) + || test_kin_vars[1] > differential_cross_section_.upper_extent(1)) + accept = false; + if(test_kin_vars[2] < differential_cross_section_.lower_extent(2) + || test_kin_vars[2] > differential_cross_section_.upper_extent(2)) + accept = false; + if(!accept) + continue; + + accept = differential_cross_section_.searchcenters(test_kin_vars.data(), test_spline_table_center.data()); + if(!accept) + continue; + + double measure = pow(10., test_kin_vars[1] + test_kin_vars[2]); + double eval = differential_cross_section_.ndsplineeval(test_kin_vars.data(), test_spline_table_center.data(), 0); + if(std::isnan(eval)) + continue; + test_cross_section = measure * pow(10., eval); + + double odds = (test_cross_section / cross_section); + accept = (cross_section == 0 || (odds > 1.) || random->Uniform(0, 1) < odds); + + if(accept) { + kin_vars = test_kin_vars; + cross_section = test_cross_section; + } + } + double final_x = pow(10., kin_vars[1]); + double final_y = pow(10., kin_vars[2]); + + record.interaction_parameters.clear(); + record.interaction_parameters["energy"] = E1_lab; + record.interaction_parameters["bjorken_x"] = final_x; + record.interaction_parameters["bjorken_y"] = final_y; + + double Q2 = 2 * E1_lab * E2_lab * pow(10.0, kin_vars[1] + kin_vars[2]); + double p1x_lab = std::sqrt(p1_lab.px() * p1_lab.px() + p1_lab.py() * p1_lab.py() + p1_lab.pz() * p1_lab.pz()); + double pqx_lab = (m1*m1 + m3*m3 + 2 * p1x_lab * p1x_lab + Q2 + 2 * E1_lab * E1_lab * (final_y - 1)) / (2.0 * p1x_lab); + double momq_lab = std::sqrt(m1*m1 + p1x_lab*p1x_lab + Q2 + E1_lab * E1_lab * (final_y * final_y - 1)); + double pqy_lab = std::sqrt(momq_lab*momq_lab - pqx_lab *pqx_lab); + double Eq_lab = E1_lab * final_y; + + geom3::UnitVector3 x_dir = geom3::UnitVector3::xAxis(); + geom3::Vector3 p1_mom = p1_lab.momentum(); + geom3::UnitVector3 p1_lab_dir = p1_mom.direction(); + geom3::Rotation3 x_to_p1_lab_rot = geom3::rotationBetween(x_dir, p1_lab_dir); + + double phi = random->Uniform(0, 2.0 * M_PI); + geom3::Rotation3 rand_rot(p1_lab_dir, phi); + + rk::P4 pq_lab(Eq_lab, geom3::Vector3(pqx_lab, pqy_lab, 0)); + pq_lab.rotate(x_to_p1_lab_rot); + pq_lab.rotate(rand_rot); + + rk::P4 p3_lab((p1_lab - pq_lab).momentum(), m3); + rk::P4 p4_lab = p2_lab + pq_lab; + + rk::P4 p3; + rk::P4 p4; + p3 = p3_lab; + p4 = p4_lab; + + std::vector & secondaries = record.GetSecondaryParticleRecords(); + siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[lepton_index]; + siren::dataclasses::SecondaryParticleRecord & other = secondaries[other_index]; + + + lepton.SetFourMomentum({p3.e(), p3.px(), p3.py(), p3.pz()}); + lepton.SetMass(p3.m()); + lepton.SetHelicity(record.primary_helicity); + + other.SetFourMomentum({p4.e(), p4.px(), p4.py(), p4.pz()}); + other.SetMass(p4.m()); + other.SetHelicity(record.target_helicity); +} + +double CharmDISFromSpline::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { + double dxs = DifferentialCrossSection(interaction); + double txs = TotalCrossSection(interaction); + if(dxs == 0) { + return 0.0; + } else { + return dxs / txs; + } +} + +std::vector CharmDISFromSpline::GetPossiblePrimaries() const { + return std::vector(primary_types_.begin(), primary_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleSignatures() const { + return std::vector(signatures_.begin(), signatures_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleTargets() const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const { + std::pair key(primary_type, target_type); + if(signatures_by_parent_types_.find(key) != signatures_by_parent_types_.end()) { + return signatures_by_parent_types_.at(key); + } else { + return std::vector(); + } +} + +std::vector CharmDISFromSpline::DensityVariables() const { + return std::vector{"Bjorken x", "Bjorken y"}; +} + +} // namespace interactions +} // namespace siren diff --git a/projects/interactions/private/CharmHadronization.cxx b/projects/interactions/private/CharmHadronization.cxx new file mode 100644 index 000000000..e36ee5540 --- /dev/null +++ b/projects/interactions/private/CharmHadronization.cxx @@ -0,0 +1,166 @@ +#include "SIREN/interactions/CharmHadronization.h" + +#include // for array +#include // for sqrt, M_PI +#include // for basic_s... +#include // for vector +#include // for size_t + +#include // for Vector3 +#include // for P4, Boost + +#include "SIREN/interactions/Hadronization.h" // for Hadronization +#include "SIREN/dataclasses/InteractionRecord.h" // for Interac... +#include "SIREN/dataclasses/InteractionSignature.h" // for Interac... +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/utilities/Errors.h" // for PythonImplementationError +#include "SIREN/utilities/Constants.h" // for electronMass + + + +namespace siren { +namespace interactions { + +CharmHadronization::CharmHadronization() {} + +// pybind11::object CharmHadronization::get_self() { +// return pybind11::cast(Py_None); +// } + +bool CharmHadronization::equal(Hadronization const & other) const { + const CharmHadronization* x = dynamic_cast(&other); + + if(!x) + return false; + else + return primary_types == x->primary_types; +} + +double CharmHadronization::getHadronMass(siren::dataclasses::ParticleType hadron_type) { + switch(hadron_type){ + case siren::dataclasses::ParticleType::D0: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::D0Bar: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::DPlus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::DMinus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::Charm: + return( siren::utilities::Constants::CharmMass); + case siren::dataclasses::ParticleType::CharmBar: + return( siren::utilities::Constants::CharmMass); + default: + return(0.0); + } +} + + +std::vector CharmHadronization::GetPossibleSignatures() const +{ + std::vector signatures; + for(auto primary : primary_types) { + std::vector new_signatures = GetPossibleSignaturesFromParent(primary); + signatures.insert(signatures.end(),new_signatures.begin(),new_signatures.end()); + } + return signatures; +} + +std::vector CharmHadronization::GetPossibleSignaturesFromParent(siren::dataclasses::Particle::ParticleType primary) const { + std::vector signatures; + dataclasses::InteractionSignature signature; + signature.primary_type = primary; + signature.target_type = siren::dataclasses::Particle::ParticleType::Decay; + + signature.secondary_types.resize(2); + signature.secondary_types[0] = siren::dataclasses::Particle::ParticleType::Hadrons; + if(primary==siren::dataclasses::Particle::ParticleType::Charm) { + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::D0; + signatures.push_back(signature); + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::DPlus; + signatures.push_back(signature); + } + else if(primary==siren::dataclasses::Particle::ParticleType::CharmBar) { + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::D0Bar; + signatures.push_back(signature); + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::DMinus; + signatures.push_back(signature); + } + return signatures; +} + +void CharmHadronization::SampleFinalState(dataclasses::CrossSectionDistributionRecord & interaction, std::shared_ptr random) const { + // Uses Perterson distribution with epsilon = 0.2 + // charm quark momentum + rk::P4 pc(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + double p3c = std::sqrt(std::pow(interaction.primary_momentum[1], 2) + std::pow(interaction.primary_momentum[2], 2) + std::pow(interaction.primary_momentum[3], 2)); + double Ec = pc.e(); //energy of primary charm + // std::cout << "hadronization sample final state" << std::endl; + // double peterson_distribution(double z) { + // return 0.8 / x * std::pow((1 - 1 / x - 0.2 / (1 - x)), 2) + // } + + double z = 0.6; // replace by actual sampling: inverse CDF + double ECH = z * Ec; + + // is it ok to compute everything in lab frame? + double mCH = getHadronMass(interaction.signature.secondary_types[1]); // obtain charmed hadron mass + double p3CH = std::sqrt(std::pow(ECH, 2) - std::pow(mCH, 2)); //obtain charmed hadron 3-momentum + double rCH = p3CH/p3c; // ratio of momentum carried away by the charmed hadron, assume collinearity + rk::P4 p4CH(geom3::Vector3(rCH * interaction.primary_momentum[1], rCH * interaction.primary_momentum[2], rCH * interaction.primary_momentum[3]), mCH); + + double EX = (1 - z) * Ec; // energy of the hadronic shower + double p3X = EX; // assume no hadronic mass + double rX = p3X/p3c; // assume collinear + rk::P4 p4X(geom3::Vector3(rX * interaction.primary_momentum[1], rX * interaction.primary_momentum[2], rX * interaction.primary_momentum[3]), 0); + + + // update interaction parameters: to be added here later + + // new implementation of updateing outgoing particles + std::vector & secondaries = interaction.GetSecondaryParticleRecords(); + siren::dataclasses::SecondaryParticleRecord & hadronic_vertex = secondaries[0]; + siren::dataclasses::SecondaryParticleRecord & d_meson = secondaries[1]; // these indices are hard-coded, should be automated in a future time + + hadronic_vertex.SetFourMomentum({p4X.e(), p4X.px(), p4X.py(), p4X.pz()}); + hadronic_vertex.SetMass(p4X.m()); + hadronic_vertex.SetHelicity(interaction.primary_helicity); + + d_meson.SetFourMomentum({p4CH.e(), p4CH.px(), p4CH.py(), p4CH.pz()}); + d_meson.SetMass(p4CH.m()); + d_meson.SetHelicity(interaction.primary_helicity); + + // interaction.secondary_momenta.resize(2); + // interaction.secondary_masses.resize(2); + // interaction.secondary_helicities.resize(2); + + // // the hadronic shower + // interaction.secondary_momenta[0][0] = p4X.e(); + // interaction.secondary_momenta[0][1] = p4X.px(); + // interaction.secondary_momenta[0][2] = p4X.py(); + // interaction.secondary_momenta[0][3] = p4X.pz(); + // interaction.secondary_masses[0] = 0; + // interaction.secondary_helicities[0] = interaction.primary_helicity; // true? + + // // the charmed hadron + // interaction.secondary_momenta[1][0] = p4CH.e(); + // interaction.secondary_momenta[1][1] = p4CH.px(); + // interaction.secondary_momenta[1][2] = p4CH.py(); + // interaction.secondary_momenta[1][3] = p4CH.pz(); + // interaction.secondary_masses[1] = p4CH.m(); + // interaction.secondary_helicities[1] = interaction.primary_helicity; // true? + +} + +double CharmHadronization::FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const { + if (secondary == siren::dataclasses::Particle::ParticleType::D0 || secondary == siren::dataclasses::Particle::ParticleType::D0Bar) { + return 0.6; + } else if (secondary == siren::dataclasses::Particle::ParticleType::DPlus || secondary == siren::dataclasses::Particle::ParticleType::DMinus) { + return 0.23; + } // D_s and Lambda^+ not yet implemented + return 0; +} + +} // namespace interactions +} // namespace siren \ No newline at end of file diff --git a/projects/interactions/private/CharmMesonDecay.cxx b/projects/interactions/private/CharmMesonDecay.cxx new file mode 100644 index 000000000..7f6520abf --- /dev/null +++ b/projects/interactions/private/CharmMesonDecay.cxx @@ -0,0 +1,505 @@ +#include "SIREN/interactions/CharmMesonDecay.h" + +#include + +#include +#include + +#include "SIREN/dataclasses/InteractionRecord.h" // for Interac... +#include "SIREN/dataclasses/InteractionSignature.h" // for Interac... +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/math/Vector3D.h" // for Vector3D +#include "SIREN/utilities/Constants.h" // for GeV, pi +#include "SIREN/utilities/Random.h" // for SIREN_random + +#include "SIREN/utilities/Integration.h" // for rombergInt... +#include "SIREN/utilities/Interpolator.h" + +#include "SIREN/interactions/Decay.h" + +namespace siren { +namespace interactions { + +CharmMesonDecay::CharmMesonDecay() { + // this is the default initialization but should never be used + + // we need to compute cdf here b/c otherwise SampleFinalState becomes non constant + // in this case, we will need to compute all the possible dGamma's here + // maybe add a map here, but for now we hard code first + std::vector constants; + constants.resize(3); + // check the primary and secondaries of the signature + constants[0] = 0.725; // this is f^+(0)|V_cs| for charged D + constants[1] = 0.44; // this is alpha, same for all K final states + constants[2] = 2.01027; // this is excited charged D meson + + double mD = particleMass(siren::dataclasses::Particle::ParticleType::DPlus); + double mK = particleMass(siren::dataclasses::Particle::ParticleType::K0Bar); + + computeDiffGammaCDF(constants, mD, mK); +} + +CharmMesonDecay::CharmMesonDecay(siren::dataclasses::Particle::ParticleType primary) { + + //standard stuff, constant across primary types + std::vector constants; + constants.resize(3); + double mD; + double mK; + + if (primary == siren::dataclasses::Particle::ParticleType::DPlus) { + constants[0] = 0.725; // this is f^+(0)|V_cs| for charged D + constants[1] = 0.50; // this is alpha, same for all K final states + constants[2] = 2.01027; // this is excited charged D meson + + mD = particleMass(siren::dataclasses::Particle::ParticleType::DPlus); + mK = particleMass(siren::dataclasses::Particle::ParticleType::K0Bar); + + } else if (primary == siren::dataclasses::Particle::ParticleType::D0) { + constants[0] = 0.719; // this is f^+(0)|V_cs| for charged D + constants[1] = 0.50; // this is alpha, same for all K final states + constants[2] = 2.00697; // this is excited charged D meson + + mD = particleMass(siren::dataclasses::Particle::ParticleType::D0); + mK = particleMass(siren::dataclasses::Particle::ParticleType::KMinus); + } + + computeDiffGammaCDF(constants, mD, mK); + +} + +bool CharmMesonDecay::equal(Decay const & other) const { + const CharmMesonDecay* x = dynamic_cast(&other); + + if(!x) + return false; + else + return primary_types == x->primary_types; +} + + +double CharmMesonDecay::particleMass(siren::dataclasses::ParticleType particle) { + switch(particle){ + case siren::dataclasses::ParticleType::D0: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::D0Bar: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::DPlus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::DMinus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::K0: + return( siren::utilities::Constants::K0Mass); + case siren::dataclasses::ParticleType::K0Bar: + return( siren::utilities::Constants::K0Mass); + case siren::dataclasses::ParticleType::KPlus: + return( siren::utilities::Constants::KplusMass); + case siren::dataclasses::ParticleType::KMinus: + return( siren::utilities::Constants::KminusMass); + case siren::dataclasses::ParticleType::EPlus: + return( siren::utilities::Constants::electronMass ); + case siren::dataclasses::ParticleType::EMinus: + return( siren::utilities::Constants::electronMass ); + case siren::dataclasses::ParticleType::MuPlus: + return( siren::utilities::Constants::muonMass ); + case siren::dataclasses::ParticleType::MuMinus: + return( siren::utilities::Constants::muonMass ); + case siren::dataclasses::ParticleType::TauPlus: + return( siren::utilities::Constants::tauMass ); + case siren::dataclasses::ParticleType::TauMinus: + return( siren::utilities::Constants::tauMass ); + default: + return(0.0); + } +} + +double CharmMesonDecay::TotalDecayWidth(dataclasses::InteractionRecord const & record) const { + return TotalDecayWidth(record.signature.primary_type); +} + +// in this implementation, should we take total decay width to be only the channels we considered? +double CharmMesonDecay::TotalDecayWidth(siren::dataclasses::Particle::ParticleType primary) const { + double total_width = 0; + std::vector possible_signatures = GetPossibleSignaturesFromParent(primary); + for (auto sig : possible_signatures) { + // make a fake record and full from total decay width for final state + siren::dataclasses::InteractionRecord fake_record; + fake_record.signature = sig; + double this_width = TotalDecayWidthForFinalState(fake_record); + total_width += this_width; + } + return total_width; +} + +// current problem: in implementation we see kaons and pions both as hadrons, but they should have different branching ratios and form factors +double CharmMesonDecay::TotalDecayWidthForFinalState(dataclasses::InteractionRecord const & record) const { + double branching_ratio; + double tau; // total lifetime for all visible and invisible modes + // read in the signature and types + siren::dataclasses::Particle::ParticleType primary = record.signature.primary_type; + std::vector secondaries_vector = record.signature.secondary_types; + std::set secondaries = std::set(secondaries_vector.begin(), secondaries_vector.end()); + + // define the decay modes + std::set k0_eplus_nue = {siren::dataclasses::Particle::ParticleType::K0Bar, + siren::dataclasses::Particle::ParticleType::EPlus, + siren::dataclasses::Particle::ParticleType::NuE}; + std::set kminus_eplus_nue = {siren::dataclasses::Particle::ParticleType::KMinus, + siren::dataclasses::Particle::ParticleType::EPlus, + siren::dataclasses::Particle::ParticleType::NuE}; + if (primary == siren::dataclasses::Particle::ParticleType::DPlus && secondaries == k0_eplus_nue) { + // branching_ratio = 0.089; + branching_ratio = 1; + tau = 1040 * (1e-15); + } else if (primary == siren::dataclasses::Particle::ParticleType::D0 && secondaries == kminus_eplus_nue) { + // branching_ratio = 0.03538; + branching_ratio = 1; + tau = 410.1 * (1e-15); + } + else { + std::cout << "this decay mode is not yet implemented!" << std::endl; + } + return branching_ratio * siren::utilities::Constants::hbar / tau * siren::utilities::Constants::GeV; +} + + +std::vector CharmMesonDecay::GetPossibleSignatures() const { + std::vector signatures; + for(auto primary : primary_types) { + std::vector new_signatures = GetPossibleSignaturesFromParent(primary); + signatures.insert(signatures.end(),new_signatures.begin(),new_signatures.end()); + } + return signatures; +} + +std::vector CharmMesonDecay::GetPossibleSignaturesFromParent(siren::dataclasses::Particle::ParticleType primary) const { + std::vector signatures; + dataclasses::InteractionSignature signature; + signature.primary_type = primary; + signature.target_type = siren::dataclasses::Particle::ParticleType::Decay; + + // first we deal with semileptonic decays where there are 3 final state particles + signature.secondary_types.resize(3); + if(primary==siren::dataclasses::Particle::ParticleType::DPlus) { + signature.secondary_types[0] = siren::dataclasses::Particle::ParticleType::K0Bar; + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::EPlus; + signature.secondary_types[2] = siren::dataclasses::Particle::ParticleType::NuE; + signatures.push_back(signature); + } else if (primary==siren::dataclasses::Particle::ParticleType::D0) { + signature.secondary_types[0] = siren::dataclasses::Particle::ParticleType::KMinus; + signature.secondary_types[1] = siren::dataclasses::Particle::ParticleType::EPlus; + signature.secondary_types[2] = siren::dataclasses::Particle::ParticleType::NuE; + signatures.push_back(signature); + } + else { + std::cout << "this D meson decay has not been implemented yet" << std::endl; + } + return signatures; +} + +std::vector CharmMesonDecay::FormFactorFromRecord(dataclasses::CrossSectionDistributionRecord const & record) const { + dataclasses::InteractionSignature signature = record.signature; + std::vector constants; + constants.resize(3); + // check the primary and secondaries of the signature + if (signature.primary_type == dataclasses::Particle::ParticleType::DPlus && signature.secondary_types[0] == siren::dataclasses::Particle::ParticleType::K0Bar) { + constants[0] = 0.725; // this is f^+(0)|V_cs| for charged D + constants[1] = 0.44; // this is alpha, same for all K final states + constants[2] = 2.01027; // this is excited charged D meson + } else if (signature.primary_type == dataclasses::Particle::ParticleType::D0 && signature.secondary_types[0] == siren::dataclasses::Particle::ParticleType::KMinus) { + constants[0] = 0.719; // this is f^+(0)|V_cs| for neutral D + constants[1] = 0.44; // this is alpha, same for all K final states + constants[2] = 2.00697; // this is excited neutral D meson + } + return constants; +} + +double CharmMesonDecay::DifferentialDecayWidth(dataclasses::InteractionRecord const & record) const { + // get the form factor constants + std::vector constants = FormFactorFromRecord(record); + // calculate the q^2 + rk::P4 pD(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); + rk::P4 pKPi(geom3::Vector3(record.secondary_momenta[0][1], record.secondary_momenta[0][2], record.secondary_momenta[0][3]), record.secondary_masses[0]); + double Q2 = (pD - pKPi).dot(pD - pKPi); + // primary and secondary masses are also needed + double mD = record.primary_mass; + double mK = record.secondary_masses[0]; + return DifferentialDecayWidth(constants, Q2, mD, mK); +} + +double CharmMesonDecay::DifferentialDecayWidth(std::vector constants, double Q2, double mD, double mK) const { + // get the numerical constants from the vector + double F0CKM = constants[0]; + double alpha = constants[1]; + double ms = constants[2]; + double Q2tilde = Q2 / ms; + // compute the 3-momentum as a function of Q2 + double EK = 0.5 * (Q2 - pow(mD, 2 + pow(mK, 2))) / mD; // energy of Kaon + double PK = pow(pow(EK, 2) - pow(mK, 2), 1/2); + // plug in the constants + double dGamma = pow(siren::utilities::Constants::FermiConstant,2) / (24 * pow(siren::utilities::Constants::pi,3)) * pow(F0CKM,2) * + pow((1/((1-Q2tilde) * (1 - alpha * Q2tilde))),2) * pow(PK,3); + return dGamma; +} + +void CharmMesonDecay::computeDiffGammaCDF(std::vector constants, double mD, double mK) { + + // returns a 1D interpolater table for dGamma cdf + // define the pdf with only Q2 as the input + std::function pdf = [&] (double x) -> double { + return DifferentialDecayWidth(constants, x, mD, mK); + }; + // first normalize the integral + double min = 0; + double max = 1.4; // these set the min and max of the Q2 considered + double normalization = siren::utilities::rombergIntegrate(pdf, min, max); + std::function normed_pdf = [&] (double x) -> double { + return DifferentialDecayWidth(constants, x, mD, mK) / normalization; + }; + // now create the spline and compute the CDF + + // set the Q2 nodes (use 100 nodes) + std::vector Q2spline; + for (int i = 0; i < 100; ++i) { + Q2spline.push_back(0.01 + i * (max-min) / 100 ); + } + + // declare the cdf vectors + std::vector cdf_vector; + std::vector cdf_Q2_nodes; + std::vector pdf_vector; + + cdf_Q2_nodes.push_back(0); + cdf_vector.push_back(0); + pdf_vector.push_back(0); + + // compute the spline table + for (int i = 0; i < Q2spline.size(); ++i) { + if (i == 0) { + double cur_Q2 = Q2spline[i]; + double cur_pdf = normed_pdf(cur_Q2); + double area = cur_Q2 * cur_pdf * 0.5; + pdf_vector.push_back(cur_pdf); + cdf_vector.push_back(area); + cdf_Q2_nodes.push_back(cur_Q2); + continue; + } + double cur_Q2 = Q2spline[i]; + double cur_pdf = normed_pdf(cur_Q2); + double area = 0.5 * (pdf_vector[i - 1] + cur_pdf) * (Q2spline[i] - Q2spline[i - 1]); + pdf_vector.push_back(cur_pdf); + cdf_Q2_nodes.push_back(cur_Q2); + cdf_vector.push_back(area + cdf_vector.back()); + } + + cdf_Q2_nodes.push_back(max); + cdf_vector.push_back(1); + pdf_vector.push_back(0); + + // for debugging and plotting, print the pdf and cdf tables + // for (size_t i = 0; auto& element : cdf_Q2_nodes) { + // std::cout << element; + // // Print comma if it's not the last element + // if (++i != cdf_Q2_nodes.size()) { + // std::cout << ", "; + // } + // } + // std::cout << std::endl; + + // for (size_t i = 0; auto& element : cdf_vector) { + // std::cout << element; + // // Print comma if it's not the last element + // if (++i != cdf_vector.size()) { + // std::cout << ", "; + // } + // } + // std::cout << std::endl; + + // for (size_t i = 0; auto& element : pdf_vector) { + // std::cout << element; + // // Print comma if it's not the last element + // if (++i != pdf_vector.size()) { + // std::cout << ", "; + // } + // } + // std::cout << std::endl; + + // set the spline table + siren::utilities::TableData1D inverse_cdf_data; + inverse_cdf_data.x = cdf_vector; + inverse_cdf_data.f = cdf_Q2_nodes; + + inverseCdf = siren::utilities::Interpolator1D(inverse_cdf_data); + + return; + +} + + + +void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { + // first obtain the constants needed for further computation from the signature + // std::cout<<"b1"< constants = FormFactorFromRecord(record); + double mD = particleMass(record.signature.primary_type); + double mK = particleMass(record.signature.secondary_types[0]); + + // std::cout << "input masses: " << mD << " " << mK << std::endl; + + // first sample a q^2 + //////////////////////////////////////////// + // computeDiffGammaCDF(constants, mD, mK);// + //////////////////////////////////////////// + double rand_value_for_Q2 = random->Uniform(0, 1); + double Q2 = inverseCdf(rand_value_for_Q2); + + // now sample isotropically the "zenith" direction + double cosTheta = random->Uniform(-1, 1); + double sinTheta = std::sin(std::acos(cosTheta)); + // set the x axis to be the D direction + geom3::UnitVector3 x_dir = geom3::UnitVector3::xAxis(); + // std::cout<<"b2"<Uniform(0, 2 * M_PI); + geom3::Rotation3 azimuth_rand_rot(p3D_lab_dir, phi); + p4K_Drest.rotate(azimuth_rand_rot); + p4W_Drest.rotate(azimuth_rand_rot); + // finally, boost the 4 momenta back to the lab frame + rk::Boost boost_from_Drest_to_lab = p4D_lab.labBoost(); + rk::P4 p4K_lab = p4K_Drest.boost(boost_from_Drest_to_lab); + rk::P4 p4W_lab = p4W_Drest.boost(boost_from_Drest_to_lab); + // std::cout<<"b4"<W+K/Pi decay, now treat the W->l+nu decay + double ml = particleMass(record.signature.secondary_types[1]); + double mnu = 0; + double W_cosTheta = random->Uniform(-1, 1); // sampling the direction + double W_sinTheta = std::sin(std::acos(W_cosTheta)); + double El = (Q2 + pow(ml, 2)) / (2 * sqrt(Q2)); + double Enu = (Q2 - pow(ml, 2)) / (2 * sqrt(Q2)); // the energies of the outgoing lepton and neutrino + double P = (Q2 - pow(ml, 2)) / (2 * sqrt(Q2)); + // now we have thr four vectors of the outgoing particle kinematics in tne W rest frame wrt x direction + rk::P4 p4l_Wrest(P * geom3::Vector3(W_cosTheta, W_sinTheta, 0), ml); + rk::P4 p4nu_Wrest(P * geom3::Vector3(-W_cosTheta, -W_sinTheta, 0), 0); + + // std::cout << "momentums: " << p4l_Wrest << " " << p4nu_Wrest << std::endl; + // std::cout << "check mass of l and nu: " << p4l_Wrest.m() << " " << p4nu_Wrest.m() << std::endl; + //now rotate so they are defined wrt the lab frame W direction + // std::cout<<"b5"<Uniform(0, 2 * M_PI); + geom3::Rotation3 W_azimuth_rand_rot(p3W_lab_dir, W_phi); + rk::P4 p4l_lab = p4l_Wrest.rotate(W_azimuth_rand_rot); + rk::P4 p4nu_lab = p4nu_Wrest.rotate(W_azimuth_rand_rot); + + // std::cout<<"b6"< & secondaries = record.GetSecondaryParticleRecords(); + siren::dataclasses::SecondaryParticleRecord & kpi = secondaries[0]; + siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[1]; + siren::dataclasses::SecondaryParticleRecord & neutrino = secondaries[2]; //these are all hardcoded at the time + + kpi.SetFourMomentum({p4K_lab.e(), p4K_lab.px(), p4K_lab.py(), p4K_lab.pz()}); + kpi.SetMass(p4K_lab.m()); + kpi.SetHelicity(record.primary_helicity); + + lepton.SetFourMomentum({p4l_lab.e(), p4l_lab.px(), p4l_lab.py(), p4l_lab.pz()}); + lepton.SetMass(p4l_lab.m()); + lepton.SetHelicity(record.primary_helicity); + + neutrino.SetFourMomentum({p4nu_lab.e(), p4nu_lab.px(), p4nu_lab.py(), p4nu_lab.pz()}); + neutrino.SetMass(p4nu_lab.m()); + neutrino.SetHelicity(record.primary_helicity); + + // finally, we can populate the record, for implementation in prometheus, maybe add treatment of hadrons, but could be implemnted on p side + // record.secondary_momenta.resize(3); + // record.secondary_masses.resize(3); + // record.secondary_helicity.resize(3); // 0 is the hadron, 1 is the lepton, 2 is the neutrino + // // the K/pi + // record.secondary_momenta[0][0] = p4K_lab.e(); + // record.secondary_momenta[0][1] = p4K_lab.px(); + // record.secondary_momenta[0][2] = p4K_lab.py(); + // record.secondary_momenta[0][3] = p4K_lab.pz(); + // record.secondary_masses[0] = p4K_lab.m(); + // record.secondary_helicity[0] = 0; + // // the lepton + // record.secondary_momenta[1][0] = p4l_lab.e(); + // record.secondary_momenta[1][1] = p4l_lab.px(); + // record.secondary_momenta[1][2] = p4l_lab.py(); + // record.secondary_momenta[1][3] = p4l_lab.pz(); + // record.secondary_masses[1] = p4l_lab.m(); + // record.secondary_helicity[1] = 1; + // // the neutrino + // record.secondary_momenta[2][0] = p4nu_lab.e(); + // record.secondary_momenta[2][1] = p4nu_lab.px(); + // record.secondary_momenta[2][2] = p4nu_lab.py(); + // record.secondary_momenta[2][3] = p4nu_lab.pz(); + // record.secondary_masses[2] = p4nu_lab.m(); + // record.secondary_helicity[2] = 1; + + //for debug purposes + // double p4w_rest_Q2 = pow(p4W_Drest.e(), 2) - pow(p4W_Drest.px(), 2) - + // pow(p4W_Drest.py(), 2) - pow(p4W_Drest.pz(), 2); + // double p4w_lab_Q2 = pow(p4W_lab.e(), 2) - pow(p4W_lab.px(), 2) - + // pow(p4W_lab.py(), 2) - pow(p4W_lab.pz(), 2); + // std::cout << p4W_Drest.e() << " " << p4W_lab.e() << " " << PW << " " << p4W_Drest.p() << " " << p4W_Drest << std::endl; + // std::cout << p4K_Drest.e() << " " << p4K_lab.e() << " " << PK << " " << p4K_Drest.p() << " " << p4K_Drest << std::endl; + // std::cout << Q2 << " " << sqrt(Q2)<< std::endl; + // std::cout << "invariant mass of the W in two frames are " << p4w_lab_Q2 << " " << p4w_rest_Q2 << std::endl; + // std::cout << "check mass of W: " << p4W_lab.m() << " " << p4W_Drest.m() << std::endl; + // std::cout << "check mass of K: " << p4K_lab.m() << " " << p4K_Drest.m() << std::endl; + + + + // rk::P4 inv_mass_Wrest = p4l_Wrest + p4nu_Wrest; + // rk::P4 inv_mass_lab = p4l_lab + p4nu_lab; + // std::cout << "inv masses in two frames: " << pow(inv_mass_Wrest.m(), 2) << " " << pow(inv_mass_lab.m(), 2) << std::endl; + // std::cout << "using inv mass calculator: " << pow(invMass(p4l_Wrest, p4nu_Wrest), 2) << " " << pow(invMass(p4l_lab, p4nu_lab), 2) << std::endl; + // std::cout << "energy of l and nu and inv mass: " << El << " " << Enu << " " << pow(El+Enu, 2) << std::endl; + // std::cout << "momentums: " << p4l_Wrest << " " << p4nu_Wrest << std::endl; + // std::cout << "check mass of l and nu: " << p4l_Wrest.m() << " " << p4nu_Wrest.m() << std::endl; + // std::cout << "W energy in rest frame: " << pow(p4W_lab.m(), 2) << std::endl; + + +} + +double CharmMesonDecay::FinalStateProbability(dataclasses::InteractionRecord const & record) const { + double dd = DifferentialDecayWidth(record); + double td = TotalDecayWidthForFinalState(record); + if (dd == 0) return 0.; + else if (td == 0) return 0.; + else return dd/td; +} + +std::vector CharmMesonDecay::DensityVariables() const { + return std::vector{"Q2"}; +} + + + +} // namespace interactions +} // namespace siren + diff --git a/projects/interactions/private/DMesonELoss.cxx b/projects/interactions/private/DMesonELoss.cxx new file mode 100644 index 000000000..867f7edf2 --- /dev/null +++ b/projects/interactions/private/DMesonELoss.cxx @@ -0,0 +1,281 @@ +#include "SIREN/interactions/DMesonELoss.h" + +#include // for map, opera... +#include // for set, opera... +#include // for array +#include // for pow, log10 +#include // for tie, opera... +#include // for allocator +#include // for basic_string +#include // for vector +#include // for assert +#include // for size_t + +#include // for P4, Boost +#include // for Vector3 + +#include // for splinetable +//#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSection +#include "SIREN/dataclasses/InteractionRecord.h" // for Interactio... +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/utilities/Constants.h" // for electronMass +#include "SIREN/utilities/Integration.h" // for rombergInt... + + +namespace siren { +namespace interactions { + +DMesonELoss::DMesonELoss() { +} + + +bool DMesonELoss::equal(CrossSection const & other) const { + const DMesonELoss* x = dynamic_cast(&other); + + if(!x) + return false; + else + return + std::tie( + primary_types_, + target_types_) + == + std::tie( + x->primary_types_, + x->target_types_); +} + + + +std::vector DMesonELoss::GetPossiblePrimaries() const { + return std::vector(primary_types_.begin(), primary_types_.end()); +} + +// getting target should be the same for all the primary types +std::vector DMesonELoss::GetPossibleTargetsFromPrimary(siren::dataclasses::Particle::ParticleType primary_type) const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector DMesonELoss::GetPossibleTargets() const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector DMesonELoss::GetPossibleSignatures() const { + std::vector signatures; + for(auto primary : primary_types_) { + // hardcode the target type here, this should be fine + std::vector new_signatures = GetPossibleSignaturesFromParents(primary, siren::dataclasses::Particle::ParticleType::PPlus); + signatures.insert(signatures.end(),new_signatures.begin(),new_signatures.end()); + } + return signatures; +} + +std::vector DMesonELoss::GetPossibleSignaturesFromParents(siren::dataclasses::Particle::ParticleType primary_type, siren::dataclasses::Particle::ParticleType target_type) const { + std::vector signatures; + dataclasses::InteractionSignature signature; + signature.primary_type = primary_type; + signature.target_type = target_type; + + // first we deal with semileptonic decays where there are 3 final state particles + signature.secondary_types.resize(1); + signature.secondary_types[0] = primary_type; // same particle comes out + signatures.push_back(signature); + return signatures; +} + +// i am here + +double DMesonELoss::TotalCrossSection(dataclasses::InteractionRecord const & interaction) const { + siren::dataclasses::Particle::ParticleType primary_type = interaction.signature.primary_type; + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + // rk::P4 p2(geom3::Vector3(interaction.target_momentum[1], interaction.target_momentum[2], interaction.target_momentum[3]), interaction.target_mass); + // double primary_energy; + + // // make sure of the reference frame before assigning energy + // if(interaction.target_momentum[1] == 0 and interaction.target_momentum[2] == 0 and interaction.target_momentum[3] == 0) { + // primary_energy = interaction.primary_momentum[0]; + // } else { + // rk::Boost boost_start_to_lab = p2.restBoost(); + // rk::P4 p1_lab = boost_start_to_lab * p1; + // primary_energy = p1_lab.e(); + // } + double primary_energy = interaction.primary_momentum[0]; + + + return TotalCrossSection(primary_type, primary_energy); +} + +double DMesonELoss::TotalCrossSection(siren::dataclasses::Particle::ParticleType primary_type, double primary_energy) const { + if(not primary_types_.count(primary_type)) { + throw std::runtime_error("Supplied primary not supported by cross section!"); + } + double log_energy = log10(primary_energy); + double mb_to_cm2 = 1e-27; + + // current implementation uses only > 1PeV data + double xsec = exp(1.891 + 0.205 * log_energy) - 2.157 + 1.264 * log_energy; + + return xsec * mb_to_cm2; +} + +// double DMesonELoss::TotalCrossSection(siren::dataclasses::Particle::ParticleType primary_type, double primary_energy, siren::dataclasses::Particle::ParticleType target) const { +// return DMesonELoss::TotalCrossSection(primary_type,primary_energy); +// } + + +double DMesonELoss::DifferentialCrossSection(dataclasses::InteractionRecord const & interaction) const { + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + // rk::P4 p2(geom3::Vector3(interaction.target_momentum[1], interaction.target_momentum[2], interaction.target_momentum[3]), interaction.target_mass); + double primary_energy; + rk::P4 p1_lab; + // rk::P4 p2_lab; + // if(interaction.target_momentum[1] == 0 and interaction.target_momentum[2] == 0 and interaction.target_momentum[3] == 0) { + primary_energy = interaction.primary_momentum[0]; + p1_lab = p1; + // p2_lab = p2; + // } else { + // rk::Boost boost_start_to_lab = p2.restBoost(); + // p1_lab = boost_start_to_lab * p1; + // p2_lab = boost_start_to_lab * p2; + // primary_energy = p1_lab.e(); + // std::cout << "D Meson Diff Xsec: not in lab frame???" << std::endl; + // } + + double final_energy = interaction.secondary_momenta[0][0]; + double z = 1 - final_energy / primary_energy; + + // now normalize the gaussian + double total_xsec = TotalCrossSection(interaction.signature.primary_type, primary_energy); + double z0 = 0.56; + double sigma = 0.2; + std::function integrand = [&] (double z) -> double { + return exp(-(pow(z - z0, 2))/(2 * pow(sigma, 2))); + }; + double unnormalized = siren::utilities::rombergIntegrate(integrand, 0.001, 0.999); + double normalization = total_xsec / unnormalized; + + double diff_xsec = normalization * exp(-(pow(z - z0, 2))/(2 * pow(sigma, 2))); + + return diff_xsec; +} + + +double DMesonELoss::InteractionThreshold(dataclasses::InteractionRecord const & interaction) const { + // Consider implementing DIS thershold at some point + return 0; +} + +void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& interaction, std::shared_ptr random) const { + + // std::cout << "In D Meson E Loss Sample Final State" << std::endl; + + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + // rk::P4 p2(geom3::Vector3(interaction.target_momentum[1], interaction.target_momentum[2], interaction.target_momentum[3]), interaction.target_mass); + + // we assume that: + // the target is stationary so its energy is just its mass + // the incoming neutrino is massless, so its kinetic energy is its total energy + // double s = target_mass_ * tinteraction.secondary_momentarget_mass_ + 2 * target_mass_ * primary_energy; + // double s = std::pow(rk::invMass(p1, p2), 2); + + double primary_energy; + double Dmass = interaction.primary_mass; + rk::P4 p1_lab; + // rk::P4 p2_lab; + // if(interaction.target_momentum[1] == 0 and interaction.target_momentum[2] == 0 and interaction.target_momentum[3] == 0) { + p1_lab = p1; + // p2_lab = p2; + primary_energy = p1_lab.e(); + // } else { + // // this is currently not implemented + // // Rest frame of p2 will be our "lab" frame + // rk::Boost boost_start_to_lab = p2.restBoost(); + // p1_lab = boost_start_to_lab * p1; + // p2_lab = boost_start_to_lab * p2; + // primary_energy = p1_lab.e(); + // // std::cout << "D Meson Energy Loss: not in lab frame???" << std::endl; + // } + // following line is wrong but i dont want to change it now fuck it. + // std::cout << " " << interaction.primary_momentum[0] << " " << interaction.primary_momentum[1] << " " << interaction.primary_momentum[2] << " " << interaction.primary_momentum[3]; + // std::cout << primary_energy << " " << pow(primary_energy, 2) - pow(Dmass, 2) << " " << + // sqrt(pow(interaction.primary_momentum[1], 2) +pow(interaction.primary_momentum[2], 2) +pow(interaction.primary_momentum[3], 2)) << std::endl; + // sample an inelasticity from gaussian using Box-Muller Transform + double sigma = 0.2; + double z0 = 0.56; // for mesons only, for baryons it's 0.59, but not implemented yet + double u1, u2; + double final_energy; + bool accept; + + + do { + do + { + u1 = random->Uniform(0, 1); + } + while (u1 == 0); + u2 = random->Uniform(0, 1); + double z = sigma * sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2) + z0; + // std::cout << z<< std::endl; + + // now modify the energy of the charm hadron and the corresponding momentum + final_energy = primary_energy * (1-z); + if (pow(final_energy, 2) - pow(Dmass, 2) >= 0) { + accept = true; + } else { + accept = false; + } + } while (!accept); + // this might be an infinite loop??????? + // need to check if the cross section length is good enough, how to make some relevant plots? + // std::cout << final_energy << std::endl; + double p3f = sqrt(pow(final_energy, 2) - pow(Dmass, 2)); + double p3i = std::sqrt(std::pow(p1.px(), 2) + std::pow(p1.py(), 2) + std::pow(p1.pz(), 2)); + double p_ratio = p3f / p3i; + + // std::cout << " " << p3f << " " << p3i << " " << p_ratio << std::endl; + rk::P4 pf(p_ratio * geom3::Vector3(p1.px(), p1.py(), p1.pz()), Dmass); + + std::vector & secondaries = interaction.GetSecondaryParticleRecords(); + siren::dataclasses::SecondaryParticleRecord & dmeson = secondaries[0]; + + + dmeson.SetFourMomentum({pf.e(), pf.px(), pf.py(), pf.pz()}); + dmeson.SetMass(pf.m()); + dmeson.SetHelicity(interaction.primary_helicity); + + // interaction.secondary_momenta.resize(1); + // interaction.secondary_masses.resize(1); + // interaction.secondary_helicity.resize(1); + + // interaction.secondary_momenta[0][0] = pf.e(); // p3_energy + // interaction.secondary_momenta[0][1] = pf.px(); // p3_x + // interaction.secondary_momenta[0][2] = pf.py(); // p3_y + // interaction.secondary_momenta[0][3] = pf.pz(); // p3_z + // interaction.secondary_masses[0] = pf.m(); + + // interaction.secondary_helicity[0] = interaction.primary_helicity; +} + +double DMesonELoss::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { + double dxs = DifferentialCrossSection(interaction); + double txs = TotalCrossSection(interaction); + if(dxs == 0) { + return 0.0; + } else { + return dxs / txs; + } +} + +std::vector DMesonELoss::DensityVariables() const { + return std::vector{"Bjorken y"}; +} + +void DMesonELoss::InitializeSignatures() { + return; +} + +} // namespace interactions +} // namespace siren diff --git a/projects/interactions/private/Hadronization.cxx b/projects/interactions/private/Hadronization.cxx new file mode 100644 index 000000000..7833c0735 --- /dev/null +++ b/projects/interactions/private/Hadronization.cxx @@ -0,0 +1,17 @@ +#include "SIREN/interactions/Hadronization.h" +#include "SIREN/dataclasses/InteractionRecord.h" + +namespace siren { +namespace interactions { + +Hadronization::Hadronization() {} + +bool Hadronization::operator==(Hadronization const & other) const { + if(this == &other) + return true; + else + return this->equal(other); +} + +} // namespace interactions +} // namespace siren \ No newline at end of file diff --git a/projects/interactions/private/InteractionCollection.cxx b/projects/interactions/private/InteractionCollection.cxx index 0f68772ba..e4a3a87d0 100644 --- a/projects/interactions/private/InteractionCollection.cxx +++ b/projects/interactions/private/InteractionCollection.cxx @@ -8,7 +8,8 @@ #include // for pair #include "SIREN/interactions/CrossSection.h" // for CrossSe... -#include "SIREN/interactions/Decay.h" // for Decay +#include "SIREN/interactions/Decay.h" // for Decay +#include "SIREN/interactions/Hadronization.h" // for Decay #include "SIREN/dataclasses/InteractionRecord.h" // for Interac... #include "SIREN/dataclasses/InteractionSignature.h" // for Interac... #include "SIREN/dataclasses/Particle.h" // for Particle @@ -60,6 +61,22 @@ InteractionCollection::InteractionCollection(siren::dataclasses::ParticleType pr InitializeTargetTypes(); } +InteractionCollection::InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> hadronizations) : primary_type(primary_type), hadronizations(hadronizations) { + InitializeTargetTypes(); +} + +InteractionCollection::InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> cross_sections, std::vector> hadronizations) : primary_type(primary_type), cross_sections(cross_sections), hadronizations(hadronizations) { + InitializeTargetTypes(); +} + +InteractionCollection::InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> decays, std::vector> hadronizations) : primary_type(primary_type), decays(decays), hadronizations(hadronizations) { + InitializeTargetTypes(); +} + +InteractionCollection::InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> cross_sections, std::vector> decays, std::vector> hadronizations) : primary_type(primary_type), cross_sections(cross_sections), decays(decays), hadronizations(hadronizations) { + InitializeTargetTypes(); +} + bool InteractionCollection::operator==(InteractionCollection const & other) const { return std::tie(primary_type, target_types, cross_sections, decays) diff --git a/projects/interactions/private/pybindings/CharmDISFromSpline.h b/projects/interactions/private/pybindings/CharmDISFromSpline.h new file mode 100644 index 000000000..486556be4 --- /dev/null +++ b/projects/interactions/private/pybindings/CharmDISFromSpline.h @@ -0,0 +1,87 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/CrossSection.h" +#include "../../public/SIREN/interactions/CharmDISFromSpline.h" +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionRecord.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionSignature.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +void register_CharmDISFromSpline(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_, CrossSection> charmdisfromspline(m, "CharmDISFromSpline"); + + charmdisfromspline + + .def(init<>()) + .def(init, std::vector, int, double, double, std::set, std::set, std::string>(), + arg("total_xs_data"), + arg("differential_xs_data"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, int, double, double, std::vector, std::vector, std::string>(), + arg("total_xs_data"), + arg("differential_xs_data"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::set, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::set, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(self == self) + .def("TotalCrossSection",overload_cast(&CharmDISFromSpline::TotalCrossSection, const_)) + .def("TotalCrossSection",overload_cast(&CharmDISFromSpline::TotalCrossSection, const_)) + .def("DifferentialCrossSection",overload_cast(&CharmDISFromSpline::DifferentialCrossSection, const_)) + .def("DifferentialCrossSection",overload_cast(&CharmDISFromSpline::DifferentialCrossSection, const_)) + .def("InteractionThreshold",&CharmDISFromSpline::InteractionThreshold) + .def("GetPossibleTargets",&CharmDISFromSpline::GetPossibleTargets) + .def("GetPossibleTargetsFromPrimary",&CharmDISFromSpline::GetPossibleTargetsFromPrimary) + .def("GetPossiblePrimaries",&CharmDISFromSpline::GetPossiblePrimaries) + .def("GetPossibleSignatures",&CharmDISFromSpline::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParents",&CharmDISFromSpline::GetPossibleSignaturesFromParents) + .def("FinalStateProbability",&CharmDISFromSpline::FinalStateProbability); +} + diff --git a/projects/interactions/private/pybindings/CharmHadronization.h b/projects/interactions/private/pybindings/CharmHadronization.h new file mode 100644 index 000000000..c0c7c7788 --- /dev/null +++ b/projects/interactions/private/pybindings/CharmHadronization.h @@ -0,0 +1,38 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/Hadronization.h" +#include "../../public/SIREN/interactions/CharmHadronization.h" + + +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionRecord.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionSignature.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +using namespace pybind11; +using namespace siren::interactions; + + +void register_CharmHadronization(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_, Hadronization> charmhadronization(m, "CharmHadronization"); + + charmhadronization + + .def(init<>()) + .def(self == self) + .def("SampleFinalState",&CharmHadronization::SampleFinalState) + .def("GetPossibleSignatures",&CharmHadronization::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParent",&CharmHadronization::GetPossibleSignaturesFromParent) + .def("FragmentationFraction",&CharmHadronization::FragmentationFraction); + ; +} diff --git a/projects/interactions/private/pybindings/CharmMesonDecay.h b/projects/interactions/private/pybindings/CharmMesonDecay.h new file mode 100644 index 000000000..224ef9ad2 --- /dev/null +++ b/projects/interactions/private/pybindings/CharmMesonDecay.h @@ -0,0 +1,34 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/CrossSection.h" +#include "../../public/SIREN/interactions/Decay.h" +#include "../../public/SIREN/interactions/CharmMesonDecay.h" + +#include "../../public/SIREN/interactions/pyDecay.h" +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +void register_CharmMesonDecay(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_, Decay> charmmesondecay(m, "CharmMesonDecay"); + + charmmesondecay + + .def(init<>()) + .def(init(), + arg("primary_type")) + .def(self == self) + .def("SampleFinalState",&CharmMesonDecay::SampleFinalState) + .def("GetPossibleSignatures",&CharmMesonDecay::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParent",&CharmMesonDecay::GetPossibleSignaturesFromParent); + +} diff --git a/projects/interactions/private/pybindings/DMesonELoss.h b/projects/interactions/private/pybindings/DMesonELoss.h new file mode 100644 index 000000000..427633c4e --- /dev/null +++ b/projects/interactions/private/pybindings/DMesonELoss.h @@ -0,0 +1,37 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/CrossSection.h" +#include "../../public/SIREN/interactions/CharmDISFromSpline.h" +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionRecord.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionSignature.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +void register_DMesonELoss(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_, CrossSection> dmesoneloss(m, "DMesonELoss"); + + dmesoneloss + .def(init<>()) + .def(self == self) + .def("TotalCrossSection",overload_cast(&DMesonELoss::TotalCrossSection, const_)) + .def("TotalCrossSection",overload_cast(&DMesonELoss::TotalCrossSection, const_)) + // .def("DifferentialCrossSection",overload_cast(&DMesonELoss::DifferentialCrossSection, const_)) + .def("InteractionThreshold",&DMesonELoss::InteractionThreshold) + .def("GetPossibleTargets",&DMesonELoss::GetPossibleTargets) + .def("GetPossibleTargetsFromPrimary",&DMesonELoss::GetPossibleTargetsFromPrimary) + .def("GetPossiblePrimaries",&DMesonELoss::GetPossiblePrimaries) + .def("GetPossibleSignatures",&DMesonELoss::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParents",&DMesonELoss::GetPossibleSignaturesFromParents) + .def("FinalStateProbability",&DMesonELoss::FinalStateProbability); +} + diff --git a/projects/interactions/private/pybindings/Hadronization.h b/projects/interactions/private/pybindings/Hadronization.h new file mode 100644 index 000000000..513836cfe --- /dev/null +++ b/projects/interactions/private/pybindings/Hadronization.h @@ -0,0 +1,34 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/Hadronization.h" + +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionRecord.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionSignature.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +using namespace pybind11; +using namespace siren::interactions; + + +void register_Hadronization(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_>(m, "Hadronization") + // .def(init<>()) + .def("__eq__", [](const Hadronization &self, const Hadronization &other){ return self == other; }) + .def("equal", &Hadronization::equal) + .def("SampleFinalState", &Hadronization::SampleFinalState) + .def("GetPossibleSignatures", &Hadronization::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParent", &Hadronization::GetPossibleSignaturesFromParent) + .def("FragmentationFraction", &Hadronization::FragmentationFraction) + ; +} diff --git a/projects/interactions/private/pybindings/InteractionCollection.h b/projects/interactions/private/pybindings/InteractionCollection.h index 5d8314acc..7eaf5b2c0 100644 --- a/projects/interactions/private/pybindings/InteractionCollection.h +++ b/projects/interactions/private/pybindings/InteractionCollection.h @@ -9,6 +9,8 @@ #include "../../public/SIREN/interactions/CrossSection.h" #include "../../public/SIREN/interactions/InteractionCollection.h" #include "../../public/SIREN/interactions/Decay.h" +#include "../../public/SIREN/interactions/Hadronization.h" + #include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" #include "../../../geometry/public/SIREN/geometry/Geometry.h" #include "../../../utilities/public/SIREN/utilities/Random.h" @@ -21,12 +23,17 @@ void register_InteractionCollection(pybind11::module_ & m) { .def(init<>()) .def(init>>()) .def(init>>()) + .def(init>>()) .def(init>, std::vector>>()) + .def(init>, std::vector>>()) + .def(init>, std::vector>>()) + .def(init>, std::vector>, std::vector>>()) .def(self == self) .def("GetCrossSections",&InteractionCollection::GetCrossSections, return_value_policy::reference_internal) .def("GetDecays",&InteractionCollection::GetDecays, return_value_policy::reference_internal) .def("HasCrossSections",&InteractionCollection::HasCrossSections) .def("HasDecays",&InteractionCollection::HasDecays) + .def("HasHadronizations",&InteractionCollection::HasHadronizations) .def("GetCrossSectionsForTarget",&InteractionCollection::GetCrossSectionsForTarget, return_value_policy::reference_internal) .def("GetCrossSectionsByTarget",&InteractionCollection::GetCrossSectionsByTarget, return_value_policy::reference_internal) .def("TotalCrossSectionByTarget",&InteractionCollection::TotalCrossSectionByTarget) diff --git a/projects/interactions/private/pybindings/interactions.cxx b/projects/interactions/private/pybindings/interactions.cxx index e030ac759..7cfc91cda 100644 --- a/projects/interactions/private/pybindings/interactions.cxx +++ b/projects/interactions/private/pybindings/interactions.cxx @@ -5,21 +5,31 @@ #include "../../public/SIREN/interactions/NeutrissimoDecay.h" #include "../../public/SIREN/interactions/InteractionCollection.h" #include "../../public/SIREN/interactions/DISFromSpline.h" +#include "../../public/SIREN/interactions/CharmDISFromSpline.h" #include "../../public/SIREN/interactions/HNLFromSpline.h" #include "../../public/SIREN/interactions/DipoleFromTable.h" #include "../../public/SIREN/interactions/DarkNewsCrossSection.h" #include "../../public/SIREN/interactions/DarkNewsDecay.h" +#include "../../public/SIREN/interactions/Hadronization.h" +#include "../../public/SIREN/interactions/CharmHadronization.h" +#include "../../public/SIREN/interactions/CharmMesonDecay.h" +#include "../../public/SIREN/interactions/DMesonELoss.h" #include "./CrossSection.h" #include "./DipoleFromTable.h" #include "./DarkNewsCrossSection.h" #include "./DarkNewsDecay.h" #include "./DISFromSpline.h" +#include "./CharmDISFromSpline.h" #include "./HNLFromSpline.h" #include "./Decay.h" #include "./NeutrissimoDecay.h" #include "./InteractionCollection.h" #include "./DummyCrossSection.h" +#include "./Hadronization.h" +#include "./CharmHadronization.h" +#include "./CharmMesonDecay.h" +#include "./DMesonELoss.h" #include #include @@ -34,10 +44,17 @@ PYBIND11_MODULE(interactions,m) { register_CrossSection(m); register_Decay(m); + register_Hadronization(m); + + register_CharmHadronization(m); + register_CharmMesonDecay(m); + register_DMesonELoss(m); + register_DipoleFromTable(m); register_DarkNewsCrossSection(m); register_DarkNewsDecay(m); register_DISFromSpline(m); + register_CharmDISFromSpline(m); register_HNLFromSpline(m); register_NeutrissimoDecay(m); register_InteractionCollection(m); diff --git a/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h b/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h new file mode 100644 index 000000000..80f97a386 --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h @@ -0,0 +1,162 @@ +#pragma once +#ifndef SIREN_CharmDISFromSpline_H +#define SIREN_CharmDISFromSpline_H + +#include // for set +#include // for map +#include +#include // for vector +#include // for uint32_t +#include // for pair +#include +#include // for runtime... + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSe... +#include "SIREN/dataclasses/InteractionSignature.h" // for Interac... +#include "SIREN/dataclasses/Particle.h" // for Particle + +namespace siren { namespace dataclasses { class InteractionRecord; } } +namespace siren { namespace utilities { class SIREN_random; } } + +namespace siren { +namespace interactions { + +class CharmDISFromSpline : public CrossSection { +friend cereal::access; +private: + photospline::splinetable<> differential_cross_section_; + photospline::splinetable<> total_cross_section_; + + std::vector signatures_; + std::set primary_types_; + std::set target_types_; + std::map> targets_by_primary_types_; + std::map, std::vector> signatures_by_parent_types_; + + int interaction_type_; + double target_mass_; + double minimum_Q2_; + + double unit; + +public: + CharmDISFromSpline(); + CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minumum_Q2, std::set primary_types, std::set target_types, std::string units = "cm"); + CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minumum_Q2, std::vector primary_types, std::vector target_types, std::string units = "cm"); + CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minumum_Q2, std::set primary_types, std::set target_types, std::string units = "cm"); + CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units = "cm"); + CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minumum_Q2, std::vector primary_types, std::vector target_types, std::string units = "cm"); + CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units = "cm"); + + void SetUnits(std::string units); + + virtual bool equal(CrossSection const & other) const override; + + double TotalCrossSection(dataclasses::InteractionRecord const &) const override; + double TotalCrossSection(siren::dataclasses::ParticleType primary, double energy) const; + double DifferentialCrossSection(dataclasses::InteractionRecord const &) const override; + double DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2=std::numeric_limits::quiet_NaN()) const; + double InteractionThreshold(dataclasses::InteractionRecord const &) const override; + void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr random) const override; + + std::vector GetPossibleTargets() const override; + std::vector GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const override; + std::vector GetPossiblePrimaries() const override; + std::vector GetPossibleSignatures() const override; + std::vector GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const override; + + virtual double FinalStateProbability(dataclasses::InteractionRecord const & record) const override; + + void LoadFromFile(std::string differential_filename, std::string total_filename); + void LoadFromMemory(std::vector & differential_data, std::vector & total_data); + + double GetMinimumQ2() const {return minimum_Q2_;}; + double GetTargetMass() const {return target_mass_;}; + int GetInteractionType() const {return interaction_type_;}; + + static double GetLeptonMass(siren::dataclasses::ParticleType lepton_type); + +public: + virtual std::vector DensityVariables() const override; + template + void save(Archive & archive, std::uint32_t const version) const { + if(version == 0) { + splinetable_buffer buf; + buf.size = 0; + auto result_obj = differential_cross_section_.write_fits_mem(); + buf.data = result_obj.first; + buf.size = result_obj.second; + + std::vector diff_blob; + diff_blob.resize(buf.size); + std::copy((char*)buf.data, (char*)buf.data + buf.size, &diff_blob[0]); + + archive(::cereal::make_nvp("DifferentialCrossSectionSpline", diff_blob)); + + buf.size = 0; + result_obj = total_cross_section_.write_fits_mem(); + buf.data = result_obj.first; + buf.size = result_obj.second; + + std::vector total_blob; + total_blob.resize(buf.size); + std::copy((char*)buf.data, (char*)buf.data + buf.size, &total_blob[0]); + + archive(::cereal::make_nvp("TotalCrossSectionSpline", total_blob)); + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(::cereal::make_nvp("TargetTypes", target_types_)); + archive(::cereal::make_nvp("InteractionType", interaction_type_)); + archive(::cereal::make_nvp("TargetMass", target_mass_)); + archive(::cereal::make_nvp("MinimumQ2", minimum_Q2_)); + archive(::cereal::make_nvp("Unit", unit)); + archive(cereal::virtual_base_class(this)); + } else { + throw std::runtime_error("CharmDISFromSpline only supports version <= 0!"); + } + } + template + void load(Archive & archive, std::uint32_t version) { + if(version == 0) { + std::vector differential_data; + std::vector total_data; + archive(::cereal::make_nvp("DifferentialCrossSectionSpline", differential_data)); + archive(::cereal::make_nvp("TotalCrossSectionSpline", total_data)); + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(::cereal::make_nvp("TargetTypes", target_types_)); + archive(::cereal::make_nvp("InteractionType", interaction_type_)); + archive(::cereal::make_nvp("TargetMass", target_mass_)); + archive(::cereal::make_nvp("MinimumQ2", minimum_Q2_)); + archive(::cereal::make_nvp("Unit", unit)); + archive(cereal::virtual_base_class(this)); + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + } else { + throw std::runtime_error("CharmDISFromSpline only supports version <= 0!"); + } + } +private: + void ReadParamsFromSplineTable(); + void InitializeSignatures(); +}; + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::CharmDISFromSpline, 0); +CEREAL_REGISTER_TYPE(siren::interactions::CharmDISFromSpline); +CEREAL_REGISTER_POLYMORPHIC_RELATION(siren::interactions::CrossSection, siren::interactions::CharmDISFromSpline); + +#endif // SIREN_CharmDISFromSpline_H diff --git a/projects/interactions/public/SIREN/interactions/CharmHadronization.h b/projects/interactions/public/SIREN/interactions/CharmHadronization.h new file mode 100644 index 000000000..86b7cc839 --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/CharmHadronization.h @@ -0,0 +1,82 @@ +#pragma once +#ifndef SIREN_CharmHadronization_H +#define SIREN_CharmHadronization_H + +#include // for shared_ptr +#include // for string +#include // for vector +#include // for uint32_t + +#include +#include +#include +#include +#include +#include + +#include "SIREN/interactions/Hadronization.h" // for Hadronization +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/dataclasses/InteractionSignature.h" // for InteractionSignature +#include "SIREN/dataclasses/InteractionRecord.h" // for InteractionSignature + +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/geometry/Geometry.h" +#include "SIREN/utilities/Constants.h" // for electronMass + + +namespace siren { namespace dataclasses { class InteractionRecord; } } +namespace siren { namespace dataclasses { struct InteractionSignature; } } +namespace siren { namespace utilities { class SIREN_random; } } + +namespace siren { +namespace interactions { + +class CharmHadronization : public Hadronization { +friend cereal::access; +private: + const std::set primary_types = {siren::dataclasses::Particle::ParticleType::Charm, siren::dataclasses::Particle::ParticleType::CharmBar}; + +public: + + CharmHadronization(); + + // virtual pybind11::object get_self(); + + virtual bool equal(Hadronization const & other) const override; + + void SampleFinalState(dataclasses::CrossSectionDistributionRecord & interaction, std::shared_ptr random) const override; + + virtual std::vector GetPossibleSignatures() const override; // Requires python-side implementation + virtual std::vector GetPossibleSignaturesFromParent(siren::dataclasses::Particle::ParticleType primary) const override; // Requires python-side implementation + + double FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const override; + + static double getHadronMass(siren::dataclasses::ParticleType hadron_type); + +public: + template + void save(Archive & archive, std::uint32_t const version) const { + if(version == 0) { + archive(::cereal::make_nvp("Hadronization", cereal::virtual_base_class(this))); + } else { + throw std::runtime_error("CharmHadronization only supports version <= 0!"); + } + } + template + void load_and_construct(Archive & archive, cereal::construct & construct, std::uint32_t version) { + if(version == 0) { + archive(::cereal::make_nvp("Hadronization", cereal::virtual_base_class(construct.ptr()))); + } else { + throw std::runtime_error("CharmHadronization only supports version <= 0!"); + } + } + +}; + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::CharmHadronization, 0); + + +#endif // SIREN_CharmHadronization_H diff --git a/projects/interactions/public/SIREN/interactions/CharmMesonDecay.h b/projects/interactions/public/SIREN/interactions/CharmMesonDecay.h new file mode 100644 index 000000000..bcb66b6a2 --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/CharmMesonDecay.h @@ -0,0 +1,89 @@ +#pragma once +#ifndef SIREN_CharmMesonDecay_H +#define SIREN_CharmMesonDecay_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "SIREN/dataclasses/Particle.h" +#include "SIREN/dataclasses/InteractionSignature.h" +#include "SIREN/dataclasses/InteractionRecord.h" + +#include "SIREN/interactions/Decay.h" +#include "SIREN/utilities/Interpolator.h" + + +namespace siren { +namespace interactions { + +class CharmMesonDecay : public Decay { +friend cereal::access; +private: + const std::set primary_types = {siren::dataclasses::Particle::ParticleType::D0, siren::dataclasses::Particle::ParticleType::DPlus}; + siren::utilities::Interpolator1D inverseCdf; // for dGamma +public: + CharmMesonDecay(); + CharmMesonDecay(siren::dataclasses::Particle::ParticleType primary); + virtual bool equal(Decay const & other) const override; + static double particleMass(siren::dataclasses::ParticleType particle); + double TotalDecayWidth(dataclasses::InteractionRecord const &) const override; + double TotalDecayWidth(siren::dataclasses::Particle::ParticleType primary) const override; + double TotalDecayWidthForFinalState(dataclasses::InteractionRecord const &) const override; + double DifferentialDecayWidth(dataclasses::InteractionRecord const &) const override; + double DifferentialDecayWidth(std::vector constants, double Q2, double mD, double mK) const; + void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr) const override; + std::vector GetPossibleSignatures() const override; + std::vector GetPossibleSignaturesFromParent(siren::dataclasses::Particle::ParticleType primary) const override; + virtual double FinalStateProbability(dataclasses::InteractionRecord const & record) const override; + std::vector FormFactorFromRecord(dataclasses::CrossSectionDistributionRecord const & record) const; + void computeDiffGammaCDF(std::vector constants, double mD, double mK); + +public: + virtual std::vector DensityVariables() const override; + template + void save(Archive & archive, std::uint32_t const version) const { + if(version == 0) { + archive(::cereal::make_nvp("PrimaryTypes", primary_types)); + archive(::cereal::make_nvp("Decay", cereal::virtual_base_class(this))); + } else { + throw std::runtime_error("CharmMesonDecay only supports version <= 0!"); + } + } + template + void load_and_construct(Archive & archive, cereal::construct & construct, std::uint32_t version) { + if(version == 0) { + std::set _primary_types; + + archive(::cereal::make_nvp("PrimaryTypes", _primary_types)); + construct(_primary_types); + archive(::cereal::make_nvp("Decay", cereal::virtual_base_class(construct.ptr()))); + } else { + throw std::runtime_error("CharmMesonDecay only supports version <= 0!"); + } + } + +}; + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::CharmMesonDecay, 0); +CEREAL_REGISTER_TYPE(siren::interactions::CharmMesonDecay); +CEREAL_REGISTER_POLYMORPHIC_RELATION(siren::interactions::Decay, siren::interactions::CharmMesonDecay); + +#endif // SIREN_CharmMesonDecay_H diff --git a/projects/interactions/public/SIREN/interactions/DMesonELoss.h b/projects/interactions/public/SIREN/interactions/DMesonELoss.h new file mode 100644 index 000000000..d6d5097d3 --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/DMesonELoss.h @@ -0,0 +1,96 @@ +#pragma once +#ifndef SIREN_DMesonELoss_H +#define SIREN_DMesonELoss_H + +#include // for set +#include // for map +#include +#include // for vector +#include // for uint32_t +#include // for pair +#include +#include // for runtime... + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSe... +#include "SIREN/dataclasses/InteractionSignature.h" // for Interac... +#include "SIREN/dataclasses/Particle.h" // for Particle + +namespace siren { namespace dataclasses { class InteractionRecord; } } +namespace siren { namespace utilities { class SIREN_random; } } + +namespace siren { +namespace interactions { + +class DMesonELoss : public CrossSection { +friend cereal::access; +private: + std::set primary_types_ = {siren::dataclasses::Particle::ParticleType::D0, siren::dataclasses::Particle::ParticleType::DPlus}; + std::set target_types_ = {siren::dataclasses::Particle::ParticleType::PPlus}; + +public: + DMesonELoss(); + + virtual bool equal(CrossSection const & other) const override; + + double TotalCrossSection(dataclasses::InteractionRecord const &) const override; + double TotalCrossSection(siren::dataclasses::Particle::ParticleType primary, double energy) const; + // double TotalCrossSection(siren::dataclasses::Particle::ParticleType primary, double energy, siren::dataclasses::Particle::ParticleType target) const override; + + double DifferentialCrossSection(dataclasses::InteractionRecord const &) const override; + double InteractionThreshold(dataclasses::InteractionRecord const &) const override; + void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr random) const override; + + std::vector GetPossibleTargets() const override; + std::vector GetPossibleTargetsFromPrimary(siren::dataclasses::Particle::ParticleType primary_type) const override; + std::vector GetPossiblePrimaries() const override; + std::vector GetPossibleSignatures() const override; + std::vector GetPossibleSignaturesFromParents(siren::dataclasses::Particle::ParticleType primary_type, siren::dataclasses::Particle::ParticleType target_type) const override; + + virtual double FinalStateProbability(dataclasses::InteractionRecord const & record) const override; + +public: + virtual std::vector DensityVariables() const override; + template + void save(Archive & archive, std::uint32_t const version) const { + if(version == 0) { + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(cereal::virtual_base_class(this)); + } else { + throw std::runtime_error("DMesonELoss only supports version <= 0!"); + } + } + template + void load(Archive & archive, std::uint32_t version) { + if(version == 0) { + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(cereal::virtual_base_class(this)); + InitializeSignatures(); + } else { + throw std::runtime_error("DMesonELoss only supports version <= 0!"); + } + } +private: + void InitializeSignatures(); +}; + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::DMesonELoss, 0); +CEREAL_REGISTER_TYPE(siren::interactions::DMesonELoss); +CEREAL_REGISTER_POLYMORPHIC_RELATION(siren::interactions::CrossSection, siren::interactions::DMesonELoss); + +#endif // SIREN_DMesonELoss_H diff --git a/projects/interactions/public/SIREN/interactions/Hadronization.h b/projects/interactions/public/SIREN/interactions/Hadronization.h new file mode 100644 index 000000000..6f9312769 --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/Hadronization.h @@ -0,0 +1,58 @@ +#pragma once +#ifndef SIREN_Hadronization_H +#define SIREN_Hadronization_H + +#include // for shared_ptr +#include // for string +#include // for vector +#include // for uint32_t + +#include +#include +#include +#include +#include +#include + +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/dataclasses/InteractionSignature.h" // for InteractionSignature +#include "SIREN/dataclasses/InteractionRecord.h" // for InteractionSignature + +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/geometry/Geometry.h" + +namespace siren { namespace dataclasses { class InteractionRecord; } } +namespace siren { namespace dataclasses { struct InteractionSignature; } } +namespace siren { namespace utilities { class SIREN_random; } } + +namespace siren { +namespace interactions { + +class Hadronization { +friend cereal::access; +private: +public: + Hadronization(); + virtual ~Hadronization() {}; + bool operator==(Hadronization const & other) const; + virtual bool equal(Hadronization const & other) const = 0; + + virtual void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr) const = 0; + virtual std::vector GetPossibleSignatures() const = 0; + virtual std::vector GetPossibleSignaturesFromParent(siren::dataclasses::Particle::ParticleType primary) const = 0; + virtual double FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const = 0; + + template + void save(Archive & archive, std::uint32_t const version) const {}; + template + void load(Archive & archive, std::uint32_t const version) {}; + +}; // class Hadronization + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::Hadronization, 0); + + +#endif // SIREN_Hadronization_H diff --git a/projects/interactions/public/SIREN/interactions/InteractionCollection.h b/projects/interactions/public/SIREN/interactions/InteractionCollection.h index 3a8e8f80b..287b87a59 100644 --- a/projects/interactions/public/SIREN/interactions/InteractionCollection.h +++ b/projects/interactions/public/SIREN/interactions/InteractionCollection.h @@ -21,12 +21,14 @@ #include #include #include -#include +#include #include #include "SIREN/dataclasses/Particle.h" // for Particle #include "SIREN/interactions/CrossSection.h" #include "SIREN/interactions/Decay.h" +#include "SIREN/interactions/Hadronization.h" + namespace siren { namespace dataclasses { class InteractionRecord; } } @@ -38,6 +40,8 @@ class InteractionCollection { siren::dataclasses::ParticleType primary_type; std::vector> cross_sections; std::vector> decays; + std::vector> hadronizations; + std::map>> cross_sections_by_target; std::set target_types; static const std::vector> empty; @@ -47,12 +51,21 @@ class InteractionCollection { virtual ~InteractionCollection() {}; InteractionCollection(siren::dataclasses::ParticleType primary_type, std::vector> cross_sections); InteractionCollection(siren::dataclasses::ParticleType primary_type, std::vector> decays); + InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> hadronizations); + InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> decays, std::vector> hadronizations); + InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> cross_sections, std::vector> hadronizations); + InteractionCollection(siren::dataclasses::Particle::ParticleType primary_type, std::vector> cross_sections, std::vector> decays, std::vector> hadronizations); + InteractionCollection(siren::dataclasses::ParticleType primary_type, std::vector> cross_sections, std::vector> decays); bool operator==(InteractionCollection const & other) const; std::vector> const & GetCrossSections() const {return cross_sections;} std::vector> const & GetDecays() const {return decays;} + std::vector> const & GetHadronizations() const {return hadronizations;} + bool const HasCrossSections() const {return cross_sections.size() > 0;} bool const HasDecays() const {return decays.size() > 0;} + bool const HasHadronizations() const {return hadronizations.size() > 0;} + std::vector> const & GetCrossSectionsForTarget(siren::dataclasses::ParticleType p) const; std::map>> const & GetCrossSectionsByTarget() const { return cross_sections_by_target; @@ -73,6 +86,7 @@ class InteractionCollection { archive(cereal::make_nvp("TargetTypes", target_types)); archive(cereal::make_nvp("CrossSections", cross_sections)); archive(cereal::make_nvp("Decays", decays)); + archive(cereal::make_nvp("Hadronizations", hadronizations)); } else { throw std::runtime_error("InteractionCollection only supports version <= 0!"); } @@ -85,6 +99,7 @@ class InteractionCollection { archive(cereal::make_nvp("TargetTypes", target_types)); archive(cereal::make_nvp("CrossSections", cross_sections)); archive(cereal::make_nvp("Decays", decays)); + archive(cereal::make_nvp("Hadronizations", hadronizations)); InitializeTargetTypes(); } else { throw std::runtime_error("InteractionCollection only supports version <= 0!"); diff --git a/projects/utilities/public/SIREN/utilities/Constants.h b/projects/utilities/public/SIREN/utilities/Constants.h index bf7c29190..f01cb1567 100644 --- a/projects/utilities/public/SIREN/utilities/Constants.h +++ b/projects/utilities/public/SIREN/utilities/Constants.h @@ -53,6 +53,9 @@ static const double EtaMass = 0.547862; // GeV static const double EtaPrimeMass = 0.95778; // GeV static const double RhoMass = 0.77526; // GeV static const double OmegaMass = 0.78266; // GeV +static const double D0Mass = 1.86962; // GeV +static const double DPlusMass = 1.86484; // GeV +static const double CharmMass = 1.27; // GeV // confusing units // static const double second = 1.523e15; // [eV^-1 sec^-1] @@ -103,6 +106,9 @@ static const double gravConstant = 6.6700e-11; // [m^3 kg^-1 s^-2] static const double fineStructure = 1.0/137.0; // dimensionless static const double hbarc = 197.3*(1e-9)*(1e-7)*GeV*cm; // [GeV m] +//hbar +static const double hbar = 6.58211957 * (1e-25); // GeV seconds + } // namespace Constants } // namespace utilities diff --git a/python/SIREN_Controller.py b/python/SIREN_Controller.py index 7f714b115..df7854cd4 100644 --- a/python/SIREN_Controller.py +++ b/python/SIREN_Controller.py @@ -580,7 +580,7 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, datasets["num_interactions"].append(id+1) # save injector and weighter - self.injector.SaveInjector(filename) + # self.injector.SaveInjector(filename) # weighter saving not yet supported #self.weighter.SaveWeighter(filename) From f1fb7cfb5167b4db7d10c6b928a71338a274fa32 Mon Sep 17 00:00:00 2001 From: MiaochenJin Date: Wed, 12 Jun 2024 15:24:07 -0400 Subject: [PATCH 02/11] add examples --- resources/Examples/DMesonExample/DIS_D.py | 110 +++++++++ .../Examples/DMesonExample/make_plots.py | 216 ++++++++++++++++++ .../Examples/DMesonExample/paper.mplstyle | 30 +++ .../Examples/DMesonExample/parse_output.py | 96 ++++++++ 4 files changed, 452 insertions(+) create mode 100644 resources/Examples/DMesonExample/DIS_D.py create mode 100644 resources/Examples/DMesonExample/make_plots.py create mode 100644 resources/Examples/DMesonExample/paper.mplstyle create mode 100644 resources/Examples/DMesonExample/parse_output.py diff --git a/resources/Examples/DMesonExample/DIS_D.py b/resources/Examples/DMesonExample/DIS_D.py new file mode 100644 index 000000000..3114111ca --- /dev/null +++ b/resources/Examples/DMesonExample/DIS_D.py @@ -0,0 +1,110 @@ +import os + +import siren +from siren.SIREN_Controller import SIREN_Controller + +# Number of events to inject +events_to_inject = 50000 + +# Expeirment to run +experiment = "IceCube" + +# Define the controller +controller = SIREN_Controller(events_to_inject, experiment, seed = 1) + +# Particle to inject +primary_type = siren.dataclasses.Particle.ParticleType.NuMu + +cross_section_model = "CSMSDISSplines" + +xsfiledir = siren.utilities.get_cross_section_model_path(cross_section_model) + +# Cross Section Model +target_type = siren.dataclasses.Particle.ParticleType.Nucleon + +DIS_xs = siren.interactions.CharmDISFromSpline( + os.path.join(xsfiledir, "dsdxdy_nu_CC_iso.fits"), + os.path.join(xsfiledir, "sigma_nu_CC_iso.fits"), + [primary_type], + [target_type], "m" +) + +primary_xs = siren.interactions.InteractionCollection(primary_type, [DIS_xs]) +controller.SetInteractions(primary_xs) + +# Primary distributions +primary_injection_distributions = {} +primary_physical_distributions = {} + +mass_dist = siren.distributions.PrimaryMass(0) +primary_injection_distributions["mass"] = mass_dist +primary_physical_distributions["mass"] = mass_dist + +# energy distribution +edist = siren.distributions.PowerLaw(2, 1e5, 1e10) +primary_injection_distributions["energy"] = edist +primary_physical_distributions["energy"] = edist + +# direction distribution +direction_distribution = siren.distributions.IsotropicDirection() +primary_injection_distributions["direction"] = direction_distribution +primary_physical_distributions["direction"] = direction_distribution + +# position distribution +muon_range_func = siren.distributions.LeptonDepthFunction() +position_distribution = siren.distributions.ColumnDepthPositionDistribution( + 600, 600.0, muon_range_func, set(controller.GetDetectorModelTargets()[0]) +) +primary_injection_distributions["position"] = position_distribution + +# SetProcesses +controller.SetProcesses( + primary_type, primary_injection_distributions, primary_physical_distributions +) + +def add_secondary_to_controller(controller, secondary_type, secondary_xsecs, secondary_decays = None): + if secondary_decays is not None: + secondary_collection = siren.interactions.InteractionCollection(secondary_type, [secondary_xsecs], [secondary_decays]) + else: + secondary_collection = siren.interactions.InteractionCollection(secondary_type, [secondary_xsecs]) + secondary_injection_process = siren.injection.SecondaryInjectionProcess() + secondary_physical_process = siren.injection.PhysicalProcess() + secondary_injection_process.primary_type = secondary_type + secondary_physical_process.primary_type = secondary_type + secondary_injection_process.AddSecondaryInjectionDistribution(siren.distributions.SecondaryPhysicalVertexDistribution()) + controller.secondary_injection_processes.append(secondary_injection_process) + controller.secondary_physical_processes.append(secondary_physical_process) + + return secondary_collection + +# secondary interactions +charms = siren.dataclasses.Particle.ParticleType.Charm +DPlus = siren.dataclasses.Particle.ParticleType.DPlus +D0 = siren.dataclasses.Particle.ParticleType.D0 +charm_hadronization = siren.interactions.CharmHadronization() +DPlus_decay = siren.interactions.CharmMesonDecay(primary_type = DPlus) +D0_decay = siren.interactions.CharmMesonDecay(primary_type = D0) +D_energy_loss = siren.interactions.DMesonELoss() + +secondary_charm_collection = add_secondary_to_controller(controller, charms, charm_hadronization) +secondary_DPlus_collection = add_secondary_to_controller(controller, DPlus, D_energy_loss, DPlus_decay) +secondary_D0_collection = add_secondary_to_controller(controller, D0, D_energy_loss, D0_decay) + +controller.SetInteractions(primary_xs, [secondary_charm_collection, secondary_D0_collection, secondary_DPlus_collection]) + +controller.Initialize() + +# def stop(datum, i): +# secondary_type = datum.record.signature.secondary_types[i] +# return ((secondary_type != siren.dataclasses.Particle.ParticleType.Charm) and (secondary_type != siren.dataclasses.Particle.ParticleType.DPlus)) + +def stop(datum, i): + return False + +controller.SetInjectorStoppingCondition(stop) + +events = controller.GenerateEvents() + +os.makedirs("output", exist_ok=True) + +controller.SaveEvents("output/FullSim") diff --git a/resources/Examples/DMesonExample/make_plots.py b/resources/Examples/DMesonExample/make_plots.py new file mode 100644 index 000000000..c8b246aa9 --- /dev/null +++ b/resources/Examples/DMesonExample/make_plots.py @@ -0,0 +1,216 @@ +import h5py +import numpy as np +from matplotlib import pyplot as plt +from matplotlib.colors import LogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable +from parse_output import analysis +filename = "output/FullSim.parquet" +plt.style.use('paper.mplstyle') + + +sim = analysis(filename) + + +c = 3 * 1e8 # m/s +m_D0 = 1.86962 # GeV +m_Dp = 1.86484 +t_Dp = 1040 * 1e-15 # s +t_D0 = 410 * 1e-15 +m_ice = 18.02 # g/mol +N = 6.02214 * 1e23 #mol^-1 +rho = 0.917 # g/cm^3 + + +def normalize(hist, xbins, ybins): + normed_hist = np.zeros_like(hist) + for i in range(len(xbins) - 1): + tot = 0 + for j in range(len(ybins) - 1): + tot += hist[i][j] + for j in range(len(ybins) - 1): + if tot != 0: + normed_hist[i][j] = hist[i][j] / tot + else: + normed_hist[i][j] = 0 + return normed_hist + +def analytic_decay_length(E, t, m): + return E * t / ((m/(c ** 2)) * c) + +def xsec(E): + return (np.exp(1.891 + 0.2095 * np.log10(E)) - 2.157 + 1.263 * np.log10(E)) * 1e-27 # convert to cm^2 + +def analytic_free_path(E): + return (m_ice / (rho * N * xsec(E))) / 100 # convert to m + +def plot_separation_distribution(analysis_): + D0_energies, D0_separations, Dp_energies, Dp_separations = analysis_.separation_analysis() + min_eng = 1e1 + max_eng = 1e9 + energy_bins = np.logspace(np.log10(min_eng), np.log10(max_eng), 20) + + energy_bins_centers = np.zeros((len(energy_bins) - 1,)) + for i in range(len(energy_bins_centers)): + energy_bins_centers[i] = np.sqrt(energy_bins[i] * energy_bins[i + 1]) + D0_analytic_lengths = analytic_decay_length(energy_bins_centers, t_D0, m_D0) + Dp_analytic_lengths = analytic_decay_length(energy_bins_centers, t_Dp, m_Dp) + + min_sep = 1e-3 + max_sep = 50000 + log_separation_bins = np.logspace(np.log10(min_sep), np.log10(max_sep), 20) + + X2, Y2 = np.meshgrid(energy_bins, log_separation_bins) + log_hist_D0, _, _ = np.histogram2d(D0_energies, D0_separations, bins = (energy_bins, log_separation_bins)) + log_hist_Dp, _, _ = np.histogram2d(Dp_energies, Dp_separations, bins = (energy_bins, log_separation_bins)) + log_hist_D0 = normalize(log_hist_D0, energy_bins, log_separation_bins) + log_hist_Dp = normalize(log_hist_Dp, energy_bins, log_separation_bins) + + fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) + + log_im1 = axes[0].pcolor(X2, Y2, log_hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + log_im2 = axes[1].pcolor(X2, Y2, log_hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + + # divider1 = make_axes_locatable(axes[0]) + # cax1 = divider1.append_axes('right', size='5%', pad=0.05) + divider2 = make_axes_locatable(axes[1]) + cax2 = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(log_im2, cax=cax2, orientation='vertical', alpha = 0.7) + # fig.colorbar(log_im1, cax=cax1, orientation='vertical', alpha = 0.7) + + axes[0].set_title(r"$D^0$ Separation") + axes[1].set_title(r"$D^+$ Separation") + + axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") + axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") + + axes[0].set_ylabel("Separation Length [m]") + + axes[0].set_xscale('log') + axes[1].set_xscale('log') + axes[0].set_yscale('log') + axes[1].set_yscale('log') + + axes[0].set_ylim(min_sep, max_sep) + axes[1].set_ylim(min_sep, max_sep) + axes[0].set_xlim(min_eng, max_eng) + axes[1].set_xlim(min_eng, max_eng) + + # also plot the analytic lines + axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) + axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#FEF3E8', alpha = 0.7) + + legend = axes[1].legend(loc = 'upper left') + for text in legend.get_texts(): + text.set_color('#FEF3E8') + + fig.savefig("./plots/Separation_Length_Distribution", bbox_inches = 'tight') + +# plot_separation_distribution(sim) + +def plot_2d_energy_loss(analysis_): + E_D0, E_Dp, n_D0, n_Dp = analysis_.energy_loss_analysis_2d() + energy_bins = np.logspace(np.log10(min(min(E_D0), min(E_Dp))), np.log10(max(max(E_D0), max(E_Dp))), 20) + num_bins = np.linspace(-0.01, 7.99, 9) + X, Y = np.meshgrid(energy_bins, num_bins) + hist_D0, _, _ = np.histogram2d(E_D0, n_D0, bins = (energy_bins, num_bins)) + hist_Dp, _, _ = np.histogram2d(E_Dp, n_Dp, bins = (energy_bins, num_bins)) + + hist_D0 = normalize(hist_D0, energy_bins, num_bins) + hist_Dp = normalize(hist_Dp, energy_bins, num_bins) + + fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) + im1 = axes[0].pcolor(X, Y, hist_D0.T, cmap="plasma", alpha = 0.7) + im2 = axes[1].pcolor(X, Y, hist_Dp.T, cmap="plasma", alpha = 0.7) + divider2 = make_axes_locatable(axes[1]) + cax2 = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax2, orientation='vertical', alpha = 0.7) + axes[0].axvline(x = 53 * 1e3, color = '#FEF3E8', alpha = 0.7, label = r"$d_{D^0} = l_{D^0}$") + axes[1].axvline(x = 22 * 1e3, color = '#FEF3E8', alpha = 0.7, label = r"$d_{D^+} = l_{D^+}$") + + axes[0].set_title(r"$D^0-p$ Collision") + axes[1].set_title(r"$D^+-p$ Collision") + + axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") + axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") + + axes[0].set_ylim(0, 8) + axes[1].set_ylim(0, 8) + + axes[0].set_ylabel(r"$n_{\textrm{Elastic Collision}}$") + axes[0].set_xscale('log') + axes[1].set_xscale('log') + legend0 = axes[0].legend() + legend1 = axes[1].legend() + for text in legend0.get_texts(): + text.set_color('#FEF3E8') + for text in legend1.get_texts(): + text.set_color('#FEF3E8') + + + fig.savefig("./plots/Energy_loss_2d_Distribution", bbox_inches = 'tight') + +plot_2d_energy_loss(sim) +exit(0) + +def plot_free_path_distribution(): + D0_E_list, D0_free_path_list, Dp_E_list, Dp_free_path_list = analysis_.free_path_analysis() + energy_bins = np.logspace(1.5, 9, 20) + distance_bins = np.logspace(-3, np.log10(5000), 20) + X, Y = np.meshgrid(energy_bins, distance_bins) + hist_D0, _, _ = np.histogram2d(D0_E_list, D0_free_path_list, bins = (energy_bins, distance_bins)) + hist_Dp, _, _ = np.histogram2d(Dp_E_list, Dp_free_path_list, bins = (energy_bins, distance_bins)) + + hist_D0 = normalize(hist_D0, energy_bins, distance_bins) + hist_Dp = normalize(hist_Dp, energy_bins, distance_bins) + + energy_bins_centers = np.zeros((len(energy_bins) - 1,)) + for i in range(len(energy_bins_centers)): + energy_bins_centers[i] = np.sqrt(energy_bins[i] * energy_bins[i + 1]) + D0_analytic_lengths = analytic_free_path(energy_bins_centers) + Dp_analytic_lengths = analytic_free_path(energy_bins_centers) + + + fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) + im1 = axes[0].pcolor(X, Y, hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + im2 = axes[1].pcolor(X, Y, hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + divider2 = make_axes_locatable(axes[1]) + cax2 = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax2, orientation='vertical', alpha = 0.7) + + axes[0].set_title(r"$D^0-p$ Free Path") + axes[1].set_title(r"$D^+-p$ Free Path") + + axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") + axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") + + axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) + axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$l = \frac{m_{\textrm{ice}}}{\rho N_A \sigma(E)}$", color = '#FEF3E8', alpha = 0.7) + + # also plot the decay lengths to explain low energy increase + D0_decay_analytic_lengths = analytic_decay_length(energy_bins_centers, t_D0, m_D0) + Dp_decay_analytic_lengths = analytic_decay_length(energy_bins_centers, t_Dp, m_Dp) + + axes[0].plot(energy_bins_centers, D0_decay_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#A597B6', alpha = 0.7) + axes[1].plot(energy_bins_centers, Dp_decay_analytic_lengths, color = '#A597B6', alpha = 0.7) + + axes[0].set_ylabel(r"$l_{\textrm{Free}}$") + axes[0].set_xscale('log') + axes[1].set_xscale('log') + axes[0].set_yscale('log') + axes[1].set_yscale('log') + + axes[0].set_ylim(1e-3, 5000) + axes[1].set_ylim(1e-3, 5000) + + legend0 = axes[0].legend(loc = 'upper left') + for text in legend0.get_texts(): + text.set_color('#A597B6') + + legend1 = axes[1].legend(loc = 'upper left') + for text in legend1.get_texts(): + text.set_color('#FEF3E8') + + fig.savefig("./plots/Free_Path_Distribution", bbox_inches = 'tight') + return + +plot_free_path_distribution() \ No newline at end of file diff --git a/resources/Examples/DMesonExample/paper.mplstyle b/resources/Examples/DMesonExample/paper.mplstyle new file mode 100644 index 000000000..77b5af0b3 --- /dev/null +++ b/resources/Examples/DMesonExample/paper.mplstyle @@ -0,0 +1,30 @@ +figure.figsize : 5, 5 # figure size in inches +savefig.dpi : 600 # figure dots per inch + +font.size: 20 +font.family: serif +font.serif: Computer Modern, Latin Modern Roman, Bitstream Vera Serif +text.usetex: True + +axes.prop_cycle: cycler('color', ['29A2C6','FF6D31','73B66B','9467BD','FFCB18', 'EF597B']) +axes.grid: False + +image.cmap : plasma + +lines.linewidth: 2 +patch.linewidth: 2 +xtick.labelsize: large +ytick.labelsize: large +xtick.minor.visible: True # visibility of minor ticks on x-axis +ytick.minor.visible: True # visibility of minor ticks on y-axis +xtick.major.size: 6 # major tick size in points +xtick.minor.size: 3 # minor tick size in points +ytick.major.size: 6 # major tick size in points +ytick.minor.size: 3 # minor tick size in points +xtick.major.width: 1 +xtick.minor.width: 1 +ytick.major.width: 1 +ytick.minor.width: 1 + +legend.frameon: False +legend.fontsize: 16 diff --git a/resources/Examples/DMesonExample/parse_output.py b/resources/Examples/DMesonExample/parse_output.py new file mode 100644 index 000000000..32a316953 --- /dev/null +++ b/resources/Examples/DMesonExample/parse_output.py @@ -0,0 +1,96 @@ +import pandas as pd +import h5py +import numpy as np +from matplotlib import pyplot as plt +from matplotlib import pyplot as plt +from matplotlib.colors import LogNorm +from mpl_toolkits.axes_grid1 import make_axes_locatable + +filename = "output/FullSim.parquet" + +def normalize(hist, xbins, ybins): + normed_hist = np.zeros_like(hist) + for i in range(len(xbins) - 1): + tot = 0 + for j in range(len(ybins) - 1): + tot += hist[i][j] + for j in range(len(ybins) - 1): + if tot != 0: + normed_hist[i][j] = hist[i][j] / tot + else: + normed_hist[i][j] = 0 + return normed_hist + +def mass(p): + return np.sqrt(p[0] ** 2 - (p[1] ** 2 + p[2] ** 2 + p[3] ** 2)) + +def decay_length(v1, v2): + return np.sqrt((v1[0] - v2[0]) ** 2 + (v1[1] - v2[1]) ** 2 + (v1[2] - v2[2]) ** 2) + +def extract_Q2(pe, pnu): + return (pe[0] + pnu[0]) ** 2 - ((pe[1] + pnu[1]) ** 2 + (pe[2] + pnu[2]) ** 2 + (pe[3] + pnu[3]) ** 2) + +def add_to_dict(list, dictionary): + for item in list: + if item in dictionary: + dictionary[item] += 1 + else: dictionary[item] = 1 + +class event: + def __init__(self, row) -> None: + self.row = row + self.num_interaction = row["num_interactions"] + + def DTYPE(self) -> int: + # type of charmed meson is the second one + return int(self.row["secondary_types"][1][1]) + + def D_DECAY_LEN(self) -> float: + # the first vertex is 0th, the decay vertex is the last one + return decay_length(self.row["vertex"][0], self.row["vertex"][-1]) + +class analysis: + def __init__(self, f) -> None: + self.df = pd.read_parquet(f) + self.num_events = len(self.df["event_weight"]) + # print("Initializing... There are {} events".format(f.attrs["num_events"])) + self.num_interactions = {} + self.secondary_types = {} + + def separation_analysis(self): + D0_energies = [] + D0_separations = [] + Dp_energies = [] + Dp_separations = [] + for i in range(self.num_events): + cur_event = event(self.df.iloc[i]) + print("{}/{}".format(i, self.num_events), end = '\r') + # check if current event is D0 or D+ + if cur_event.DTYPE() == 421: # This is D0 + # extract the vertex separations + D0_separations.append(cur_event.D_DECAY_LEN()) + D0_energies.append(cur_event.row["primary_momentum"][2][0]) + elif cur_event.DTYPE() == 411: # This is D+ + # extract the vertex separations + Dp_separations.append(cur_event.D_DECAY_LEN()) + Dp_energies.append(cur_event.row["primary_momentum"][2][0]) + return D0_energies, D0_separations, Dp_energies, Dp_separations + + def energy_loss_analysis_2d(self): + E_D0 = [] + E_Dp = [] + n_D0 = [] + n_Dp = [] + for i in range(self.num_events): + cur_event = event(self.df.iloc[i]) + print("{}/{}".format(i, self.num_events), end = '\r') + if cur_event.DTYPE() == 421: # This is D0 + # extract the vertex separations + n_D0.append(cur_event.row["num_interactions"] - 3) + E_D0.append(cur_event.row["primary_momentum"][2][0]) + elif cur_event.DTYPE() == 411: # This is D+ + # extract the vertex separations + n_Dp.append(cur_event.row["num_interactions"] - 3) + E_Dp.append(cur_event.row["primary_momentum"][2][0]) + return E_D0, E_Dp, n_D0, n_Dp + From 3ce2b8ff13e173bd1f8d8e6742722b215ce51b59 Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Thu, 22 Aug 2024 15:16:01 -0400 Subject: [PATCH 03/11] preparing for PR --- projects/detector/private/DetectorModel.cxx | 11 +- .../SecondaryBoundedVertexDistribution.cxx | 4 +- .../SecondaryPhysicalVertexDistribution.cxx | 12 +- .../SecondaryVertexPositionDistribution.cxx | 2 +- projects/injection/private/Injector.cxx | 119 +++++++++++++---- .../private/CharmDISFromSpline.cxx | 87 +++++++++++-- .../private/CharmHadronization.cxx | 120 +++++++++++++----- .../SIREN/interactions/CharmHadronization.h | 15 ++- python/SIREN_Controller.py | 11 +- resources/Examples/DMesonExample/DIS_D.py | 2 +- .../Examples/DMesonExample/make_plots.py | 118 +++++++++++------ .../Examples/DMesonExample/parse_output.py | 16 ++- 12 files changed, 391 insertions(+), 126 deletions(-) diff --git a/projects/detector/private/DetectorModel.cxx b/projects/detector/private/DetectorModel.cxx index 3bdc63051..e8cbcffb6 100644 --- a/projects/detector/private/DetectorModel.cxx +++ b/projects/detector/private/DetectorModel.cxx @@ -683,7 +683,8 @@ double DetectorModel::GetInteractionDensity(Geometry::IntersectionList const & i std::vector const & total_cross_sections, double const & total_decay_length) const { Vector3D direction = p0 - intersections.position; - if(direction.magnitude() == 0) { + // std::cout << "direction: " << direction.magnitude() << std::endl; + if(direction.magnitude() == 0 || direction.magnitude() <= 1e-05) { direction = intersections.direction; } else { direction.normalize(); @@ -978,18 +979,26 @@ double DetectorModel::GetInteractionDepthInCGS(Geometry::IntersectionList const std::vector const & targets, std::vector const & total_cross_sections, double const & total_decay_length) const { + + // std::cout << p0 << " " << p1 << " " << intersections.direction << std::endl; if(p0 == p1) { return 0.0; } Vector3D direction = p1 - p0; double distance = direction.magnitude(); + // std::cout << "distance is " << distance << std::endl; if(distance == 0.0) { return 0.0; } + if(direction.magnitude() <= 1e-05) { + // std::cout << "triggered" << std::endl; + return 0.0; + } direction.normalize(); double dot = intersections.direction * direction; assert(std::abs(1.0 - std::abs(dot)) < 1e-6); + // std::cout << "not at this point" << std::endl; double offset = (intersections.position - p0) * direction; if(dot < 0) { diff --git a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx index ca3c407cd..a86fffb1c 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx @@ -53,7 +53,7 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - std::cout << "in sample bounded vertex" << std::endl; + // std::cout << "in sample bounded vertex" << std::endl; siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; @@ -123,7 +123,7 @@ void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { - std::cout << "in sample bounded vertex gen prob" << std::endl; + // std::cout << "in sample bounded vertex gen prob" << std::endl; siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); diff --git a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx index f9fb53b41..d49a42f03 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx @@ -51,20 +51,20 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - std::cout << "in sample physical vertex" << std::endl; + // // std::cout << "in sample physical vertex" << std::endl; siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; - // std::cout << "in sample physical vertex-1" << std::endl; + // // std::cout << "in sample physical vertex-1" << std::endl; siren::math::Vector3D endcap_0 = pos; // treat hadronizations differntely if (interactions->HasHadronizations()) { - std::cout << "in sample physical vertex-hadron" << std::endl; + // std::cout << "in sample physical vertex-hadron" << std::endl; record.SetLength(0); return; } - // std::cout << "in sample physical vertex-shouldnm't be here" << std::endl; + // // std::cout << "in sample physical vertex-shouldnm't be here" << std::endl; siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), std::numeric_limits::infinity()); @@ -86,7 +86,7 @@ void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { - std::cout << "in sample physical vertex gen prob" << std::endl; + // std::cout << "in sample physical vertex gen prob" << std::endl; siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); diff --git a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx index 18e9c0b69..70af06641 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx @@ -15,7 +15,7 @@ namespace distributions { // class SecondaryVertexPositionDistribution : InjectionDistribution //--------------- void SecondaryVertexPositionDistribution::Sample(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - // std::cout << "sampling vertex" << std::endl; + // // // // // std::cout << "sampling vertex" << std::endl; SampleVertex(rand, detector_model, interactions, record); } diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index 1725b3f64..40069d4ab 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -188,7 +188,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record double fake_prob; // if contains hadronization, then perform only hadronization if (interactions->HasHadronizations()) { - std::cout << "saw hadronization" << std::endl; + // std::cout << "saw hadronization" << std::endl; double total_frag_prob = 0; std::vector frag_probs; for(auto const & hadronization : interactions->GetHadronizations() ) { @@ -209,7 +209,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } - std::cout << "Hadronization finished signatures" << std::endl; + // std::cout << "Hadronization finished signatures" << std::endl; // now choose the specific charmed hadron to fragment into @@ -225,25 +225,33 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record matching_hadronizations[index]->SampleFinalState(xsec_record, random); xsec_record.Finalize(record); - std::cout << "hadronization done!" << std::endl; + // std::cout << "hadronization done!" << std::endl; } else { if (interactions->HasCrossSections()) { - std::cout << "saw xsec" << std::endl; + // std::cout << "saw xsec" << std::endl; for(auto const target : available_targets) { if(possible_targets.find(target) != possible_targets.end()) { + // std::cout << "saw xsec: in first for loop" << std::endl; // Get target density double target_density = detector_model->GetParticleDensity(intersections, DetectorPosition(interaction_vertex), target); // Loop over cross sections that have this target std::vector> const & target_cross_sections = interactions->GetCrossSectionsForTarget(target); for(auto const & cross_section : target_cross_sections) { + // std::cout << "saw xsec: in second for loop" << std::endl; // Loop over cross section signatures with the same target std::vector signatures = cross_section->GetPossibleSignaturesFromParents(record.signature.primary_type, target); for(auto const & signature : signatures) { + // std::cout << "saw xsec: in third for loop" << std::endl; + fake_record.signature = signature; fake_record.target_mass = detector_model->GetTargetMass(target); // Add total cross section times density to the total prob + // std::cout << "about to sample total xsec" << std::endl; + fake_prob = target_density * cross_section->TotalCrossSection(fake_record); + // std::cout << "finished sampling total xsec" << std::endl; + total_prob += fake_prob; xsec_prob += fake_prob; // Add total prob to probs @@ -258,7 +266,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } if (interactions->HasDecays()) { - std::cout << "saw decay" << std::endl; + // std::cout << "saw decay" << std::endl; for(auto const & decay : interactions->GetDecays() ) { for(auto const & signature : decay->GetPossibleSignaturesFromParent(record.signature.primary_type)) { fake_record.signature = signature; @@ -274,7 +282,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } } - + // std::cout << "continuing...." << std::endl; if(total_prob == 0) throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); // Throw a random number @@ -285,6 +293,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record record.signature.target_type = matching_targets[index]; record.signature = matching_signatures[index]; double selected_prob = 0.0; + // std::cout << "finished initializing stuff" << std::endl; for(unsigned int i=0; i 0 ? probs[i] - probs[i - 1] : probs[i]); @@ -294,9 +303,14 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); record.target_mass = detector_model->GetTargetMass(record.signature.target_type); siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); + // std::cout << "finished sampling from primary process" << std::endl; if(r <= xsec_prob) { + // std::cout << "about to sample primary process final state" << std::endl; + matching_cross_sections[index]->SampleFinalState(xsec_record, random); } else { + // std::cout << "about to sample primary process final state" << std::endl; + matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); } xsec_record.Finalize(record); @@ -307,30 +321,41 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record // // Modifies an InteractionRecord with the new event siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::dataclasses::SecondaryDistributionRecord & secondary_record) const { - std::cout << "sampling secondary" << std::endl; - std::cout << "secondary record type is " << secondary_record.type << " " << secondary_record.id << std::endl; + // std::cout << "sampling secondary" << std::endl; + // std::cout << "secondary record type is " << secondary_record.type << " " << secondary_record.id << std::endl; std::shared_ptr secondary_process = secondary_process_map.at(secondary_record.type); std::shared_ptr secondary_interactions = secondary_process->GetInteractions(); std::vector> secondary_distributions = secondary_process->GetSecondaryInjectionDistributions(); - size_t max_tries = 10; + size_t max_tries = 1000; size_t tries = 0; size_t failed_tries = 0; while(true) { - // std::cout << "gotcha" << std::endl; + // // std::cout << "gotcha" << std::endl; + // for(auto & distribution : secondary_distributions) { + // // std::cout << "sample distribution" << std::endl; + // distribution->Sample(random, detector_model, secondary_process->GetInteractions(), secondary_record); + // } + // siren::dataclasses::InteractionRecord record; + // secondary_record.Finalize(record); + // // // std::cout << "sample distribution" << std::endl; + + // SampleCrossSection(record, secondary_interactions); + // return record; + try { for(auto & distribution : secondary_distributions) { - std::cout << "sample distribution" << std::endl; + // std::cout << "sample distribution" << std::endl; distribution->Sample(random, detector_model, secondary_process->GetInteractions(), secondary_record); } siren::dataclasses::InteractionRecord record; secondary_record.Finalize(record); - // std::cout << "sample distribution" << std::endl; + // // std::cout << "sample distribution" << std::endl; SampleCrossSection(record, secondary_interactions); return record; } catch(siren::utilities::InjectionFailure const & e) { - std::cout << "caught error" << std::endl; + // std::cout << "caught error" << std::endl; failed_tries += 1; if(failed_tries > max_tries) { @@ -362,18 +387,19 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { distribution->Sample(random, detector_model, primary_process->GetInteractions(), primary_record); } primary_record.Finalize(record); + // std::cout << "primary record fixed" << std:: endl; SampleCrossSection(record); break; } catch(siren::utilities::InjectionFailure const & e) { failed_tries += 1; - if(tries > max_tries) { + if(failed_tries > max_tries) { throw(siren::utilities::InjectionFailure("Failed to generate primary process!")); break; } continue; } if(tries > max_tries) { - throw(siren::utilities::InjectionFailure("Failed to generate primary process!")); + throw(siren::utilities::InjectionFailure("Failed to generate primary process!!")); break; } } @@ -381,12 +407,12 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { std::shared_ptr parent = tree.add_entry(record); // Secondary Processes - // std::cout << "Sampling primary interactions" << std::endl; + // std::cout << "Sampling primary interactions 2" << std::endl; std::deque, std::shared_ptr>> secondaries; std::function)> add_secondaries = [&](std::shared_ptr parent) { for(size_t i=0; irecord.signature.secondary_types.size(); ++i) { - // std::cout << "for loop 1" << std::endl; + // // std::cout << "for loop 1" << std::endl; siren::dataclasses::ParticleType const & type = parent->record.signature.secondary_types[i]; std::map>::iterator it = secondary_process_map.find(type); if(it == secondary_process_map.end()) { @@ -405,24 +431,67 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { add_secondaries(parent); // std::cout << "num secondaries: " << secondaries.size() << std::endl; while(secondaries.size() > 0) { - // std::cout << "while loop 1" << std::endl; + // // std::cout << "while loop 1" << std::endl; for(int i = secondaries.size() - 1; i >= 0; --i) { - // std::cout << "for loop 2" << std::endl; + // // std::cout << "for loop 2" << std::endl; std::shared_ptr parent = std::get<0>(secondaries[i]); std::shared_ptr secondary_dist = std::get<1>(secondaries[i]); - // std::cout << "for loop 2-1" << std::endl; + // // std::cout << "for loop 2-1" << std::endl; secondaries.erase(secondaries.begin() + i); - siren::dataclasses::InteractionRecord secondary_record = SampleSecondaryProcess(*secondary_dist); - std::shared_ptr secondary_datum = tree.add_entry(secondary_record, parent); - // std::cout << "for loop 2-2" << std::endl; + // std::cout << "Primary Type: " << secondary_dist->record.signature.primary_type << std::endl; + // std::cout << "Secondary Types: "; + // for (const auto& type : secondary_dist->record.signature.secondary_types) { + // std::cout << type << " "; + // } + // std::cout << std::endl; - add_secondaries(secondary_datum); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // this try-catch clock is to debug the secondary process LE no available interaction error + try{ + siren::dataclasses::InteractionRecord secondary_record = SampleSecondaryProcess(*secondary_dist); + std::shared_ptr secondary_datum = tree.add_entry(secondary_record, parent); + add_secondaries(secondary_datum); + } catch (const std::exception& e) { + std::cerr << "Error occurred: " << e.what() << std::endl; + + // Print the primary type and secondary types for debugging + std::cerr << "Primary Type: " << secondary_dist->record.signature.primary_type << std::endl; + std::cerr << "Secondary Types: "; + for (const auto& type : secondary_dist->record.signature.secondary_types) { + std::cerr << type << " "; + } + std::cerr << std::endl; + + // Print the primary momentum + std::cerr << "Primary Momentum: "; + for (double component : secondary_dist->record.primary_momentum) { + std::cerr << component << " "; + } + std::cerr << std::endl; + + // Print the secondary IDs + std::cerr << "Secondary IDs: "; + for (const auto& id : secondary_dist->record.secondary_ids) { + std::cerr << id << " "; + } + std::cerr << std::endl; + throw; + } catch (...) { + std::cerr << "Unknown exception caught!" << std::endl; + throw; + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + } - // std::cout << "while loop 1-2" << std::endl; + // // std::cout << "while loop 1-2" << std::endl; } injected_events += 1; diff --git a/projects/interactions/private/CharmDISFromSpline.cxx b/projects/interactions/private/CharmDISFromSpline.cxx index 45f15b98e..d24740568 100644 --- a/projects/interactions/private/CharmDISFromSpline.cxx +++ b/projects/interactions/private/CharmDISFromSpline.cxx @@ -49,7 +49,17 @@ bool kinematicallyAllowed(double x, double y, double E, double M, double m) { double term = 1 - ((m * m) / (2 * M * E * x)); //the numerator of b (or b*d) double bd = sqrt(term * term - ((m * m) / (E * E))); - return (ad - bd) <= d * y and d * y <= (ad + bd); //Eq. 7 + + // also try the D-Meson Mass? + double s = 2 * M * E; + double Q2 = s * x * y; + double Mc = siren::utilities::Constants::D0Mass; + // if ((Q2 / (1 - x) + pow(M, 2) < pow(M + Mc, 2))) { + // std::cout << "SIREN D Meson constraint is trigged!" << std::endl; + // } + return ((ad - bd) <= d * y and d * y <= (ad + bd)) && (Q2 / (1 - x) + pow(M, 2) >= pow(M + Mc, 2)); //Eq. 7 + // return ((ad - bd) <= d * y and d * y <= (ad + bd)); //Eq. 7 + } } @@ -187,6 +197,8 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { // returns true if successfully read minimum Q2 bool q2_good = differential_cross_section_.read_key("Q2MIN", minimum_Q2_); + // std::cout << "reading results: " << mass_good << " " << int_good << " " << q2_good << std::endl; + if(!int_good) { // assume DIS to preserve compatability with previous versions interaction_type_ = 1; @@ -209,16 +221,20 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { } } else { + // // std::cout << "mass and int both not good" << std::endl; if(differential_cross_section_.get_ndim() == 3) { - target_mass_ = (siren::dataclasses::isLepton(siren::dataclasses::ParticleType::PPlus)+ - siren::dataclasses::isLepton(siren::dataclasses::ParticleType::Neutron))/2; + // std::cout << "dim is 3" << std::endl; + target_mass_ = siren::utilities::Constants::isoscalarMass; + } else if(differential_cross_section_.get_ndim() == 2) { - target_mass_ = siren::dataclasses::isLepton(siren::dataclasses::ParticleType::EMinus); + target_mass_ = siren::utilities::Constants::electronMass; } else { throw std::runtime_error("Logic error. Spline dimensionality is not 2, or 3!"); } } } + + // std::cout << "target mass is " << target_mass_ << std::endl; } void CharmDISFromSpline::InitializeSignatures() { @@ -277,6 +293,7 @@ double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord cons rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); double primary_energy; primary_energy = interaction.primary_momentum[0]; + // std::cout << "primary energy is " << primary_energy << std::endl; // if we are below threshold, return 0 if(primary_energy < InteractionThreshold(interaction)) return 0; @@ -287,6 +304,7 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr if(not primary_types_.count(primary_type)) { throw std::runtime_error("Supplied primary not supported by cross section!"); } + // std::cout << "now in real sample total xsec func" << std::endl; double log_energy = log10(primary_energy); if(log_energy < total_cross_section_.lower_extent(0) @@ -298,7 +316,10 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr } int center; + // std::cout << "maybe problem is here?" << std::endl; total_cross_section_.searchcenters(&log_energy, ¢er); + // std::cout << "maybe problem is here??" << std::endl; + double log_xs = total_cross_section_.ndsplineeval(&log_energy, ¢er, 0); return unit * std::pow(10.0, log_xs); @@ -323,12 +344,18 @@ double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionReco double Q2 = -q.dot(q); double y = 1.0 - p2.dot(p3) / p2.dot(p1); double x = Q2 / (2.0 * p2.dot(q)); + // apply slow scaling here + // double slow_scale = 1 + pow(siren::utilities::Constants::CharmMass, 2) / pow(Q2, 2); + // double xi = x * slow_scale; double lepton_mass = GetLeptonMass(interaction.signature.secondary_types[lepton_index]); + // return DifferentialCrossSection(primary_energy, xi, y, lepton_mass, Q2); return DifferentialCrossSection(primary_energy, x, y, lepton_mass, Q2); + } double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { + bool check_criteria = false; // this is used to gauge kinematic constraint in xsec and SIREN impementations, will eventually be deleted double log_energy = log10(energy); // check preconditions if(log_energy < differential_cross_section_.lower_extent(0) @@ -348,10 +375,11 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou if(Q2 < minimum_Q2_) // cross section not calculated, assumed to be zero return 0; - // cross section should be zero, but this check is missing from the original - // CSMS calculation, so we must add it here - if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) - return 0; + if (!check_criteria) { + if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + return 0; + } + } std::array coordinates{{log_energy, log10(x), log10(y)}}; std::array centers; @@ -359,6 +387,33 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou return 0; double result = pow(10., differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0)); assert(result >= 0); + + if (check_criteria) { + // this is a check of kinematic constraint implementation + if (result == 0) { + if(kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + std::cout << "xsec gives 0 but kinematically allowed!" << std::endl; + } + } + + if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + // check if this is due to charm production constraint + double M = target_mass_; + double E = energy; + double s = 2 * M * E; + double Q2 = s * x * y; + double Mc = siren::utilities::Constants::D0Mass; + if ((Q2 / (1 - x) + pow(M, 2) < pow(M + Mc, 2))) { // if so check result + if (result != 0) { + std::cout << "SIREN constraint not passed but xsec does not give 0!" << std::endl; + } + } + return 0; + } + } + + + return unit * result; } @@ -373,7 +428,7 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR if (differential_cross_section_.get_ndim() != 3) { throw std::runtime_error("I expected 3 dimensions in the cross section spline, but got " + std::to_string(differential_cross_section_.get_ndim()) +". Maybe your fits file doesn't have the right 'INTERACTION' key?"); } - + // std::cout << "in sample final state of charm DIS from spline" << std::endl; rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); @@ -395,10 +450,11 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR double m = GetLeptonMass(record.signature.secondary_types[lepton_index]); double m1 = record.primary_mass; + // std::cout << "getting mass of primary: " << m1 << std::endl; double m3 = m; double E1_lab = p1_lab.e(); double E2_lab = p2_lab.e(); - + // std::cout << "getting energy of primary: " << E1_lab << std::endl; // The out-going particle always gets at least enough energy for its rest mass double yMax = 1 - m / primary_energy; @@ -437,10 +493,17 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR do { // rejection sample a point which is kinematically allowed by calculation limits double trialQ; + double trials = 0; + // std::cout << "do loop 1" << std::endl; do { + // std::cout << "do loop 2" << std::endl; + if (trials >= 100) throw std::runtime_error("too much trials"); + trials += 1; kin_vars[1] = random->Uniform(logXMin,0); kin_vars[2] = random->Uniform(logYMin,logYMax); trialQ = (2 * E1_lab * E2_lab) * pow(10., kin_vars[1] + kin_vars[2]); + // std::cout << kin_vars[1] << " " << kin_vars[2] << " " << trialQ << " " << minimum_Q2_ << std::endl; + // std::cout << primary_energy << " " << target_mass_ << " " << m << std::endl; } while(trialQ(Py_None); @@ -37,6 +41,72 @@ bool CharmHadronization::equal(Hadronization const & other) const { return primary_types == x->primary_types; } +void CharmHadronization::normalize_pdf() { + if (fragmentation_integral == 0){ + std::function integrand = [&] (double x) -> double { + return (0.8 / x ) / (std::pow(1 - (1 / x) - (0.2 / (1 - x)), 2)); + }; + fragmentation_integral = siren::utilities::rombergIntegrate(integrand, 0.001, 0.999); + } else { + std::cout << "Something is wrong... you already computed the normalization" << std::endl; + return; + } +} + +double CharmHadronization::sample_pdf(double x) const { + return (0.8 / x ) / (std::pow(1 - (1 / x) - (0.2 / (1 - x)), 2)) / fragmentation_integral; +} + +void CharmHadronization::compute_cdf() { + // first set the z nodes + std::vector zspline; + for (int i = 0; i < 100; ++i) { + zspline.push_back(0.01 + i * (0.99-0.01) / 100 ); + } + + // declare the cdf vectors + std::vector cdf_vector; + std::vector cdf_z_nodes; + std::vector pdf_vector; + + cdf_z_nodes.push_back(0); + cdf_vector.push_back(0); + pdf_vector.push_back(0); + + // compute the spline table + for (int i = 0; i < zspline.size(); ++i) { + if (i == 0) { + double cur_z = zspline[i]; + double cur_pdf = sample_pdf(cur_z); + double area = cur_z * cur_pdf * 0.5; + pdf_vector.push_back(cur_pdf); + cdf_vector.push_back(area); + cdf_z_nodes.push_back(cur_z); + continue; + } + double cur_z = zspline[i]; + double cur_pdf = sample_pdf(cur_z); + double area = 0.5 * (pdf_vector[i - 1] + cur_pdf) * (zspline[i] - zspline[i - 1]); + pdf_vector.push_back(cur_pdf); + cdf_z_nodes.push_back(cur_z); + cdf_vector.push_back(area + cdf_vector.back()); + } + + cdf_z_nodes.push_back(1); + cdf_vector.push_back(1); + pdf_vector.push_back(0); + + + // set the spline table + siren::utilities::TableData1D inverse_cdf_data; + inverse_cdf_data.x = cdf_vector; + inverse_cdf_data.f = cdf_z_nodes; + + inverseCdfTable = siren::utilities::Interpolator1D(inverse_cdf_data); + + return; +} + double CharmHadronization::getHadronMass(siren::dataclasses::ParticleType hadron_type) { switch(hadron_type){ case siren::dataclasses::ParticleType::D0: @@ -96,16 +166,27 @@ void CharmHadronization::SampleFinalState(dataclasses::CrossSectionDistributionR rk::P4 pc(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); double p3c = std::sqrt(std::pow(interaction.primary_momentum[1], 2) + std::pow(interaction.primary_momentum[2], 2) + std::pow(interaction.primary_momentum[3], 2)); double Ec = pc.e(); //energy of primary charm - // std::cout << "hadronization sample final state" << std::endl; - // double peterson_distribution(double z) { - // return 0.8 / x * std::pow((1 - 1 / x - 0.2 / (1 - x)), 2) - // } + double mCH = getHadronMass(interaction.signature.secondary_types[1]); // obtain charmed hadron mass - double z = 0.6; // replace by actual sampling: inverse CDF - double ECH = z * Ec; + bool accept; + double randValue; + double z; + double ECH; + + // sample again if this eenrgy is not kinematically allowed + do { + randValue = random->Uniform(0,1); + z = inverseCdfTable(randValue); + ECH = z * Ec; + if (std::pow(ECH, 2) - std::pow(mCH, 2) <= 0) { + accept = false; + } else { + accept = true; + } + double new_debug = std::pow(ECH, 2) - std::pow(mCH, 2); + } while (!accept); // is it ok to compute everything in lab frame? - double mCH = getHadronMass(interaction.signature.secondary_types[1]); // obtain charmed hadron mass double p3CH = std::sqrt(std::pow(ECH, 2) - std::pow(mCH, 2)); //obtain charmed hadron 3-momentum double rCH = p3CH/p3c; // ratio of momentum carried away by the charmed hadron, assume collinearity rk::P4 p4CH(geom3::Vector3(rCH * interaction.primary_momentum[1], rCH * interaction.primary_momentum[2], rCH * interaction.primary_momentum[3]), mCH); @@ -115,9 +196,6 @@ void CharmHadronization::SampleFinalState(dataclasses::CrossSectionDistributionR double rX = p3X/p3c; // assume collinear rk::P4 p4X(geom3::Vector3(rX * interaction.primary_momentum[1], rX * interaction.primary_momentum[2], rX * interaction.primary_momentum[3]), 0); - - // update interaction parameters: to be added here later - // new implementation of updateing outgoing particles std::vector & secondaries = interaction.GetSecondaryParticleRecords(); siren::dataclasses::SecondaryParticleRecord & hadronic_vertex = secondaries[0]; @@ -131,26 +209,6 @@ void CharmHadronization::SampleFinalState(dataclasses::CrossSectionDistributionR d_meson.SetMass(p4CH.m()); d_meson.SetHelicity(interaction.primary_helicity); - // interaction.secondary_momenta.resize(2); - // interaction.secondary_masses.resize(2); - // interaction.secondary_helicities.resize(2); - - // // the hadronic shower - // interaction.secondary_momenta[0][0] = p4X.e(); - // interaction.secondary_momenta[0][1] = p4X.px(); - // interaction.secondary_momenta[0][2] = p4X.py(); - // interaction.secondary_momenta[0][3] = p4X.pz(); - // interaction.secondary_masses[0] = 0; - // interaction.secondary_helicities[0] = interaction.primary_helicity; // true? - - // // the charmed hadron - // interaction.secondary_momenta[1][0] = p4CH.e(); - // interaction.secondary_momenta[1][1] = p4CH.px(); - // interaction.secondary_momenta[1][2] = p4CH.py(); - // interaction.secondary_momenta[1][3] = p4CH.pz(); - // interaction.secondary_masses[1] = p4CH.m(); - // interaction.secondary_helicities[1] = interaction.primary_helicity; // true? - } double CharmHadronization::FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const { diff --git a/projects/interactions/public/SIREN/interactions/CharmHadronization.h b/projects/interactions/public/SIREN/interactions/CharmHadronization.h index 86b7cc839..e81cfa4bb 100644 --- a/projects/interactions/public/SIREN/interactions/CharmHadronization.h +++ b/projects/interactions/public/SIREN/interactions/CharmHadronization.h @@ -22,6 +22,10 @@ #include "SIREN/utilities/Random.h" // for SIREN_random #include "SIREN/geometry/Geometry.h" #include "SIREN/utilities/Constants.h" // for electronMass +#include "SIREN/utilities/Interpolator.h" +#include "SIREN/utilities/Integration.h" + + namespace siren { namespace dataclasses { class InteractionRecord; } } @@ -35,7 +39,11 @@ class CharmHadronization : public Hadronization { friend cereal::access; private: const std::set primary_types = {siren::dataclasses::Particle::ParticleType::Charm, siren::dataclasses::Particle::ParticleType::CharmBar}; - + // z pdf setting should be enabled in the future, for now we hard code the Peterson function + double fragmentation_integral = 0; // for storing the integrated unnormed pdf + void normalize_pdf(); // for normalizing pdf and stroing integral, to be called at initialization + + siren::utilities::Interpolator1D inverseCdfTable; public: CharmHadronization(); @@ -51,6 +59,8 @@ friend cereal::access; double FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const override; + double sample_pdf(double z) const; + void compute_cdf(); static double getHadronMass(siren::dataclasses::ParticleType hadron_type); public: @@ -77,6 +87,7 @@ friend cereal::access; } // namespace siren CEREAL_CLASS_VERSION(siren::interactions::CharmHadronization, 0); - +CEREAL_REGISTER_TYPE(siren::interactions::CharmHadronization); +CEREAL_REGISTER_POLYMORPHIC_RELATION(siren::interactions::Hadronization, siren::interactions::CharmHadronization); #endif // SIREN_CharmHadronization_H diff --git a/python/SIREN_Controller.py b/python/SIREN_Controller.py index df7854cd4..f754a4598 100644 --- a/python/SIREN_Controller.py +++ b/python/SIREN_Controller.py @@ -580,9 +580,16 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, datasets["num_interactions"].append(id+1) # save injector and weighter - # self.injector.SaveInjector(filename) + self.injector.SaveInjector(filename) # weighter saving not yet supported - #self.weighter.SaveWeighter(filename) + self.weighter.SaveWeighter(filename) + + # Add print statements to check the lengths of all datasets + # for key, value in datasets.items(): + # print(f"Length of {key}: {len(value)}") + # if isinstance(value[0], list): # If it's a list of lists, check the inner lengths + # for idx, sublist in enumerate(value): + # print(f" Length of {key}[{idx}]: {len(sublist)}") # save events ak_array = ak.Array(datasets) diff --git a/resources/Examples/DMesonExample/DIS_D.py b/resources/Examples/DMesonExample/DIS_D.py index 3114111ca..8fb483a9e 100644 --- a/resources/Examples/DMesonExample/DIS_D.py +++ b/resources/Examples/DMesonExample/DIS_D.py @@ -4,7 +4,7 @@ from siren.SIREN_Controller import SIREN_Controller # Number of events to inject -events_to_inject = 50000 +events_to_inject = 5 # Expeirment to run experiment = "IceCube" diff --git a/resources/Examples/DMesonExample/make_plots.py b/resources/Examples/DMesonExample/make_plots.py index c8b246aa9..52d4dad98 100644 --- a/resources/Examples/DMesonExample/make_plots.py +++ b/resources/Examples/DMesonExample/make_plots.py @@ -4,9 +4,18 @@ from matplotlib.colors import LogNorm from mpl_toolkits.axes_grid1 import make_axes_locatable from parse_output import analysis -filename = "output/FullSim.parquet" -plt.style.use('paper.mplstyle') +import os + +pathname = "/n/holylfs05/LABS/arguelles_delgado_lab/Everyone/miaochenjin/DBSearch/SIREN_outputs/" +# parquetname = "0708_test/0708_test_.parquet" +expname = "0709_astro_flux" +parquetname = "{}/{}_.parquet".format(expname, expname) + +filename = os.path.join(pathname, parquetname) +savedir = os.path.join(pathname, "plots/") + +plt.style.use('paper.mplstyle') sim = analysis(filename) @@ -43,8 +52,8 @@ def xsec(E): def analytic_free_path(E): return (m_ice / (rho * N * xsec(E))) / 100 # convert to m -def plot_separation_distribution(analysis_): - D0_energies, D0_separations, Dp_energies, Dp_separations = analysis_.separation_analysis() +def plot_separation_distribution(analysis_, dim = 2): + D0_energies, D0_separations, Dp_energies, Dp_separations, D0_weights, Dp_weights = analysis_.separation_analysis() min_eng = 1e1 max_eng = 1e9 energy_bins = np.logspace(np.log10(min_eng), np.log10(max_eng), 20) @@ -58,54 +67,79 @@ def plot_separation_distribution(analysis_): min_sep = 1e-3 max_sep = 50000 log_separation_bins = np.logspace(np.log10(min_sep), np.log10(max_sep), 20) + sep_bin_widths = np.sqrt(log_separation_bins[1:] * log_separation_bins[:-1]) - X2, Y2 = np.meshgrid(energy_bins, log_separation_bins) - log_hist_D0, _, _ = np.histogram2d(D0_energies, D0_separations, bins = (energy_bins, log_separation_bins)) - log_hist_Dp, _, _ = np.histogram2d(Dp_energies, Dp_separations, bins = (energy_bins, log_separation_bins)) - log_hist_D0 = normalize(log_hist_D0, energy_bins, log_separation_bins) - log_hist_Dp = normalize(log_hist_Dp, energy_bins, log_separation_bins) + if dim == 2: - fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) + X2, Y2 = np.meshgrid(energy_bins, log_separation_bins) + log_hist_D0, _, _ = np.histogram2d(D0_energies, D0_separations, bins = (energy_bins, log_separation_bins), weights = D0_weights) + log_hist_Dp, _, _ = np.histogram2d(Dp_energies, Dp_separations, bins = (energy_bins, log_separation_bins), weights = Dp_weights) + log_hist_D0 = normalize(log_hist_D0, energy_bins, log_separation_bins) + log_hist_Dp = normalize(log_hist_Dp, energy_bins, log_separation_bins) - log_im1 = axes[0].pcolor(X2, Y2, log_hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - log_im2 = axes[1].pcolor(X2, Y2, log_hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) - # divider1 = make_axes_locatable(axes[0]) - # cax1 = divider1.append_axes('right', size='5%', pad=0.05) - divider2 = make_axes_locatable(axes[1]) - cax2 = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(log_im2, cax=cax2, orientation='vertical', alpha = 0.7) - # fig.colorbar(log_im1, cax=cax1, orientation='vertical', alpha = 0.7) + log_im1 = axes[0].pcolor(X2, Y2, log_hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) + log_im2 = axes[1].pcolor(X2, Y2, log_hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - axes[0].set_title(r"$D^0$ Separation") - axes[1].set_title(r"$D^+$ Separation") + # divider1 = make_axes_locatable(axes[0]) + # cax1 = divider1.append_axes('right', size='5%', pad=0.05) + divider2 = make_axes_locatable(axes[1]) + cax2 = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(log_im2, cax=cax2, orientation='vertical', alpha = 0.7) + # fig.colorbar(log_im1, cax=cax1, orientation='vertical', alpha = 0.7) - axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") - axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") + axes[0].set_title(r"$D^0$ Separation") + axes[1].set_title(r"$D^+$ Separation") - axes[0].set_ylabel("Separation Length [m]") + axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") + axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") - axes[0].set_xscale('log') - axes[1].set_xscale('log') - axes[0].set_yscale('log') - axes[1].set_yscale('log') + axes[0].set_ylabel("Separation Length [m]") - axes[0].set_ylim(min_sep, max_sep) - axes[1].set_ylim(min_sep, max_sep) - axes[0].set_xlim(min_eng, max_eng) - axes[1].set_xlim(min_eng, max_eng) + axes[0].set_xscale('log') + axes[1].set_xscale('log') + axes[0].set_yscale('log') + axes[1].set_yscale('log') - # also plot the analytic lines - axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) - axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#FEF3E8', alpha = 0.7) + axes[0].set_ylim(min_sep, max_sep) + axes[1].set_ylim(min_sep, max_sep) + axes[0].set_xlim(min_eng, max_eng) + axes[1].set_xlim(min_eng, max_eng) - legend = axes[1].legend(loc = 'upper left') - for text in legend.get_texts(): - text.set_color('#FEF3E8') + # also plot the analytic lines + axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) + axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#FEF3E8', alpha = 0.7) + + legend = axes[1].legend(loc = 'upper left') + for text in legend.get_texts(): + text.set_color('#FEF3E8') + + savename = os.path.join(savedir, "Separation_Length_Distribution") + + elif dim == 1: + + D0_separations.extend(Dp_weights) + D0_weights.extend(Dp_weights) + + sep_hist, _ = np.histogram(D0_separations, log_separation_bins, weights = D0_weights) + fig, ax = plt.subplots(1, 1, figsize = (8, 6)) + ax.hist(log_separation_bins[:-1], bins = log_separation_bins, weights = sep_hist / sep_bin_widths, \ + label = r"$\phi \sim E^{-2}$", \ + alpha = 0.9, color = '#D06C9D', histtype = 'step') + + savename = os.path.join(savedir, "PowerLaw_2_Separation_Length_Distribution") + ax.legend(loc = 'upper right') + ax.set_xscale('log') + ax.set_yscale('log') + ax.set_xlabel(r'$d_{\textrm{Sep}}$ [m]') + ax.set_ylabel('Normalized Weights Event Count') + + fig.savefig(savename, bbox_inches = 'tight') - fig.savefig("./plots/Separation_Length_Distribution", bbox_inches = 'tight') +plot_separation_distribution(sim, dim = 1) +plot_separation_distribution(sim, dim = 2) -# plot_separation_distribution(sim) def plot_2d_energy_loss(analysis_): E_D0, E_Dp, n_D0, n_Dp = analysis_.energy_loss_analysis_2d() @@ -149,8 +183,8 @@ def plot_2d_energy_loss(analysis_): fig.savefig("./plots/Energy_loss_2d_Distribution", bbox_inches = 'tight') -plot_2d_energy_loss(sim) -exit(0) +# plot_2d_energy_loss(sim) +# exit(0) def plot_free_path_distribution(): D0_E_list, D0_free_path_list, Dp_E_list, Dp_free_path_list = analysis_.free_path_analysis() @@ -213,4 +247,4 @@ def plot_free_path_distribution(): fig.savefig("./plots/Free_Path_Distribution", bbox_inches = 'tight') return -plot_free_path_distribution() \ No newline at end of file +# plot_free_path_distribution() \ No newline at end of file diff --git a/resources/Examples/DMesonExample/parse_output.py b/resources/Examples/DMesonExample/parse_output.py index 32a316953..c7a79fde5 100644 --- a/resources/Examples/DMesonExample/parse_output.py +++ b/resources/Examples/DMesonExample/parse_output.py @@ -62,6 +62,8 @@ def separation_analysis(self): D0_separations = [] Dp_energies = [] Dp_separations = [] + D0_weights = [] + Dp_weights = [] for i in range(self.num_events): cur_event = event(self.df.iloc[i]) print("{}/{}".format(i, self.num_events), end = '\r') @@ -70,17 +72,23 @@ def separation_analysis(self): # extract the vertex separations D0_separations.append(cur_event.D_DECAY_LEN()) D0_energies.append(cur_event.row["primary_momentum"][2][0]) + D0_weights.append(cur_event.row["event_weight"]) + elif cur_event.DTYPE() == 411: # This is D+ # extract the vertex separations Dp_separations.append(cur_event.D_DECAY_LEN()) Dp_energies.append(cur_event.row["primary_momentum"][2][0]) - return D0_energies, D0_separations, Dp_energies, Dp_separations + Dp_weights.append(cur_event.row["event_weight"]) + + return D0_energies, D0_separations, Dp_energies, Dp_separations, D0_weights, Dp_weights def energy_loss_analysis_2d(self): E_D0 = [] E_Dp = [] n_D0 = [] n_Dp = [] + w_D0 = [] + w_Dp = [] for i in range(self.num_events): cur_event = event(self.df.iloc[i]) print("{}/{}".format(i, self.num_events), end = '\r') @@ -88,9 +96,13 @@ def energy_loss_analysis_2d(self): # extract the vertex separations n_D0.append(cur_event.row["num_interactions"] - 3) E_D0.append(cur_event.row["primary_momentum"][2][0]) + w_D0.append(cur_event.row["event_weight"]) + elif cur_event.DTYPE() == 411: # This is D+ # extract the vertex separations n_Dp.append(cur_event.row["num_interactions"] - 3) E_Dp.append(cur_event.row["primary_momentum"][2][0]) - return E_D0, E_Dp, n_D0, n_Dp + w_Dp.append(cur_event.row["event_weight"]) + + return E_D0, E_Dp, n_D0, n_Dp, w_D0, w_Dp From a6d66aa1c45fbd34eaca149889c608551df047ea Mon Sep 17 00:00:00 2001 From: Nicholas Kamp <43788191+nickkamp1@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:07:59 -0400 Subject: [PATCH 04/11] Update DetectorModel.cxx for PR --- projects/detector/private/DetectorModel.cxx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/projects/detector/private/DetectorModel.cxx b/projects/detector/private/DetectorModel.cxx index e8cbcffb6..75a86dc75 100644 --- a/projects/detector/private/DetectorModel.cxx +++ b/projects/detector/private/DetectorModel.cxx @@ -683,8 +683,7 @@ double DetectorModel::GetInteractionDensity(Geometry::IntersectionList const & i std::vector const & total_cross_sections, double const & total_decay_length) const { Vector3D direction = p0 - intersections.position; - // std::cout << "direction: " << direction.magnitude() << std::endl; - if(direction.magnitude() == 0 || direction.magnitude() <= 1e-05) { + if(direction.magnitude() == 0 || direction.magnitude() <= 1e-6) { direction = intersections.direction; } else { direction.normalize(); @@ -980,25 +979,21 @@ double DetectorModel::GetInteractionDepthInCGS(Geometry::IntersectionList const std::vector const & total_cross_sections, double const & total_decay_length) const { - // std::cout << p0 << " " << p1 << " " << intersections.direction << std::endl; if(p0 == p1) { return 0.0; } Vector3D direction = p1 - p0; double distance = direction.magnitude(); - // std::cout << "distance is " << distance << std::endl; if(distance == 0.0) { return 0.0; } - if(direction.magnitude() <= 1e-05) { - // std::cout << "triggered" << std::endl; - return 0.0; + if(direction.magnitude() <= 1e-6) { + direction = intersections.direction; } direction.normalize(); double dot = intersections.direction * direction; assert(std::abs(1.0 - std::abs(dot)) < 1e-6); - // std::cout << "not at this point" << std::endl; double offset = (intersections.position - p0) * direction; if(dot < 0) { From 4414cbb02a0307b19216117284620c95a71bd60d Mon Sep 17 00:00:00 2001 From: Nicholas Kamp <43788191+nickkamp1@users.noreply.github.com> Date: Thu, 22 Aug 2024 16:18:45 -0400 Subject: [PATCH 05/11] Update Injector.cxx to fixes failed_tries bug --- projects/injection/private/Injector.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index 40069d4ab..9bf04c1b5 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -331,6 +331,7 @@ siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::da size_t tries = 0; size_t failed_tries = 0; while(true) { + tries += 1; // // std::cout << "gotcha" << std::endl; // for(auto & distribution : secondary_distributions) { // // std::cout << "sample distribution" << std::endl; @@ -364,7 +365,7 @@ siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::da } continue; } - if(failed_tries > max_tries) { + if(tries > max_tries) { throw(siren::utilities::InjectionFailure("Failed to generate secondary process!")); break; } From 7e21f18d7134bf461911a5b0e236536820907337 Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Thu, 22 Aug 2024 17:46:04 -0400 Subject: [PATCH 06/11] updated file for PR comments, preparing for PR again --- .../SIREN/dataclasses/ParticleTypes.def | 1 + projects/detector/private/DetectorModel.cxx | 5 +- .../SecondaryBoundedVertexDistribution.cxx | 4 - .../SecondaryPhysicalVertexDistribution.cxx | 10 - .../SecondaryVertexPositionDistribution.cxx | 1 - projects/injection/private/Injector.cxx | 108 +------- projects/injection/private/Weighter.cxx | 2 +- .../private/CharmDISFromSpline.cxx | 58 +--- .../private/CharmHadronization.cxx | 2 +- .../interactions/private/CharmMesonDecay.cxx | 97 ------- projects/interactions/private/DMesonELoss.cxx | 50 +--- resources/Examples/DMesonExample/DIS_D.py | 71 +++-- .../Examples/DMesonExample/make_plots.py | 250 ------------------ 13 files changed, 69 insertions(+), 590 deletions(-) delete mode 100644 resources/Examples/DMesonExample/make_plots.py diff --git a/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def b/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def index 73b578f97..af36148c8 100644 --- a/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def +++ b/projects/dataclasses/public/SIREN/dataclasses/ParticleTypes.def @@ -194,6 +194,7 @@ X(NuclInt, -2000001004) X(MuPair, -2000001005) X(Hadrons, -2000001006) X(Decay, -2000001007) +X(Hadronization, 99) X(ContinuousEnergyLoss, -2000001111) X(FiberLaser, -2000002100) X(N2Laser, -2000002101) diff --git a/projects/detector/private/DetectorModel.cxx b/projects/detector/private/DetectorModel.cxx index 75a86dc75..37cce5212 100644 --- a/projects/detector/private/DetectorModel.cxx +++ b/projects/detector/private/DetectorModel.cxx @@ -683,7 +683,7 @@ double DetectorModel::GetInteractionDensity(Geometry::IntersectionList const & i std::vector const & total_cross_sections, double const & total_decay_length) const { Vector3D direction = p0 - intersections.position; - if(direction.magnitude() == 0 || direction.magnitude() <= 1e-6) { + if(direction.magnitude() == 0 || direction.magnitude() <= 1e-5) { direction = intersections.direction; } else { direction.normalize(); @@ -987,8 +987,9 @@ double DetectorModel::GetInteractionDepthInCGS(Geometry::IntersectionList const if(distance == 0.0) { return 0.0; } - if(direction.magnitude() <= 1e-6) { + if(direction.magnitude() <= 1e-5) { direction = intersections.direction; + // return 1e-05; // I have to do this to ensure that it works } direction.normalize(); diff --git a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx index a86fffb1c..43705a3ea 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx @@ -53,8 +53,6 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - // std::cout << "in sample bounded vertex" << std::endl; - siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; @@ -123,8 +121,6 @@ void SecondaryBoundedVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { - // std::cout << "in sample bounded vertex gen prob" << std::endl; - siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); siren::math::Vector3D vertex(record.interaction_vertex); diff --git a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx index d49a42f03..f5d327b7a 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx @@ -51,22 +51,15 @@ double log_one_minus_exp_of_negative(double x) { void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - // // std::cout << "in sample physical vertex" << std::endl; siren::math::Vector3D pos = record.initial_position; siren::math::Vector3D dir = record.direction; - // // std::cout << "in sample physical vertex-1" << std::endl; siren::math::Vector3D endcap_0 = pos; // treat hadronizations differntely if (interactions->HasHadronizations()) { - // std::cout << "in sample physical vertex-hadron" << std::endl; - record.SetLength(0); return; } - // // std::cout << "in sample physical vertex-shouldnm't be here" << std::endl; - - siren::detector::Path path(detector_model, DetectorPosition(endcap_0), DetectorDirection(dir), std::numeric_limits::infinity()); path.ClipToOuterBounds(); @@ -86,7 +79,6 @@ void SecondaryPhysicalVertexDistribution::SampleVertex(std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::InteractionRecord const & record) const { - // std::cout << "in sample physical vertex gen prob" << std::endl; - siren::math::Vector3D dir(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]); dir.normalize(); siren::math::Vector3D vertex(record.interaction_vertex); diff --git a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx index 70af06641..87b44e63d 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryVertexPositionDistribution.cxx @@ -15,7 +15,6 @@ namespace distributions { // class SecondaryVertexPositionDistribution : InjectionDistribution //--------------- void SecondaryVertexPositionDistribution::Sample(std::shared_ptr rand, std::shared_ptr detector_model, std::shared_ptr interactions, siren::dataclasses::SecondaryDistributionRecord & record) const { - // // // // // std::cout << "sampling vertex" << std::endl; SampleVertex(rand, detector_model, interactions, record); } diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index 9bf04c1b5..a77338cff 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -188,7 +188,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record double fake_prob; // if contains hadronization, then perform only hadronization if (interactions->HasHadronizations()) { - // std::cout << "saw hadronization" << std::endl; double total_frag_prob = 0; std::vector frag_probs; for(auto const & hadronization : interactions->GetHadronizations() ) { @@ -209,9 +208,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } - // std::cout << "Hadronization finished signatures" << std::endl; - - // now choose the specific charmed hadron to fragment into double r = random->Uniform(0, total_frag_prob); unsigned int index = 0; @@ -224,34 +220,22 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record matching_hadronizations[index]->SampleFinalState(xsec_record, random); xsec_record.Finalize(record); - - // std::cout << "hadronization done!" << std::endl; - } else { if (interactions->HasCrossSections()) { - // std::cout << "saw xsec" << std::endl; for(auto const target : available_targets) { if(possible_targets.find(target) != possible_targets.end()) { - // std::cout << "saw xsec: in first for loop" << std::endl; // Get target density double target_density = detector_model->GetParticleDensity(intersections, DetectorPosition(interaction_vertex), target); // Loop over cross sections that have this target std::vector> const & target_cross_sections = interactions->GetCrossSectionsForTarget(target); for(auto const & cross_section : target_cross_sections) { - // std::cout << "saw xsec: in second for loop" << std::endl; // Loop over cross section signatures with the same target std::vector signatures = cross_section->GetPossibleSignaturesFromParents(record.signature.primary_type, target); for(auto const & signature : signatures) { - // std::cout << "saw xsec: in third for loop" << std::endl; - fake_record.signature = signature; fake_record.target_mass = detector_model->GetTargetMass(target); // Add total cross section times density to the total prob - // std::cout << "about to sample total xsec" << std::endl; - fake_prob = target_density * cross_section->TotalCrossSection(fake_record); - // std::cout << "finished sampling total xsec" << std::endl; - total_prob += fake_prob; xsec_prob += fake_prob; // Add total prob to probs @@ -266,7 +250,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } if (interactions->HasDecays()) { - // std::cout << "saw decay" << std::endl; for(auto const & decay : interactions->GetDecays() ) { for(auto const & signature : decay->GetPossibleSignaturesFromParent(record.signature.primary_type)) { fake_record.signature = signature; @@ -282,7 +265,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } } - // std::cout << "continuing...." << std::endl; if(total_prob == 0) throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); // Throw a random number @@ -293,7 +275,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record record.signature.target_type = matching_targets[index]; record.signature = matching_signatures[index]; double selected_prob = 0.0; - // std::cout << "finished initializing stuff" << std::endl; for(unsigned int i=0; i 0 ? probs[i] - probs[i - 1] : probs[i]); @@ -303,14 +284,9 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); record.target_mass = detector_model->GetTargetMass(record.signature.target_type); siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); - // std::cout << "finished sampling from primary process" << std::endl; if(r <= xsec_prob) { - // std::cout << "about to sample primary process final state" << std::endl; - matching_cross_sections[index]->SampleFinalState(xsec_record, random); } else { - // std::cout << "about to sample primary process final state" << std::endl; - matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); } xsec_record.Finalize(record); @@ -321,8 +297,6 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record // // Modifies an InteractionRecord with the new event siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::dataclasses::SecondaryDistributionRecord & secondary_record) const { - // std::cout << "sampling secondary" << std::endl; - // std::cout << "secondary record type is " << secondary_record.type << " " << secondary_record.id << std::endl; std::shared_ptr secondary_process = secondary_process_map.at(secondary_record.type); std::shared_ptr secondary_interactions = secondary_process->GetInteractions(); std::vector> secondary_distributions = secondary_process->GetSecondaryInjectionDistributions(); @@ -332,32 +306,15 @@ siren::dataclasses::InteractionRecord Injector::SampleSecondaryProcess(siren::da size_t failed_tries = 0; while(true) { tries += 1; - // // std::cout << "gotcha" << std::endl; - // for(auto & distribution : secondary_distributions) { - // // std::cout << "sample distribution" << std::endl; - // distribution->Sample(random, detector_model, secondary_process->GetInteractions(), secondary_record); - // } - // siren::dataclasses::InteractionRecord record; - // secondary_record.Finalize(record); - // // // std::cout << "sample distribution" << std::endl; - - // SampleCrossSection(record, secondary_interactions); - // return record; - try { for(auto & distribution : secondary_distributions) { - // std::cout << "sample distribution" << std::endl; distribution->Sample(random, detector_model, secondary_process->GetInteractions(), secondary_record); } siren::dataclasses::InteractionRecord record; secondary_record.Finalize(record); - // // std::cout << "sample distribution" << std::endl; - SampleCrossSection(record, secondary_interactions); return record; } catch(siren::utilities::InjectionFailure const & e) { - // std::cout << "caught error" << std::endl; - failed_tries += 1; if(failed_tries > max_tries) { throw(siren::utilities::InjectionFailure("Failed to generate secondary process!")); @@ -379,7 +336,6 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { size_t tries = 0; size_t failed_tries = 0; // Initial Process - // std::cout << "Sampling primary interactions" << std::endl; while(true) { tries += 1; try { @@ -388,7 +344,6 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { distribution->Sample(random, detector_model, primary_process->GetInteractions(), primary_record); } primary_record.Finalize(record); - // std::cout << "primary record fixed" << std:: endl; SampleCrossSection(record); break; } catch(siren::utilities::InjectionFailure const & e) { @@ -408,12 +363,9 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { std::shared_ptr parent = tree.add_entry(record); // Secondary Processes - // std::cout << "Sampling primary interactions 2" << std::endl; - std::deque, std::shared_ptr>> secondaries; std::function)> add_secondaries = [&](std::shared_ptr parent) { for(size_t i=0; irecord.signature.secondary_types.size(); ++i) { - // // std::cout << "for loop 1" << std::endl; siren::dataclasses::ParticleType const & type = parent->record.signature.secondary_types[i]; std::map>::iterator it = secondary_process_map.find(type); if(it == secondary_process_map.end()) { @@ -430,70 +382,18 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { }; add_secondaries(parent); - // std::cout << "num secondaries: " << secondaries.size() << std::endl; while(secondaries.size() > 0) { - // // std::cout << "while loop 1" << std::endl; - for(int i = secondaries.size() - 1; i >= 0; --i) { - // // std::cout << "for loop 2" << std::endl; - std::shared_ptr parent = std::get<0>(secondaries[i]); std::shared_ptr secondary_dist = std::get<1>(secondaries[i]); - // // std::cout << "for loop 2-1" << std::endl; secondaries.erase(secondaries.begin() + i); - - // std::cout << "Primary Type: " << secondary_dist->record.signature.primary_type << std::endl; - // std::cout << "Secondary Types: "; - // for (const auto& type : secondary_dist->record.signature.secondary_types) { - // std::cout << type << " "; - // } - // std::cout << std::endl; - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // this try-catch clock is to debug the secondary process LE no available interaction error - try{ - siren::dataclasses::InteractionRecord secondary_record = SampleSecondaryProcess(*secondary_dist); - std::shared_ptr secondary_datum = tree.add_entry(secondary_record, parent); - add_secondaries(secondary_datum); - } catch (const std::exception& e) { - std::cerr << "Error occurred: " << e.what() << std::endl; - - // Print the primary type and secondary types for debugging - std::cerr << "Primary Type: " << secondary_dist->record.signature.primary_type << std::endl; - std::cerr << "Secondary Types: "; - for (const auto& type : secondary_dist->record.signature.secondary_types) { - std::cerr << type << " "; - } - std::cerr << std::endl; - - // Print the primary momentum - std::cerr << "Primary Momentum: "; - for (double component : secondary_dist->record.primary_momentum) { - std::cerr << component << " "; - } - std::cerr << std::endl; - - // Print the secondary IDs - std::cerr << "Secondary IDs: "; - for (const auto& id : secondary_dist->record.secondary_ids) { - std::cerr << id << " "; - } - std::cerr << std::endl; - throw; - } catch (...) { - std::cerr << "Unknown exception caught!" << std::endl; - throw; - } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - + siren::dataclasses::InteractionRecord secondary_record = SampleSecondaryProcess(*secondary_dist); + std::shared_ptr secondary_datum = tree.add_entry(secondary_record, parent); + add_secondaries(secondary_datum); + } - // // std::cout << "while loop 1-2" << std::endl; - } injected_events += 1; return tree; diff --git a/projects/injection/private/Weighter.cxx b/projects/injection/private/Weighter.cxx index afd11cfb8..bd166909a 100644 --- a/projects/injection/private/Weighter.cxx +++ b/projects/injection/private/Weighter.cxx @@ -114,7 +114,7 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c double generation_probability = injectors[idx]->EventsToInject();//GenerationProbability(tree); for(auto const & datum : tree.tree) { // skip weighting if record contains hadronization - if (isQuark(datum->record.signature.primary_type)) { + if (datum->record.signature.target_type == siren::dataclasses::Particle::ParticleType::Hadronization) { continue; } std::tuple bounds; diff --git a/projects/interactions/private/CharmDISFromSpline.cxx b/projects/interactions/private/CharmDISFromSpline.cxx index d24740568..42189f5a4 100644 --- a/projects/interactions/private/CharmDISFromSpline.cxx +++ b/projects/interactions/private/CharmDISFromSpline.cxx @@ -50,16 +50,10 @@ bool kinematicallyAllowed(double x, double y, double E, double M, double m) { //the numerator of b (or b*d) double bd = sqrt(term * term - ((m * m) / (E * E))); - // also try the D-Meson Mass? double s = 2 * M * E; double Q2 = s * x * y; double Mc = siren::utilities::Constants::D0Mass; - // if ((Q2 / (1 - x) + pow(M, 2) < pow(M + Mc, 2))) { - // std::cout << "SIREN D Meson constraint is trigged!" << std::endl; - // } return ((ad - bd) <= d * y and d * y <= (ad + bd)) && (Q2 / (1 - x) + pow(M, 2) >= pow(M + Mc, 2)); //Eq. 7 - // return ((ad - bd) <= d * y and d * y <= (ad + bd)); //Eq. 7 - } } @@ -197,8 +191,6 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { // returns true if successfully read minimum Q2 bool q2_good = differential_cross_section_.read_key("Q2MIN", minimum_Q2_); - // std::cout << "reading results: " << mass_good << " " << int_good << " " << q2_good << std::endl; - if(!int_good) { // assume DIS to preserve compatability with previous versions interaction_type_ = 1; @@ -221,9 +213,7 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { } } else { - // // std::cout << "mass and int both not good" << std::endl; if(differential_cross_section_.get_ndim() == 3) { - // std::cout << "dim is 3" << std::endl; target_mass_ = siren::utilities::Constants::isoscalarMass; } else if(differential_cross_section_.get_ndim() == 2) { @@ -233,8 +223,6 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { } } } - - // std::cout << "target mass is " << target_mass_ << std::endl; } void CharmDISFromSpline::InitializeSignatures() { @@ -293,7 +281,6 @@ double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord cons rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); double primary_energy; primary_energy = interaction.primary_momentum[0]; - // std::cout << "primary energy is " << primary_energy << std::endl; // if we are below threshold, return 0 if(primary_energy < InteractionThreshold(interaction)) return 0; @@ -304,7 +291,6 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr if(not primary_types_.count(primary_type)) { throw std::runtime_error("Supplied primary not supported by cross section!"); } - // std::cout << "now in real sample total xsec func" << std::endl; double log_energy = log10(primary_energy); if(log_energy < total_cross_section_.lower_extent(0) @@ -316,9 +302,7 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr } int center; - // std::cout << "maybe problem is here?" << std::endl; total_cross_section_.searchcenters(&log_energy, ¢er); - // std::cout << "maybe problem is here??" << std::endl; double log_xs = total_cross_section_.ndsplineeval(&log_energy, ¢er, 0); @@ -355,7 +339,6 @@ double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionReco } double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { - bool check_criteria = false; // this is used to gauge kinematic constraint in xsec and SIREN impementations, will eventually be deleted double log_energy = log10(energy); // check preconditions if(log_energy < differential_cross_section_.lower_extent(0) @@ -375,12 +358,9 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou if(Q2 < minimum_Q2_) // cross section not calculated, assumed to be zero return 0; - if (!check_criteria) { - if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { - return 0; - } + if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + return 0; } - std::array coordinates{{log_energy, log10(x), log10(y)}}; std::array centers; if(!differential_cross_section_.searchcenters(coordinates.data(), centers.data())) @@ -388,30 +368,6 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou double result = pow(10., differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0)); assert(result >= 0); - if (check_criteria) { - // this is a check of kinematic constraint implementation - if (result == 0) { - if(kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { - std::cout << "xsec gives 0 but kinematically allowed!" << std::endl; - } - } - - if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { - // check if this is due to charm production constraint - double M = target_mass_; - double E = energy; - double s = 2 * M * E; - double Q2 = s * x * y; - double Mc = siren::utilities::Constants::D0Mass; - if ((Q2 / (1 - x) + pow(M, 2) < pow(M + Mc, 2))) { // if so check result - if (result != 0) { - std::cout << "SIREN constraint not passed but xsec does not give 0!" << std::endl; - } - } - return 0; - } - } - return unit * result; @@ -428,7 +384,6 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR if (differential_cross_section_.get_ndim() != 3) { throw std::runtime_error("I expected 3 dimensions in the cross section spline, but got " + std::to_string(differential_cross_section_.get_ndim()) +". Maybe your fits file doesn't have the right 'INTERACTION' key?"); } - // std::cout << "in sample final state of charm DIS from spline" << std::endl; rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); @@ -450,11 +405,9 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR double m = GetLeptonMass(record.signature.secondary_types[lepton_index]); double m1 = record.primary_mass; - // std::cout << "getting mass of primary: " << m1 << std::endl; double m3 = m; double E1_lab = p1_lab.e(); double E2_lab = p2_lab.e(); - // std::cout << "getting energy of primary: " << E1_lab << std::endl; // The out-going particle always gets at least enough energy for its rest mass double yMax = 1 - m / primary_energy; @@ -494,16 +447,12 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR // rejection sample a point which is kinematically allowed by calculation limits double trialQ; double trials = 0; - // std::cout << "do loop 1" << std::endl; do { - // std::cout << "do loop 2" << std::endl; if (trials >= 100) throw std::runtime_error("too much trials"); trials += 1; kin_vars[1] = random->Uniform(logXMin,0); kin_vars[2] = random->Uniform(logYMin,logYMax); trialQ = (2 * E1_lab * E2_lab) * pow(10., kin_vars[1] + kin_vars[2]); - // std::cout << kin_vars[1] << " " << kin_vars[2] << " " << trialQ << " " << minimum_Q2_ << std::endl; - // std::cout << primary_energy << " " << target_mass_ << " " << m << std::endl; } while(trialQ CharmHadronization::GetPossibleSi std::vector signatures; dataclasses::InteractionSignature signature; signature.primary_type = primary; - signature.target_type = siren::dataclasses::Particle::ParticleType::Decay; + signature.target_type = siren::dataclasses::Particle::ParticleType::Hadronization; signature.secondary_types.resize(2); signature.secondary_types[0] = siren::dataclasses::Particle::ParticleType::Hadrons; diff --git a/projects/interactions/private/CharmMesonDecay.cxx b/projects/interactions/private/CharmMesonDecay.cxx index 7f6520abf..5b92f0f1a 100644 --- a/projects/interactions/private/CharmMesonDecay.cxx +++ b/projects/interactions/private/CharmMesonDecay.cxx @@ -148,11 +148,9 @@ double CharmMesonDecay::TotalDecayWidthForFinalState(dataclasses::InteractionRec siren::dataclasses::Particle::ParticleType::EPlus, siren::dataclasses::Particle::ParticleType::NuE}; if (primary == siren::dataclasses::Particle::ParticleType::DPlus && secondaries == k0_eplus_nue) { - // branching_ratio = 0.089; branching_ratio = 1; tau = 1040 * (1e-15); } else if (primary == siren::dataclasses::Particle::ParticleType::D0 && secondaries == kminus_eplus_nue) { - // branching_ratio = 0.03538; branching_ratio = 1; tau = 410.1 * (1e-15); } @@ -296,34 +294,6 @@ void CharmMesonDecay::computeDiffGammaCDF(std::vector constants, double cdf_vector.push_back(1); pdf_vector.push_back(0); - // for debugging and plotting, print the pdf and cdf tables - // for (size_t i = 0; auto& element : cdf_Q2_nodes) { - // std::cout << element; - // // Print comma if it's not the last element - // if (++i != cdf_Q2_nodes.size()) { - // std::cout << ", "; - // } - // } - // std::cout << std::endl; - - // for (size_t i = 0; auto& element : cdf_vector) { - // std::cout << element; - // // Print comma if it's not the last element - // if (++i != cdf_vector.size()) { - // std::cout << ", "; - // } - // } - // std::cout << std::endl; - - // for (size_t i = 0; auto& element : pdf_vector) { - // std::cout << element; - // // Print comma if it's not the last element - // if (++i != pdf_vector.size()) { - // std::cout << ", "; - // } - // } - // std::cout << std::endl; - // set the spline table siren::utilities::TableData1D inverse_cdf_data; inverse_cdf_data.x = cdf_vector; @@ -339,17 +309,11 @@ void CharmMesonDecay::computeDiffGammaCDF(std::vector constants, double void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { // first obtain the constants needed for further computation from the signature - // std::cout<<"b1"< constants = FormFactorFromRecord(record); double mD = particleMass(record.signature.primary_type); double mK = particleMass(record.signature.secondary_types[0]); - // std::cout << "input masses: " << mD << " " << mK << std::endl; - // first sample a q^2 - //////////////////////////////////////////// - // computeDiffGammaCDF(constants, mD, mK);// - //////////////////////////////////////////// double rand_value_for_Q2 = random->Uniform(0, 1); double Q2 = inverseCdf(rand_value_for_Q2); @@ -358,8 +322,6 @@ void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionReco double sinTheta = std::sin(std::acos(cosTheta)); // set the x axis to be the D direction geom3::UnitVector3 x_dir = geom3::UnitVector3::xAxis(); - // std::cout<<"b2"<W+K/Pi decay, now treat the W->l+nu decay double ml = particleMass(record.signature.secondary_types[1]); double mnu = 0; @@ -400,11 +359,6 @@ void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionReco rk::P4 p4l_Wrest(P * geom3::Vector3(W_cosTheta, W_sinTheta, 0), ml); rk::P4 p4nu_Wrest(P * geom3::Vector3(-W_cosTheta, -W_sinTheta, 0), 0); - // std::cout << "momentums: " << p4l_Wrest << " " << p4nu_Wrest << std::endl; - // std::cout << "check mass of l and nu: " << p4l_Wrest.m() << " " << p4nu_Wrest.m() << std::endl; - //now rotate so they are defined wrt the lab frame W direction - // std::cout<<"b5"< & secondaries = record.GetSecondaryParticleRecords(); siren::dataclasses::SecondaryParticleRecord & kpi = secondaries[0]; siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[1]; @@ -434,56 +387,6 @@ void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionReco neutrino.SetMass(p4nu_lab.m()); neutrino.SetHelicity(record.primary_helicity); - // finally, we can populate the record, for implementation in prometheus, maybe add treatment of hadrons, but could be implemnted on p side - // record.secondary_momenta.resize(3); - // record.secondary_masses.resize(3); - // record.secondary_helicity.resize(3); // 0 is the hadron, 1 is the lepton, 2 is the neutrino - // // the K/pi - // record.secondary_momenta[0][0] = p4K_lab.e(); - // record.secondary_momenta[0][1] = p4K_lab.px(); - // record.secondary_momenta[0][2] = p4K_lab.py(); - // record.secondary_momenta[0][3] = p4K_lab.pz(); - // record.secondary_masses[0] = p4K_lab.m(); - // record.secondary_helicity[0] = 0; - // // the lepton - // record.secondary_momenta[1][0] = p4l_lab.e(); - // record.secondary_momenta[1][1] = p4l_lab.px(); - // record.secondary_momenta[1][2] = p4l_lab.py(); - // record.secondary_momenta[1][3] = p4l_lab.pz(); - // record.secondary_masses[1] = p4l_lab.m(); - // record.secondary_helicity[1] = 1; - // // the neutrino - // record.secondary_momenta[2][0] = p4nu_lab.e(); - // record.secondary_momenta[2][1] = p4nu_lab.px(); - // record.secondary_momenta[2][2] = p4nu_lab.py(); - // record.secondary_momenta[2][3] = p4nu_lab.pz(); - // record.secondary_masses[2] = p4nu_lab.m(); - // record.secondary_helicity[2] = 1; - - //for debug purposes - // double p4w_rest_Q2 = pow(p4W_Drest.e(), 2) - pow(p4W_Drest.px(), 2) - - // pow(p4W_Drest.py(), 2) - pow(p4W_Drest.pz(), 2); - // double p4w_lab_Q2 = pow(p4W_lab.e(), 2) - pow(p4W_lab.px(), 2) - - // pow(p4W_lab.py(), 2) - pow(p4W_lab.pz(), 2); - // std::cout << p4W_Drest.e() << " " << p4W_lab.e() << " " << PW << " " << p4W_Drest.p() << " " << p4W_Drest << std::endl; - // std::cout << p4K_Drest.e() << " " << p4K_lab.e() << " " << PK << " " << p4K_Drest.p() << " " << p4K_Drest << std::endl; - // std::cout << Q2 << " " << sqrt(Q2)<< std::endl; - // std::cout << "invariant mass of the W in two frames are " << p4w_lab_Q2 << " " << p4w_rest_Q2 << std::endl; - // std::cout << "check mass of W: " << p4W_lab.m() << " " << p4W_Drest.m() << std::endl; - // std::cout << "check mass of K: " << p4K_lab.m() << " " << p4K_Drest.m() << std::endl; - - - - // rk::P4 inv_mass_Wrest = p4l_Wrest + p4nu_Wrest; - // rk::P4 inv_mass_lab = p4l_lab + p4nu_lab; - // std::cout << "inv masses in two frames: " << pow(inv_mass_Wrest.m(), 2) << " " << pow(inv_mass_lab.m(), 2) << std::endl; - // std::cout << "using inv mass calculator: " << pow(invMass(p4l_Wrest, p4nu_Wrest), 2) << " " << pow(invMass(p4l_lab, p4nu_lab), 2) << std::endl; - // std::cout << "energy of l and nu and inv mass: " << El << " " << Enu << " " << pow(El+Enu, 2) << std::endl; - // std::cout << "momentums: " << p4l_Wrest << " " << p4nu_Wrest << std::endl; - // std::cout << "check mass of l and nu: " << p4l_Wrest.m() << " " << p4nu_Wrest.m() << std::endl; - // std::cout << "W energy in rest frame: " << pow(p4W_lab.m(), 2) << std::endl; - - } double CharmMesonDecay::FinalStateProbability(dataclasses::InteractionRecord const & record) const { diff --git a/projects/interactions/private/DMesonELoss.cxx b/projects/interactions/private/DMesonELoss.cxx index 867f7edf2..5779d77f4 100644 --- a/projects/interactions/private/DMesonELoss.cxx +++ b/projects/interactions/private/DMesonELoss.cxx @@ -14,9 +14,6 @@ #include // for P4, Boost #include // for Vector3 -#include // for splinetable -//#include - #include "SIREN/interactions/CrossSection.h" // for CrossSection #include "SIREN/dataclasses/InteractionRecord.h" // for Interactio... #include "SIREN/dataclasses/Particle.h" // for Particle @@ -131,18 +128,9 @@ double DMesonELoss::DifferentialCrossSection(dataclasses::InteractionRecord cons // rk::P4 p2(geom3::Vector3(interaction.target_momentum[1], interaction.target_momentum[2], interaction.target_momentum[3]), interaction.target_mass); double primary_energy; rk::P4 p1_lab; - // rk::P4 p2_lab; - // if(interaction.target_momentum[1] == 0 and interaction.target_momentum[2] == 0 and interaction.target_momentum[3] == 0) { primary_energy = interaction.primary_momentum[0]; p1_lab = p1; - // p2_lab = p2; - // } else { - // rk::Boost boost_start_to_lab = p2.restBoost(); - // p1_lab = boost_start_to_lab * p1; - // p2_lab = boost_start_to_lab * p2; - // primary_energy = p1_lab.e(); - // std::cout << "D Meson Diff Xsec: not in lab frame???" << std::endl; - // } + double final_energy = interaction.secondary_momenta[0][0]; double z = 1 - final_energy / primary_energy; @@ -170,8 +158,6 @@ double DMesonELoss::InteractionThreshold(dataclasses::InteractionRecord const & void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& interaction, std::shared_ptr random) const { - // std::cout << "In D Meson E Loss Sample Final State" << std::endl; - rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); // rk::P4 p2(geom3::Vector3(interaction.target_momentum[1], interaction.target_momentum[2], interaction.target_momentum[3]), interaction.target_mass); @@ -184,24 +170,8 @@ void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& double primary_energy; double Dmass = interaction.primary_mass; rk::P4 p1_lab; - // rk::P4 p2_lab; - // if(interaction.target_momentum[1] == 0 and interaction.target_momentum[2] == 0 and interaction.target_momentum[3] == 0) { p1_lab = p1; - // p2_lab = p2; primary_energy = p1_lab.e(); - // } else { - // // this is currently not implemented - // // Rest frame of p2 will be our "lab" frame - // rk::Boost boost_start_to_lab = p2.restBoost(); - // p1_lab = boost_start_to_lab * p1; - // p2_lab = boost_start_to_lab * p2; - // primary_energy = p1_lab.e(); - // // std::cout << "D Meson Energy Loss: not in lab frame???" << std::endl; - // } - // following line is wrong but i dont want to change it now fuck it. - // std::cout << " " << interaction.primary_momentum[0] << " " << interaction.primary_momentum[1] << " " << interaction.primary_momentum[2] << " " << interaction.primary_momentum[3]; - // std::cout << primary_energy << " " << pow(primary_energy, 2) - pow(Dmass, 2) << " " << - // sqrt(pow(interaction.primary_momentum[1], 2) +pow(interaction.primary_momentum[2], 2) +pow(interaction.primary_momentum[3], 2)) << std::endl; // sample an inelasticity from gaussian using Box-Muller Transform double sigma = 0.2; double z0 = 0.56; // for mesons only, for baryons it's 0.59, but not implemented yet @@ -218,8 +188,6 @@ void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& while (u1 == 0); u2 = random->Uniform(0, 1); double z = sigma * sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2) + z0; - // std::cout << z<< std::endl; - // now modify the energy of the charm hadron and the corresponding momentum final_energy = primary_energy * (1-z); if (pow(final_energy, 2) - pow(Dmass, 2) >= 0) { @@ -228,14 +196,9 @@ void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& accept = false; } } while (!accept); - // this might be an infinite loop??????? - // need to check if the cross section length is good enough, how to make some relevant plots? - // std::cout << final_energy << std::endl; double p3f = sqrt(pow(final_energy, 2) - pow(Dmass, 2)); double p3i = std::sqrt(std::pow(p1.px(), 2) + std::pow(p1.py(), 2) + std::pow(p1.pz(), 2)); double p_ratio = p3f / p3i; - - // std::cout << " " << p3f << " " << p3i << " " << p_ratio << std::endl; rk::P4 pf(p_ratio * geom3::Vector3(p1.px(), p1.py(), p1.pz()), Dmass); std::vector & secondaries = interaction.GetSecondaryParticleRecords(); @@ -246,17 +209,6 @@ void DMesonELoss::SampleFinalState(dataclasses::CrossSectionDistributionRecord& dmeson.SetMass(pf.m()); dmeson.SetHelicity(interaction.primary_helicity); - // interaction.secondary_momenta.resize(1); - // interaction.secondary_masses.resize(1); - // interaction.secondary_helicity.resize(1); - - // interaction.secondary_momenta[0][0] = pf.e(); // p3_energy - // interaction.secondary_momenta[0][1] = pf.px(); // p3_x - // interaction.secondary_momenta[0][2] = pf.py(); // p3_y - // interaction.secondary_momenta[0][3] = pf.pz(); // p3_z - // interaction.secondary_masses[0] = pf.m(); - - // interaction.secondary_helicity[0] = interaction.primary_helicity; } double DMesonELoss::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { diff --git a/resources/Examples/DMesonExample/DIS_D.py b/resources/Examples/DMesonExample/DIS_D.py index 8fb483a9e..c89d25b00 100644 --- a/resources/Examples/DMesonExample/DIS_D.py +++ b/resources/Examples/DMesonExample/DIS_D.py @@ -1,30 +1,38 @@ import os - +import numpy as np import siren from siren.SIREN_Controller import SIREN_Controller +import nuflux # Number of events to inject -events_to_inject = 5 +events_to_inject = 10000 # Expeirment to run experiment = "IceCube" +# physical flux model to use +physical_flux = "atmos" + # Define the controller controller = SIREN_Controller(events_to_inject, experiment, seed = 1) # Particle to inject primary_type = siren.dataclasses.Particle.ParticleType.NuMu -cross_section_model = "CSMSDISSplines" +xs_option = "" # current choices are the empty string and cutoff-"" +xsfiledir = "/n/holylfs05/LABS/arguelles_delgado_lab/Everyone/miaochenjin/CharmXS/xsec_splines/M_Muon-{}105MeV".format(xs_option) -xsfiledir = siren.utilities.get_cross_section_model_path(cross_section_model) +if xs_option == "": + spline_option = "M_Muon-105MeV" # this is to account for the fact that I put the output names of this particular spline incorrectly +else: + spline_option = "" # Cross Section Model target_type = siren.dataclasses.Particle.ParticleType.Nucleon DIS_xs = siren.interactions.CharmDISFromSpline( - os.path.join(xsfiledir, "dsdxdy_nu_CC_iso.fits"), - os.path.join(xsfiledir, "sigma_nu_CC_iso.fits"), + os.path.join(xsfiledir, "{}dsdxdynu-N-cc-HERAPDF15LO_EIG_central.fits".format(spline_option)), + os.path.join(xsfiledir, "{}sigmanu-N-cc-HERAPDF15LO_EIG_central.fits".format(spline_option)), [primary_type], [target_type], "m" ) @@ -41,9 +49,35 @@ primary_physical_distributions["mass"] = mass_dist # energy distribution -edist = siren.distributions.PowerLaw(2, 1e5, 1e10) +edist = siren.distributions.PowerLaw(2, 1e4, 1e10) primary_injection_distributions["energy"] = edist -primary_physical_distributions["energy"] = edist + +# make an atmospheric flux +flux = nuflux.makeFlux('honda2006') +nu_type=nuflux.NuMu +erange = np.logspace(2,6,100) +erange_atmo = np.logspace(2,6,100) +cosrange = np.linspace(0,1,100) +atmo_flux_tables = {} +particle = nuflux.NuMu +siren_key = siren.dataclasses.Particle.ParticleType(int(particle)) +atmo_flux_tables[siren_key] = np.zeros(len(erange)) +for i,e in enumerate(erange): + f = flux.getFlux(particle,e,cosrange) + atmo_flux_tables[siren_key][i] += 0.01*np.sum(f) * 1e4 * 2 * np.pi + +# this is for weighting the events to the astrophysical flux +if physical_flux == "astro": + edist_astro = siren.distributions.PowerLaw(2, 1e4, 1e10) + norm = 1e-18 * 1e4 * 4 * np.pi # GeV^-1 m^-2 s^-1 + edist_astro.SetNormalizationAtEnergy(norm,1e5) + primary_physical_distributions["energy"] = edist_astro +elif physical_flux == "atmos": + edist_atmo = siren.distributions.TabulatedFluxDistribution(erange_atmo,atmo_flux_tables[primary_type],True) + primary_physical_distributions["energy"] = edist_atmo +else: + primary_injection_distributions["energy"] = edist + # direction distribution direction_distribution = siren.distributions.IsotropicDirection() @@ -77,7 +111,7 @@ def add_secondary_to_controller(controller, secondary_type, secondary_xsecs, sec return secondary_collection -# secondary interactions +# # secondary interactions charms = siren.dataclasses.Particle.ParticleType.Charm DPlus = siren.dataclasses.Particle.ParticleType.DPlus D0 = siren.dataclasses.Particle.ParticleType.D0 @@ -90,14 +124,15 @@ def add_secondary_to_controller(controller, secondary_type, secondary_xsecs, sec secondary_DPlus_collection = add_secondary_to_controller(controller, DPlus, D_energy_loss, DPlus_decay) secondary_D0_collection = add_secondary_to_controller(controller, D0, D_energy_loss, D0_decay) +# secondary_DPlus_collection = add_secondary_to_controller(controller, DPlus, DPlus_decay) +# secondary_D0_collection = add_secondary_to_controller(controller, D0, D0_decay) + controller.SetInteractions(primary_xs, [secondary_charm_collection, secondary_D0_collection, secondary_DPlus_collection]) +# controller.SetInteractions(primary_xs, [secondary_charm_collection]) +# controller.SetInteractions(primary_xs, []) controller.Initialize() -# def stop(datum, i): -# secondary_type = datum.record.signature.secondary_types[i] -# return ((secondary_type != siren.dataclasses.Particle.ParticleType.Charm) and (secondary_type != siren.dataclasses.Particle.ParticleType.DPlus)) - def stop(datum, i): return False @@ -105,6 +140,12 @@ def stop(datum, i): events = controller.GenerateEvents() -os.makedirs("output", exist_ok=True) +print("finished generating events") + +outdir = "/n/holylfs05/LABS/arguelles_delgado_lab/Everyone/miaochenjin/DBSearch/SIREN_outputs" +expname = "0819_LE_debug_CharmHadron_atmos" +savedir = os.path.join(outdir, expname) + +os.makedirs(savedir, exist_ok=True) -controller.SaveEvents("output/FullSim") +controller.SaveEvents("{}/{}_".format(savedir, expname)) diff --git a/resources/Examples/DMesonExample/make_plots.py b/resources/Examples/DMesonExample/make_plots.py deleted file mode 100644 index 52d4dad98..000000000 --- a/resources/Examples/DMesonExample/make_plots.py +++ /dev/null @@ -1,250 +0,0 @@ -import h5py -import numpy as np -from matplotlib import pyplot as plt -from matplotlib.colors import LogNorm -from mpl_toolkits.axes_grid1 import make_axes_locatable -from parse_output import analysis -import os - -pathname = "/n/holylfs05/LABS/arguelles_delgado_lab/Everyone/miaochenjin/DBSearch/SIREN_outputs/" -# parquetname = "0708_test/0708_test_.parquet" -expname = "0709_astro_flux" -parquetname = "{}/{}_.parquet".format(expname, expname) - - -filename = os.path.join(pathname, parquetname) -savedir = os.path.join(pathname, "plots/") - -plt.style.use('paper.mplstyle') - -sim = analysis(filename) - - -c = 3 * 1e8 # m/s -m_D0 = 1.86962 # GeV -m_Dp = 1.86484 -t_Dp = 1040 * 1e-15 # s -t_D0 = 410 * 1e-15 -m_ice = 18.02 # g/mol -N = 6.02214 * 1e23 #mol^-1 -rho = 0.917 # g/cm^3 - - -def normalize(hist, xbins, ybins): - normed_hist = np.zeros_like(hist) - for i in range(len(xbins) - 1): - tot = 0 - for j in range(len(ybins) - 1): - tot += hist[i][j] - for j in range(len(ybins) - 1): - if tot != 0: - normed_hist[i][j] = hist[i][j] / tot - else: - normed_hist[i][j] = 0 - return normed_hist - -def analytic_decay_length(E, t, m): - return E * t / ((m/(c ** 2)) * c) - -def xsec(E): - return (np.exp(1.891 + 0.2095 * np.log10(E)) - 2.157 + 1.263 * np.log10(E)) * 1e-27 # convert to cm^2 - -def analytic_free_path(E): - return (m_ice / (rho * N * xsec(E))) / 100 # convert to m - -def plot_separation_distribution(analysis_, dim = 2): - D0_energies, D0_separations, Dp_energies, Dp_separations, D0_weights, Dp_weights = analysis_.separation_analysis() - min_eng = 1e1 - max_eng = 1e9 - energy_bins = np.logspace(np.log10(min_eng), np.log10(max_eng), 20) - - energy_bins_centers = np.zeros((len(energy_bins) - 1,)) - for i in range(len(energy_bins_centers)): - energy_bins_centers[i] = np.sqrt(energy_bins[i] * energy_bins[i + 1]) - D0_analytic_lengths = analytic_decay_length(energy_bins_centers, t_D0, m_D0) - Dp_analytic_lengths = analytic_decay_length(energy_bins_centers, t_Dp, m_Dp) - - min_sep = 1e-3 - max_sep = 50000 - log_separation_bins = np.logspace(np.log10(min_sep), np.log10(max_sep), 20) - sep_bin_widths = np.sqrt(log_separation_bins[1:] * log_separation_bins[:-1]) - - if dim == 2: - - X2, Y2 = np.meshgrid(energy_bins, log_separation_bins) - log_hist_D0, _, _ = np.histogram2d(D0_energies, D0_separations, bins = (energy_bins, log_separation_bins), weights = D0_weights) - log_hist_Dp, _, _ = np.histogram2d(Dp_energies, Dp_separations, bins = (energy_bins, log_separation_bins), weights = Dp_weights) - log_hist_D0 = normalize(log_hist_D0, energy_bins, log_separation_bins) - log_hist_Dp = normalize(log_hist_Dp, energy_bins, log_separation_bins) - - fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) - - log_im1 = axes[0].pcolor(X2, Y2, log_hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - log_im2 = axes[1].pcolor(X2, Y2, log_hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - - # divider1 = make_axes_locatable(axes[0]) - # cax1 = divider1.append_axes('right', size='5%', pad=0.05) - divider2 = make_axes_locatable(axes[1]) - cax2 = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(log_im2, cax=cax2, orientation='vertical', alpha = 0.7) - # fig.colorbar(log_im1, cax=cax1, orientation='vertical', alpha = 0.7) - - axes[0].set_title(r"$D^0$ Separation") - axes[1].set_title(r"$D^+$ Separation") - - axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") - axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") - - axes[0].set_ylabel("Separation Length [m]") - - axes[0].set_xscale('log') - axes[1].set_xscale('log') - axes[0].set_yscale('log') - axes[1].set_yscale('log') - - axes[0].set_ylim(min_sep, max_sep) - axes[1].set_ylim(min_sep, max_sep) - axes[0].set_xlim(min_eng, max_eng) - axes[1].set_xlim(min_eng, max_eng) - - # also plot the analytic lines - axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) - axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#FEF3E8', alpha = 0.7) - - legend = axes[1].legend(loc = 'upper left') - for text in legend.get_texts(): - text.set_color('#FEF3E8') - - savename = os.path.join(savedir, "Separation_Length_Distribution") - - elif dim == 1: - - D0_separations.extend(Dp_weights) - D0_weights.extend(Dp_weights) - - sep_hist, _ = np.histogram(D0_separations, log_separation_bins, weights = D0_weights) - fig, ax = plt.subplots(1, 1, figsize = (8, 6)) - ax.hist(log_separation_bins[:-1], bins = log_separation_bins, weights = sep_hist / sep_bin_widths, \ - label = r"$\phi \sim E^{-2}$", \ - alpha = 0.9, color = '#D06C9D', histtype = 'step') - - savename = os.path.join(savedir, "PowerLaw_2_Separation_Length_Distribution") - ax.legend(loc = 'upper right') - ax.set_xscale('log') - ax.set_yscale('log') - ax.set_xlabel(r'$d_{\textrm{Sep}}$ [m]') - ax.set_ylabel('Normalized Weights Event Count') - - fig.savefig(savename, bbox_inches = 'tight') - -plot_separation_distribution(sim, dim = 1) -plot_separation_distribution(sim, dim = 2) - - -def plot_2d_energy_loss(analysis_): - E_D0, E_Dp, n_D0, n_Dp = analysis_.energy_loss_analysis_2d() - energy_bins = np.logspace(np.log10(min(min(E_D0), min(E_Dp))), np.log10(max(max(E_D0), max(E_Dp))), 20) - num_bins = np.linspace(-0.01, 7.99, 9) - X, Y = np.meshgrid(energy_bins, num_bins) - hist_D0, _, _ = np.histogram2d(E_D0, n_D0, bins = (energy_bins, num_bins)) - hist_Dp, _, _ = np.histogram2d(E_Dp, n_Dp, bins = (energy_bins, num_bins)) - - hist_D0 = normalize(hist_D0, energy_bins, num_bins) - hist_Dp = normalize(hist_Dp, energy_bins, num_bins) - - fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) - im1 = axes[0].pcolor(X, Y, hist_D0.T, cmap="plasma", alpha = 0.7) - im2 = axes[1].pcolor(X, Y, hist_Dp.T, cmap="plasma", alpha = 0.7) - divider2 = make_axes_locatable(axes[1]) - cax2 = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im2, cax=cax2, orientation='vertical', alpha = 0.7) - axes[0].axvline(x = 53 * 1e3, color = '#FEF3E8', alpha = 0.7, label = r"$d_{D^0} = l_{D^0}$") - axes[1].axvline(x = 22 * 1e3, color = '#FEF3E8', alpha = 0.7, label = r"$d_{D^+} = l_{D^+}$") - - axes[0].set_title(r"$D^0-p$ Collision") - axes[1].set_title(r"$D^+-p$ Collision") - - axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") - axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") - - axes[0].set_ylim(0, 8) - axes[1].set_ylim(0, 8) - - axes[0].set_ylabel(r"$n_{\textrm{Elastic Collision}}$") - axes[0].set_xscale('log') - axes[1].set_xscale('log') - legend0 = axes[0].legend() - legend1 = axes[1].legend() - for text in legend0.get_texts(): - text.set_color('#FEF3E8') - for text in legend1.get_texts(): - text.set_color('#FEF3E8') - - - fig.savefig("./plots/Energy_loss_2d_Distribution", bbox_inches = 'tight') - -# plot_2d_energy_loss(sim) -# exit(0) - -def plot_free_path_distribution(): - D0_E_list, D0_free_path_list, Dp_E_list, Dp_free_path_list = analysis_.free_path_analysis() - energy_bins = np.logspace(1.5, 9, 20) - distance_bins = np.logspace(-3, np.log10(5000), 20) - X, Y = np.meshgrid(energy_bins, distance_bins) - hist_D0, _, _ = np.histogram2d(D0_E_list, D0_free_path_list, bins = (energy_bins, distance_bins)) - hist_Dp, _, _ = np.histogram2d(Dp_E_list, Dp_free_path_list, bins = (energy_bins, distance_bins)) - - hist_D0 = normalize(hist_D0, energy_bins, distance_bins) - hist_Dp = normalize(hist_Dp, energy_bins, distance_bins) - - energy_bins_centers = np.zeros((len(energy_bins) - 1,)) - for i in range(len(energy_bins_centers)): - energy_bins_centers[i] = np.sqrt(energy_bins[i] * energy_bins[i + 1]) - D0_analytic_lengths = analytic_free_path(energy_bins_centers) - Dp_analytic_lengths = analytic_free_path(energy_bins_centers) - - - fig, axes = plt.subplots(nrows = 1, ncols = 2, figsize = (11, 5)) - im1 = axes[0].pcolor(X, Y, hist_D0.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - im2 = axes[1].pcolor(X, Y, hist_Dp.T, cmap="plasma", alpha = 0.7, vmin=0, vmax=1) - divider2 = make_axes_locatable(axes[1]) - cax2 = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im2, cax=cax2, orientation='vertical', alpha = 0.7) - - axes[0].set_title(r"$D^0-p$ Free Path") - axes[1].set_title(r"$D^+-p$ Free Path") - - axes[0].set_xlabel(r"$E_{D^0}$ [GeV]") - axes[1].set_xlabel(r"$E_{D^+}$ [GeV]") - - axes[0].plot(energy_bins_centers, D0_analytic_lengths, color = '#FEF3E8', alpha = 0.7) - axes[1].plot(energy_bins_centers, Dp_analytic_lengths, label = r"$l = \frac{m_{\textrm{ice}}}{\rho N_A \sigma(E)}$", color = '#FEF3E8', alpha = 0.7) - - # also plot the decay lengths to explain low energy increase - D0_decay_analytic_lengths = analytic_decay_length(energy_bins_centers, t_D0, m_D0) - Dp_decay_analytic_lengths = analytic_decay_length(energy_bins_centers, t_Dp, m_Dp) - - axes[0].plot(energy_bins_centers, D0_decay_analytic_lengths, label = r"$d = \frac{E \tau}{mc}$", color = '#A597B6', alpha = 0.7) - axes[1].plot(energy_bins_centers, Dp_decay_analytic_lengths, color = '#A597B6', alpha = 0.7) - - axes[0].set_ylabel(r"$l_{\textrm{Free}}$") - axes[0].set_xscale('log') - axes[1].set_xscale('log') - axes[0].set_yscale('log') - axes[1].set_yscale('log') - - axes[0].set_ylim(1e-3, 5000) - axes[1].set_ylim(1e-3, 5000) - - legend0 = axes[0].legend(loc = 'upper left') - for text in legend0.get_texts(): - text.set_color('#A597B6') - - legend1 = axes[1].legend(loc = 'upper left') - for text in legend1.get_texts(): - text.set_color('#FEF3E8') - - fig.savefig("./plots/Free_Path_Distribution", bbox_inches = 'tight') - return - -# plot_free_path_distribution() \ No newline at end of file From 9f5b7d9d1bb543ea22962d0862f0c7b8cf43655c Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Thu, 22 Aug 2024 17:47:18 -0400 Subject: [PATCH 07/11] Delete resources/Examples/DMesonExample/paper.mplstyle --- .../Examples/DMesonExample/paper.mplstyle | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 resources/Examples/DMesonExample/paper.mplstyle diff --git a/resources/Examples/DMesonExample/paper.mplstyle b/resources/Examples/DMesonExample/paper.mplstyle deleted file mode 100644 index 77b5af0b3..000000000 --- a/resources/Examples/DMesonExample/paper.mplstyle +++ /dev/null @@ -1,30 +0,0 @@ -figure.figsize : 5, 5 # figure size in inches -savefig.dpi : 600 # figure dots per inch - -font.size: 20 -font.family: serif -font.serif: Computer Modern, Latin Modern Roman, Bitstream Vera Serif -text.usetex: True - -axes.prop_cycle: cycler('color', ['29A2C6','FF6D31','73B66B','9467BD','FFCB18', 'EF597B']) -axes.grid: False - -image.cmap : plasma - -lines.linewidth: 2 -patch.linewidth: 2 -xtick.labelsize: large -ytick.labelsize: large -xtick.minor.visible: True # visibility of minor ticks on x-axis -ytick.minor.visible: True # visibility of minor ticks on y-axis -xtick.major.size: 6 # major tick size in points -xtick.minor.size: 3 # minor tick size in points -ytick.major.size: 6 # major tick size in points -ytick.minor.size: 3 # minor tick size in points -xtick.major.width: 1 -xtick.minor.width: 1 -ytick.major.width: 1 -ytick.minor.width: 1 - -legend.frameon: False -legend.fontsize: 16 From a9c3ac1a6b173bc51b16f2f9609d9827858d1c52 Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Thu, 22 Aug 2024 17:47:25 -0400 Subject: [PATCH 08/11] Delete resources/Examples/DMesonExample/parse_output.py --- .../Examples/DMesonExample/parse_output.py | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 resources/Examples/DMesonExample/parse_output.py diff --git a/resources/Examples/DMesonExample/parse_output.py b/resources/Examples/DMesonExample/parse_output.py deleted file mode 100644 index c7a79fde5..000000000 --- a/resources/Examples/DMesonExample/parse_output.py +++ /dev/null @@ -1,108 +0,0 @@ -import pandas as pd -import h5py -import numpy as np -from matplotlib import pyplot as plt -from matplotlib import pyplot as plt -from matplotlib.colors import LogNorm -from mpl_toolkits.axes_grid1 import make_axes_locatable - -filename = "output/FullSim.parquet" - -def normalize(hist, xbins, ybins): - normed_hist = np.zeros_like(hist) - for i in range(len(xbins) - 1): - tot = 0 - for j in range(len(ybins) - 1): - tot += hist[i][j] - for j in range(len(ybins) - 1): - if tot != 0: - normed_hist[i][j] = hist[i][j] / tot - else: - normed_hist[i][j] = 0 - return normed_hist - -def mass(p): - return np.sqrt(p[0] ** 2 - (p[1] ** 2 + p[2] ** 2 + p[3] ** 2)) - -def decay_length(v1, v2): - return np.sqrt((v1[0] - v2[0]) ** 2 + (v1[1] - v2[1]) ** 2 + (v1[2] - v2[2]) ** 2) - -def extract_Q2(pe, pnu): - return (pe[0] + pnu[0]) ** 2 - ((pe[1] + pnu[1]) ** 2 + (pe[2] + pnu[2]) ** 2 + (pe[3] + pnu[3]) ** 2) - -def add_to_dict(list, dictionary): - for item in list: - if item in dictionary: - dictionary[item] += 1 - else: dictionary[item] = 1 - -class event: - def __init__(self, row) -> None: - self.row = row - self.num_interaction = row["num_interactions"] - - def DTYPE(self) -> int: - # type of charmed meson is the second one - return int(self.row["secondary_types"][1][1]) - - def D_DECAY_LEN(self) -> float: - # the first vertex is 0th, the decay vertex is the last one - return decay_length(self.row["vertex"][0], self.row["vertex"][-1]) - -class analysis: - def __init__(self, f) -> None: - self.df = pd.read_parquet(f) - self.num_events = len(self.df["event_weight"]) - # print("Initializing... There are {} events".format(f.attrs["num_events"])) - self.num_interactions = {} - self.secondary_types = {} - - def separation_analysis(self): - D0_energies = [] - D0_separations = [] - Dp_energies = [] - Dp_separations = [] - D0_weights = [] - Dp_weights = [] - for i in range(self.num_events): - cur_event = event(self.df.iloc[i]) - print("{}/{}".format(i, self.num_events), end = '\r') - # check if current event is D0 or D+ - if cur_event.DTYPE() == 421: # This is D0 - # extract the vertex separations - D0_separations.append(cur_event.D_DECAY_LEN()) - D0_energies.append(cur_event.row["primary_momentum"][2][0]) - D0_weights.append(cur_event.row["event_weight"]) - - elif cur_event.DTYPE() == 411: # This is D+ - # extract the vertex separations - Dp_separations.append(cur_event.D_DECAY_LEN()) - Dp_energies.append(cur_event.row["primary_momentum"][2][0]) - Dp_weights.append(cur_event.row["event_weight"]) - - return D0_energies, D0_separations, Dp_energies, Dp_separations, D0_weights, Dp_weights - - def energy_loss_analysis_2d(self): - E_D0 = [] - E_Dp = [] - n_D0 = [] - n_Dp = [] - w_D0 = [] - w_Dp = [] - for i in range(self.num_events): - cur_event = event(self.df.iloc[i]) - print("{}/{}".format(i, self.num_events), end = '\r') - if cur_event.DTYPE() == 421: # This is D0 - # extract the vertex separations - n_D0.append(cur_event.row["num_interactions"] - 3) - E_D0.append(cur_event.row["primary_momentum"][2][0]) - w_D0.append(cur_event.row["event_weight"]) - - elif cur_event.DTYPE() == 411: # This is D+ - # extract the vertex separations - n_Dp.append(cur_event.row["num_interactions"] - 3) - E_Dp.append(cur_event.row["primary_momentum"][2][0]) - w_Dp.append(cur_event.row["event_weight"]) - - return E_D0, E_Dp, n_D0, n_Dp, w_D0, w_Dp - From 8ea969d938ecfe7057f015e0f4e65d9e85c408dc Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Wed, 13 Nov 2024 13:55:47 -0500 Subject: [PATCH 09/11] update handling of numerical instabilities --- .../private/pybindings/dataclasses.cxx | 1 + .../SecondaryBoundedVertexDistribution.cxx | 5 + .../SecondaryPhysicalVertexDistribution.cxx | 5 + projects/injection/private/Injector.cxx | 4 + projects/injection/private/Weighter.cxx | 36 + projects/injection/private/WeightingUtils.cxx | 19 +- .../public/SIREN/injection/Weighter.tcc | 23 +- .../private/CharmDISFromSpline.cxx | 133 +++- .../private/CharmHadronization.cxx | 11 + projects/interactions/private/DMesonELoss.cxx | 4 + .../private/QuarkDISFromSpline.cxx | 709 ++++++++++++++++++ .../private/pybindings/CharmDISFromSpline.h | 1 + .../SIREN/interactions/CharmDISFromSpline.h | 4 + .../SIREN/interactions/QuarkDISFromSpline.h | 166 ++++ python/SIREN_Controller.py | 7 + 15 files changed, 1107 insertions(+), 21 deletions(-) create mode 100644 projects/interactions/private/QuarkDISFromSpline.cxx create mode 100644 projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h diff --git a/projects/dataclasses/private/pybindings/dataclasses.cxx b/projects/dataclasses/private/pybindings/dataclasses.cxx index a3a74f06f..2e004eabc 100644 --- a/projects/dataclasses/private/pybindings/dataclasses.cxx +++ b/projects/dataclasses/private/pybindings/dataclasses.cxx @@ -124,6 +124,7 @@ PYBIND11_MODULE(dataclasses,m) { .def(init<>()) .def("__str__", [](InteractionRecord const & r) { std::stringstream ss; ss << r; return ss.str(); }) .def_readwrite("signature",&InteractionRecord::signature) + .def_readwrite("primary_initial_position",&InteractionRecord::primary_initial_position) .def_readwrite("primary_mass",&InteractionRecord::primary_mass) .def_readwrite("primary_momentum",&InteractionRecord::primary_momentum) .def_readwrite("primary_helicity",&InteractionRecord::primary_helicity) diff --git a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx index 43705a3ea..55bdd786e 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryBoundedVertexDistribution.cxx @@ -188,6 +188,11 @@ double SecondaryBoundedVertexDistribution::GenerationProbability(std::shared_ptr prob_density = interaction_density * exp(-log_one_minus_exp_of_negative(total_interaction_depth) - traversed_interaction_depth); } + if (prob_density == 0) { + std::cout << "observed prob density 0 in physical vertex under process " << record.signature.primary_type << " to " + << record.signature.secondary_types[0] << " with total depth " << total_interaction_depth << std::endl; + } + return prob_density; } diff --git a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx index f5d327b7a..c758fc3ba 100644 --- a/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx +++ b/projects/distributions/private/secondary/vertex/SecondaryPhysicalVertexDistribution.cxx @@ -148,6 +148,11 @@ double SecondaryPhysicalVertexDistribution::GenerationProbability(std::shared_pt prob_density = interaction_density * exp(-log_one_minus_exp_of_negative(total_interaction_depth) - traversed_interaction_depth); } + if (prob_density == 0) { + std::cout << "observed prob density 0 in physical vertex under process " << record.signature.primary_type << " to " + << record.signature.secondary_types[0] << " with total depth " << total_interaction_depth << std::endl; + } + return prob_density; } diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index a77338cff..7ca32979c 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -159,6 +159,8 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record throw(siren::utilities::InjectionFailure("No particle interaction!")); } + //std::cout << "in sample cross section" << std::endl; + std::set const & possible_targets = interactions->TargetTypes(); siren::math::Vector3D interaction_vertex( @@ -285,6 +287,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record record.target_mass = detector_model->GetTargetMass(record.signature.target_type); siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); if(r <= xsec_prob) { + //std::cout << "going into sampel final state" << std::endl; matching_cross_sections[index]->SampleFinalState(xsec_record, random); } else { matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); @@ -339,6 +342,7 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { while(true) { tries += 1; try { + //std::cout << "generating primary process" << std::endl; siren::dataclasses::PrimaryDistributionRecord primary_record(primary_process->GetPrimaryType()); for(auto & distribution : primary_process->GetPrimaryInjectionDistributions()) { distribution->Sample(random, detector_model, primary_process->GetInteractions(), primary_record); diff --git a/projects/injection/private/Weighter.cxx b/projects/injection/private/Weighter.cxx index bd166909a..1aad0dc47 100644 --- a/projects/injection/private/Weighter.cxx +++ b/projects/injection/private/Weighter.cxx @@ -110,9 +110,11 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c double inv_weight = 0; for(unsigned int idx = 0; idx < injectors.size(); ++idx) { + // std::cout << "New Event" << std::endl; double physical_probability = 1.0; double generation_probability = injectors[idx]->EventsToInject();//GenerationProbability(tree); for(auto const & datum : tree.tree) { + // std::cout << "new depth " << datum->depth() << std::endl; // skip weighting if record contains hadronization if (datum->record.signature.target_type == siren::dataclasses::Particle::ParticleType::Hadronization) { continue; @@ -122,6 +124,17 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c bounds = injectors[idx]->PrimaryInjectionBounds(datum->record); physical_probability *= primary_process_weighters[idx]->PhysicalProbability(bounds, datum->record); generation_probability *= primary_process_weighters[idx]->GenerationProbability(*datum); + // for debugging purposes: nan weights are frequnetly detected + if (physical_probability == 0) { + std::cout << "zero physics depth 0: " << datum->record.signature.primary_type << std::endl; + } else if (std::isinf(physical_probability)) { + std::cout << "inf physics depth 0: " << datum->record.signature.primary_type << std::endl; + } + if (generation_probability == 0) { + std::cout << "zero gen depth 0: " << datum->record.signature.primary_type << std::endl; + } else if (std::isinf(generation_probability)) { + std::cout << "inf gen depth 0: " << datum->record.signature.primary_type << std::endl; + } } else { try { @@ -130,6 +143,16 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c double gen_prob = secondary_process_weighter_maps[idx].at(datum->record.signature.primary_type)->GenerationProbability(*datum); physical_probability *= phys_prob; generation_probability *= gen_prob; + // if (phys_prob == 0) { + // std::cout << "zero physics: " << datum->record.signature.primary_type << std::endl; + // } else if (std::isinf(phys_prob)) { + // std::cout << "inf physics: " << datum->record.signature.primary_type << std::endl; + // } + // if (gen_prob == 0) { + // std::cout << "zero gen: " << datum->record.signature.primary_type << std::endl; + // } else if (std::isinf(gen_prob)) { + // std::cout << "inf gen: " << datum->record.signature.primary_type << std::endl; + // } } catch(const std::out_of_range& oor) { std::cout << "Out of Range error: " << oor.what() << '\n'; return 0; @@ -137,6 +160,19 @@ double Weighter::EventWeight(siren::dataclasses::InteractionTree const & tree) c } } inv_weight += generation_probability / physical_probability; + + // if (physical_probability == 0) { + // std::cout << "Event has 0 physical probability, leading to: " << inv_weight << " " << 1./inv_weight << std::endl; + // } else if (physical_probability != physical_probability) { + // std::cout << "Event has inf physical probability, leading to: " << inv_weight << " " << 1./inv_weight << std::endl; + // } + // if (generation_probability == 0) { + // std::cout << "Event has 0 generation probability, leading to: " << inv_weight << " " << 1./inv_weight << std::endl; + // } else if (generation_probability != generation_probability) { + // std::cout << "Event has inf generation probability, leading to: " << inv_weight << " " << 1./inv_weight << std::endl; + // } + // std::cout << "gen and physics prob is " << generation_probability << " " << physical_probability << std::endl; + // std::cout << "inverse weight and final weight is " << inv_weight << " " << 1./inv_weight << std::endl; } return 1./inv_weight; } diff --git a/projects/injection/private/WeightingUtils.cxx b/projects/injection/private/WeightingUtils.cxx index 118e9139d..0b92b5cc7 100644 --- a/projects/injection/private/WeightingUtils.cxx +++ b/projects/injection/private/WeightingUtils.cxx @@ -69,15 +69,30 @@ double CrossSectionProbability(std::shared_ptr signatures = cross_section->GetPossibleSignaturesFromParents(record.signature.primary_type, target); for(auto const & signature : signatures) { + // check here for 0 generation probability fake_record.signature = signature; fake_record.target_mass = detector_model->GetTargetMass(target); // Add total cross section times density to the total prob - double target_prob = target_density * cross_section->TotalCrossSection(fake_record); + double total_xs = cross_section->TotalCrossSection(fake_record); + double target_prob = target_density * total_xs; + // if (total_xs == 0) { + // std::cout << "total cross section give 0 for process of " << record.signature.primary_type << std::endl; + // std::cout << "for signature " << fake_record.signature << std::endl; + // } else if (std::isinf(total_xs)) { + // std::cout << "total cross section give inf for process of " << record.signature.primary_type << std::endl; + // std::cout << "for signature " << fake_record.signature << std::endl; + // } total_prob += target_prob; // Add up total cross section times density times final state prob for matching signatures if(signature == record.signature) { // selected_prob += target_prob; - selected_final_state += target_prob * cross_section->FinalStateProbability(record); + double final_prob = cross_section->FinalStateProbability(record); + // if (final_prob == 0) { + // std::cout << "final state prob give 0 for process of " << record.signature.primary_type << std::endl; + // } else if (std::isinf(final_prob)) { + // std::cout << "final state prob give inf for process of " << record.signature.primary_type << std::endl; + // } + selected_final_state += target_prob * final_prob; } } } diff --git a/projects/injection/public/SIREN/injection/Weighter.tcc b/projects/injection/public/SIREN/injection/Weighter.tcc index fa85d0f55..f153be6fa 100644 --- a/projects/injection/public/SIREN/injection/Weighter.tcc +++ b/projects/injection/public/SIREN/injection/Weighter.tcc @@ -204,16 +204,28 @@ double ProcessWeighter::PhysicalProbability(std::tupleGetInteractions(), record); + // if (prob == 0) { + // std::cout << "XSec probability is 0" << std::endl; + // } physical_probability *= prob; for(auto physical_dist : unique_phys_distributions) { physical_probability *= physical_dist->GenerationProbability(detector_model, phys_process->GetInteractions(), record); + // if (physical_dist->GenerationProbability(detector_model, phys_process->GetInteractions(), record) == 0) { + // std::cout << "physical dist Generation probablity is 0" << std::endl; + // } } return normalization * physical_probability; @@ -222,10 +234,19 @@ double ProcessWeighter::PhysicalProbability(std::tuple double ProcessWeighter::GenerationProbability(siren::dataclasses::InteractionTreeDatum const & datum ) const { double gen_probability = siren::injection::CrossSectionProbability(detector_model, inj_process->GetInteractions(), datum.record); - + // if (gen_probability == 0) { + // std::cout << "Gen Cross section probability is 0" << std::endl; + // } for(auto gen_dist : unique_gen_distributions) { + // if (gen_dist->GenerationProbability(detector_model, inj_process->GetInteractions(), datum.record) == 0) { + // std::cout << "generation dist gen probability is 0" << std::endl; + // } gen_probability *= gen_dist->GenerationProbability(detector_model, inj_process->GetInteractions(), datum.record); } + + // if (gen_probability == 0) { + // std::cout << "tcc file gen prob is 0" << std::endl; + // } return gen_probability; } diff --git a/projects/interactions/private/CharmDISFromSpline.cxx b/projects/interactions/private/CharmDISFromSpline.cxx index 42189f5a4..9844231ed 100644 --- a/projects/interactions/private/CharmDISFromSpline.cxx +++ b/projects/interactions/private/CharmDISFromSpline.cxx @@ -10,6 +10,11 @@ #include // for vector #include // for assert #include // for size_t +#include +#include +#include +#include +#include #include // for P4, Boost #include // for Vector3 @@ -109,6 +114,10 @@ void CharmDISFromSpline::SetUnits(std::string units) { } } +void CharmDISFromSpline::SetInteractionType(int interaction) { + interaction_type_ = interaction; +} + bool CharmDISFromSpline::equal(CrossSection const & other) const { const CharmDISFromSpline* x = dynamic_cast(&other); @@ -186,6 +195,7 @@ double CharmDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton void CharmDISFromSpline::ReadParamsFromSplineTable() { // returns true if successfully read target mass bool mass_good = differential_cross_section_.read_key("TARGETMASS", target_mass_); + if (mass_good) {std::cout << "read target mass!!" << std::endl;} // for debugging purposes // returns true if successfully read interaction type bool int_good = differential_cross_section_.read_key("INTERACTION", interaction_type_); // returns true if successfully read minimum Q2 @@ -201,6 +211,8 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { minimum_Q2_ = 1; } + std::cout << "Q2 good status is " << q2_good << "and is set to " << minimum_Q2_; + if(!mass_good) { if(int_good) { if(interaction_type_ == 1 or interaction_type_ == 2) { @@ -223,6 +235,8 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { } } } + std::cout << "target mass is " << target_mass_ << std::endl; + } void CharmDISFromSpline::InitializeSignatures() { @@ -282,8 +296,10 @@ double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord cons double primary_energy; primary_energy = interaction.primary_momentum[0]; // if we are below threshold, return 0 - if(primary_energy < InteractionThreshold(interaction)) + if(primary_energy < InteractionThreshold(interaction)) { + std::cout << "DIS::interaction threshold not satisfied" << std::endl; return 0; + } return TotalCrossSection(primary_type, primary_energy); } @@ -305,6 +321,9 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr total_cross_section_.searchcenters(&log_energy, ¢er); double log_xs = total_cross_section_.ndsplineeval(&log_energy, ¢er, 0); + if (std::pow(10.0, log_xs) == 0) { + std::cout << "DIS::cross section evaluated to 0" << std::endl; + } return unit * std::pow(10.0, log_xs); } @@ -326,16 +345,29 @@ double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionReco rk::P4 q = p1 - p3; double Q2 = -q.dot(q); - double y = 1.0 - p2.dot(p3) / p2.dot(p1); - double x = Q2 / (2.0 * p2.dot(q)); - // apply slow scaling here - // double slow_scale = 1 + pow(siren::utilities::Constants::CharmMass, 2) / pow(Q2, 2); - // double xi = x * slow_scale; + double x, y; double lepton_mass = GetLeptonMass(interaction.signature.secondary_types[lepton_index]); - // return DifferentialCrossSection(primary_energy, xi, y, lepton_mass, Q2); + + y = 1.0 - p2.dot(p3) / p2.dot(p1); + x = Q2 / (2.0 * p2.dot(q)); + double log_energy = log10(primary_energy); + std::array coordinates{{log_energy, log10(x), log10(y)}}; + std::array centers; + + + if (Q2 < minimum_Q2_ || !kinematicallyAllowed(x, y, primary_energy, target_mass_, lepton_mass) + || !differential_cross_section_.searchcenters(coordinates.data(), centers.data())) { + // std::cout << "weighting: revert back to saved x and y" << std::endl; + double E1_lab = interaction.interaction_parameters.at("energy"); + double E2_lab = p2.e(); + x = interaction.interaction_parameters.at("bjorken_x"); + y = interaction.interaction_parameters.at("bjorken_y"); + Q2 = 2. * E1_lab * E2_lab * x * y; + } return DifferentialCrossSection(primary_energy, x, y, lepton_mass, Q2); + } double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { @@ -343,11 +375,16 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou // check preconditions if(log_energy < differential_cross_section_.lower_extent(0) || log_energy>differential_cross_section_.upper_extent(0)) + {std::cout << "Diff xsec: not in bounds" << std::endl; + return 0.0;} + if(x <= 0 || x >= 1) { + std::cout << "x is out of bounds with x = " << x << std::endl; return 0.0; - if(x <= 0 || x >= 1) - return 0.0; - if(y <= 0 || y >= 1) + } + if(y <= 0 || y >= 1){ + std::cout << "y is out of bounds with x = " << y << std::endl; return 0.0; + } // we assume that: // the target is stationary so its energy is just its mass @@ -355,20 +392,27 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou if(std::isnan(Q2)) { Q2 = 2.0 * energy * target_mass_ * x * y; } - if(Q2 < minimum_Q2_) // cross section not calculated, assumed to be zero + if(Q2 < minimum_Q2_) { + std::cout << "Q2 is smaller than minimum Q2 with " << Q2 << " < " << minimum_Q2_ << std::endl; return 0; + } // cross section not calculated, assumed to be zero if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + std::cout << "not kinematically allowed!" << std::endl; return 0; } std::array coordinates{{log_energy, log10(x), log10(y)}}; std::array centers; - if(!differential_cross_section_.searchcenters(coordinates.data(), centers.data())) + if(!differential_cross_section_.searchcenters(coordinates.data(), centers.data())) { + std::cout << "search centers failed!" << std::endl; return 0; + } double result = pow(10., differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0)); assert(result >= 0); - - + if (std::isinf(result)) { + std::cout << "energy, x, y, Q2 are " << energy << " " << x << " " << y << " " << Q2 << " " << std::endl; + std::cout << "spline value read is " << differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0) << std::endl; + } return unit * result; } @@ -519,12 +563,14 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR if(accept) { kin_vars = test_kin_vars; cross_section = test_cross_section; + // std::cout << "trial Q is" << trialQ << std::endl; } } - + //////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////// double final_x = pow(10., kin_vars[1]); double final_y = pow(10., kin_vars[2]); - record.interaction_parameters.clear(); record.interaction_parameters["energy"] = E1_lab; record.interaction_parameters["bjorken_x"] = final_x; @@ -534,8 +580,54 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR double p1x_lab = std::sqrt(p1_lab.px() * p1_lab.px() + p1_lab.py() * p1_lab.py() + p1_lab.pz() * p1_lab.pz()); double pqx_lab = (m1*m1 + m3*m3 + 2 * p1x_lab * p1x_lab + Q2 + 2 * E1_lab * E1_lab * (final_y - 1)) / (2.0 * p1x_lab); double momq_lab = std::sqrt(m1*m1 + p1x_lab*p1x_lab + Q2 + E1_lab * E1_lab * (final_y * final_y - 1)); - double pqy_lab = std::sqrt(momq_lab*momq_lab - pqx_lab *pqx_lab); - double Eq_lab = E1_lab * final_y; + double pqy_lab, Eq_lab; + + if (pqx_lab>momq_lab){ + // if current setting does not work, start looping through scalings + int maxIterations = 10; + int iteration = 0; + double p1_lab_x = p1_lab.px(); + double p1_lab_y = p1_lab.py(); + double p1_lab_z = p1_lab.pz(); + // loop to resolve precision issue + while (iteration <= maxIterations) { + Q2 = 2. * E1_lab * E2_lab * pow(10.0, kin_vars[1] + kin_vars[2]); + p1x_lab = std::sqrt(p1_lab_x * p1_lab_x + p1_lab_y * p1_lab_y + p1_lab_z * p1_lab_z); + pqx_lab = (m1*m1 + m3*m3 + 2 * p1x_lab * p1x_lab + Q2 + 2 * E1_lab * E1_lab * (final_y - 1)) / (2.0 * p1x_lab); + momq_lab = std::sqrt(m1*m1 + p1x_lab*p1x_lab + Q2 + E1_lab * E1_lab * (final_y * final_y - 1)); + if (pqx_lab>momq_lab){ + // std::cout << "triggered on " << momq_lab << " and " << pqx_lab << std::endl; + //scale down + E1_lab /= 10; + E2_lab /= 10; + p1_lab_x /= 10; + p1_lab_y /= 10; + p1_lab_z /= 10; + m1 /= 10; + m3 /= 10; + //iteration += 1 to scale back + iteration += 1; + continue; + } + pqy_lab = std::sqrt((momq_lab + pqx_lab) * (momq_lab - pqx_lab)); + // std::cout << "finished with " << iteration << " iterations and " << momq_lab << " and " << pqx_lab << std::endl; + break; + } + // //scale back + if (iteration > 0) { + // std::cout << "scaling back with " << pow(10.0, iteration); + E1_lab *= pow(10.0, iteration); + E2_lab *= pow(10.0, iteration); + p1_lab_x *= pow(10.0, iteration); + p1_lab_y *= pow(10.0, iteration); + p1_lab_z *= pow(10.0, iteration); + m1 *= pow(10.0, iteration); + m3 *= pow(10.0, iteration); + // std::cout << "and finished with " << momq_lab << " and " << pqx_lab << std::endl; + } + // pqy_lab = 0; + } else {pqy_lab = std::sqrt(momq_lab*momq_lab - pqx_lab *pqx_lab);} + Eq_lab = E1_lab * final_y; geom3::UnitVector3 x_dir = geom3::UnitVector3::xAxis(); geom3::Vector3 p1_mom = p1_lab.momentum(); @@ -571,10 +663,15 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR double CharmDISFromSpline::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { double dxs = DifferentialCrossSection(interaction); + // if (dxs == 0) { + // std::cout << "diff xsec gives 0" << std::endl; + // } double txs = TotalCrossSection(interaction); if(dxs == 0) { return 0.0; } else { + // if (txs == 0) {std::cout << "wtf??? txs is 0 in final state prob" << txs << std::endl;} + // if (std::isinf(dxs)) {std::cout << "dxs is inf in final state prob" << std::endl;} return dxs / txs; } } diff --git a/projects/interactions/private/CharmHadronization.cxx b/projects/interactions/private/CharmHadronization.cxx index 3da05db72..008b608ff 100644 --- a/projects/interactions/private/CharmHadronization.cxx +++ b/projects/interactions/private/CharmHadronization.cxx @@ -173,8 +173,19 @@ void CharmHadronization::SampleFinalState(dataclasses::CrossSectionDistributionR double z; double ECH; + // add a maximum number of trials in the while loop + int max_sampling = 100; + int sampling = 0; + // sample again if this eenrgy is not kinematically allowed do { + sampling += 1; + if (sampling > max_sampling) { + std::cout << "energy of the charm is " << Ec << " and momentum is " << p3c << std::endl; + std::cout << "desired mass of hadron is " << mCH << std::endl; + throw(siren::utilities::InjectionFailure("Failed to sample hadronization!")); + break; + } randValue = random->Uniform(0,1); z = inverseCdfTable(randValue); ECH = z * Ec; diff --git a/projects/interactions/private/DMesonELoss.cxx b/projects/interactions/private/DMesonELoss.cxx index 5779d77f4..6454c3a63 100644 --- a/projects/interactions/private/DMesonELoss.cxx +++ b/projects/interactions/private/DMesonELoss.cxx @@ -115,6 +115,10 @@ double DMesonELoss::TotalCrossSection(siren::dataclasses::Particle::ParticleType // current implementation uses only > 1PeV data double xsec = exp(1.891 + 0.205 * log_energy) - 2.157 + 1.264 * log_energy; + if (xsec == 0) { + std::cout << "DMesonELoss total xsec gives 0 prob!" << std::endl; + } + return xsec * mb_to_cm2; } diff --git a/projects/interactions/private/QuarkDISFromSpline.cxx b/projects/interactions/private/QuarkDISFromSpline.cxx new file mode 100644 index 000000000..9844231ed --- /dev/null +++ b/projects/interactions/private/QuarkDISFromSpline.cxx @@ -0,0 +1,709 @@ +#include "SIREN/interactions/CharmDISFromSpline.h" + +#include // for map, opera... +#include // for set, opera... +#include // for array +#include // for pow, log10 +#include // for tie, opera... +#include // for allocator +#include // for basic_string +#include // for vector +#include // for assert +#include // for size_t +#include +#include +#include +#include +#include + +#include // for P4, Boost +#include // for Vector3 + +#include // for splinetable +//#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSection +#include "SIREN/dataclasses/InteractionRecord.h" // for Interactio... +#include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/utilities/Random.h" // for SIREN_random +#include "SIREN/utilities/Constants.h" // for electronMass + +namespace siren { +namespace interactions { + +namespace { +///Check whether a given point in phase space is physically realizable. +///Based on equations 6-8 of http://dx.doi.org/10.1103/PhysRevD.66.113007 +///S. Kretzer and M. H. Reno +///"Tau neutrino deep inelastic charged current interactions" +///Phys. Rev. D 66, 113007 +///\param x Bjorken x of the interaction +///\param y Bjorken y of the interaction +///\param E Incoming neutrino in energy in the lab frame ($E_\nu$) +///\param M Mass of the target nucleon ($M_N$) +///\param m Mass of the secondary lepton ($m_\tau$) +bool kinematicallyAllowed(double x, double y, double E, double M, double m) { + if(x > 1) //Eq. 6 right inequality + return false; + if(x < ((m * m) / (2 * M * (E - m)))) //Eq. 6 left inequality + return false; + //denominator of a and b + double d = 2 * (1 + (M * x) / (2 * E)); + //the numerator of a (or a*d) + double ad = 1 - m * m * ((1 / (2 * M * E * x)) + (1 / (2 * E * E))); + double term = 1 - ((m * m) / (2 * M * E * x)); + //the numerator of b (or b*d) + double bd = sqrt(term * term - ((m * m) / (E * E))); + + double s = 2 * M * E; + double Q2 = s * x * y; + double Mc = siren::utilities::Constants::D0Mass; + return ((ad - bd) <= d * y and d * y <= (ad + bd)) && (Q2 / (1 - x) + pow(M, 2) >= pow(M + Mc, 2)); //Eq. 7 +} +} + +CharmDISFromSpline::CharmDISFromSpline() {} + +CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromFile(differential_filename, total_filename); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types) { + LoadFromFile(differential_filename, total_filename); + ReadParamsFromSplineTable(); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + LoadFromFile(differential_filename, total_filename); + InitializeSignatures(); + SetUnits(units); +} + +CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()) { + LoadFromFile(differential_filename, total_filename); + ReadParamsFromSplineTable(); + InitializeSignatures(); + SetUnits(units); +} + +void CharmDISFromSpline::SetUnits(std::string units) { + std::transform(units.begin(), units.end(), units.begin(), + [](unsigned char c){ return std::tolower(c); }); + if(units == "cm") { + unit = 1.0; + } else if(units == "m") { + unit = 10000.0; + } else { + throw std::runtime_error("Cross section units not supported!"); + } +} + +void CharmDISFromSpline::SetInteractionType(int interaction) { + interaction_type_ = interaction; +} + +bool CharmDISFromSpline::equal(CrossSection const & other) const { + const CharmDISFromSpline* x = dynamic_cast(&other); + + if(!x) + return false; + else + return + std::tie( + interaction_type_, + target_mass_, + minimum_Q2_, + signatures_, + primary_types_, + target_types_, + differential_cross_section_, + total_cross_section_) + == + std::tie( + x->interaction_type_, + x->target_mass_, + x->minimum_Q2_, + x->signatures_, + x->primary_types_, + x->target_types_, + x->differential_cross_section_, + x->total_cross_section_); +} + +void CharmDISFromSpline::LoadFromFile(std::string dd_crossSectionFile, std::string total_crossSectionFile) { + + differential_cross_section_ = photospline::splinetable<>(dd_crossSectionFile.c_str()); + + if(differential_cross_section_.get_ndim()!=3 && differential_cross_section_.get_ndim()!=2) + throw std::runtime_error("cross section spline has " + std::to_string(differential_cross_section_.get_ndim()) + + " dimensions, should have either 3 (log10(E), log10(x), log10(y)) or 2 (log10(E), log10(y))"); + + total_cross_section_ = photospline::splinetable<>(total_crossSectionFile.c_str()); + + if(total_cross_section_.get_ndim() != 1) + throw std::runtime_error("Total cross section spline has " + std::to_string(total_cross_section_.get_ndim()) + + " dimensions, should have 1, log10(E)"); +} + +void CharmDISFromSpline::LoadFromMemory(std::vector & differential_data, std::vector & total_data) { + differential_cross_section_.read_fits_mem(differential_data.data(), differential_data.size()); + total_cross_section_.read_fits_mem(total_data.data(), total_data.size()); +} + +double CharmDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton_type) { + int32_t lepton_number = std::abs(static_cast(lepton_type)); + double lepton_mass; + switch(lepton_number) { + case 11: + lepton_mass = siren::utilities::Constants::electronMass; + break; + case 13: + lepton_mass = siren::utilities::Constants::muonMass; + break; + case 15: + lepton_mass = siren::utilities::Constants::tauMass; + break; + case 12: + lepton_mass = 0; + case 14: + lepton_mass = 0; + case 16: + lepton_mass = 0; + break; + default: + throw std::runtime_error("Unknown lepton type!"); + } + return lepton_mass; +} + +void CharmDISFromSpline::ReadParamsFromSplineTable() { + // returns true if successfully read target mass + bool mass_good = differential_cross_section_.read_key("TARGETMASS", target_mass_); + if (mass_good) {std::cout << "read target mass!!" << std::endl;} // for debugging purposes + // returns true if successfully read interaction type + bool int_good = differential_cross_section_.read_key("INTERACTION", interaction_type_); + // returns true if successfully read minimum Q2 + bool q2_good = differential_cross_section_.read_key("Q2MIN", minimum_Q2_); + + if(!int_good) { + // assume DIS to preserve compatability with previous versions + interaction_type_ = 1; + } + + if(!q2_good) { + // assume 1 GeV^2 + minimum_Q2_ = 1; + } + + std::cout << "Q2 good status is " << q2_good << "and is set to " << minimum_Q2_; + + if(!mass_good) { + if(int_good) { + if(interaction_type_ == 1 or interaction_type_ == 2) { + target_mass_ = (siren::dataclasses::isLepton(siren::dataclasses::ParticleType::PPlus)+ + siren::dataclasses::isLepton(siren::dataclasses::ParticleType::Neutron))/2; + } else if(interaction_type_ == 3) { + target_mass_ = siren::dataclasses::isLepton(siren::dataclasses::ParticleType::EMinus); + } else { + throw std::runtime_error("Logic error. Interaction type is not 1, 2, or 3!"); + } + + } else { + if(differential_cross_section_.get_ndim() == 3) { + target_mass_ = siren::utilities::Constants::isoscalarMass; + + } else if(differential_cross_section_.get_ndim() == 2) { + target_mass_ = siren::utilities::Constants::electronMass; + } else { + throw std::runtime_error("Logic error. Spline dimensionality is not 2, or 3!"); + } + } + } + std::cout << "target mass is " << target_mass_ << std::endl; + +} + +void CharmDISFromSpline::InitializeSignatures() { + signatures_.clear(); + for(auto primary_type : primary_types_) { + dataclasses::InteractionSignature signature; + signature.primary_type = primary_type; + + if(not isNeutrino(primary_type)) { + throw std::runtime_error("This DIS implementation only supports neutrinos as primaries!"); + } + + siren::dataclasses::ParticleType charged_lepton_product = siren::dataclasses::ParticleType::unknown; + siren::dataclasses::ParticleType neutral_lepton_product = primary_type; + + if(primary_type == siren::dataclasses::ParticleType::NuE) { + charged_lepton_product = siren::dataclasses::ParticleType::EMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuEBar) { + charged_lepton_product = siren::dataclasses::ParticleType::EPlus; + } else if(primary_type == siren::dataclasses::ParticleType::NuMu) { + charged_lepton_product = siren::dataclasses::ParticleType::MuMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuMuBar) { + charged_lepton_product = siren::dataclasses::ParticleType::MuPlus; + } else if(primary_type == siren::dataclasses::ParticleType::NuTau) { + charged_lepton_product = siren::dataclasses::ParticleType::TauMinus; + } else if(primary_type == siren::dataclasses::ParticleType::NuTauBar) { + charged_lepton_product = siren::dataclasses::ParticleType::TauPlus; + } else { + throw std::runtime_error("InitializeSignatures: Unkown parent neutrino type!"); + } + + if(interaction_type_ == 1) { + signature.secondary_types.push_back(charged_lepton_product); + } else if(interaction_type_ == 2) { + signature.secondary_types.push_back(neutral_lepton_product); + } else if(interaction_type_ == 3) { + signature.secondary_types.push_back(siren::dataclasses::ParticleType::Hadrons); + } else { + throw std::runtime_error("InitializeSignatures: Unkown interaction type!"); + } + + signature.secondary_types.push_back(siren::dataclasses::ParticleType::Charm); + for(auto target_type : target_types_) { + signature.target_type = target_type; + + signatures_.push_back(signature); + + std::pair key(primary_type, target_type); + signatures_by_parent_types_[key].push_back(signature); + } + } +} + +double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord const & interaction) const { + siren::dataclasses::ParticleType primary_type = interaction.signature.primary_type; + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + double primary_energy; + primary_energy = interaction.primary_momentum[0]; + // if we are below threshold, return 0 + if(primary_energy < InteractionThreshold(interaction)) { + std::cout << "DIS::interaction threshold not satisfied" << std::endl; + return 0; + } + return TotalCrossSection(primary_type, primary_energy); +} + +double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType primary_type, double primary_energy) const { + if(not primary_types_.count(primary_type)) { + throw std::runtime_error("Supplied primary not supported by cross section!"); + } + double log_energy = log10(primary_energy); + + if(log_energy < total_cross_section_.lower_extent(0) + or log_energy > total_cross_section_.upper_extent(0)) { + throw std::runtime_error("Interaction energy ("+ std::to_string(primary_energy) + + ") out of cross section table range: [" + + std::to_string(pow(10.,total_cross_section_.lower_extent(0))) + " GeV," + + std::to_string(pow(10.,total_cross_section_.upper_extent(0))) + " GeV]"); + } + + int center; + total_cross_section_.searchcenters(&log_energy, ¢er); + + double log_xs = total_cross_section_.ndsplineeval(&log_energy, ¢er, 0); + if (std::pow(10.0, log_xs) == 0) { + std::cout << "DIS::cross section evaluated to 0" << std::endl; + } + + return unit * std::pow(10.0, log_xs); +} + +double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionRecord const & interaction) const { + rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); + rk::P4 p2(geom3::Vector3(0, 0, 0), interaction.target_mass); + double primary_energy; + primary_energy = interaction.primary_momentum[0]; + assert(interaction.signature.secondary_types.size() == 2); + unsigned int lepton_index = (isLepton(interaction.signature.secondary_types[0])) ? 0 : 1; + unsigned int other_index = 1 - lepton_index; + + std::array const & mom3 = interaction.secondary_momenta[lepton_index]; + std::array const & mom4 = interaction.secondary_momenta[other_index]; + rk::P4 p3(geom3::Vector3(mom3[1], mom3[2], mom3[3]), interaction.secondary_masses[lepton_index]); + rk::P4 p4(geom3::Vector3(mom4[1], mom4[2], mom4[3]), interaction.secondary_masses[other_index]); + + rk::P4 q = p1 - p3; + + double Q2 = -q.dot(q); + double x, y; + double lepton_mass = GetLeptonMass(interaction.signature.secondary_types[lepton_index]); + + + y = 1.0 - p2.dot(p3) / p2.dot(p1); + x = Q2 / (2.0 * p2.dot(q)); + double log_energy = log10(primary_energy); + std::array coordinates{{log_energy, log10(x), log10(y)}}; + std::array centers; + + + if (Q2 < minimum_Q2_ || !kinematicallyAllowed(x, y, primary_energy, target_mass_, lepton_mass) + || !differential_cross_section_.searchcenters(coordinates.data(), centers.data())) { + // std::cout << "weighting: revert back to saved x and y" << std::endl; + double E1_lab = interaction.interaction_parameters.at("energy"); + double E2_lab = p2.e(); + x = interaction.interaction_parameters.at("bjorken_x"); + y = interaction.interaction_parameters.at("bjorken_y"); + Q2 = 2. * E1_lab * E2_lab * x * y; + } + return DifferentialCrossSection(primary_energy, x, y, lepton_mass, Q2); + + +} + +double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { + double log_energy = log10(energy); + // check preconditions + if(log_energy < differential_cross_section_.lower_extent(0) + || log_energy>differential_cross_section_.upper_extent(0)) + {std::cout << "Diff xsec: not in bounds" << std::endl; + return 0.0;} + if(x <= 0 || x >= 1) { + std::cout << "x is out of bounds with x = " << x << std::endl; + return 0.0; + } + if(y <= 0 || y >= 1){ + std::cout << "y is out of bounds with x = " << y << std::endl; + return 0.0; + } + + // we assume that: + // the target is stationary so its energy is just its mass + // the incoming neutrino is massless, so its kinetic energy is its total energy + if(std::isnan(Q2)) { + Q2 = 2.0 * energy * target_mass_ * x * y; + } + if(Q2 < minimum_Q2_) { + std::cout << "Q2 is smaller than minimum Q2 with " << Q2 << " < " << minimum_Q2_ << std::endl; + return 0; + } // cross section not calculated, assumed to be zero + + if(!kinematicallyAllowed(x, y, energy, target_mass_, secondary_lepton_mass)) { + std::cout << "not kinematically allowed!" << std::endl; + return 0; + } + std::array coordinates{{log_energy, log10(x), log10(y)}}; + std::array centers; + if(!differential_cross_section_.searchcenters(coordinates.data(), centers.data())) { + std::cout << "search centers failed!" << std::endl; + return 0; + } + double result = pow(10., differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0)); + assert(result >= 0); + if (std::isinf(result)) { + std::cout << "energy, x, y, Q2 are " << energy << " " << x << " " << y << " " << Q2 << " " << std::endl; + std::cout << "spline value read is " << differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0) << std::endl; + } + + return unit * result; +} + +double CharmDISFromSpline::InteractionThreshold(dataclasses::InteractionRecord const & interaction) const { + // Consider implementing DIS thershold at some point + return 0; +} + +void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { + // Uses Metropolis-Hastings Algorithm! + // useful for cases where we don't know the supremum of our distribution, and the distribution is multi-dimensional + if (differential_cross_section_.get_ndim() != 3) { + throw std::runtime_error("I expected 3 dimensions in the cross section spline, but got " + std::to_string(differential_cross_section_.get_ndim()) +". Maybe your fits file doesn't have the right 'INTERACTION' key?"); + } + rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); + rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); + + // we assume that: + // the target is stationary so its energy is just its mass + // the incoming neutrino is massless, so its kinetic energy is its total energy + // double s = target_mass_ * trecord.secondary_momentarget_mass_ + 2 * target_mass_ * primary_energy; + // double s = std::pow(rk::invMass(p1, p2), 2); + + double primary_energy; + rk::P4 p1_lab; + rk::P4 p2_lab; + p1_lab = p1; + p2_lab = p2; + primary_energy = p1_lab.e(); + + unsigned int lepton_index = (isLepton(record.signature.secondary_types[0])) ? 0 : 1; + unsigned int other_index = 1 - lepton_index; + double m = GetLeptonMass(record.signature.secondary_types[lepton_index]); + + double m1 = record.primary_mass; + double m3 = m; + double E1_lab = p1_lab.e(); + double E2_lab = p2_lab.e(); + + // The out-going particle always gets at least enough energy for its rest mass + double yMax = 1 - m / primary_energy; + double logYMax = log10(yMax); + + // The minimum allowed value of y occurs when x = 1 and Q is minimized + double yMin = minimum_Q2_ / (2 * E1_lab * E2_lab); + double logYMin = log10(yMin); + // The minimum allowed value of x occurs when y = yMax and Q is minimized + // double xMin = minimum_Q2_ / ((s - target_mass_ * target_mass_) * yMax); + double xMin = minimum_Q2_ / (2 * E1_lab * E2_lab * yMax); + double logXMin = log10(xMin); + + bool accept; + + // kin_vars and its twin are 3-vectors containing [nu-energy, Bjorken X, Bjorken Y] + std::array kin_vars, test_kin_vars; + + // centers of the cross section spline tales. + std::array spline_table_center, test_spline_table_center; + + // values of cross_section from the splines. By * Bx * Spline(E,x,y) + double cross_section, test_cross_section; + + // No matter what, we're evaluating at this specific energy. + kin_vars[0] = test_kin_vars[0] = log10(primary_energy); + + // check preconditions + if(kin_vars[0] < differential_cross_section_.lower_extent(0) + || kin_vars[0] > differential_cross_section_.upper_extent(0)) + throw std::runtime_error("Interaction energy out of cross section table range: [" + + std::to_string(pow(10.,differential_cross_section_.lower_extent(0))) + " GeV," + + std::to_string(pow(10.,differential_cross_section_.upper_extent(0))) + " GeV]"); + + // sample an intial point + do { + // rejection sample a point which is kinematically allowed by calculation limits + double trialQ; + double trials = 0; + do { + if (trials >= 100) throw std::runtime_error("too much trials"); + trials += 1; + kin_vars[1] = random->Uniform(logXMin,0); + kin_vars[2] = random->Uniform(logYMin,logYMax); + trialQ = (2 * E1_lab * E2_lab) * pow(10., kin_vars[1] + kin_vars[2]); + } while(trialQ differential_cross_section_.upper_extent(1)) { + accept = false; + } + if(kin_vars[2] < differential_cross_section_.lower_extent(2) + || kin_vars[2] > differential_cross_section_.upper_extent(2)) { + accept = false; + } + + if(accept) { + // finds the centers in the cross section spline table, returns true if it's successful + // also sets the centers + accept = differential_cross_section_.searchcenters(kin_vars.data(),spline_table_center.data()); + } + } while(!accept); + + //TODO: better proposal distribution? + double measure = pow(10., kin_vars[1] + kin_vars[2]); // Bx * By + + // Bx * By * xs(E, x, y) + // evalutates the differential spline at that point + cross_section = measure*pow(10., differential_cross_section_.ndsplineeval(kin_vars.data(), spline_table_center.data(), 0)); + + // this is the magic part. Metropolis Hastings Algorithm. + // MCMC method! + const size_t burnin = 40; // converges to the correct distribution over multiple samplings. + // big number means more accurate, but slower + for(size_t j = 0; j <= burnin; j++) { + // repeat the sampling from above to get a new valid point + double trialQ; + do { + test_kin_vars[1] = random->Uniform(logXMin, 0); + test_kin_vars[2] = random->Uniform(logYMin, logYMax); + trialQ = (2 * E1_lab * E2_lab) * pow(10., test_kin_vars[1] + test_kin_vars[2]); + } while(trialQ < minimum_Q2_ || !kinematicallyAllowed(pow(10., test_kin_vars[1]), pow(10., test_kin_vars[2]), primary_energy, target_mass_, m)); + + accept = true; + if(test_kin_vars[1] < differential_cross_section_.lower_extent(1) + || test_kin_vars[1] > differential_cross_section_.upper_extent(1)) + accept = false; + if(test_kin_vars[2] < differential_cross_section_.lower_extent(2) + || test_kin_vars[2] > differential_cross_section_.upper_extent(2)) + accept = false; + if(!accept) + continue; + + accept = differential_cross_section_.searchcenters(test_kin_vars.data(), test_spline_table_center.data()); + if(!accept) + continue; + + double measure = pow(10., test_kin_vars[1] + test_kin_vars[2]); + double eval = differential_cross_section_.ndsplineeval(test_kin_vars.data(), test_spline_table_center.data(), 0); + if(std::isnan(eval)) + continue; + test_cross_section = measure * pow(10., eval); + + double odds = (test_cross_section / cross_section); + accept = (cross_section == 0 || (odds > 1.) || random->Uniform(0, 1) < odds); + + if(accept) { + kin_vars = test_kin_vars; + cross_section = test_cross_section; + // std::cout << "trial Q is" << trialQ << std::endl; + } + } + //////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////////////////// + double final_x = pow(10., kin_vars[1]); + double final_y = pow(10., kin_vars[2]); + record.interaction_parameters.clear(); + record.interaction_parameters["energy"] = E1_lab; + record.interaction_parameters["bjorken_x"] = final_x; + record.interaction_parameters["bjorken_y"] = final_y; + + double Q2 = 2 * E1_lab * E2_lab * pow(10.0, kin_vars[1] + kin_vars[2]); + double p1x_lab = std::sqrt(p1_lab.px() * p1_lab.px() + p1_lab.py() * p1_lab.py() + p1_lab.pz() * p1_lab.pz()); + double pqx_lab = (m1*m1 + m3*m3 + 2 * p1x_lab * p1x_lab + Q2 + 2 * E1_lab * E1_lab * (final_y - 1)) / (2.0 * p1x_lab); + double momq_lab = std::sqrt(m1*m1 + p1x_lab*p1x_lab + Q2 + E1_lab * E1_lab * (final_y * final_y - 1)); + double pqy_lab, Eq_lab; + + if (pqx_lab>momq_lab){ + // if current setting does not work, start looping through scalings + int maxIterations = 10; + int iteration = 0; + double p1_lab_x = p1_lab.px(); + double p1_lab_y = p1_lab.py(); + double p1_lab_z = p1_lab.pz(); + // loop to resolve precision issue + while (iteration <= maxIterations) { + Q2 = 2. * E1_lab * E2_lab * pow(10.0, kin_vars[1] + kin_vars[2]); + p1x_lab = std::sqrt(p1_lab_x * p1_lab_x + p1_lab_y * p1_lab_y + p1_lab_z * p1_lab_z); + pqx_lab = (m1*m1 + m3*m3 + 2 * p1x_lab * p1x_lab + Q2 + 2 * E1_lab * E1_lab * (final_y - 1)) / (2.0 * p1x_lab); + momq_lab = std::sqrt(m1*m1 + p1x_lab*p1x_lab + Q2 + E1_lab * E1_lab * (final_y * final_y - 1)); + if (pqx_lab>momq_lab){ + // std::cout << "triggered on " << momq_lab << " and " << pqx_lab << std::endl; + //scale down + E1_lab /= 10; + E2_lab /= 10; + p1_lab_x /= 10; + p1_lab_y /= 10; + p1_lab_z /= 10; + m1 /= 10; + m3 /= 10; + //iteration += 1 to scale back + iteration += 1; + continue; + } + pqy_lab = std::sqrt((momq_lab + pqx_lab) * (momq_lab - pqx_lab)); + // std::cout << "finished with " << iteration << " iterations and " << momq_lab << " and " << pqx_lab << std::endl; + break; + } + // //scale back + if (iteration > 0) { + // std::cout << "scaling back with " << pow(10.0, iteration); + E1_lab *= pow(10.0, iteration); + E2_lab *= pow(10.0, iteration); + p1_lab_x *= pow(10.0, iteration); + p1_lab_y *= pow(10.0, iteration); + p1_lab_z *= pow(10.0, iteration); + m1 *= pow(10.0, iteration); + m3 *= pow(10.0, iteration); + // std::cout << "and finished with " << momq_lab << " and " << pqx_lab << std::endl; + } + // pqy_lab = 0; + } else {pqy_lab = std::sqrt(momq_lab*momq_lab - pqx_lab *pqx_lab);} + Eq_lab = E1_lab * final_y; + + geom3::UnitVector3 x_dir = geom3::UnitVector3::xAxis(); + geom3::Vector3 p1_mom = p1_lab.momentum(); + geom3::UnitVector3 p1_lab_dir = p1_mom.direction(); + geom3::Rotation3 x_to_p1_lab_rot = geom3::rotationBetween(x_dir, p1_lab_dir); + + double phi = random->Uniform(0, 2.0 * M_PI); + geom3::Rotation3 rand_rot(p1_lab_dir, phi); + + rk::P4 pq_lab(Eq_lab, geom3::Vector3(pqx_lab, pqy_lab, 0)); + pq_lab.rotate(x_to_p1_lab_rot); + pq_lab.rotate(rand_rot); + + rk::P4 p3_lab((p1_lab - pq_lab).momentum(), m3); + rk::P4 p4_lab = p2_lab + pq_lab; + + rk::P4 p3; + rk::P4 p4; + p3 = p3_lab; + p4 = p4_lab; + + std::vector & secondaries = record.GetSecondaryParticleRecords(); + siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[lepton_index]; + siren::dataclasses::SecondaryParticleRecord & other = secondaries[other_index]; + + lepton.SetFourMomentum({p3.e(), p3.px(), p3.py(), p3.pz()}); + lepton.SetMass(p3.m()); + lepton.SetHelicity(record.primary_helicity); + other.SetFourMomentum({p4.e(), p4.px(), p4.py(), p4.pz()}); + other.SetMass(p4.m()); + other.SetHelicity(record.target_helicity); +} + +double CharmDISFromSpline::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { + double dxs = DifferentialCrossSection(interaction); + // if (dxs == 0) { + // std::cout << "diff xsec gives 0" << std::endl; + // } + double txs = TotalCrossSection(interaction); + if(dxs == 0) { + return 0.0; + } else { + // if (txs == 0) {std::cout << "wtf??? txs is 0 in final state prob" << txs << std::endl;} + // if (std::isinf(dxs)) {std::cout << "dxs is inf in final state prob" << std::endl;} + return dxs / txs; + } +} + +std::vector CharmDISFromSpline::GetPossiblePrimaries() const { + return std::vector(primary_types_.begin(), primary_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleSignatures() const { + return std::vector(signatures_.begin(), signatures_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleTargets() const { + return std::vector(target_types_.begin(), target_types_.end()); +} + +std::vector CharmDISFromSpline::GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const { + std::pair key(primary_type, target_type); + if(signatures_by_parent_types_.find(key) != signatures_by_parent_types_.end()) { + return signatures_by_parent_types_.at(key); + } else { + return std::vector(); + } +} + +std::vector CharmDISFromSpline::DensityVariables() const { + return std::vector{"Bjorken x", "Bjorken y"}; +} + +} // namespace interactions +} // namespace siren diff --git a/projects/interactions/private/pybindings/CharmDISFromSpline.h b/projects/interactions/private/pybindings/CharmDISFromSpline.h index 486556be4..e7349dc96 100644 --- a/projects/interactions/private/pybindings/CharmDISFromSpline.h +++ b/projects/interactions/private/pybindings/CharmDISFromSpline.h @@ -72,6 +72,7 @@ void register_CharmDISFromSpline(pybind11::module_ & m) { arg("target_types"), arg("units") = std::string("cm")) .def(self == self) + .def("SetInteractionType",&CharmDISFromSpline::SetInteractionType) .def("TotalCrossSection",overload_cast(&CharmDISFromSpline::TotalCrossSection, const_)) .def("TotalCrossSection",overload_cast(&CharmDISFromSpline::TotalCrossSection, const_)) .def("DifferentialCrossSection",overload_cast(&CharmDISFromSpline::DifferentialCrossSection, const_)) diff --git a/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h b/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h index 80f97a386..102ce4725 100644 --- a/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h +++ b/projects/interactions/public/SIREN/interactions/CharmDISFromSpline.h @@ -62,6 +62,10 @@ friend cereal::access; CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units = "cm"); void SetUnits(std::string units); + // this might be integrated later? could also make another initialization method + // problem with current implementation is that EM is not supported b/c at initialization we assume int = 1 + // this sets the isoscalar target mass + void SetInteractionType(int interaction); virtual bool equal(CrossSection const & other) const override; diff --git a/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h b/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h new file mode 100644 index 000000000..62dc88a7d --- /dev/null +++ b/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h @@ -0,0 +1,166 @@ +#pragma once +#ifndef SIREN_QuarkDISFromSpline_H +#define SIREN_QuarkDISFromSpline_H + +#include // for set +#include // for map +#include +#include // for vector +#include // for uint32_t +#include // for pair +#include +#include // for runtime... + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SIREN/interactions/CrossSection.h" // for CrossSe... +#include "SIREN/dataclasses/InteractionSignature.h" // for Interac... +#include "SIREN/dataclasses/Particle.h" // for Particle + +namespace siren { namespace dataclasses { class InteractionRecord; } } +namespace siren { namespace utilities { class SIREN_random; } } + +namespace siren { +namespace interactions { + +class QuarkDISFromSpline : public CrossSection { +friend cereal::access; +private: + photospline::splinetable<> differential_cross_section_; + photospline::splinetable<> total_cross_section_; + + std::vector signatures_; + std::set primary_types_; + std::set target_types_; + std::map> targets_by_primary_types_; + std::map, std::vector> signatures_by_parent_types_; + + int interaction_type_; + double target_mass_; + double minimum_Q2_; + + double unit; + +public: + QuarkDISFromSpline(); + QuarkDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minumum_Q2, std::set primary_types, std::set target_types, std::string units = "cm"); + QuarkDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minumum_Q2, std::vector primary_types, std::vector target_types, std::string units = "cm"); + QuarkDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minumum_Q2, std::set primary_types, std::set target_types, std::string units = "cm"); + QuarkDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units = "cm"); + QuarkDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minumum_Q2, std::vector primary_types, std::vector target_types, std::string units = "cm"); + QuarkDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units = "cm"); + + void SetUnits(std::string units); + // this might be integrated later? could also make another initialization method + // problem with current implementation is that EM is not supported b/c at initialization we assume int = 1 + // this sets the isoscalar target mass + void SetInteractionType(int interaction); + + virtual bool equal(CrossSection const & other) const override; + + double TotalCrossSection(dataclasses::InteractionRecord const &) const override; + double TotalCrossSection(siren::dataclasses::ParticleType primary, double energy) const; + double DifferentialCrossSection(dataclasses::InteractionRecord const &) const override; + double DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2=std::numeric_limits::quiet_NaN()) const; + double InteractionThreshold(dataclasses::InteractionRecord const &) const override; + void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr random) const override; + + std::vector GetPossibleTargets() const override; + std::vector GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const override; + std::vector GetPossiblePrimaries() const override; + std::vector GetPossibleSignatures() const override; + std::vector GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const override; + + virtual double FinalStateProbability(dataclasses::InteractionRecord const & record) const override; + + void LoadFromFile(std::string differential_filename, std::string total_filename); + void LoadFromMemory(std::vector & differential_data, std::vector & total_data); + + double GetMinimumQ2() const {return minimum_Q2_;}; + double GetTargetMass() const {return target_mass_;}; + int GetInteractionType() const {return interaction_type_;}; + + static double GetLeptonMass(siren::dataclasses::ParticleType lepton_type); + +public: + virtual std::vector DensityVariables() const override; + template + void save(Archive & archive, std::uint32_t const version) const { + if(version == 0) { + splinetable_buffer buf; + buf.size = 0; + auto result_obj = differential_cross_section_.write_fits_mem(); + buf.data = result_obj.first; + buf.size = result_obj.second; + + std::vector diff_blob; + diff_blob.resize(buf.size); + std::copy((char*)buf.data, (char*)buf.data + buf.size, &diff_blob[0]); + + archive(::cereal::make_nvp("DifferentialCrossSectionSpline", diff_blob)); + + buf.size = 0; + result_obj = total_cross_section_.write_fits_mem(); + buf.data = result_obj.first; + buf.size = result_obj.second; + + std::vector total_blob; + total_blob.resize(buf.size); + std::copy((char*)buf.data, (char*)buf.data + buf.size, &total_blob[0]); + + archive(::cereal::make_nvp("TotalCrossSectionSpline", total_blob)); + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(::cereal::make_nvp("TargetTypes", target_types_)); + archive(::cereal::make_nvp("InteractionType", interaction_type_)); + archive(::cereal::make_nvp("TargetMass", target_mass_)); + archive(::cereal::make_nvp("MinimumQ2", minimum_Q2_)); + archive(::cereal::make_nvp("Unit", unit)); + archive(cereal::virtual_base_class(this)); + } else { + throw std::runtime_error("QuarkDISFromSpline only supports version <= 0!"); + } + } + template + void load(Archive & archive, std::uint32_t version) { + if(version == 0) { + std::vector differential_data; + std::vector total_data; + archive(::cereal::make_nvp("DifferentialCrossSectionSpline", differential_data)); + archive(::cereal::make_nvp("TotalCrossSectionSpline", total_data)); + archive(::cereal::make_nvp("PrimaryTypes", primary_types_)); + archive(::cereal::make_nvp("TargetTypes", target_types_)); + archive(::cereal::make_nvp("InteractionType", interaction_type_)); + archive(::cereal::make_nvp("TargetMass", target_mass_)); + archive(::cereal::make_nvp("MinimumQ2", minimum_Q2_)); + archive(::cereal::make_nvp("Unit", unit)); + archive(cereal::virtual_base_class(this)); + LoadFromMemory(differential_data, total_data); + InitializeSignatures(); + } else { + throw std::runtime_error("QuarkDISFromSpline only supports version <= 0!"); + } + } +private: + void ReadParamsFromSplineTable(); + void InitializeSignatures(); +}; + +} // namespace interactions +} // namespace siren + +CEREAL_CLASS_VERSION(siren::interactions::QuarkDISFromSpline, 0); +CEREAL_REGISTER_TYPE(siren::interactions::QuarkDISFromSpline); +CEREAL_REGISTER_POLYMORPHIC_RELATION(siren::interactions::CrossSection, siren::interactions::QuarkDISFromSpline); + +#endif // SIREN_QuarkDISFromSpline_H diff --git a/python/SIREN_Controller.py b/python/SIREN_Controller.py index f754a4598..7daf7721a 100644 --- a/python/SIREN_Controller.py +++ b/python/SIREN_Controller.py @@ -480,6 +480,8 @@ def GenerateEvents(self, N=None, fill_tables_at_exit=True): prev_time = time.time() while (self.injector.InjectedEvents() < self.events_to_inject) and (count < N): print("Injecting Event %d/%d " % (count, N), end="\r") + # print("Injecting Event %d/%d " % (count, N)) # for debugging purposes + event = self.injector.GenerateEvent() self.events.append(event) t = time.time() @@ -513,6 +515,7 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, "event_global_time":[], # global time of each event "num_interactions":[], # number of interactions per event "vertex":[], # vertex of each interaction in an event + "primary_initial_position":[], # initial position of primary in each interaction of an event "in_fiducial":[], # whether or not each vertex is in the fiducial volume "primary_type":[], # primary type of each interaction "target_type":[], # target type of each interaction @@ -524,6 +527,8 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, } for ie, event in enumerate(self.events): print("Saving Event %d/%d " % (ie, len(self.events)), end="\r") + # print("Saving Event %d/%d " % (ie, len(self.events))) # for debugging purposes + t0 = time.time() datasets["event_weight"].append(self.weighter.EventWeight(event) if hasattr(self,"weighter") else 0) datasets["event_weight_time"].append(time.time()-t0) @@ -531,6 +536,7 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, datasets["event_global_time"].append(self.global_times[ie]) # add empty lists for each per interaction dataset for k in ["vertex", + "primary_initial_position", "in_fiducial", "primary_type", "target_type", @@ -543,6 +549,7 @@ def SaveEvents(self, filename, fill_tables_at_exit=True, # loop over interactions for id, datum in enumerate(event.tree): datasets["vertex"][-1].append(np.array(datum.record.interaction_vertex,dtype=float)) + datasets["primary_initial_position"][-1].append(np.array(datum.record.primary_initial_position,dtype=float)) # primary particle stuff datasets["primary_type"][-1].append(int(datum.record.signature.primary_type)) From abf68839d41dd92b138fa66fad83a9abdec45bd8 Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Tue, 19 Nov 2024 15:41:19 -0500 Subject: [PATCH 10/11] new stuff --- .../dataclasses/private/InteractionRecord.cxx | 20 + projects/dataclasses/private/Particle.cxx | 6 +- .../public/SIREN/dataclasses/Particle.h | 1 + projects/injection/private/Injector.cxx | 16 +- projects/interactions/CMakeLists.txt | 2 + .../private/CharmDISFromSpline.cxx | 2 + .../private/QuarkDISFromSpline.cxx | 367 ++++++++++++++---- .../private/pybindings/QuarkDISFromSpline.h | 90 +++++ .../private/pybindings/interactions.cxx | 3 + .../SIREN/interactions/QuarkDISFromSpline.h | 32 +- python/SIREN_Controller.py | 1 + 11 files changed, 458 insertions(+), 82 deletions(-) create mode 100644 projects/interactions/private/pybindings/QuarkDISFromSpline.h diff --git a/projects/dataclasses/private/InteractionRecord.cxx b/projects/dataclasses/private/InteractionRecord.cxx index 48c52530b..4b79b75b8 100644 --- a/projects/dataclasses/private/InteractionRecord.cxx +++ b/projects/dataclasses/private/InteractionRecord.cxx @@ -327,13 +327,22 @@ void PrimaryDistributionRecord::FinalizeAvailable(InteractionRecord & record) co } void PrimaryDistributionRecord::Finalize(InteractionRecord & record) const { + //std::cout << "starting finalize" << std::endl; + //std::cout << record.signature << std::endl; + //std::cout << record.primary_momentum[0] << std::endl; + record.signature.primary_type = type; record.primary_id = GetID(); record.interaction_vertex = GetInteractionVertex(); record.primary_initial_position = GetInitialPosition(); + //std::cout << "in primary record finalize" << std::endl; record.primary_mass = GetMass(); record.primary_momentum = GetFourMomentum(); record.primary_helicity = GetHelicity(); + //std::cout << "finished primary record finalize" << std::endl; + + //std::cout << record.signature << std::endl; + //std::cout << record.primary_momentum[0] << std::endl; } ///////////////////////////////////////// @@ -553,11 +562,16 @@ void SecondaryParticleRecord::UpdateMomentum() const { } void SecondaryParticleRecord::Finalize(InteractionRecord & record) const { + ////std::cout << "SecPartRecord::Finalize : starting for " << type << std::endl; + assert(record.signature.secondary_types.at(secondary_index) == type); record.secondary_ids.at(secondary_index) = GetID(); record.secondary_masses.at(secondary_index) = GetMass(); record.secondary_momenta.at(secondary_index) = GetFourMomentum(); record.secondary_helicities.at(secondary_index) = GetHelicity(); + ////std::cout << "SecPartRecord::Finalize : finished for " << type << " with" << std::endl; + ////std::cout << record << std::endl; + } ///////////////////////////////////////// @@ -676,6 +690,11 @@ SecondaryParticleRecord const & CrossSectionDistributionRecord::GetSecondaryPart } void CrossSectionDistributionRecord::Finalize(InteractionRecord & record) const { + //std::cout << "XsecDistRecord::Finalize : starting" << std::endl; + //std::cout << record.signature << std::endl; + //std::cout << signature << std::endl; + //std::cout << primary_momentum[0] << std::endl; + record.target_id = target_id; record.target_mass = target_mass; record.target_helicity = target_helicity; @@ -688,6 +707,7 @@ void CrossSectionDistributionRecord::Finalize(InteractionRecord & record) const record.secondary_helicities.resize(secondary_particles.size()); for(SecondaryParticleRecord const & secondary : secondary_particles) { + //std::cout << "XsecDistRecord::Finalize : going to secondaries: " << secondary << std::endl; secondary.Finalize(record); } } diff --git a/projects/dataclasses/private/Particle.cxx b/projects/dataclasses/private/Particle.cxx index 7952adc1e..afc9e559f 100644 --- a/projects/dataclasses/private/Particle.cxx +++ b/projects/dataclasses/private/Particle.cxx @@ -93,6 +93,10 @@ bool isHadron(Particle::ParticleType p){ p==Particle::ParticleType::DPlus || p==Particle::ParticleType::DMinus); } - +bool isD(Particle::ParticleType p){ + return(p==Particle::ParticleType::D0 || p==Particle::ParticleType::D0Bar || + p==Particle::ParticleType::DPlus || p==Particle::ParticleType::DMinus); + } + } // namespace utilities } // namespace siren diff --git a/projects/dataclasses/public/SIREN/dataclasses/Particle.h b/projects/dataclasses/public/SIREN/dataclasses/Particle.h index fff79c4a6..a272b8c58 100644 --- a/projects/dataclasses/public/SIREN/dataclasses/Particle.h +++ b/projects/dataclasses/public/SIREN/dataclasses/Particle.h @@ -76,6 +76,7 @@ bool isCharged(ParticleType p); bool isNeutrino(ParticleType p); bool isQuark(Particle::ParticleType p); bool isHadron(Particle::ParticleType p); +bool isD(Particle::ParticleType p); } // namespace dataclasses diff --git a/projects/injection/private/Injector.cxx b/projects/injection/private/Injector.cxx index 7ca32979c..197da2c84 100644 --- a/projects/injection/private/Injector.cxx +++ b/projects/injection/private/Injector.cxx @@ -159,7 +159,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record throw(siren::utilities::InjectionFailure("No particle interaction!")); } - //std::cout << "in sample cross section" << std::endl; + ////std::cout << "in sample cross section" << std::endl; std::set const & possible_targets = interactions->TargetTypes(); @@ -267,6 +267,7 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record } } } + //std::cout << "injector :: sample cross sections: after obtaining signatures" << std::endl; if(total_prob == 0) throw(siren::utilities::InjectionFailure("No valid interactions for this event!")); // Throw a random number @@ -287,12 +288,17 @@ void Injector::SampleCrossSection(siren::dataclasses::InteractionRecord & record record.target_mass = detector_model->GetTargetMass(record.signature.target_type); siren::dataclasses::CrossSectionDistributionRecord xsec_record(record); if(r <= xsec_prob) { - //std::cout << "going into sampel final state" << std::endl; + //std::cout << "injector::sample cross section: going into sampel final state" << std::endl; matching_cross_sections[index]->SampleFinalState(xsec_record, random); + //std::cout << "injector::sample cross section: finished sampling" << std::endl; + } else { matching_decays[index - matching_cross_sections.size()]->SampleFinalState(xsec_record, random); } + ////std::cout << "injector::sample cross section: calling finalizing" << std::endl; xsec_record.Finalize(record); + ////std::cout << "injector::sample cross section: finished finalizing" << std::endl; + } } @@ -341,13 +347,15 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { // Initial Process while(true) { tries += 1; + ////std::cout << "injector::GenerateEvent : trying to generate with trial number " << tries << std::endl; try { - //std::cout << "generating primary process" << std::endl; + ////std::cout << "injector::GenerateEvent : generating primary process" << std::endl; siren::dataclasses::PrimaryDistributionRecord primary_record(primary_process->GetPrimaryType()); for(auto & distribution : primary_process->GetPrimaryInjectionDistributions()) { distribution->Sample(random, detector_model, primary_process->GetInteractions(), primary_record); } primary_record.Finalize(record); + //std::cout << "injector::GenerateEvent : sampling cross section" << std::endl; SampleCrossSection(record); break; } catch(siren::utilities::InjectionFailure const & e) { @@ -367,6 +375,7 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { std::shared_ptr parent = tree.add_entry(record); // Secondary Processes + //std::cout << "injector::GenerateEvent : sampling secondary process" << std::endl; std::deque, std::shared_ptr>> secondaries; std::function)> add_secondaries = [&](std::shared_ptr parent) { for(size_t i=0; irecord.signature.secondary_types.size(); ++i) { @@ -399,6 +408,7 @@ siren::dataclasses::InteractionTree Injector::GenerateEvent() { } } + //std::cout << "finished sampling secondary process" << std::endl; injected_events += 1; return tree; } diff --git a/projects/interactions/CMakeLists.txt b/projects/interactions/CMakeLists.txt index a8eec569d..ddd9936c8 100644 --- a/projects/interactions/CMakeLists.txt +++ b/projects/interactions/CMakeLists.txt @@ -21,6 +21,8 @@ LIST (APPEND interactions_SOURCES ${PROJECT_SOURCE_DIR}/projects/interactions/private/CharmHadronization.cxx ${PROJECT_SOURCE_DIR}/projects/interactions/private/CharmMesonDecay.cxx ${PROJECT_SOURCE_DIR}/projects/interactions/private/DMesonELoss.cxx + ${PROJECT_SOURCE_DIR}/projects/interactions/private/QuarkDISFromSpline.cxx + ) add_library(SIREN_interactions OBJECT ${interactions_SOURCES}) set_property(TARGET SIREN_interactions PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/projects/interactions/private/CharmDISFromSpline.cxx b/projects/interactions/private/CharmDISFromSpline.cxx index 9844231ed..27921cb33 100644 --- a/projects/interactions/private/CharmDISFromSpline.cxx +++ b/projects/interactions/private/CharmDISFromSpline.cxx @@ -45,6 +45,8 @@ namespace { bool kinematicallyAllowed(double x, double y, double E, double M, double m) { if(x > 1) //Eq. 6 right inequality return false; + // this is to get rid of the infinities as a temporary solution + if (x < 1e-6 or y < 1e-6) return false; if(x < ((m * m) / (2 * M * (E - m)))) //Eq. 6 left inequality return false; //denominator of a and b diff --git a/projects/interactions/private/QuarkDISFromSpline.cxx b/projects/interactions/private/QuarkDISFromSpline.cxx index 9844231ed..0c1842e0c 100644 --- a/projects/interactions/private/QuarkDISFromSpline.cxx +++ b/projects/interactions/private/QuarkDISFromSpline.cxx @@ -1,4 +1,4 @@ -#include "SIREN/interactions/CharmDISFromSpline.h" +#include "SIREN/interactions/QuarkDISFromSpline.h" #include // for map, opera... #include // for set, opera... @@ -27,6 +27,8 @@ #include "SIREN/dataclasses/Particle.h" // for Particle #include "SIREN/utilities/Random.h" // for SIREN_random #include "SIREN/utilities/Constants.h" // for electronMass +#include "SIREN/utilities/Errors.h" // for PythonImplementationError + namespace siren { namespace interactions { @@ -47,6 +49,8 @@ bool kinematicallyAllowed(double x, double y, double E, double M, double m) { return false; if(x < ((m * m) / (2 * M * (E - m)))) //Eq. 6 left inequality return false; + if (x < 1e-6 || y < 1e-6) return false; + //denominator of a and b double d = 2 * (1 + (M * x) / (2 * E)); //the numerator of a (or a*d) @@ -62,47 +66,63 @@ bool kinematicallyAllowed(double x, double y, double E, double M, double m) { } } -CharmDISFromSpline::CharmDISFromSpline() {} +QuarkDISFromSpline::QuarkDISFromSpline() { + // initialize the pdf normalization and cdf table for the hadronization process + normalize_pdf(); + compute_cdf(); +} -CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { +QuarkDISFromSpline::QuarkDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + normalize_pdf(); + compute_cdf(); LoadFromMemory(differential_data, total_data); InitializeSignatures(); SetUnits(units); } -CharmDISFromSpline::CharmDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { +QuarkDISFromSpline::QuarkDISFromSpline(std::vector differential_data, std::vector total_data, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + normalize_pdf(); + compute_cdf(); LoadFromMemory(differential_data, total_data); InitializeSignatures(); SetUnits(units); } -CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { +QuarkDISFromSpline::QuarkDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + normalize_pdf(); + compute_cdf(); LoadFromFile(differential_filename, total_filename); InitializeSignatures(); SetUnits(units); } -CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types) { +QuarkDISFromSpline::QuarkDISFromSpline(std::string differential_filename, std::string total_filename, std::set primary_types, std::set target_types, std::string units) : primary_types_(primary_types), target_types_(target_types) { + normalize_pdf(); + compute_cdf(); LoadFromFile(differential_filename, total_filename); ReadParamsFromSplineTable(); InitializeSignatures(); SetUnits(units); } -CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { +QuarkDISFromSpline::QuarkDISFromSpline(std::string differential_filename, std::string total_filename, int interaction, double target_mass, double minimum_Q2, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()), interaction_type_(interaction), target_mass_(target_mass), minimum_Q2_(minimum_Q2) { + normalize_pdf(); + compute_cdf(); LoadFromFile(differential_filename, total_filename); InitializeSignatures(); SetUnits(units); } -CharmDISFromSpline::CharmDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()) { +QuarkDISFromSpline::QuarkDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units) : primary_types_(primary_types.begin(), primary_types.end()), target_types_(target_types.begin(), target_types.end()) { + normalize_pdf(); + compute_cdf(); LoadFromFile(differential_filename, total_filename); ReadParamsFromSplineTable(); InitializeSignatures(); SetUnits(units); } -void CharmDISFromSpline::SetUnits(std::string units) { +void QuarkDISFromSpline::SetUnits(std::string units) { std::transform(units.begin(), units.end(), units.begin(), [](unsigned char c){ return std::tolower(c); }); if(units == "cm") { @@ -114,13 +134,17 @@ void CharmDISFromSpline::SetUnits(std::string units) { } } -void CharmDISFromSpline::SetInteractionType(int interaction) { +void QuarkDISFromSpline::SetInteractionType(int interaction) { interaction_type_ = interaction; } -bool CharmDISFromSpline::equal(CrossSection const & other) const { - const CharmDISFromSpline* x = dynamic_cast(&other); +void QuarkDISFromSpline::SetQuarkType(int q_type) { + quark_type_ = q_type; +} +bool QuarkDISFromSpline::equal(CrossSection const & other) const { + const QuarkDISFromSpline* x = dynamic_cast(&other); + // to do: include more features in the hadronization side to check equivalence if(!x) return false; else @@ -146,7 +170,7 @@ bool CharmDISFromSpline::equal(CrossSection const & other) const { x->total_cross_section_); } -void CharmDISFromSpline::LoadFromFile(std::string dd_crossSectionFile, std::string total_crossSectionFile) { +void QuarkDISFromSpline::LoadFromFile(std::string dd_crossSectionFile, std::string total_crossSectionFile) { differential_cross_section_ = photospline::splinetable<>(dd_crossSectionFile.c_str()); @@ -161,12 +185,12 @@ void CharmDISFromSpline::LoadFromFile(std::string dd_crossSectionFile, std::stri + " dimensions, should have 1, log10(E)"); } -void CharmDISFromSpline::LoadFromMemory(std::vector & differential_data, std::vector & total_data) { +void QuarkDISFromSpline::LoadFromMemory(std::vector & differential_data, std::vector & total_data) { differential_cross_section_.read_fits_mem(differential_data.data(), differential_data.size()); total_cross_section_.read_fits_mem(total_data.data(), total_data.size()); } -double CharmDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton_type) { +double QuarkDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton_type) { int32_t lepton_number = std::abs(static_cast(lepton_type)); double lepton_mass; switch(lepton_number) { @@ -192,7 +216,45 @@ double CharmDISFromSpline::GetLeptonMass(siren::dataclasses::ParticleType lepton return lepton_mass; } -void CharmDISFromSpline::ReadParamsFromSplineTable() { +double QuarkDISFromSpline::getHadronMass(siren::dataclasses::ParticleType hadron_type) { + switch(hadron_type){ + case siren::dataclasses::ParticleType::D0: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::D0Bar: + return( siren::utilities::Constants::D0Mass); + case siren::dataclasses::ParticleType::DPlus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::DMinus: + return( siren::utilities::Constants::DPlusMass); + case siren::dataclasses::ParticleType::Charm: + return( siren::utilities::Constants::CharmMass); + case siren::dataclasses::ParticleType::CharmBar: + return( siren::utilities::Constants::CharmMass); + default: + return(0.0); + } +} + + +std::map QuarkDISFromSpline::getIndices(siren::dataclasses::InteractionSignature signature) { + int lepton_id, hadron_id, meson_id; + for (size_t i = 0; i < signature.secondary_types.size(); i++){ + if (isLepton(signature.secondary_types[i])) { + lepton_id = i; + continue; + } else if (isD(signature.secondary_types[i])) { + meson_id = i; + continue; + } else { + hadron_id = i; + continue; + } + } + return {{"lepton", lepton_id}, {"hadron", hadron_id}, {"meson", meson_id}}; +} + + +void QuarkDISFromSpline::ReadParamsFromSplineTable() { // returns true if successfully read target mass bool mass_good = differential_cross_section_.read_key("TARGETMASS", target_mass_); if (mass_good) {std::cout << "read target mass!!" << std::endl;} // for debugging purposes @@ -200,19 +262,24 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { bool int_good = differential_cross_section_.read_key("INTERACTION", interaction_type_); // returns true if successfully read minimum Q2 bool q2_good = differential_cross_section_.read_key("Q2MIN", minimum_Q2_); + // returns true if successfully read quark type + bool qtype_good = differential_cross_section_.read_key("QUARKTYPE", quark_type_); + if(!int_good) { // assume DIS to preserve compatability with previous versions interaction_type_ = 1; } + if (!qtype_good) { + quark_type_ = 1; // assume quark is produced + } + if(!q2_good) { // assume 1 GeV^2 minimum_Q2_ = 1; } - std::cout << "Q2 good status is " << q2_good << "and is set to " << minimum_Q2_; - if(!mass_good) { if(int_good) { if(interaction_type_ == 1 or interaction_type_ == 2) { @@ -235,23 +302,19 @@ void CharmDISFromSpline::ReadParamsFromSplineTable() { } } } - std::cout << "target mass is " << target_mass_ << std::endl; - } -void CharmDISFromSpline::InitializeSignatures() { +void QuarkDISFromSpline::InitializeSignatures() { signatures_.clear(); for(auto primary_type : primary_types_) { dataclasses::InteractionSignature signature; signature.primary_type = primary_type; - if(not isNeutrino(primary_type)) { throw std::runtime_error("This DIS implementation only supports neutrinos as primaries!"); } - + // first push back the charged lepton product siren::dataclasses::ParticleType charged_lepton_product = siren::dataclasses::ParticleType::unknown; siren::dataclasses::ParticleType neutral_lepton_product = primary_type; - if(primary_type == siren::dataclasses::ParticleType::NuE) { charged_lepton_product = siren::dataclasses::ParticleType::EMinus; } else if(primary_type == siren::dataclasses::ParticleType::NuEBar) { @@ -267,7 +330,6 @@ void CharmDISFromSpline::InitializeSignatures() { } else { throw std::runtime_error("InitializeSignatures: Unkown parent neutrino type!"); } - if(interaction_type_ == 1) { signature.secondary_types.push_back(charged_lepton_product); } else if(interaction_type_ == 2) { @@ -277,20 +339,100 @@ void CharmDISFromSpline::InitializeSignatures() { } else { throw std::runtime_error("InitializeSignatures: Unkown interaction type!"); } + // now push back the hadron product + signature.secondary_types.push_back(siren::dataclasses::ParticleType::Hadrons); + // define the charmed meson types based on the quark type, now considering only D0 and D+ + if (quark_type_ == 1) { + D_types_ = {siren::dataclasses::Particle::ParticleType::D0, + siren::dataclasses::Particle::ParticleType::DPlus}; + } else { + D_types_ = {siren::dataclasses::Particle::ParticleType::D0Bar, + siren::dataclasses::Particle::ParticleType::DMinus}; + } + // push back the meson type + for (auto meson_type : D_types_) { + dataclasses::InteractionSignature full_signature = signature; + full_signature.secondary_types.push_back(meson_type); + // and finally set the target type and push back the entire signature as well as sig by target + for(auto target_type : target_types_) { + full_signature.target_type = target_type; + + signatures_.push_back(full_signature); + + std::pair key(primary_type, target_type); + signatures_by_parent_types_[key].push_back(full_signature); + } + } + } +} - signature.secondary_types.push_back(siren::dataclasses::ParticleType::Charm); - for(auto target_type : target_types_) { - signature.target_type = target_type; +void QuarkDISFromSpline::normalize_pdf() { + if (fragmentation_integral == 0){ + std::function integrand = [&] (double x) -> double { + return (0.8 / x ) / (std::pow(1 - (1 / x) - (0.2 / (1 - x)), 2)); + }; + fragmentation_integral = siren::utilities::rombergIntegrate(integrand, 0.001, 0.999); + } else { + std::cout << "Something is wrong... you already computed the normalization" << std::endl; + return; + } +} - signatures_.push_back(signature); +void QuarkDISFromSpline::compute_cdf() { + // first set the z nodes + std::vector zspline; + for (int i = 0; i < 100; ++i) { + zspline.push_back(0.01 + i * (0.99-0.01) / 100 ); + } - std::pair key(primary_type, target_type); - signatures_by_parent_types_[key].push_back(signature); + // declare the cdf vectors + std::vector cdf_vector; + std::vector cdf_z_nodes; + std::vector pdf_vector; + + cdf_z_nodes.push_back(0); + cdf_vector.push_back(0); + pdf_vector.push_back(0); + + // compute the spline table + for (int i = 0; i < zspline.size(); ++i) { + if (i == 0) { + double cur_z = zspline[i]; + double cur_pdf = sample_pdf(cur_z); + double area = cur_z * cur_pdf * 0.5; + pdf_vector.push_back(cur_pdf); + cdf_vector.push_back(area); + cdf_z_nodes.push_back(cur_z); + continue; } + double cur_z = zspline[i]; + double cur_pdf = sample_pdf(cur_z); + double area = 0.5 * (pdf_vector[i - 1] + cur_pdf) * (zspline[i] - zspline[i - 1]); + pdf_vector.push_back(cur_pdf); + cdf_z_nodes.push_back(cur_z); + cdf_vector.push_back(area + cdf_vector.back()); } + + cdf_z_nodes.push_back(1); + cdf_vector.push_back(1); + pdf_vector.push_back(0); + + + // set the spline table + siren::utilities::TableData1D inverse_cdf_data; + inverse_cdf_data.x = cdf_vector; + inverse_cdf_data.f = cdf_z_nodes; + + inverseCdfTable = siren::utilities::Interpolator1D(inverse_cdf_data); + + return; +} + +double QuarkDISFromSpline::sample_pdf(double x) const { + return (0.8 / x ) / (std::pow(1 - (1 / x) - (0.2 / (1 - x)), 2)) / fragmentation_integral; } -double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord const & interaction) const { +double QuarkDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord const & interaction) const { siren::dataclasses::ParticleType primary_type = interaction.signature.primary_type; rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); double primary_energy; @@ -303,7 +445,7 @@ double CharmDISFromSpline::TotalCrossSection(dataclasses::InteractionRecord cons return TotalCrossSection(primary_type, primary_energy); } -double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType primary_type, double primary_energy) const { +double QuarkDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType primary_type, double primary_energy) const { if(not primary_types_.count(primary_type)) { throw std::runtime_error("Supplied primary not supported by cross section!"); } @@ -328,34 +470,38 @@ double CharmDISFromSpline::TotalCrossSection(siren::dataclasses::ParticleType pr return unit * std::pow(10.0, log_xs); } -double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionRecord const & interaction) const { +double QuarkDISFromSpline::DifferentialCrossSection(dataclasses::InteractionRecord const & interaction) const { rk::P4 p1(geom3::Vector3(interaction.primary_momentum[1], interaction.primary_momentum[2], interaction.primary_momentum[3]), interaction.primary_mass); rk::P4 p2(geom3::Vector3(0, 0, 0), interaction.target_mass); double primary_energy; primary_energy = interaction.primary_momentum[0]; - assert(interaction.signature.secondary_types.size() == 2); - unsigned int lepton_index = (isLepton(interaction.signature.secondary_types[0])) ? 0 : 1; - unsigned int other_index = 1 - lepton_index; + assert(interaction.signature.secondary_types.size() == 3); + std::map secondaries = getIndices(interaction.signature); + unsigned int lepton_index = secondaries["lepton"]; + unsigned int hadron_index = secondaries["hadron"]; + unsigned int meson_index = secondaries["meson"]; std::array const & mom3 = interaction.secondary_momenta[lepton_index]; - std::array const & mom4 = interaction.secondary_momenta[other_index]; - rk::P4 p3(geom3::Vector3(mom3[1], mom3[2], mom3[3]), interaction.secondary_masses[lepton_index]); - rk::P4 p4(geom3::Vector3(mom4[1], mom4[2], mom4[3]), interaction.secondary_masses[other_index]); + std::array const & mom_x = interaction.secondary_momenta[hadron_index]; + std::array const & mom_d = interaction.secondary_momenta[meson_index]; + rk::P4 p3(geom3::Vector3(mom3[1], mom3[2], mom3[3]), interaction.secondary_masses[lepton_index]); + rk::P4 p_x(geom3::Vector3(mom_x[1], mom_x[2], mom_x[3]), interaction.secondary_masses[hadron_index]); + rk::P4 p_d(geom3::Vector3(mom_d[1], mom_d[2], mom_d[3]), interaction.secondary_masses[meson_index]); + rk::P4 p4 = p_x + p_d; // this assume that we are working in a good frame where the hadronization vertex has 4-momentum conserved rk::P4 q = p1 - p3; + // however p4 is not used in computation here so we should be fine... double Q2 = -q.dot(q); double x, y; double lepton_mass = GetLeptonMass(interaction.signature.secondary_types[lepton_index]); - y = 1.0 - p2.dot(p3) / p2.dot(p1); x = Q2 / (2.0 * p2.dot(q)); double log_energy = log10(primary_energy); std::array coordinates{{log_energy, log10(x), log10(y)}}; std::array centers; - if (Q2 < minimum_Q2_ || !kinematicallyAllowed(x, y, primary_energy, target_mass_, lepton_mass) || !differential_cross_section_.searchcenters(coordinates.data(), centers.data())) { // std::cout << "weighting: revert back to saved x and y" << std::endl; @@ -366,11 +512,9 @@ double CharmDISFromSpline::DifferentialCrossSection(dataclasses::InteractionReco Q2 = 2. * E1_lab * E2_lab * x * y; } return DifferentialCrossSection(primary_energy, x, y, lepton_mass, Q2); - - } -double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { +double QuarkDISFromSpline::DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2) const { double log_energy = log10(energy); // check preconditions if(log_energy < differential_cross_section_.lower_extent(0) @@ -386,9 +530,6 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou return 0.0; } - // we assume that: - // the target is stationary so its energy is just its mass - // the incoming neutrino is massless, so its kinetic energy is its total energy if(std::isnan(Q2)) { Q2 = 2.0 * energy * target_mass_ * x * y; } @@ -413,22 +554,29 @@ double CharmDISFromSpline::DifferentialCrossSection(double energy, double x, dou std::cout << "energy, x, y, Q2 are " << energy << " " << x << " " << y << " " << Q2 << " " << std::endl; std::cout << "spline value read is " << differential_cross_section_.ndsplineeval(coordinates.data(), centers.data(), 0) << std::endl; } - return unit * result; } -double CharmDISFromSpline::InteractionThreshold(dataclasses::InteractionRecord const & interaction) const { +double QuarkDISFromSpline::InteractionThreshold(dataclasses::InteractionRecord const & interaction) const { // Consider implementing DIS thershold at some point return 0; } -void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { +void QuarkDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionRecord & record, std::shared_ptr random) const { + // first obtain the indices from secondaries + // std::cout << "in sample final state" << std::endl; + std::map secondary_indices = getIndices(record.signature); + unsigned int lepton_index = secondary_indices["lepton"]; + unsigned int hadron_index = secondary_indices["hadron"]; + unsigned int meson_index = secondary_indices["meson"]; + // Uses Metropolis-Hastings Algorithm! // useful for cases where we don't know the supremum of our distribution, and the distribution is multi-dimensional if (differential_cross_section_.get_ndim() != 3) { throw std::runtime_error("I expected 3 dimensions in the cross section spline, but got " + std::to_string(differential_cross_section_.get_ndim()) +". Maybe your fits file doesn't have the right 'INTERACTION' key?"); } rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); + // std::cout << "quark::sampleFinalState : primary momentum is read to be " << p1 << std::endl; rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); // we assume that: @@ -444,8 +592,7 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR p2_lab = p2; primary_energy = p1_lab.e(); - unsigned int lepton_index = (isLepton(record.signature.secondary_types[0])) ? 0 : 1; - unsigned int other_index = 1 - lepton_index; + // correctly assign lepton, hadron and meson index double m = GetLeptonMass(record.signature.secondary_types[lepton_index]); double m1 = record.primary_mass; @@ -566,9 +713,8 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR // std::cout << "trial Q is" << trialQ << std::endl; } } - //////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////////////////// + + // scaling down to handle numerical issues double final_x = pow(10., kin_vars[1]); double final_y = pow(10., kin_vars[2]); record.interaction_parameters.clear(); @@ -646,53 +792,132 @@ void CharmDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR rk::P4 p3; rk::P4 p4; - p3 = p3_lab; - p4 = p4_lab; - + p3 = p3_lab; // now we have our lepton momentum set, which should not be modified from here on + p4 = p4_lab; // momentum of the virtual charm + // std::cout << "charm momentum is " << p4 << std::endl; + + // compute the energy and 3-momentum of the virtual charm + // std::cout << "the virtual charm off-shell mass is " << p4.m() << std::endl; + double p3c = std::sqrt(std::pow(p4.px(), 2) + std::pow(p4.py(), 2) + std::pow(p4.pz(), 2)); + double Ec = p4.e(); //energy of primary charm + double mCH = getHadronMass(record.signature.secondary_types[meson_index]); // obtain charmed hadron mass + + // accept-reject sampling for a valid momentum fragmentation + bool frag_accept; + double randValue; + double z; + double ECH; + + // add a maximum number of trials in the while loop + int max_sampling = 500; + int sampling = 0; + + // sample again if this eenrgy is not kinematically allowed + do { + sampling += 1; + if (sampling > max_sampling) { + std::cout << "energy of the charm is " << Ec << " and momentum is " << p3c << std::endl; + std::cout << "desired mass of hadron is " << mCH << std::endl; + // throw(siren::utilities::InjectionFailure("Failed to sample hadronization!")); + break; + } + randValue = random->Uniform(0,1); + z = inverseCdfTable(randValue); + ECH = z * Ec; + if (std::pow(ECH, 2) - std::pow(mCH, 2) <= 0) { + frag_accept = false; + } else { + frag_accept = true; + } + double test_ED = ECH; + double test_EX = (1-z) * Ec; + double test_p3D = std::sqrt(std::pow(test_ED, 2) - std::pow(mCH, 2)); + double test_rD = test_p3D / p3c; + double test_p3X = std::pow((1 - test_rD), 2) * std::pow(p3c, 2); + if (std::pow(test_EX, 2) - std::pow(test_p3X, 2) <= 0) {frag_accept = false;} else {frag_accept = true;} + } while (!frag_accept); + + // set the 3-momentum of the charmed meson and subsequently the 4-momentum + double p3CH = std::sqrt(std::pow(ECH, 2) - std::pow(mCH, 2)); //obtain charmed hadron 3-momentum + double rCH = p3CH/p3c; // ratio of momentum carried away by the charmed hadron, assume collinearity + rk::P4 p4CH(geom3::Vector3(rCH * record.primary_momentum[1], rCH * record.primary_momentum[2], rCH * record.primary_momentum[3]), mCH); + + // the 4 momentum (and the mass) of the resulting hadronic vertex is determined solely via 4-momentum conservation + rk::P4 p4X = p4 - p4CH; + // let's first assume massless hadron final state + // double EX = (1 - z) * Ec; // energy of the hadronic shower + // double p3X = EX; // assume no hadronic mass + // double rX = p3X/p3c; // assume collinear + // rk::P4 p4X(geom3::Vector3(rX * record.primary_momentum[1], rX * record.primary_momentum[2], rX * record.primary_momentum[3]), 0); + + // now we proceed to saving the final state kinematics std::vector & secondaries = record.GetSecondaryParticleRecords(); siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[lepton_index]; - siren::dataclasses::SecondaryParticleRecord & other = secondaries[other_index]; + siren::dataclasses::SecondaryParticleRecord & hadron = secondaries[hadron_index]; + siren::dataclasses::SecondaryParticleRecord & meson = secondaries[meson_index]; + // std::cout << "QuarkDIS::SampleFInalState : the indices are: " << lepton_index << hadron_index<< meson_index << std::endl; lepton.SetFourMomentum({p3.e(), p3.px(), p3.py(), p3.pz()}); + // std::cout << "setting lepton mass with lepton momentum " << p3 << std::endl; lepton.SetMass(p3.m()); lepton.SetHelicity(record.primary_helicity); - other.SetFourMomentum({p4.e(), p4.px(), p4.py(), p4.pz()}); - other.SetMass(p4.m()); - other.SetHelicity(record.target_helicity); + hadron.SetFourMomentum({p4X.e(), p4X.px(), p4X.py(), p4X.pz()}); + // std::cout << "setting hadron mass with hadron momentum " << p4X << std::endl; + hadron.SetMass(p4X.m()); + hadron.SetHelicity(record.target_helicity); + meson.SetFourMomentum({p4CH.e(), p4CH.px(), p4CH.py(), p4CH.pz()}); + // std::cout << "setting meson mass with meson momentum " << p4CH << std::endl; + meson.SetMass(p4CH.m()); + meson.SetHelicity(record.target_helicity); // this needs working on + // std::cout << "finished sampling final state" << std::endl; +} + +double QuarkDISFromSpline::FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const { + if (secondary == siren::dataclasses::Particle::ParticleType::D0 || secondary == siren::dataclasses::Particle::ParticleType::D0Bar) { + return 0.6; + } else if (secondary == siren::dataclasses::Particle::ParticleType::DPlus || secondary == siren::dataclasses::Particle::ParticleType::DMinus) { + return 0.23; + } // D_s and Lambda^+ not yet implemented + return 0; } -double CharmDISFromSpline::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { +double QuarkDISFromSpline::FinalStateProbability(dataclasses::InteractionRecord const & interaction) const { + // first compute the differential and total cross section double dxs = DifferentialCrossSection(interaction); // if (dxs == 0) { // std::cout << "diff xsec gives 0" << std::endl; // } double txs = TotalCrossSection(interaction); + //then compute the fragmentation probability + std::map secondaries = getIndices(interaction.signature); + unsigned int meson_index = secondaries["meson"]; + double fragfrac = FragmentationFraction(interaction.signature.secondary_types[meson_index]); if(dxs == 0) { return 0.0; } else { // if (txs == 0) {std::cout << "wtf??? txs is 0 in final state prob" << txs << std::endl;} // if (std::isinf(dxs)) {std::cout << "dxs is inf in final state prob" << std::endl;} - return dxs / txs; + return dxs / txs * fragfrac; } } -std::vector CharmDISFromSpline::GetPossiblePrimaries() const { +std::vector QuarkDISFromSpline::GetPossiblePrimaries() const { return std::vector(primary_types_.begin(), primary_types_.end()); } -std::vector CharmDISFromSpline::GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const { +std::vector QuarkDISFromSpline::GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const { return std::vector(target_types_.begin(), target_types_.end()); } -std::vector CharmDISFromSpline::GetPossibleSignatures() const { +std::vector QuarkDISFromSpline::GetPossibleSignatures() const { return std::vector(signatures_.begin(), signatures_.end()); } -std::vector CharmDISFromSpline::GetPossibleTargets() const { +std::vector QuarkDISFromSpline::GetPossibleTargets() const { return std::vector(target_types_.begin(), target_types_.end()); } -std::vector CharmDISFromSpline::GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const { +std::vector QuarkDISFromSpline::GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const { std::pair key(primary_type, target_type); if(signatures_by_parent_types_.find(key) != signatures_by_parent_types_.end()) { return signatures_by_parent_types_.at(key); @@ -701,7 +926,7 @@ std::vector CharmDISFromSpline::GetPossibleSi } } -std::vector CharmDISFromSpline::DensityVariables() const { +std::vector QuarkDISFromSpline::DensityVariables() const { return std::vector{"Bjorken x", "Bjorken y"}; } diff --git a/projects/interactions/private/pybindings/QuarkDISFromSpline.h b/projects/interactions/private/pybindings/QuarkDISFromSpline.h new file mode 100644 index 000000000..65e60c44c --- /dev/null +++ b/projects/interactions/private/pybindings/QuarkDISFromSpline.h @@ -0,0 +1,90 @@ +#include +#include +#include + +#include +#include +#include + +#include "../../public/SIREN/interactions/CrossSection.h" +#include "../../public/SIREN/interactions/QuarkDISFromSpline.h" +#include "../../../dataclasses/public/SIREN/dataclasses/Particle.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionRecord.h" +#include "../../../dataclasses/public/SIREN/dataclasses/InteractionSignature.h" +#include "../../../geometry/public/SIREN/geometry/Geometry.h" +#include "../../../utilities/public/SIREN/utilities/Random.h" + +void register_QuarkDISFromSpline(pybind11::module_ & m) { + using namespace pybind11; + using namespace siren::interactions; + + class_, CrossSection> quarkdisfromspline(m, "QuarkDISFromSpline"); + + quarkdisfromspline + + .def(init<>()) + .def(init, std::vector, int, double, double, std::set, std::set, std::string>(), + arg("total_xs_data"), + arg("differential_xs_data"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, int, double, double, std::vector, std::vector, std::string>(), + arg("total_xs_data"), + arg("differential_xs_data"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::set, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::set, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("interaction"), + arg("target_mass"), + arg("minimum_Q2"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(init, std::vector, std::string>(), + arg("total_xs_filename"), + arg("differential_xs_filename"), + arg("primary_types"), + arg("target_types"), + arg("units") = std::string("cm")) + .def(self == self) + .def("SetInteractionType",&QuarkDISFromSpline::SetInteractionType) + .def("SetQuarkType",&QuarkDISFromSpline::SetQuarkType) + .def("TotalCrossSection",overload_cast(&QuarkDISFromSpline::TotalCrossSection, const_)) + .def("TotalCrossSection",overload_cast(&QuarkDISFromSpline::TotalCrossSection, const_)) + .def("DifferentialCrossSection",overload_cast(&QuarkDISFromSpline::DifferentialCrossSection, const_)) + .def("DifferentialCrossSection",overload_cast(&QuarkDISFromSpline::DifferentialCrossSection, const_)) + .def("InteractionThreshold",&QuarkDISFromSpline::InteractionThreshold) + .def("FragmentationFraction",&QuarkDISFromSpline::FragmentationFraction) + .def("GetPossibleTargets",&QuarkDISFromSpline::GetPossibleTargets) + .def("GetPossibleTargetsFromPrimary",&QuarkDISFromSpline::GetPossibleTargetsFromPrimary) + .def("GetPossiblePrimaries",&QuarkDISFromSpline::GetPossiblePrimaries) + .def("GetPossibleSignatures",&QuarkDISFromSpline::GetPossibleSignatures) + .def("GetPossibleSignaturesFromParents",&QuarkDISFromSpline::GetPossibleSignaturesFromParents) + .def("FinalStateProbability",&QuarkDISFromSpline::FinalStateProbability); +} + diff --git a/projects/interactions/private/pybindings/interactions.cxx b/projects/interactions/private/pybindings/interactions.cxx index 7cfc91cda..9a2c628e2 100644 --- a/projects/interactions/private/pybindings/interactions.cxx +++ b/projects/interactions/private/pybindings/interactions.cxx @@ -6,6 +6,7 @@ #include "../../public/SIREN/interactions/InteractionCollection.h" #include "../../public/SIREN/interactions/DISFromSpline.h" #include "../../public/SIREN/interactions/CharmDISFromSpline.h" +#include "../../public/SIREN/interactions/QuarkDISFromSpline.h" #include "../../public/SIREN/interactions/HNLFromSpline.h" #include "../../public/SIREN/interactions/DipoleFromTable.h" #include "../../public/SIREN/interactions/DarkNewsCrossSection.h" @@ -21,6 +22,7 @@ #include "./DarkNewsDecay.h" #include "./DISFromSpline.h" #include "./CharmDISFromSpline.h" +#include "./QuarkDISFromSpline.h" #include "./HNLFromSpline.h" #include "./Decay.h" #include "./NeutrissimoDecay.h" @@ -55,6 +57,7 @@ PYBIND11_MODULE(interactions,m) { register_DarkNewsDecay(m); register_DISFromSpline(m); register_CharmDISFromSpline(m); + register_QuarkDISFromSpline(m); register_HNLFromSpline(m); register_NeutrissimoDecay(m); register_InteractionCollection(m); diff --git a/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h b/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h index 62dc88a7d..f17139bfa 100644 --- a/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h +++ b/projects/interactions/public/SIREN/interactions/QuarkDISFromSpline.h @@ -27,6 +27,8 @@ #include "SIREN/interactions/CrossSection.h" // for CrossSe... #include "SIREN/dataclasses/InteractionSignature.h" // for Interac... #include "SIREN/dataclasses/Particle.h" // for Particle +#include "SIREN/utilities/Interpolator.h" +#include "SIREN/utilities/Integration.h" namespace siren { namespace dataclasses { class InteractionRecord; } } namespace siren { namespace utilities { class SIREN_random; } } @@ -45,10 +47,18 @@ friend cereal::access; std::set target_types_; std::map> targets_by_primary_types_; std::map, std::vector> signatures_by_parent_types_; - + std::set D_types_; + + // used by the DIS process int interaction_type_; + int quark_type_; double target_mass_; double minimum_Q2_; + + // used by the hadronization process + double fragmentation_integral = 0; // for storing the integrated unnormed pdf + void normalize_pdf(); // for normalizing pdf and integral, to be called at initialization + siren::utilities::Interpolator1D inverseCdfTable; // for storing the CDF-1 table for the hadronization double unit; @@ -62,36 +72,44 @@ friend cereal::access; QuarkDISFromSpline(std::string differential_filename, std::string total_filename, std::vector primary_types, std::vector target_types, std::string units = "cm"); void SetUnits(std::string units); - // this might be integrated later? could also make another initialization method - // problem with current implementation is that EM is not supported b/c at initialization we assume int = 1 - // this sets the isoscalar target mass void SetInteractionType(int interaction); + void SetQuarkType(int q_type); virtual bool equal(CrossSection const & other) const override; + // function definitions needed to compute the DIS vertex double TotalCrossSection(dataclasses::InteractionRecord const &) const override; double TotalCrossSection(siren::dataclasses::ParticleType primary, double energy) const; double DifferentialCrossSection(dataclasses::InteractionRecord const &) const override; double DifferentialCrossSection(double energy, double x, double y, double secondary_lepton_mass, double Q2=std::numeric_limits::quiet_NaN()) const; double InteractionThreshold(dataclasses::InteractionRecord const &) const override; - void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr random) const override; + // function definitions needed to compute the hadronization vertex + double FragmentationFraction(siren::dataclasses::Particle::ParticleType secondary) const; + double sample_pdf(double z) const; + void compute_cdf(); + static double getHadronMass(siren::dataclasses::ParticleType hadron_type); + + // used for both processes + void SampleFinalState(dataclasses::CrossSectionDistributionRecord &, std::shared_ptr random) const override; std::vector GetPossibleTargets() const override; std::vector GetPossibleTargetsFromPrimary(siren::dataclasses::ParticleType primary_type) const override; std::vector GetPossiblePrimaries() const override; std::vector GetPossibleSignatures() const override; std::vector GetPossibleSignaturesFromParents(siren::dataclasses::ParticleType primary_type, siren::dataclasses::ParticleType target_type) const override; - virtual double FinalStateProbability(dataclasses::InteractionRecord const & record) const override; + // other utility functions void LoadFromFile(std::string differential_filename, std::string total_filename); void LoadFromMemory(std::vector & differential_data, std::vector & total_data); + // utilities for DIS parametrs double GetMinimumQ2() const {return minimum_Q2_;}; double GetTargetMass() const {return target_mass_;}; int GetInteractionType() const {return interaction_type_;}; - static double GetLeptonMass(siren::dataclasses::ParticleType lepton_type); + static std::map getIndices(siren::dataclasses::InteractionSignature signature); + public: virtual std::vector DensityVariables() const override; diff --git a/python/SIREN_Controller.py b/python/SIREN_Controller.py index 7daf7721a..dc725e2e5 100644 --- a/python/SIREN_Controller.py +++ b/python/SIREN_Controller.py @@ -489,6 +489,7 @@ def GenerateEvents(self, N=None, fill_tables_at_exit=True): self.global_times.append(t-self.global_start) prev_time = t count += 1 + # print("finished generating one events") if hasattr(self, "DN_processes"): self.DN_processes.SaveCrossSectionTables(fill_tables_at_exit=fill_tables_at_exit) return self.events From 4dfb9ce392e72ad96c7bea0e8f1c9c7526292dfd Mon Sep 17 00:00:00 2001 From: Miaochen Jin Date: Wed, 11 Dec 2024 23:07:42 -0500 Subject: [PATCH 11/11] quarkDIS completed --- .../interactions/private/CharmMesonDecay.cxx | 7 +- .../private/QuarkDISFromSpline.cxx | 67 +++++++++++++------ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/projects/interactions/private/CharmMesonDecay.cxx b/projects/interactions/private/CharmMesonDecay.cxx index 5b92f0f1a..8f1832193 100644 --- a/projects/interactions/private/CharmMesonDecay.cxx +++ b/projects/interactions/private/CharmMesonDecay.cxx @@ -367,8 +367,11 @@ void CharmMesonDecay::SampleFinalState(dataclasses::CrossSectionDistributionReco // now finally perform the last aximuthal rotation double W_phi = random->Uniform(0, 2 * M_PI); geom3::Rotation3 W_azimuth_rand_rot(p3W_lab_dir, W_phi); - rk::P4 p4l_lab = p4l_Wrest.rotate(W_azimuth_rand_rot); - rk::P4 p4nu_lab = p4nu_Wrest.rotate(W_azimuth_rand_rot); + p4l_Wrest.rotate(W_azimuth_rand_rot); + p4nu_Wrest.rotate(W_azimuth_rand_rot); + rk::Boost boost_from_Wrest_to_lab = p4W_lab.labBoost(); + rk::P4 p4l_lab = p4l_Wrest.boost(boost_from_Wrest_to_lab); + rk::P4 p4nu_lab = p4nu_Wrest.boost(boost_from_Wrest_to_lab); std::vector & secondaries = record.GetSecondaryParticleRecords(); siren::dataclasses::SecondaryParticleRecord & kpi = secondaries[0]; diff --git a/projects/interactions/private/QuarkDISFromSpline.cxx b/projects/interactions/private/QuarkDISFromSpline.cxx index 0c1842e0c..608c07def 100644 --- a/projects/interactions/private/QuarkDISFromSpline.cxx +++ b/projects/interactions/private/QuarkDISFromSpline.cxx @@ -62,7 +62,7 @@ bool kinematicallyAllowed(double x, double y, double E, double M, double m) { double s = 2 * M * E; double Q2 = s * x * y; double Mc = siren::utilities::Constants::D0Mass; - return ((ad - bd) <= d * y and d * y <= (ad + bd)) && (Q2 / (1 - x) + pow(M, 2) >= pow(M + Mc, 2)); //Eq. 7 + return ((ad - bd) <= d * y and d * y <= (ad + bd)) && (Q2 * (1 - x) / x + pow(M, 2) >= pow(M + Mc, 2)); //Eq. 7 } } @@ -577,7 +577,7 @@ void QuarkDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR } rk::P4 p1(geom3::Vector3(record.primary_momentum[1], record.primary_momentum[2], record.primary_momentum[3]), record.primary_mass); // std::cout << "quark::sampleFinalState : primary momentum is read to be " << p1 << std::endl; - rk::P4 p2(geom3::Vector3(0, 0, 0), record.target_mass); + rk::P4 p2(geom3::Vector3(0, 0, 0), target_mass_); // we assume that: // the target is stationary so its energy is just its mass @@ -813,6 +813,7 @@ void QuarkDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR int sampling = 0; // sample again if this eenrgy is not kinematically allowed + // this samples in the lab frame the energy of the D-meson such that mass is real do { sampling += 1; if (sampling > max_sampling) { @@ -829,27 +830,55 @@ void QuarkDISFromSpline::SampleFinalState(dataclasses::CrossSectionDistributionR } else { frag_accept = true; } - double test_ED = ECH; - double test_EX = (1-z) * Ec; - double test_p3D = std::sqrt(std::pow(test_ED, 2) - std::pow(mCH, 2)); - double test_rD = test_p3D / p3c; - double test_p3X = std::pow((1 - test_rD), 2) * std::pow(p3c, 2); - if (std::pow(test_EX, 2) - std::pow(test_p3X, 2) <= 0) {frag_accept = false;} else {frag_accept = true;} } while (!frag_accept); + // new attempt of using the isoscalar mass as the remnant hadronic shower mass + double mX = target_mass_; + double Mc = p4.m(); + // std::cout << "using remnant mass " << mX << std::endl; + // std::cout << "invariant charm mass and its energy is " << Mc << ", " << p4.e() << std::endl; + // std::cout << "target sampled D meson energy is " << ECH << std::endl; + // std::cout << "and the fraction of momentum is sampled to be " << z << std::endl; + //compute the energies in the charm rest frame + double E_CH_c = (std::pow(Mc, 2) - std::pow(mX, 2) + std::pow(mCH, 2)) / (2 * Mc); + // std::cout << "energy of charm in rest frame is " << E_CH_c << std::endl; + double p_c = std::sqrt((std::pow(Mc, 2) - std::pow(mCH + mX, 2)) * (std::pow(Mc, 2) - std::pow(mCH - mX, 2))) / (2 * Mc); + // std::cout << "momentum in charm rest frame is " << p_c << std::endl; + // compute the lorentz boost parameters + double gamma = p4.gamma(); + double beta = p4.beta(); + // std::cout << "beta and gamma parameters are " << beta << ", " << gamma << std::endl; + // using the lab frame fragmented energy and the + double cosTheta = std::max(std::min(((ECH - gamma * E_CH_c)/(gamma * beta * p_c)), 1.), -1.); + // std::cout << "cosine of theta in charm frame is " << cosTheta << std::endl; + // std::cout << "without cutting, the number is " << (ECH - gamma * E_CH_c)/(gamma * beta * p_c) << std::endl; + // now compute the momentum vectors in the rest frame + double sinTheta = std::sin(std::acos(cosTheta)); + // std::cout << "and sine of theta is computed to be " << sinTheta << std::endl; + rk::P4 p4CH_c(p_c * geom3::Vector3(cosTheta, sinTheta, 0), mCH); + rk::P4 p4X_c(p_c * geom3::Vector3(-cosTheta, -sinTheta, 0), mX); + // these all assume boost direction is charm direction. Now we should rotate back to charm lab momentum direction + geom3::Vector3 pc_lab_momentum = p4.momentum(); + geom3::UnitVector3 pc_lab_dir = pc_lab_momentum.direction(); + geom3::Rotation3 x_to_pc_lab_rot = geom3::rotationBetween(x_dir, pc_lab_dir); + p4X_c.rotate(x_to_pc_lab_rot); + p4CH_c.rotate(x_to_pc_lab_rot); + + // finally, we perform a random azimuthal rotation + double c_phi = random->Uniform(0, 2 * M_PI); + geom3::Rotation3 azimuth_rand_rot(pc_lab_dir, c_phi); + p4X_c.rotate(azimuth_rand_rot); + p4CH_c.rotate(azimuth_rand_rot); - // set the 3-momentum of the charmed meson and subsequently the 4-momentum - double p3CH = std::sqrt(std::pow(ECH, 2) - std::pow(mCH, 2)); //obtain charmed hadron 3-momentum - double rCH = p3CH/p3c; // ratio of momentum carried away by the charmed hadron, assume collinearity - rk::P4 p4CH(geom3::Vector3(rCH * record.primary_momentum[1], rCH * record.primary_momentum[2], rCH * record.primary_momentum[3]), mCH); + // and boost them back to the lab frame + rk::Boost boost_from_crest_to_lab = p4.labBoost(); + rk::P4 p4X = p4X_c.boost(boost_from_crest_to_lab); + rk::P4 p4CH = p4CH_c.boost(boost_from_crest_to_lab); - // the 4 momentum (and the mass) of the resulting hadronic vertex is determined solely via 4-momentum conservation - rk::P4 p4X = p4 - p4CH; - // let's first assume massless hadron final state - // double EX = (1 - z) * Ec; // energy of the hadronic shower - // double p3X = EX; // assume no hadronic mass - // double rX = p3X/p3c; // assume collinear - // rk::P4 p4X(geom3::Vector3(rX * record.primary_momentum[1], rX * record.primary_momentum[2], rX * record.primary_momentum[3]), 0); + // std::cout << "computed remnant mass and energy is " << p4X.m() << ", " << p4X.e() << std::endl; + // std::cout << "and computed D mass and energy is " << p4CH.m() << ", " << p4CH.e() << std::endl; + // std::cout << "target sampled D meson energy is " << ECH << std::endl; + // now we proceed to saving the final state kinematics std::vector & secondaries = record.GetSecondaryParticleRecords(); siren::dataclasses::SecondaryParticleRecord & lepton = secondaries[lepton_index];