LDMX Software
TrigScintMeasurementProducer.cxx
1#include "Tracking/Reco/TrigScintMeasurementProducer.h"
2
3#include <algorithm>
4#include <cmath>
5#include <fstream>
6#include <nlohmann/json.hpp>
7
9#include "TSystem.h"
10#include "Tracking/Event/Measurement.h"
11#include "TrigScint/Event/TrigScintCluster.h"
12
13namespace tracking::reco {
14
17 // Load the TrigScint_Event ROOT dictionary so the digi collection reads as
18 // the compiled type. The accessors are inline (linker drops
19 // libTrigScint_Event) and there is no rootmap for autoload, so ROOT would
20 // otherwise fall back to an emulated streamer that crashes when cast to the
21 // compiled class.
22 gSystem->Load("libTrigScint_Event");
23
25 parameters.getParameter<std::vector<std::string>>("input_collections");
26 input_pass_ = parameters.getParameter<std::string>("input_pass", "");
27 out_collection_ = parameters.getParameter<std::string>(
28 "out_collection", "TrigScintMeasurements");
29 min_pe_ = parameters.getParameter<double>("min_pe", min_pe_);
30 sigma_y_ = parameters.getParameter<double>("sigma_y", sigma_y_);
31
32 // Optional TS DAQ map: fixes the decode-collection -> geometry-module
33 // (cabling) correspondence, analogous to the tracker's daq_map_file. Entries
34 // with geo_module < 0 (e.g. the LYSO pad, not yet in the geometry) are
35 // skipped.
36 daq_map_file_ = parameters.getParameter<std::string>("daq_map_file", "");
37 daq_modules_.clear();
38 if (!daq_map_file_.empty()) {
39 std::ifstream in(daq_map_file_);
40 if (!in) {
41 EXCEPTION_RAISE("TrigScintDaqMap",
42 "Could not open TS DAQ map '" + daq_map_file_ + "'.");
43 }
44 nlohmann::json doc;
45 in >> doc;
46 for (const auto& m : doc.at("modules")) {
47 const int geo_module = m.at("geo_module").get<int>();
48 if (geo_module < 0) continue; // not mapped to geometry (e.g. LYSO)
49 daq_modules_.emplace_back(m.at("collection").get<std::string>(),
50 geo_module);
51 }
52 ldmx_log(info) << "TS DAQ map '" << daq_map_file_ << "' -> "
53 << daq_modules_.size() << " mapped modules.";
54 }
55}
56
58 // Bar positions come from the geometry conditions object (not hard-coded).
61 const int n_bars = geom.getNumBars();
62
63 std::vector<ldmx::Measurement> measurements;
64
65 // (decoded collection, geometry module) pairs to process: from the DAQ map
66 // if provided, else the input_collections order (index = geometry module).
67 std::vector<std::pair<std::string, int>> to_process = daq_modules_;
68 if (to_process.empty()) {
69 for (std::size_t i = 0; i < input_collections_.size(); ++i)
70 to_process.emplace_back(input_collections_[i], static_cast<int>(i));
71 }
72
73 for (const auto& [collection, geo_module] : to_process) {
74 // TestBeamClusterProducer only writes the collection for events with >=1
75 // cluster, so it is legitimately absent in empty events -- skip those.
76 if (!event.exists(collection, input_pass_, false)) continue;
77 const auto& clusters =
78 event.getCollection<ldmx::TrigScintCluster>(collection, input_pass_);
79
80 for (const auto& cluster : clusters) {
81 if (cluster.getNHits() <= 0) continue;
82 if (cluster.getPE() < min_pe_) continue;
83
84 // PE-weighted fractional bar centroid (0-based); < 0 means uninitialized.
85 const double centroid = cluster.getCentroid();
86 if (centroid < 0.) continue;
87
88 // Interpolate the global position between the two bracketing bars. The
89 // bars alternate between two staggered layers (bar%2), so the geometry is
90 // piecewise in bar parity -- evaluating both integer endpoints and
91 // interpolating gives the correct y AND the correct in-between z, and
92 // reduces to the exact bar position for an integer centroid.
93 int b0 = static_cast<int>(std::floor(centroid));
94 if (b0 < 0) b0 = 0;
95 if (b0 > n_bars - 1) b0 = n_bars - 1;
96 int b1 = std::min(b0 + 1, n_bars - 1);
97 double f = centroid - b0;
98 if (f < 0.) f = 0.;
99 if (f > 1.) f = 1.;
100
101 const auto p0 = geom.getBarPosition(geo_module, b0);
102 const auto p1 = geom.getBarPosition(geo_module, b1);
103 const float x = (1. - f) * p0.X() + f * p1.X();
104 const float y = (1. - f) * p0.Y() + f * p1.Y();
105 const float z = (1. - f) * p0.Z() + f * p1.Z();
106
108 meas.setGlobalPosition(x, y, z);
109 meas.setLocalPosition(y, 0.f);
110 meas.setLocalCovariance(static_cast<float>(sigma_y_ * sigma_y_), 0.f);
111 // encode geometry module + nearest bar so downstream can trace it
112 meas.setLayerID(geo_module * 100 +
113 static_cast<int>(std::lround(centroid)));
114 meas.setTime(cluster.getTime());
115 meas.setClusterAmplitude(static_cast<float>(cluster.getPE()));
116 meas.setNStrips(cluster.getNHits());
117 measurements.push_back(meas);
118 }
119 }
120
121 event.add(out_collection_, measurements);
122}
123
124} // namespace tracking::reco
125
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that translates a trigger-scintillator (module, bar) into a global bar-center position.
const T & getCondition(const std::string &condition_name)
Access a conditions object for the current event.
Implements an event buffer system for storing event data.
Definition Event.h:40
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:107
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
void setNStrips(int n)
Set the number of strips that formed this cluster (-1 if not a cluster).
void setLocalPosition(const float &meas_u, const float &meas_v)
Set the local position i.e.
Definition Measurement.h:61
void setLayerID(const int &layer_id)
Set the layer ID of the sensor where this measurement took place.
void setGlobalPosition(const float &meas_x, const float &meas_y, const float &meas_z)
Set the global position i.e.
Definition Measurement.h:42
void setLocalCovariance(const float &cov_uu, const float &cov_vv)
Set cov(U,U) and cov(V, V).
Definition Measurement.h:77
void setClusterAmplitude(float amp)
Set the total cluster amplitude [ADC counts].
void setTime(const float &meas_t)
Set the measurement time in ns.
Definition Measurement.h:93
Stores cluster information from the trigger scintillator pads.
static constexpr const char * CONDITIONS_OBJECT_NAME
Name this conditions object is registered under (must match the python provider field name).
Turns reconstructed trigger-scintillator bar clusters into geometry-aware ldmx::Measurement objects.
void configure(framework::config::Parameters &parameters) override
Callback for the EventProcessor to configure itself from the given set of parameters.
std::string out_collection_
output ldmx::Measurement collection name
std::string input_pass_
pass name of the input collections ("" = any)
std::vector< std::pair< std::string, int > > daq_modules_
parsed DAQ map: (decoded collection name, geometry module index)
std::vector< std::string > input_collections_
input cluster collections, one per module (index = module number)
void produce(framework::Event &event) override
Process the event and put new data products into it.
std::string daq_map_file_
optional TS DAQ-map JSON (decode collection -> geometry module).
double sigma_y_
assumed y measurement resolution [mm] (local covariance)
double min_pe_
optional cluster selection: cluster PE must exceed this (0 = keep all; the clustering seed/threshold ...