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