LDMX Software
ElectroNuclearDQM.cxx
1
2#include "DQM/ElectroNuclearDQM.h"
3
4#include "Math/Vector3D.h" // IWYU pragma: keep
5
6namespace dqm {
7
8ElectroNuclearDQM::ElectroNuclearDQM(const std::string& name,
9 framework::Process& process)
10 : NuclearDQM(name, process) {}
11
12void ElectroNuclearDQM::configure(framework::config::Parameters& parameters) {
13 NuclearDQM::configure(parameters);
14}
15
16void ElectroNuclearDQM::findENElectronProperties(
17 const ldmx::SimParticle* en_electron,
18 const std::vector<const ldmx::SimParticle*>& en_daughters) {
19 histograms_.fill("en_electron_energy", en_electron->getEnergy());
20 histograms_.fill("en_electron_vertex_x", en_electron->getVertex()[0]);
21 histograms_.fill("en_electron_vertex_y", en_electron->getVertex()[1]);
22 histograms_.fill("en_electron_vertex_z", en_electron->getVertex()[2]);
23
24 // The EN interaction vertex is where the first EN daughter was created
25 if (!en_daughters.empty()) {
26 histograms_.fill("en_vertex_x", en_daughters[0]->getVertex()[0]);
27 histograms_.fill("en_vertex_y", en_daughters[0]->getVertex()[1]);
28 histograms_.fill("en_vertex_z", en_daughters[0]->getVertex()[2]);
29 }
30}
31
32void ElectroNuclearDQM::findReconstructableKinematics(
33 const std::vector<const ldmx::SimParticle*>& daughters) {
34 // Apply semi-inclusive acceptance cuts (daughters already sorted by KE desc):
35 // theta < 80 deg from beam axis
36 // |p| > 100 MeV/c for pi±
37 // |p| > 800 MeV/c for protons and kaons
38 // KE > 1000 MeV for neutrons
39 // KE > 2000 MeV for pi0
40 std::vector<const ldmx::SimParticle*> recon;
41 for (const auto* d : daughters) {
42 std::vector<double> pvec_raw{d->getMomentum()};
43 ROOT::Math::XYZVector pvec(pvec_raw[0], pvec_raw[1], pvec_raw[2]);
44 if (pvec.Theta() * (180.0 / 3.14159) >= 80.0) continue;
45
46 auto pdg_id{std::abs(d->getPdgID())};
47 double p_mag{pvec.R()};
48 double ke{d->getEnergy() - d->getMass()};
49
50 if (pdg_id == 2112) {
51 if (ke <= 1000.0) continue;
52 } else if (pdg_id == 211) {
53 if (p_mag <= 100.0) continue;
54 } else if (pdg_id == 2212 || pdg_id == 321 || pdg_id == 130 ||
55 pdg_id == 310) {
56 if (p_mag <= 800.0) continue;
57 } else if (pdg_id == 111) {
58 if (ke <= 2000.0) continue;
59 } else {
60 continue;
61 }
62 recon.push_back(d);
63 }
64
65 int recon_neutron_mult{0}, recon_proton_mult{0};
66 int recon_charged_pion_mult{0}, recon_neutral_pion_mult{0};
67 double recon_total_ke{0}, recon_total_neutron_ke{0};
68 double recon_hardest_ke{-1}, recon_hardest_theta{-1};
69 double recon_hardest_n_ke{-1}, recon_hardest_n_theta{-1};
70 double recon_hardest_p_ke{-1}, recon_hardest_p_theta{-1};
71 double recon_hardest_pi_ke{-1}, recon_hardest_pi_theta{-1};
72 double recon_hardest_pi0_ke{-1}, recon_hardest_pi0_theta{-1};
73
74 for (const auto* d : recon) {
75 auto pdg_id{std::abs(d->getPdgID())};
76 double ke{d->getEnergy() - d->getMass()};
77 recon_total_ke += ke;
78
79 std::vector<double> pvec_raw{d->getMomentum()};
80 ROOT::Math::XYZVector pvec(pvec_raw[0], pvec_raw[1], pvec_raw[2]);
81 auto theta{pvec.Theta() * (180.0 / 3.14159)};
82
83 if (recon_hardest_ke < ke) {
84 recon_hardest_ke = ke;
85 recon_hardest_theta = theta;
86 }
87
88 if (pdg_id == 2112) {
89 recon_neutron_mult++;
90 recon_total_neutron_ke += ke;
91 if (recon_hardest_n_ke < ke) {
92 recon_hardest_n_ke = ke;
93 recon_hardest_n_theta = theta;
94 }
95 } else if (pdg_id == 2212) {
96 recon_proton_mult++;
97 if (recon_hardest_p_ke < ke) {
98 recon_hardest_p_ke = ke;
99 recon_hardest_p_theta = theta;
100 }
101 } else if (pdg_id == 211) {
102 recon_charged_pion_mult++;
103 if (recon_hardest_pi_ke < ke) {
104 recon_hardest_pi_ke = ke;
105 recon_hardest_pi_theta = theta;
106 }
107 } else if (pdg_id == 111) {
108 recon_neutral_pion_mult++;
109 if (recon_hardest_pi0_ke < ke) {
110 recon_hardest_pi0_ke = ke;
111 recon_hardest_pi0_theta = theta;
112 }
113 }
114 }
115
116 if (!recon.empty()) {
117 auto leading_pdg{std::abs(recon[0]->getPdgID())};
118 int leading_type{6};
119 if (leading_pdg == 211)
120 leading_type = 0;
121 else if (leading_pdg == 111)
122 leading_type = 1;
123 else if (leading_pdg == 321)
124 leading_type = 2;
125 else if (leading_pdg == 130 || leading_pdg == 310)
126 leading_type = 3;
127 else if (leading_pdg == 2212)
128 leading_type = 4;
129 else if (leading_pdg == 2112)
130 leading_type = 5;
131 histograms_.fill("recon_leading_particle_type", leading_type);
132 }
133
134 auto recon_event_type{classifyEvent(recon, 0)};
135 histograms_.fill("event_type_recon", static_cast<int>(recon_event_type));
136
137 histograms_.fill("recon_hardest_ke", recon_hardest_ke);
138 histograms_.fill("recon_hardest_theta", recon_hardest_theta);
139 histograms_.fill("recon_hardest_n_ke", recon_hardest_n_ke);
140 histograms_.fill("recon_hardest_n_theta", recon_hardest_n_theta);
141 histograms_.fill("recon_hardest_p_ke", recon_hardest_p_ke);
142 histograms_.fill("recon_hardest_p_theta", recon_hardest_p_theta);
143 histograms_.fill("recon_hardest_pi_ke", recon_hardest_pi_ke);
144 histograms_.fill("recon_hardest_pi_theta", recon_hardest_pi_theta);
145 histograms_.fill("recon_hardest_pi0_ke", recon_hardest_pi0_ke);
146 histograms_.fill("recon_hardest_pi0_theta", recon_hardest_pi0_theta);
147 histograms_.fill("en_recon_neutron_mult", recon_neutron_mult);
148 histograms_.fill("en_recon_proton_mult", recon_proton_mult);
149 histograms_.fill("en_recon_charged_pion_mult", recon_charged_pion_mult);
150 histograms_.fill("en_recon_neutral_pion_mult", recon_neutral_pion_mult);
151 histograms_.fill("en_recon_total_ke", recon_total_ke);
152 histograms_.fill("en_recon_total_neutron_ke", recon_total_neutron_ke);
153}
154
155void ElectroNuclearDQM::analyze(const framework::Event& event) {
156 auto particle_map{event.getMap<int, ldmx::SimParticle>(
157 sim_particles_coll_name_, sim_particles_passname_)};
158 if (particle_map.empty()) return;
159
160 // The EN electron is the primary beam electron (recoil)
161 auto [trackID, en_electron] = analysis::getRecoil(particle_map);
162
163 // Collect daughters produced by the electronNuclear process, applying the
164 // same PDG filtering as PhotoNuclearDQM (exclude photons, heavy nuclei)
165 const auto en_daughters{
166 findDaughters(particle_map, en_electron,
167 ldmx::SimParticle::ProcessType::electronNuclear)};
168
169 findENElectronProperties(en_electron, en_daughters);
170
171 histograms_.fill("en_particle_mult", en_electron->getDaughters().size());
172
173 if (en_daughters.empty()) {
174 ldmx_log(warn) << "No EN daughters found, skipping kinematics";
175 return;
176 }
177
178 findExtendedKinematics(en_daughters, "en");
179 findReconstructableKinematics(en_daughters);
180}
181
182} // namespace dqm
183
#define DECLARE_ANALYZER(CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
DQM analyzer for Geant4 electro-nuclear (EN) interactions.
Implements an event buffer system for storing event data.
Definition Event.h:40
Class which represents the process under execution.
Definition Process.h:34
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
Class representing a simulated particle.
Definition SimParticle.h:25
double getEnergy() const
Get the energy of this particle [MeV].
Definition SimParticle.h:74
std::vector< double > getVertex() const
Get a vector containing the vertex of this particle in mm.