File indexing completed on 2026-06-17 07:50:58
0001
0002
0003
0004 #include <DD4hep/Detector.h>
0005 #include <DD4hep/IDDescriptor.h>
0006 #include <DD4hep/Readout.h>
0007 #include <DDSegmentation/BitFieldCoder.h>
0008 #include <algorithms/geo.h>
0009 #include <catch2/catch_approx.hpp>
0010 #include <catch2/catch_test_macros.hpp>
0011 #include <edm4eic/RawTrackerHitCollection.h>
0012 #include <edm4eic/TrackerHitCollection.h>
0013 #include <gsl/pointers>
0014 #include <string>
0015
0016 #include "algorithms/tracking/MPGDHitReconstruction.h"
0017 #include "algorithms/tracking/MPGDHitReconstructionConfig.h"
0018
0019 using eicrecon::MPGDHitReconstruction;
0020 using eicrecon::MPGDHitReconstructionConfig;
0021
0022
0023
0024
0025
0026 static dd4hep::DDSegmentation::CellID makeCellID(const dd4hep::IDDescriptor& desc, int system,
0027 int layer, int module, int strip, int x, int y) {
0028 auto encoder = desc.decoder();
0029 dd4hep::DDSegmentation::CellID cid = 0;
0030 encoder->set(cid, "system", system);
0031 encoder->set(cid, "layer", layer);
0032 encoder->set(cid, "module", module);
0033 encoder->set(cid, "strip", strip);
0034 encoder->set(cid, "x", x);
0035 encoder->set(cid, "y", y);
0036 return cid;
0037 }
0038
0039 static dd4hep::IDDescriptor getMPGDIdDesc() {
0040 return algorithms::GeoSvc::instance().detector()->readout("MockMPGDHits").idSpec();
0041 }
0042
0043 TEST_CASE("MPGDHitReconstruction: empty input produces empty output", "[MPGDHitReconstruction]") {
0044 MPGDHitReconstruction algo("test_empty");
0045 MPGDHitReconstructionConfig cfg;
0046 cfg.readout = "MockMPGDHits";
0047 cfg.timeResolution = 10.0;
0048 algo.applyConfig(cfg);
0049 algo.init();
0050
0051 edm4eic::RawTrackerHitCollection raw_hits;
0052 edm4eic::TrackerHitCollection rec_hits;
0053
0054 algo.process({&raw_hits}, {&rec_hits});
0055 REQUIRE(rec_hits.size() == 0);
0056 }
0057
0058 TEST_CASE("MPGDHitReconstruction: single p-strip hit produces one cluster",
0059 "[MPGDHitReconstruction]") {
0060 MPGDHitReconstruction algo("test_single_p");
0061 MPGDHitReconstructionConfig cfg;
0062 cfg.readout = "MockMPGDHits";
0063 cfg.timeResolution = 10.0;
0064 algo.applyConfig(cfg);
0065 algo.init();
0066
0067 auto id_desc = getMPGDIdDesc();
0068 const int pStrip = 1;
0069
0070 edm4eic::RawTrackerHitCollection raw_hits;
0071 edm4eic::TrackerHitCollection rec_hits;
0072
0073 auto cellID = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0074 raw_hits.create(cellID, 500, 100000);
0075
0076 algo.process({&raw_hits}, {&rec_hits});
0077
0078 REQUIRE(rec_hits.size() == 1);
0079
0080 CHECK(rec_hits[0].getEdep() > 0);
0081
0082 CHECK(rec_hits[0].getCellID() == cellID);
0083
0084 CHECK(rec_hits[0].getRawHit().getCellID() == cellID);
0085 }
0086
0087 TEST_CASE("MPGDHitReconstruction: single n-strip hit produces one cluster",
0088 "[MPGDHitReconstruction]") {
0089 MPGDHitReconstruction algo("test_single_n");
0090 MPGDHitReconstructionConfig cfg;
0091 cfg.readout = "MockMPGDHits";
0092 cfg.timeResolution = 10.0;
0093 algo.applyConfig(cfg);
0094 algo.init();
0095
0096 auto id_desc = getMPGDIdDesc();
0097 const int nStrip = 2;
0098
0099 edm4eic::RawTrackerHitCollection raw_hits;
0100 edm4eic::TrackerHitCollection rec_hits;
0101
0102 auto cellID = makeCellID(id_desc, 3, 0, 0, nStrip, 0, 15);
0103 raw_hits.create(cellID, 700, 200000);
0104
0105 algo.process({&raw_hits}, {&rec_hits});
0106
0107 REQUIRE(rec_hits.size() == 1);
0108 CHECK(rec_hits[0].getCellID() == cellID);
0109 CHECK(rec_hits[0].getRawHit().getCellID() == cellID);
0110 }
0111
0112 TEST_CASE("MPGDHitReconstruction: two adjacent p-strip hits cluster together",
0113 "[MPGDHitReconstruction]") {
0114 MPGDHitReconstruction algo("test_adj_p");
0115 MPGDHitReconstructionConfig cfg;
0116 cfg.readout = "MockMPGDHits";
0117 cfg.timeResolution = 10.0;
0118 algo.applyConfig(cfg);
0119 algo.init();
0120
0121 auto id_desc = getMPGDIdDesc();
0122 const int pStrip = 1;
0123
0124 edm4eic::RawTrackerHitCollection raw_hits;
0125 edm4eic::TrackerHitCollection rec_hits;
0126
0127
0128 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0129 auto cid1 = makeCellID(id_desc, 3, 0, 0, pStrip, 11, 0);
0130 raw_hits.create(cid0, 600, 100000);
0131 raw_hits.create(cid1, 400, 110000);
0132
0133 algo.process({&raw_hits}, {&rec_hits});
0134
0135
0136 REQUIRE(rec_hits.size() == 1);
0137
0138 CHECK(rec_hits[0].getEdep() > 0);
0139
0140 CHECK(rec_hits[0].getCellID() == cid0);
0141
0142 CHECK(rec_hits[0].getTime() > 0);
0143 }
0144
0145 TEST_CASE("MPGDHitReconstruction: two adjacent n-strip hits cluster together",
0146 "[MPGDHitReconstruction]") {
0147 MPGDHitReconstruction algo("test_adj_n");
0148 MPGDHitReconstructionConfig cfg;
0149 cfg.readout = "MockMPGDHits";
0150 cfg.timeResolution = 10.0;
0151 algo.applyConfig(cfg);
0152 algo.init();
0153
0154 auto id_desc = getMPGDIdDesc();
0155 const int nStrip = 2;
0156
0157 edm4eic::RawTrackerHitCollection raw_hits;
0158 edm4eic::TrackerHitCollection rec_hits;
0159
0160 auto cid0 = makeCellID(id_desc, 3, 0, 0, nStrip, 0, 20);
0161 auto cid1 = makeCellID(id_desc, 3, 0, 0, nStrip, 0, 21);
0162 raw_hits.create(cid0, 300, 100000);
0163 raw_hits.create(cid1, 500, 150000);
0164
0165 algo.process({&raw_hits}, {&rec_hits});
0166
0167 REQUIRE(rec_hits.size() == 1);
0168
0169 CHECK(rec_hits[0].getCellID() == cid1);
0170 }
0171
0172 TEST_CASE("MPGDHitReconstruction: separated hits produce separate clusters",
0173 "[MPGDHitReconstruction]") {
0174 MPGDHitReconstruction algo("test_separated");
0175 MPGDHitReconstructionConfig cfg;
0176 cfg.readout = "MockMPGDHits";
0177 cfg.timeResolution = 10.0;
0178 algo.applyConfig(cfg);
0179 algo.init();
0180
0181 auto id_desc = getMPGDIdDesc();
0182 const int pStrip = 1;
0183
0184 edm4eic::RawTrackerHitCollection raw_hits;
0185 edm4eic::TrackerHitCollection rec_hits;
0186
0187
0188 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 5, 0);
0189 auto cid1 = makeCellID(id_desc, 3, 0, 0, pStrip, 50, 0);
0190 raw_hits.create(cid0, 500, 100000);
0191 raw_hits.create(cid1, 500, 100000);
0192
0193 algo.process({&raw_hits}, {&rec_hits});
0194
0195
0196 REQUIRE(rec_hits.size() == 2);
0197 }
0198
0199 TEST_CASE("MPGDHitReconstruction: p-strip and n-strip hits produce separate clusters",
0200 "[MPGDHitReconstruction]") {
0201 MPGDHitReconstruction algo("test_pn_sep");
0202 MPGDHitReconstructionConfig cfg;
0203 cfg.readout = "MockMPGDHits";
0204 cfg.timeResolution = 10.0;
0205 algo.applyConfig(cfg);
0206 algo.init();
0207
0208 auto id_desc = getMPGDIdDesc();
0209 const int pStrip = 1;
0210 const int nStrip = 2;
0211
0212 edm4eic::RawTrackerHitCollection raw_hits;
0213 edm4eic::TrackerHitCollection rec_hits;
0214
0215
0216 auto cidP = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0217 auto cidN = makeCellID(id_desc, 3, 0, 0, nStrip, 0, 10);
0218 raw_hits.create(cidP, 500, 100000);
0219 raw_hits.create(cidN, 500, 100000);
0220
0221 algo.process({&raw_hits}, {&rec_hits});
0222
0223 REQUIRE(rec_hits.size() == 2);
0224 }
0225
0226 TEST_CASE("MPGDHitReconstruction: three-hit cluster sums charge correctly",
0227 "[MPGDHitReconstruction]") {
0228 MPGDHitReconstruction algo("test_3hit");
0229 MPGDHitReconstructionConfig cfg;
0230 cfg.readout = "MockMPGDHits";
0231 cfg.timeResolution = 10.0;
0232 algo.applyConfig(cfg);
0233 algo.init();
0234
0235 auto id_desc = getMPGDIdDesc();
0236 const int pStrip = 1;
0237
0238 edm4eic::RawTrackerHitCollection raw_hits;
0239 edm4eic::TrackerHitCollection rec_hits;
0240
0241
0242 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0243 auto cid1 = makeCellID(id_desc, 3, 0, 0, pStrip, 11, 0);
0244 auto cid2 = makeCellID(id_desc, 3, 0, 0, pStrip, 12, 0);
0245 raw_hits.create(cid0, 200, 100000);
0246 raw_hits.create(cid1, 800, 110000);
0247 raw_hits.create(cid2, 300, 120000);
0248
0249 algo.process({&raw_hits}, {&rec_hits});
0250
0251 REQUIRE(rec_hits.size() == 1);
0252
0253 float expected_edep = (200.0f + 800.0f + 300.0f) / 1.0e6f;
0254 CHECK(rec_hits[0].getEdep() == Catch::Approx(expected_edep).epsilon(0.01));
0255
0256 CHECK(rec_hits[0].getCellID() == cid1);
0257 }
0258
0259 TEST_CASE("MPGDHitReconstruction: cluster timing from max-charge hit", "[MPGDHitReconstruction]") {
0260 MPGDHitReconstruction algo("test_timing");
0261 MPGDHitReconstructionConfig cfg;
0262 cfg.readout = "MockMPGDHits";
0263 cfg.timeResolution = 10.0;
0264 algo.applyConfig(cfg);
0265 algo.init();
0266
0267 auto id_desc = getMPGDIdDesc();
0268 const int pStrip = 1;
0269
0270 edm4eic::RawTrackerHitCollection raw_hits;
0271 edm4eic::TrackerHitCollection rec_hits;
0272
0273 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0274 auto cid1 = makeCellID(id_desc, 3, 0, 0, pStrip, 11, 0);
0275
0276 raw_hits.create(cid0, 300, 100000);
0277 raw_hits.create(cid1, 700, 200000);
0278
0279 algo.process({&raw_hits}, {&rec_hits});
0280
0281 REQUIRE(rec_hits.size() == 1);
0282
0283 float expected_time = 200000.0f / 1000.0f;
0284 CHECK(rec_hits[0].getTime() == Catch::Approx(expected_time).epsilon(0.01));
0285 }
0286
0287 TEST_CASE("MPGDHitReconstruction: out-of-order input is sorted and clustered",
0288 "[MPGDHitReconstruction]") {
0289 MPGDHitReconstruction algo("test_sort");
0290 MPGDHitReconstructionConfig cfg;
0291 cfg.readout = "MockMPGDHits";
0292 cfg.timeResolution = 10.0;
0293 algo.applyConfig(cfg);
0294 algo.init();
0295
0296 auto id_desc = getMPGDIdDesc();
0297 const int pStrip = 1;
0298
0299 edm4eic::RawTrackerHitCollection raw_hits;
0300 edm4eic::TrackerHitCollection rec_hits;
0301
0302
0303 auto cid1 = makeCellID(id_desc, 3, 0, 0, pStrip, 11, 0);
0304 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0305 raw_hits.create(cid1, 400, 110000);
0306 raw_hits.create(cid0, 600, 100000);
0307
0308 algo.process({&raw_hits}, {&rec_hits});
0309
0310
0311 REQUIRE(rec_hits.size() == 1);
0312
0313 CHECK(rec_hits[0].getCellID() == cid0);
0314 }
0315
0316 TEST_CASE("MPGDHitReconstruction: mixed p/n strips with adjacency", "[MPGDHitReconstruction]") {
0317 MPGDHitReconstruction algo("test_mixed");
0318 MPGDHitReconstructionConfig cfg;
0319 cfg.readout = "MockMPGDHits";
0320 cfg.timeResolution = 10.0;
0321 algo.applyConfig(cfg);
0322 algo.init();
0323
0324 auto id_desc = getMPGDIdDesc();
0325 const int pStrip = 1;
0326 const int nStrip = 2;
0327
0328 edm4eic::RawTrackerHitCollection raw_hits;
0329 edm4eic::TrackerHitCollection rec_hits;
0330
0331
0332 auto cidP0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0333 auto cidP1 = makeCellID(id_desc, 3, 0, 0, pStrip, 11, 0);
0334 auto cidN0 = makeCellID(id_desc, 3, 0, 0, nStrip, 0, 10);
0335 raw_hits.create(cidP0, 500, 100000);
0336 raw_hits.create(cidP1, 500, 100000);
0337 raw_hits.create(cidN0, 500, 100000);
0338
0339 algo.process({&raw_hits}, {&rec_hits});
0340
0341
0342 REQUIRE(rec_hits.size() == 2);
0343 }
0344
0345 TEST_CASE("MPGDHitReconstruction: different modules produce separate clusters",
0346 "[MPGDHitReconstruction]") {
0347 MPGDHitReconstruction algo("test_modules");
0348 MPGDHitReconstructionConfig cfg;
0349 cfg.readout = "MockMPGDHits";
0350 cfg.timeResolution = 10.0;
0351 algo.applyConfig(cfg);
0352 algo.init();
0353
0354 auto id_desc = getMPGDIdDesc();
0355 const int pStrip = 1;
0356
0357 edm4eic::RawTrackerHitCollection raw_hits;
0358 edm4eic::TrackerHitCollection rec_hits;
0359
0360
0361 auto cid0 = makeCellID(id_desc, 3, 0, 0, pStrip, 10, 0);
0362 auto cid1 = makeCellID(id_desc, 3, 0, 1, pStrip, 10, 0);
0363 raw_hits.create(cid0, 500, 100000);
0364 raw_hits.create(cid1, 500, 100000);
0365
0366 algo.process({&raw_hits}, {&rec_hits});
0367
0368
0369 REQUIRE(rec_hits.size() == 2);
0370 }