CHeT Library
Loading...
Searching...
No Matches
CHeTReader.cc
Go to the documentation of this file.
1#include <memory>
2
3#include <TFile.h>
4
5#include "CHeT/CHeTReader.hh"
6
7using namespace std;
8using namespace ROOT;
9using namespace ROOT::VecOps;
10
11// Aliases for convenience
12using RVecUI = RVec<unsigned int>;
13using RVecUS = RVec<unsigned short>;
14using RVecUC = RVec<unsigned char>;
15
16namespace CHeT
17{
18namespace Data
19{
20
21namespace
22{
23std::string DiscoverTreeName(const std::string &filename, const std::string &providedName)
24{
25 if(providedName != "auto" && providedName != "")
26 return providedName;
27
28 std::unique_ptr<TFile> f(TFile::Open(filename.c_str(), "READ"));
29 if(!f || f->IsZombie())
30 return "raw";
31
32 if(f->Get("chet"))
33 return "chet";
34 if(f->Get("raw"))
35 return "raw";
36 if(f->Get("Event"))
37 return "Event";
38
39 return "raw";
40}
41} // namespace
42
43Reader::Reader(const std::string &filename, const std::string &treeName)
44 : fFilename(filename)
45 , fTreeName(DiscoverTreeName(filename, treeName))
46 , fDF(fTreeName, filename)
47 , fHeadNode(fDF)
48{
49 // Disable implicit MT if necessary, or manage it at higher level
50}
51
52void Reader::SetCuts(double toaMin, double toaMax, unsigned int totMin, unsigned int totMax)
53{
54 fToaMin = toaMin;
55 fToaMax = toaMax;
56 fTotMin = totMin;
57 fTotMax = totMax;
58}
59
60void Reader::SetSingleEntry(long entry)
61{
62 fHeadNode = fDF.Range(entry, entry + 1);
63}
64
65void Reader::SetEventByID(int eventID)
66{
67 fHeadNode = fDF.Filter([eventID](int id) { return id == eventID; }, { "EventID" });
68}
69
70void Reader::SetEnabledBoards(const std::vector<int> &boards)
71{
72 fEnabledBoards = boards;
73}
74
75void Reader::SetEnabledCylinders(const std::vector<int> &cylinders)
76{
77 fEnabledCylinders = cylinders;
78}
79
80void Reader::SetEnabledLayers(const std::vector<int> &layers)
81{
82 fEnabledLayers = layers;
83}
84
85void Reader::SetEnabledGeometries(const std::vector<std::pair<int, int>> &geometries)
86{
87 fEnabledGeometries = geometries;
88}
89
90ROOT::RDF::RNode Reader::GetRaw()
91{
92 return fHeadNode;
93}
94
95ROOT::RDF::RNode Reader::GetCHeTTree()
96{
97 // Capture parameters for lambdas
98 double tMin = fToaMin;
99 double tMax = fToaMax;
100 unsigned int totMin = fTotMin;
101 unsigned int totMax = fTotMax;
102
103 // Capture filters
104 auto enabledBoards = fEnabledBoards;
105 auto enabledCylinders = fEnabledCylinders;
106 auto enabledLayers = fEnabledLayers;
107 auto enabledGeometries = fEnabledGeometries;
108
109 // 0. Check if input is already in High-Level SEV format (ToyMC or processed data)
110 auto colNames = fHeadNode.GetColumnNames();
111 if(std::find(colNames.begin(), colNames.end(), "All_Bundle") != colNames.end())
112 {
113 auto df = fHeadNode;
114
115 // If the Toy only saved All_Bundle, we can reconstruct the rest on the fly
116 if(std::find(colNames.begin(), colNames.end(), "All_Cyl") == colNames.end())
117 {
118 df = df.Define("All_Cyl",
119 [](const RVecI &bunds)
120 {
121 RVecI cyls;
122 for(auto b : bunds)
123 cyls.push_back((b >= 0) ? CHeT::Config::GetFiberProp(b).cylinderId : -1);
124 return cyls;
125 },
126 { "All_Bundle" });
127 }
128 if(std::find(colNames.begin(), colNames.end(), "All_Lay") == colNames.end())
129 {
130 df = df.Define("All_Lay",
131 [](const RVecI &bunds)
132 {
133 RVecI lays;
134 for(auto b : bunds)
135 lays.push_back((b >= 0) ? CHeT::Config::GetFiberProp(b).layerId : -1);
136 return lays;
137 },
138 { "All_Bundle" });
139 }
140 if(std::find(colNames.begin(), colNames.end(), "nHits_Total") == colNames.end())
141 {
142 df = df.Define("nHits_Total", "(double)All_Cyl.size()")
143 .Define("nHits_Cyl0", "Sum(All_Cyl == 0)")
144 .Define("nHits_Cyl1", "Sum(All_Cyl == 1)")
145 .Define("nHits_Cyl0_Inner", "Sum(All_Cyl == 0 && All_Lay == 0)")
146 .Define("nHits_Cyl0_Outer", "Sum(All_Cyl == 0 && All_Lay == 1)")
147 .Define("nHits_Cyl1_Inner", "Sum(All_Cyl == 1 && All_Lay == 0)")
148 .Define("nHits_Cyl1_Outer", "Sum(All_Cyl == 1 && All_Lay == 1)");
149 }
150
151 if(std::find(colNames.begin(), colNames.end(), "All_ToA") == colNames.end())
152 {
153 df = df.Define(
154 "All_ToA", [](const RVecI &b) { return RVecD(b.size(), 0.0); }, { "All_Bundle" });
155 }
156 if(std::find(colNames.begin(), colNames.end(), "All_ToT") == colNames.end())
157 {
158 df = df.Define(
159 "All_ToT", [](const RVecI &b) { return RVecUS(b.size(), 0); }, { "All_Bundle" });
160 }
161 if(std::find(colNames.begin(), colNames.end(), "All_Board") == colNames.end())
162 {
163 df = df.Define(
164 "All_Board", [](const RVecI &b) { return RVecI(b.size(), 0); }, { "All_Bundle" });
165 }
166
167 // Note: For pure Toy data, FirstToA and SumToT might not be meaningful/present.
168
169 return df;
170 }
171
172 // 1. ToA Corrections (Alignment w.r.t. FD00)
173 // FD00 is the reference: only LSB -> ns conversion
174 auto df_corr = fHeadNode.Define(
175 "FD00_corrToA", [](const RVecUI &toa) { return RVecD(toa) / 2.0; }, { "FD00_ToA" });
176
177 // Other boards (FD01..FD03) are aligned using fine timestamp
178 for(const string &id : { "01", "02", "03" })
179 {
180 df_corr = df_corr.Define("FD" + id + "_corrToA",
181 [](const RVecUI &toa, Double_t t, Double_t t0)
182 {
183 // Correction: ToA/2 + fine timestamp difference (ps -> ns?)
184 // Note: original code had * 1e3, check units!
185 return RVecD(toa) / 2.0 + (t - t0) * 1e3;
186 },
187 { "FD" + id + "_ToA", "FD" + id + "_fine_tstamp", "FD00_fine_tstamp" });
188 }
189
190 // 2. Loop Board -> Selection -> Bundle Mapping
191 auto df_geo = df_corr;
192 vector<string> toa_cols, tot_cols, cyl_cols, lay_cols, bund_cols, board_cols;
193
194 // Map string ID -> numeric ID for geometry
195 map<string, int> board_id_map = { { "00", 0 }, { "01", 1 }, { "02", 2 }, { "03", 3 } };
196
197 for(const auto &[id_str, id_num] : board_id_map)
198 {
199 string id = id_str; // "00", "01"...
200 int b_id = id_num;
201
202 string toa_n = "FD" + id + "_ToA_sel";
203 string tot_n = "FD" + id + "_ToT_sel";
204 string cyl_n = "FD" + id + "_cyl_sel";
205 string lay_n = "FD" + id + "_lay_sel";
206 string bund_n = "FD" + id + "_bund_sel";
207 string board_n = "FD" + id + "_board_sel";
208
209 toa_cols.push_back(toa_n);
210 tot_cols.push_back(tot_n);
211 cyl_cols.push_back(cyl_n);
212 lay_cols.push_back(lay_n);
213 bund_cols.push_back(bund_n);
214 board_cols.push_back(board_n);
215
216 // Check Board Filtering
217 bool isBoardEnabled = true;
218 if(!enabledBoards.empty())
219 {
220 if(std::find(enabledBoards.begin(), enabledBoards.end(), b_id) == enabledBoards.end())
221 {
222 isBoardEnabled = false;
223 }
224 }
225
226 if(!isBoardEnabled)
227 {
228 // Define empty columns if board is disabled
229 // We use specific types to match the expected types in Concatenate
230 df_geo = df_geo.Define(toa_n, []() { return RVecD {}; })
231 .Define(tot_n, []() { return RVecUS {}; })
232 .Define(cyl_n, []() { return RVecI {}; })
233 .Define(lay_n, []() { return RVecI {}; })
234 .Define(bund_n, []() { return RVecI {}; })
235 .Define(board_n, []() { return RVecI {}; });
236 continue;
237 }
238
239 // If board is enabled, we first calculate everything into temporary
240 // columns, then apply cylinder/layer filtering.
241 string toa_tmp = toa_n + "_tmp";
242 string tot_tmp = tot_n + "_tmp";
243 string cyl_tmp = cyl_n + "_tmp";
244 string lay_tmp = lay_n + "_tmp";
245 string bund_tmp = bund_n + "_tmp";
246 string board_tmp = board_n + "_tmp";
247
248 // Time cuts on ToA and ToT
249 df_geo = df_geo.Define(toa_tmp,
250 [=](const RVecD &toa, const RVecUS &tot)
251 { return toa[(toa > tMin) && (toa < tMax) && (tot > totMin) && (tot < totMax)]; },
252 { "FD" + id + "_corrToA", "FD" + id + "_ToT" });
253
254 df_geo = df_geo.Define(tot_tmp,
255 [=](const RVecD &toa, const RVecUS &tot)
256 { return tot[(toa > tMin) && (toa < tMax) && (tot > totMin) && (tot < totMax)]; },
257 { "FD" + id + "_corrToA", "FD" + id + "_ToT" });
258
259 // Bundle ID Calculation (Geometry)
260 df_geo = df_geo.Define(bund_tmp,
261 [=](const RVecD &toa, const RVecUS &tot, const RVecUC &chans)
262 {
263 RVecI bunds;
264 auto mask = (toa > tMin) && (toa < tMax) && (tot > totMin) && (tot < totMax);
265 auto sel_chans = chans[mask];
266 for(auto c : sel_chans)
267 {
268 int gid = CHeT::Config::GetGlobalBundleId(b_id, (int)c);
269 bunds.push_back(gid);
270 }
271 return bunds;
272 },
273 { "FD" + id + "_corrToA", "FD" + id + "_ToT", "FD" + id + "_channel" });
274
275 // Derive Cylinder and Layer from Global IDs
276 df_geo = df_geo.Define(cyl_tmp,
277 [](const RVecI &bunds)
278 {
279 RVecI cyls;
280 for(auto b : bunds)
281 cyls.push_back((b >= 0) ? CHeT::Config::GetFiberProp(b).cylinderId : -1);
282 return cyls;
283 },
284 { bund_tmp });
285
286 df_geo = df_geo.Define(lay_tmp,
287 [](const RVecI &bunds)
288 {
289 RVecI lays;
290 for(auto b : bunds)
291 lays.push_back((b >= 0) ? CHeT::Config::GetFiberProp(b).layerId : -1);
292 return lays;
293 },
294 { bund_tmp });
295
296 df_geo = df_geo.Define(board_tmp,
297 [b_id](const RVecI &bunds) { return RVecI(bunds.size(), b_id); }, { bund_tmp });
298
299 // Create Filter Mask based on Cylinder and Layer
300 string mask_n = "FD" + id + "_geo_mask";
301 df_geo = df_geo.Define(mask_n,
302 [enabledCylinders, enabledLayers, enabledGeometries](
303 const RVecI &cyls, const RVecI &lays)
304 {
305 // By default, keep everything (mask=1)
306 RVec<int> mask(cyls.size(), 1);
307
308 // If no filters are set, return all 1s
309 if(enabledCylinders.empty() && enabledLayers.empty() && enabledGeometries.empty())
310 {
311 return mask;
312 }
313
314 for(size_t i = 0; i < cyls.size(); ++i)
315 {
316 // Check Cylinder
317 if(!enabledCylinders.empty())
318 {
319 bool found = false;
320 for(auto c : enabledCylinders)
321 if(c == cyls[i])
322 {
323 found = true;
324 break;
325 }
326 if(!found)
327 {
328 mask[i] = 0;
329 continue;
330 } // Optimization: if fail cyl, fail hit
331 }
332
333 // Check Layer
334 if(!enabledLayers.empty())
335 {
336 bool found = false;
337 for(auto l : enabledLayers)
338 if(l == lays[i])
339 {
340 found = true;
341 break;
342 }
343 if(!found)
344 mask[i] = 0;
345 }
346
347 // Check Specific Geometries (Cyl, Lay)
348 if(!enabledGeometries.empty())
349 {
350 bool found = false;
351 for(const auto &geo : enabledGeometries)
352 {
353 if(geo.first == cyls[i] && geo.second == lays[i])
354 {
355 found = true;
356 break;
357 }
358 }
359 if(!found)
360 mask[i] = 0;
361 }
362 }
363 return mask;
364 },
365 { cyl_tmp, lay_tmp });
366
367 // Apply Mask to final columns
368 // We use explicit lambdas to ensure type correctness
369 df_geo = df_geo
370 .Define(toa_n, [](const RVecD &v, const RVecI &m) { return v[m]; },
371 { toa_tmp, mask_n })
372 .Define(tot_n, [](const RVecUS &v, const RVecI &m) { return v[m]; },
373 { tot_tmp, mask_n })
374 .Define(cyl_n, [](const RVecI &v, const RVecI &m) { return v[m]; },
375 { cyl_tmp, mask_n })
376 .Define(lay_n, [](const RVecI &v, const RVecI &m) { return v[m]; },
377 { lay_tmp, mask_n })
378 .Define(bund_n, [](const RVecI &v, const RVecI &m) { return v[m]; },
379 { bund_tmp, mask_n })
380 .Define(board_n, [](const RVecI &v, const RVecI &m) { return v[m]; },
381 { board_tmp, mask_n });
382 }
383
384 // 3. Global Concatenation (All Boards -> Single Event Vector)
385 auto df_final
386 = df_geo
387 .Define(
388 "All_Cyl",
389 [](const RVecI &c0, const RVecI &c1, const RVecI &c2, const RVecI &c3)
390 { return Concatenate(c0, Concatenate(c1, Concatenate(c2, c3))); },
391 cyl_cols)
392 .Define(
393 "All_Lay",
394 [](const RVecI &l0, const RVecI &l1, const RVecI &l2, const RVecI &l3)
395 { return Concatenate(l0, Concatenate(l1, Concatenate(l2, l3))); },
396 lay_cols)
397 .Define(
398 "All_Bundle",
399 [](const RVecI &b0, const RVecI &b1, const RVecI &b2, const RVecI &b3)
400 { return Concatenate(b0, Concatenate(b1, Concatenate(b2, b3))); },
401 bund_cols)
402 .Define(
403 "All_ToA",
404 [](const RVecD &t0, const RVecD &t1, const RVecD &t2, const RVecD &t3)
405 { return Concatenate(t0, Concatenate(t1, Concatenate(t2, t3))); },
406 toa_cols)
407 .Define(
408 "All_ToT",
409 [](const RVecUS &t0, const RVecUS &t1, const RVecUS &t2, const RVecUS &t3)
410 { return Concatenate(t0, Concatenate(t1, Concatenate(t2, t3))); },
411 tot_cols)
412 .Define(
413 "All_Board",
414 [](const RVecI &b0, const RVecI &b1, const RVecI &b2, const RVecI &b3)
415 { return Concatenate(b0, Concatenate(b1, Concatenate(b2, b3))); },
416 board_cols)
417
418 // Counts and Event-Level variables
419 .Define("nHits_Total", "(double)All_Cyl.size()")
420 .Define("nHits_Cyl0", "Sum(All_Cyl == 0)")
421 .Define("nHits_Cyl1", "Sum(All_Cyl == 1)")
422 .Define("nHits_Cyl0_Inner", "Sum(All_Cyl == 0 && All_Lay == 0)")
423 .Define("nHits_Cyl0_Outer", "Sum(All_Cyl == 0 && All_Lay == 1)")
424 .Define("nHits_Cyl1_Inner", "Sum(All_Cyl == 1 && All_Lay == 0)")
425 .Define("nHits_Cyl1_Outer", "Sum(All_Cyl == 1 && All_Lay == 1)")
426 .Define(
427 "FirstToA",
428 [](const RVecD &t0, const RVecD &t1, const RVecD &t2, const RVecD &t3)
429 {
430 auto all = Concatenate(t0, Concatenate(t1, Concatenate(t2, t3)));
431 return all.empty() ? std::numeric_limits<Double_t>::infinity() : Min(all);
432 },
433 toa_cols)
434 .Define(
435 "SumToT",
436 [](const RVecUS &t0, const RVecUS &t1, const RVecUS &t2, const RVecUS &t3)
437 {
438 auto all = Concatenate(t0, Concatenate(t1, Concatenate(t2, t3)));
439 return all.empty() ? 0.0 : Sum(all);
440 },
441 tot_cols);
442
443 return df_final;
444}
445
446void Reader::SaveToTree(const std::string &filename, const std::string &treeName)
447{
448 auto df = GetCHeTTree();
449 std::vector<std::string> colsToSave;
450 for(const auto &col : df.GetColumnNames())
451 {
452 if(col.find("All_") == 0 || col.find("nHits_") == 0 || col == "FirstToA" || col == "SumToT")
453 {
454 colsToSave.push_back(col);
455 }
456 }
457 df.Snapshot(treeName, filename, colsToSave);
458}
459
460} // namespace Data
461} // namespace CHeT
RVec< unsigned char > RVecUC
Definition CHeTReader.cc:14
RVec< unsigned short > RVecUS
Definition CHeTReader.cc:13
RVec< unsigned int > RVecUI
Definition CHeTReader.cc:12
void SetEnabledCylinders(const std::vector< int > &cylinders)
Restricts the analysis to specific cylinders. Only hits belonging to these cylinders will be included...
Definition CHeTReader.cc:75
ROOT::RDF::RNode fHeadNode
Reader(const std::string &filename, const std::string &treeName="auto")
Constructor.
Definition CHeTReader.cc:43
void SetEnabledLayers(const std::vector< int > &layers)
Restricts the analysis to specific layers. Only hits belonging to these layers will be included in th...
Definition CHeTReader.cc:80
void SetSingleEntry(long entry)
Restricts the analysis to a single entry index. Replaces any previous range or filter on the entry in...
Definition CHeTReader.cc:60
ROOT::RDataFrame fDF
void SetEventByID(int eventID)
Restricts the analysis to a specific EventID. Filters the dataset where the EventID branch matches th...
Definition CHeTReader.cc:65
std::vector< int > fEnabledLayers
std::vector< int > fEnabledBoards
ROOT::RDF::RNode GetCHeTTree()
Returns the node with calculated high-level variables.
Definition CHeTReader.cc:95
std::vector< int > fEnabledCylinders
void SaveToTree(const std::string &filename, const std::string &treeName="chet")
Saves the processed high-level data to a new ROOT file.
unsigned int fTotMin
ROOT::RDF::RNode GetRaw()
Returns the Raw node (the original tree).
Definition CHeTReader.cc:90
void SetEnabledGeometries(const std::vector< std::pair< int, int > > &geometries)
Restricts the analysis to specific (cylinder, layer) combinations. Only hits matching one of the prov...
Definition CHeTReader.cc:85
void SetEnabledBoards(const std::vector< int > &boards)
Restricts the analysis to specific boards. Only hits from these boards will be processed.
Definition CHeTReader.cc:70
unsigned int fTotMax
void SetCuts(double toaMin, double toaMax, unsigned int totMin, unsigned int totMax)
Set cuts for hit selection. Must be called before GetCHeTTree().
Definition CHeTReader.cc:52
std::vector< std::pair< int, int > > fEnabledGeometries
FiberProp GetFiberProp(int b_id)
Retrieves fiber properties given a global bundle ID.
int GetGlobalBundleId(int board_id, int channel_id)
Converts Hardware Board/Channel to Global Bundle ID.
Root namespace for the Cylindrical Helix Tracker (CHeT) project.
int layerId
0 for inner, 1 for outer