File indexing completed on 2026-04-08 07:51:18
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090 #include "MPGDTrackerDigi.h"
0091
0092 #include <DD4hep/Alignments.h>
0093 #include <DD4hep/DetElement.h>
0094 #include <DD4hep/IDDescriptor.h>
0095 #include <DD4hep/Objects.h>
0096 #include <DD4hep/Readout.h>
0097 #include <DD4hep/Shapes.h>
0098 #include <DD4hep/VolumeManager.h>
0099 #include <DD4hep/detail/SegmentationsInterna.h>
0100 #include <DDSegmentation/BitFieldCoder.h>
0101 #include <DDSegmentation/MultiSegmentation.h>
0102 #include <DDSegmentation/Segmentation.h>
0103 #include <Evaluator/DD4hepUnits.h>
0104 #include <Math/GenVector/Cartesian3D.h>
0105 #include <Math/GenVector/DisplacementVector3D.h>
0106 #include <Parsers/Primitives.h>
0107 #include <TGeoMatrix.h>
0108 #include <TMath.h>
0109
0110 #include <algorithms/geo.h>
0111 #include <algorithms/logger.h>
0112 #include <edm4eic/unit_system.h>
0113 #include <edm4hep/EDM4hepVersion.h>
0114 #include <edm4hep/MCParticleCollection.h>
0115 #include <edm4hep/Vector3d.h>
0116 #include <edm4hep/Vector3f.h>
0117 #include <fmt/format.h>
0118 #include <podio/detail/Link.h>
0119 #include <podio/detail/LinkCollectionImpl.h>
0120 #include <algorithm>
0121 #include <cmath>
0122 #include <cstdint>
0123 #include <cstring>
0124 #include <gsl/pointers>
0125 #include <initializer_list>
0126 #include <iterator>
0127 #include <map>
0128 #include <memory>
0129 #include <random>
0130 #include <stdexcept>
0131 #include <tuple>
0132 #include <unordered_map>
0133 #include <utility>
0134 #include <vector>
0135
0136 #include "algorithms/digi/MPGDTrackerDigiConfig.h"
0137
0138 using namespace dd4hep;
0139
0140 namespace eicrecon {
0141
0142 void MPGDTrackerDigi::init() {
0143
0144 m_detector = algorithms::GeoSvc::instance().detector();
0145
0146 if (m_cfg.readout.empty()) {
0147 throw std::runtime_error("Readout is empty");
0148 }
0149 try {
0150 m_seg = m_detector->readout(m_cfg.readout).segmentation();
0151 m_id_dec = m_detector->readout(m_cfg.readout).idSpec().decoder();
0152 } catch (const std::runtime_error&) {
0153 critical(R"(Failed to load ID decoder for "{}" readout.)", m_cfg.readout);
0154 throw std::runtime_error("Failed to load ID decoder");
0155 }
0156
0157
0158 parseIDDescriptor();
0159 parseSegmentation();
0160
0161
0162 m_stripRank = [&](CellID vID) {
0163 CellID sID = vID & m_stripBits;
0164 for (int rank = 0; rank < 5; rank++) {
0165 if (sID == m_stripIDs[rank]) {
0166 return rank;
0167 }
0168 }
0169 return -1;
0170 };
0171 m_orientation = [&](CellID vID, CellID vJD) {
0172 int ranki = m_stripRank(vID);
0173 int rankj = m_stripRank(vJD);
0174 if (rankj > ranki) {
0175 return +1;
0176 }
0177 if (rankj < ranki) {
0178 return -1;
0179 }
0180 return 0;
0181 };
0182 m_isUpstream = [](int orientation, unsigned int status) {
0183
0184 bool isUpstream =
0185 (orientation < 0 && (status & 0x2)) ||
0186 (orientation > 0 && (status & 0x8)) ||
0187 (orientation == 0 && (status & 0x102) == 0x102);
0188 return isUpstream;
0189 };
0190 m_isDownstream = [](int orientation, unsigned int status) {
0191
0192 bool isDownstream =
0193 (orientation > 0 && (status & 0x1)) ||
0194 (orientation < 0 && (status & 0x4)) ||
0195 (orientation == 0 && (status & 0x101) == 0x101);
0196 return isDownstream;
0197 };
0198
0199 m_toleranceFactor = [](double P) {
0200 int factor = 0;
0201 if (P < 1 * dd4hep::MeV) {
0202 factor = 4;
0203 } else if (P < 10 * dd4hep::MeV) {
0204 factor = 2;
0205 } else {
0206 factor = 1;
0207 }
0208 return factor;
0209 };
0210 }
0211
0212
0213 void getLocalPosMom(const edm4hep::SimTrackerHit& sim_hit, const TGeoHMatrix& toModule,
0214 double* lpos, double* lmom);
0215 bool cExtrapolate(const double* lpos, const double* lmom,
0216 double rT,
0217 double* lext);
0218 double getRef2Cur(DetElement refVol, DetElement curVol);
0219 bool bExtrapolate(const double* lpos, const double* lmom,
0220 double zT,
0221 double* lext);
0222 std::string inconsistency(const edm4hep::EventHeader& event, unsigned int status, CellID cID,
0223 const double* lpos, const double* lmom);
0224 std::string oddity(const edm4hep::EventHeader& event, unsigned int status, double dist, CellID cID,
0225 const double* lpos, const double* lmom, CellID cJD, const double* lpoj,
0226 const double* lmoj);
0227 double outInDistance(int shape, int orientation, double lintos[][3], double louts[][3],
0228 double* lmom, double* lmoj);
0229 void flagUnexpected(const edm4hep::EventHeader& event, int shape, double expected,
0230 const edm4hep::SimTrackerHit& sim_hit, double* lpini, double* lpend,
0231 double* lpos, double* lmom);
0232
0233 void MPGDTrackerDigi::process(const MPGDTrackerDigi::Input& input,
0234 const MPGDTrackerDigi::Output& output) const {
0235
0236 const auto [headers, sim_hits] = input;
0237 #if EDM4EIC_BUILD_VERSION >= EDM4EIC_VERSION(8, 7, 0)
0238 auto [raw_hits, links, associations] = output;
0239 #else
0240 auto [raw_hits, associations] = output;
0241 #endif
0242
0243
0244 auto seed = m_uid.getUniqueID(*headers, name());
0245 std::default_random_engine generator(seed);
0246 std::normal_distribution<double> gaussian;
0247
0248
0249 std::unordered_map<std::uint64_t, edm4eic::MutableRawTrackerHit> cell_hit_map;
0250
0251 std::map<std::uint64_t, std::vector<std::uint64_t>> stripID2cIDs;
0252
0253 const Position dummy(0, 0, 0);
0254 const VolumeManager& volman = m_detector->volumeManager();
0255
0256
0257
0258
0259 const edm4hep::EventHeader& header = headers->at(0);
0260
0261
0262 size_t sim_size = sim_hits->size();
0263 for (int idx = 0; idx < (int)sim_size; idx++) {
0264 const edm4hep::SimTrackerHit& sim_hit = sim_hits->at(idx);
0265
0266
0267
0268
0269
0270
0271
0272 double time_smearing = gaussian(generator) * m_cfg.timeResolution;
0273
0274
0275 CellID refID = sim_hit.getCellID() & m_moduleBits;
0276 DetElement refVol = volman.lookupDetElement(refID);
0277
0278
0279
0280
0281
0282 double lpos[3], eDep, time;
0283 std::vector<std::uint64_t> cIDs;
0284 const auto& shape = refVol.solid();
0285 if (std::string_view{shape.type()} == "TGeoTubeSeg") {
0286
0287 if (!cCoalesceExtend(input, idx, cIDs, lpos, eDep, time))
0288 continue;
0289 } else if (std::string_view{shape.type()} == "TGeoBBox") {
0290
0291 if (!bCoalesceExtend(input, idx, cIDs, lpos, eDep, time))
0292 continue;
0293 } else {
0294 critical(R"(Bad input data: CellID {:x} has invalid shape "{}")", refID, shape.type());
0295 throw std::runtime_error(R"(Inconsistency: Inappropriate SimHits fed to "MPGDTrackerDigi".)");
0296 }
0297
0298 Position locPos(lpos[0], lpos[1], lpos[2]);
0299
0300 CellID vIDp = refID | m_pStripBit;
0301 CellID cIDp = m_seg->cellID(locPos, dummy, vIDp);
0302
0303 CellID vIDn = refID | m_nStripBit;
0304 CellID cIDn = m_seg->cellID(locPos, dummy, vIDn);
0305 double result_time = time + time_smearing;
0306 auto hit_time_stamp = (std::int32_t)(result_time * 1e3);
0307
0308
0309 if (level() >= algorithms::LogLevel::kDebug) {
0310 for (CellID cID : {cIDp, cIDn}) {
0311 std::string sCellID = cID == cIDp ? "cellIDp" : "cellIDn";
0312 CellID hID = cID >> 32, vID = cID & m_volumeBits;
0313 debug("Hit {} = 0x{:08x}, 0x{:08x}", sCellID, hID, vID);
0314 Position stripPos = m_seg->position(cID);
0315 Position globPos = refVol.nominal().localToWorld(stripPos);
0316 debug(" position = ({:7.2f},{:7.2f},{:7.2f}) [mm]", globPos.X() / mm, globPos.Y() / mm,
0317 globPos.Z() / mm);
0318 }
0319 debug(" edep = {:.0f} [eV]", eDep / eV);
0320 debug(" time = {:.2f} [ns]", time);
0321 #if EDM4HEP_BUILD_VERSION >= EDM4HEP_VERSION(0, 99, 0)
0322 debug(" particle time = {} [ns]", sim_hit.getParticle().getTime());
0323 #else
0324 debug(" particle time = {} [ns]", sim_hit.getMCParticle().getTime());
0325 #endif
0326 debug(" time smearing: {:.2f}, resulting time = {:.2f} [ns]", time_smearing, result_time);
0327 debug(" hit_time_stamp: {} [~ps]", hit_time_stamp);
0328 }
0329
0330
0331 if (eDep < m_cfg.threshold) {
0332 debug(" edep is below threshold of {:.2f} [keV]", m_cfg.threshold / keV);
0333 continue;
0334 }
0335
0336
0337 for (CellID cID : {cIDp, cIDn}) {
0338 stripID2cIDs[cID] = cIDs;
0339 if (!cell_hit_map.contains(cID)) {
0340
0341 cell_hit_map[cID] = {
0342 cID, (std::int32_t)std::llround(eDep * 1e6),
0343 hit_time_stamp
0344 };
0345 } else {
0346
0347 auto& hit = cell_hit_map[cID];
0348 debug(" Hit already exists in cell ID={}, prev. hit time: {}", cID, hit.getTimeStamp());
0349
0350
0351 hit.setTimeStamp(std::min(hit_time_stamp, hit.getTimeStamp()));
0352
0353
0354 auto charge = hit.getCharge();
0355
0356 hit.setCharge(charge + (std::int32_t)std::llround(eDep * 1e6));
0357 }
0358 }
0359 }
0360
0361
0362 for (auto item : cell_hit_map) {
0363 raw_hits->push_back(item.second);
0364 CellID stripID = item.first;
0365 const auto is = stripID2cIDs.find(stripID);
0366 if (is == stripID2cIDs.end()) {
0367 error(R"(Inconsistency: CellID {:x} not found in "stripID2cIDs" map)", stripID);
0368 throw std::runtime_error(R"(Inconsistency in the handling of "stripID2cIDs" map)");
0369 }
0370 std::vector<std::uint64_t> cIDs = is->second;
0371 for (CellID cID : cIDs) {
0372 for (const auto& sim_hit : *sim_hits) {
0373 if (sim_hit.getCellID() == cID) {
0374 #if EDM4EIC_BUILD_VERSION >= EDM4EIC_VERSION(8, 7, 0)
0375
0376 auto link = links->create();
0377 link.setFrom(item.second);
0378 link.setTo(sim_hit);
0379 link.setWeight(1.0);
0380 #endif
0381
0382 auto hitassoc = associations->create();
0383 hitassoc.setWeight(1.0);
0384 hitassoc.setRawHit(item.second);
0385 hitassoc.setSimHit(sim_hit);
0386 }
0387 }
0388 }
0389 }
0390 }
0391
0392 void MPGDTrackerDigi::parseIDDescriptor() {
0393
0394
0395
0396
0397
0398
0399 debug(R"(Parsing IDDescriptor for "{}" readout)", m_cfg.readout);
0400 for (int field = 0; field < 5; field++) {
0401 const char* fieldName = m_fieldNames[field];
0402 CellID fieldID = 0;
0403 try {
0404 fieldID = m_id_dec->get(~((CellID)0x0), fieldName);
0405 } catch (const std::runtime_error& error) {
0406 critical(R"(No field "{}" in IDDescriptor of readout "{}".)", fieldName, m_cfg.readout);
0407 throw std::runtime_error("Invalid IDDescriptor");
0408 }
0409 const BitFieldElement& fieldElement = (*m_id_dec)[fieldName];
0410 CellID fieldBits = fieldID << fieldElement.offset();
0411 m_volumeBits |= fieldBits;
0412
0413 if (std::string_view{fieldName} == "strip") {
0414 m_stripBits = fieldBits;
0415
0416 CellID bits[5] = {0x3, 0x1, 0, 0x2, 0x4};
0417 for (int subVolume = 0; subVolume < 5; subVolume++) {
0418 m_stripIDs[subVolume] = bits[subVolume] << fieldElement.offset();
0419 }
0420 }
0421 }
0422
0423 m_moduleBits = m_volumeBits & ~m_stripBits;
0424 m_pStripBit = m_stripIDs[1];
0425 m_nStripBit = m_stripIDs[3];
0426 }
0427 void MPGDTrackerDigi::parseSegmentation() {
0428
0429
0430
0431
0432
0433
0434
0435 debug(R"(Find valid "MultiSegmentation" for "{}" readout.)", m_cfg.readout);
0436 bool ok = false;
0437 using Segmentation = dd4hep::DDSegmentation::Segmentation;
0438 const Segmentation* segmentation = m_seg->segmentation;
0439 if (segmentation->type() == "MultiSegmentation") {
0440 using MultiSegmentation = dd4hep::DDSegmentation::MultiSegmentation;
0441 const auto* multiSeg = dynamic_cast<const MultiSegmentation*>(segmentation);
0442 if (multiSeg->discriminatorName() == "strip")
0443 ok = true;
0444 if (!ok) {
0445 for (const auto entry : multiSeg->subSegmentations()) {
0446 const Segmentation* subSegmentation = entry.segmentation;
0447 if (subSegmentation->type() == "MultiSegmentation") {
0448 const auto* subMultiSeg = dynamic_cast<const MultiSegmentation*>(subSegmentation);
0449 if (subMultiSeg->discriminatorName() == "strip") {
0450 ok = true;
0451 } else {
0452 ok = false;
0453 break;
0454 }
0455 }
0456 }
0457 }
0458 }
0459 if (!ok) {
0460 critical(
0461 R"(Segmentation for readout "{}" is not, or is not embedding, a MultiSegmentation discriminating on a "strip" field.)",
0462 m_cfg.readout.c_str());
0463 throw std::runtime_error("Invalid Segmentation");
0464 }
0465 }
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480 bool MPGDTrackerDigi::cCoalesceExtend(const Input& input, int& idx,
0481 std::vector<std::uint64_t>& cIDs, double* lpos, double& eDep,
0482 double& time) const {
0483 const auto [headers, sim_hits] = input;
0484 const edm4hep::EventHeader& header = headers->at(0);
0485 const edm4hep::SimTrackerHit& sim_hit = sim_hits->at(idx);
0486 CellID vID = sim_hit.getCellID() & m_volumeBits;
0487 CellID refID = vID & m_moduleBits;
0488 const VolumeManager& volman = m_detector->volumeManager();
0489 DetElement refVol = volman.lookupDetElement(refID);
0490
0491
0492 const TGeoHMatrix toRefVol = refVol.nominal().worldTransformation();
0493 double lmom[3];
0494 getLocalPosMom(sim_hit, toRefVol, lpos, lmom);
0495 const double edmm = edm4eic::unit::mm, ed2dd = dd4hep::mm / edmm;
0496 using dd4hep::mm;
0497 using edm4eic::unit::eV, edm4eic::unit::GeV;
0498
0499 eDep = sim_hit.getEDep();
0500 time = sim_hit.getTime();
0501
0502 const Tube& tRef = refVol.solid();
0503 double dZ = tRef.dZ();
0504
0505
0506
0507
0508
0509 double startPhi = tRef.startPhi() * radian;
0510 startPhi -= 2 * TMath::Pi();
0511 double endPhi = tRef.endPhi() * radian;
0512 endPhi -= 2 * TMath::Pi();
0513
0514 DetElement curVol = volman.lookupDetElement(vID);
0515 const Tube& tCur = curVol.solid();
0516 double rMin = tCur.rMin(), rMax = tCur.rMax();
0517
0518 double lintos[2][3], louts[2][3], lpini[3], lpend[3], lmend[3];
0519 std::copy(std::begin(lmom), std::end(lmom), std::begin(lmend));
0520 unsigned int status =
0521 cTraversing(lpos, lmom, sim_hit.getPathLength() * ed2dd, sim_hit.isProducedBySecondary(),
0522 rMin, rMax, dZ, startPhi, endPhi, lintos, louts, lpini, lpend);
0523 if (status & m_inconsistency) {
0524 error(inconsistency(header, status, sim_hit.getCellID(), lpos, lmom));
0525 return false;
0526 }
0527 cIDs.push_back(sim_hit.getCellID());
0528 std::vector<int> subHitList;
0529 if (level() >= algorithms::LogLevel::kDebug) {
0530 subHitList.push_back(idx);
0531 }
0532
0533 bool isContinuation = status & (m_intoLower | m_intoUpper);
0534 bool hasContinuation = status & (m_outLower | m_outUpper);
0535 bool canReEnter = status & m_canReEnter;
0536 int rank = m_stripRank(vID);
0537 if (!canReEnter) {
0538 if (rank == 0 && (status & m_intoLower))
0539 isContinuation = false;
0540 if (rank == 0 && (status & m_outLower))
0541 hasContinuation = false;
0542 }
0543 if (rank == 4 && (status & m_intoUpper))
0544 isContinuation = false;
0545 if (rank == 4 && (status & m_outUpper))
0546 hasContinuation = false;
0547 if (hasContinuation) {
0548
0549 int jdx;
0550 CellID vIDPrv = vID;
0551 size_t sim_size = sim_hits->size();
0552 for (jdx = idx + 1; jdx < (int)sim_size; jdx++) {
0553 const edm4hep::SimTrackerHit& sim_hjt = sim_hits->at(jdx);
0554 CellID vJD = sim_hjt.getCellID() & m_volumeBits;
0555
0556
0557 int orientation = m_orientation(vIDPrv, vJD);
0558 bool isUpstream = m_isUpstream(orientation, status);
0559 bool pmoStatus = samePMO(sim_hit, sim_hjt);
0560 if (!pmoStatus || !isUpstream) {
0561 if ((pmoStatus && !isUpstream) && !sim_hit.isProducedBySecondary()) {
0562
0563
0564 double P = sqrt(lmom[0] * lmom[0] + lmom[1] * lmom[1] + lmom[2] * lmom[2]);
0565 if (P > 10 * dd4hep::MeV)
0566 debug(inconsistency(header, 0, sim_hit.getCellID(), lpos, lmom));
0567 }
0568 break;
0569 }
0570
0571 curVol = volman.lookupDetElement(vJD);
0572 const Tube& tubj = curVol.solid();
0573 rMin = tubj.rMin();
0574 rMax = tubj.rMax();
0575 double lpoj[3], lmoj[3];
0576 getLocalPosMom(sim_hjt, toRefVol, lpoj, lmoj);
0577
0578 double ljns[2][3], lovts[2][3], lpjni[3], lpfnd[3];
0579 status =
0580 cTraversing(lpoj, lmoj, sim_hjt.getPathLength() * ed2dd, sim_hit.isProducedBySecondary(),
0581 rMin, rMax, dZ, startPhi, endPhi, ljns, lovts, lpjni, lpfnd);
0582 if (status & m_inconsistency) {
0583 error(inconsistency(header, status, sim_hjt.getCellID(), lpoj, lmoj));
0584 break;
0585 }
0586
0587 bool jsDownstream = m_isDownstream(orientation, status);
0588 if (!jsDownstream)
0589 break;
0590
0591 double dist = outInDistance(0, orientation, ljns, louts, lmom, lmoj);
0592
0593 double P = sqrt(lmom[0] * lmom[0] + lmom[1] * lmom[1] + lmom[2] * lmom[2]);
0594 double tolerance = m_toleranceFactor(P) * 25 * dd4hep::um;
0595 bool isCompatible = dist > 0 && dist < tolerance;
0596 if (!isCompatible) {
0597 if (!sim_hit.isProducedBySecondary())
0598 debug(oddity(header, status, dist, sim_hit.getCellID(), lpos, lmom,
0599 sim_hjt.getCellID(), lpoj, lmoj));
0600 break;
0601 }
0602
0603 vIDPrv = vJD;
0604 eDep += sim_hjt.getEDep();
0605 for (int i = 0; i < 3; i++) {
0606 lpend[i] = lpfnd[i];
0607 lmend[i] = lmoj[i];
0608 }
0609
0610 cIDs.push_back(sim_hjt.getCellID());
0611 if (level() >= algorithms::LogLevel::kDebug) {
0612 subHitList.push_back(jdx);
0613 }
0614
0615 hasContinuation = status & 0xa;
0616 canReEnter = status & 0x100;
0617 if (!canReEnter && m_stripRank(vJD) == 4)
0618 hasContinuation = false;
0619 if (!hasContinuation) {
0620 jdx++;
0621 break;
0622 } else {
0623 for (int i = 0; i < 3; i++) {
0624 louts[0][i] = lovts[0][i];
0625 louts[1][i] = lovts[1][i];
0626 }
0627 }
0628 }
0629 idx = jdx - 1;
0630 }
0631
0632 if (sim_hit.isProducedBySecondary() && cIDs.size() < 2)
0633 if (denyExtension(sim_hit, tCur.rMax() - tCur.rMin())) {
0634 isContinuation = hasContinuation = false;
0635 }
0636 for (int io = 0; io < 2; io++) {
0637 if ((io == 0 && !isContinuation) || (io == 1 && !hasContinuation))
0638 continue;
0639 int direction = io ? +1 : -1;
0640 extendHit(refID, cIDs, direction, lpini, lmom, lpend, lmend);
0641 }
0642
0643 flagUnexpected(header, 0, (tRef.rMin() + tRef.rMax()) / 2, sim_hit, lpini, lpend, lpos, lmom);
0644
0645 double DoF2 = 0, dir = 0;
0646 for (int i = 0; i < 3; i++) {
0647 double neu = (lpini[i] + lpend[i]) / 2, alt = lpos[i];
0648 lpos[i] = neu;
0649 double d = neu - alt;
0650 dir += d * lmom[i];
0651 DoF2 += d * d;
0652 }
0653
0654 time += ((dir > 0) ? 1 : ((dir < 0) ? -1 : 0)) * sqrt(DoF2) / dd4hep::c_light;
0655 if (level() >= algorithms::LogLevel::kDebug) {
0656 debug("--------------------");
0657 printSubHitList(input, subHitList);
0658 debug(" =");
0659
0660 Position locPos(lpos[0], lpos[1], lpos[2]);
0661 Position globPos = refVol.nominal().localToWorld(locPos);
0662 debug(" position = ({:7.2f},{:7.2f},{:7.2f}) [mm]", globPos.X() / mm, globPos.Y() / mm,
0663 globPos.Z() / mm);
0664 debug(" edep = {:.0f} [eV]", eDep / eV);
0665 debug(" time = {:.2f} [ns]", time);
0666 }
0667 return true;
0668 }
0669 bool MPGDTrackerDigi::bCoalesceExtend(const Input& input, int& idx,
0670 std::vector<std::uint64_t>& cIDs, double* lpos, double& eDep,
0671 double& time) const {
0672 const auto [headers, sim_hits] = input;
0673 const edm4hep::EventHeader& header = headers->at(0);
0674 const edm4hep::SimTrackerHit& sim_hit = sim_hits->at(idx);
0675 CellID vID = sim_hit.getCellID() & m_volumeBits;
0676 CellID refID = vID & m_moduleBits;
0677 const VolumeManager& volman = m_detector->volumeManager();
0678 DetElement refVol = volman.lookupDetElement(refID);
0679
0680
0681 const TGeoHMatrix toRefVol = refVol.nominal().worldTransformation();
0682 double lmom[3];
0683 getLocalPosMom(sim_hit, toRefVol, lpos, lmom);
0684 const double edmm = edm4eic::unit::mm, ed2dd = dd4hep::mm / edmm;
0685 using dd4hep::mm;
0686 using edm4eic::unit::eV, edm4eic::unit::GeV;
0687
0688 eDep = sim_hit.getEDep();
0689 time = sim_hit.getTime();
0690
0691 const Box& bRef = refVol.solid();
0692 double dX = bRef.x(), dY = bRef.y();
0693
0694 DetElement curVol = volman.lookupDetElement(vID);
0695 const Box& bCur = curVol.solid();
0696 double dZ = bCur.z();
0697 double ref2Cur = getRef2Cur(refVol, curVol);
0698
0699 double lintos[2][3], louts[2][3], lpini[3], lpend[3], lmend[3];
0700 std::copy(std::begin(lmom), std::end(lmom), std::begin(lmend));
0701 unsigned int status =
0702 bTraversing(lpos, lmom, ref2Cur, sim_hit.getPathLength() * ed2dd,
0703 sim_hit.isProducedBySecondary(), dZ, dX, dY, lintos, louts, lpini, lpend);
0704 if (status & m_inconsistency) {
0705 error(inconsistency(header, status, sim_hit.getCellID(), lpos, lmom));
0706 return false;
0707 }
0708 cIDs.push_back(sim_hit.getCellID());
0709 std::vector<int> subHitList;
0710 if (level() >= algorithms::LogLevel::kDebug) {
0711 subHitList.push_back(idx);
0712 }
0713
0714 int rank = m_stripRank(vID);
0715 bool isContinuation = status & (m_intoLower | m_intoUpper);
0716 bool hasContinuation = status & (m_outLower | m_outUpper);
0717 if ((rank == 0 && (status & m_intoLower)) || (rank == 4 && (status & m_intoUpper)))
0718 isContinuation = false;
0719 if ((rank == 0 && (status & m_outLower)) || (rank == 4 && (status & m_outUpper)))
0720 hasContinuation = false;
0721 if (hasContinuation) {
0722
0723 int jdx;
0724 CellID vIDPrv = vID;
0725 size_t sim_size = sim_hits->size();
0726 for (jdx = idx + 1; jdx < (int)sim_size; jdx++) {
0727 const edm4hep::SimTrackerHit& sim_hjt = sim_hits->at(jdx);
0728 CellID vJD = sim_hjt.getCellID() & m_volumeBits;
0729 int orientation = m_orientation(vIDPrv, vJD);
0730 bool isUpstream = m_isUpstream(orientation, status);
0731 bool pmoStatus = samePMO(sim_hit, sim_hjt);
0732 if (!pmoStatus || !isUpstream) {
0733 if ((pmoStatus && !isUpstream) && !sim_hit.isProducedBySecondary()) {
0734
0735 double P = sqrt(lmom[0] * lmom[0] + lmom[1] * lmom[1] + lmom[2] * lmom[2]);
0736 if (P > 10 * dd4hep::MeV)
0737 debug(inconsistency(header, 0, sim_hit.getCellID(), lpos, lmom));
0738 }
0739 break;
0740 }
0741
0742 curVol = volman.lookupDetElement(vJD);
0743 const Box& boxj = curVol.solid();
0744 dZ = boxj.z();
0745 double ref2j = getRef2Cur(refVol, curVol);
0746
0747 double lpoj[3], lmoj[3];
0748 getLocalPosMom(sim_hjt, toRefVol, lpoj, lmoj);
0749 double ljns[2][3], lovts[2][3], lpjni[3], lpfnd[3];
0750 status = bTraversing(lpoj, lmoj, ref2j, sim_hjt.getPathLength() * ed2dd,
0751 sim_hit.isProducedBySecondary(), dZ, dX, dY, ljns, lovts, lpjni, lpfnd);
0752 if (status & m_inconsistency) {
0753 error(inconsistency(header, status, sim_hjt.getCellID(), lpoj, lmoj));
0754 break;
0755 }
0756
0757 bool jsDownstream = m_isDownstream(orientation, status);
0758 if (!jsDownstream)
0759 break;
0760
0761 double dist = outInDistance(1, orientation, ljns, louts, lmom, lmoj);
0762
0763 double P = sqrt(lmom[0] * lmom[0] + lmom[1] * lmom[1] + lmom[2] * lmom[2]);
0764 double tolerance = m_toleranceFactor(P) * 25 * dd4hep::um;
0765 bool isCompatible = dist > 0 && dist < tolerance;
0766 if (!isCompatible) {
0767 if (!sim_hit.isProducedBySecondary())
0768 debug(oddity(header, status, dist, sim_hit.getCellID(), lpos, lmom,
0769 sim_hjt.getCellID(), lpoj, lmoj));
0770 break;
0771 }
0772
0773 vIDPrv = vJD;
0774 eDep += sim_hjt.getEDep();
0775 for (int i = 0; i < 3; i++) {
0776 lpend[i] = lpfnd[i];
0777 lmend[i] = lmoj[i];
0778 }
0779
0780 cIDs.push_back(sim_hjt.getCellID());
0781 if (level() >= algorithms::LogLevel::kDebug) {
0782 subHitList.push_back(jdx);
0783 }
0784
0785 hasContinuation = status & 0xa;
0786 if (!hasContinuation) {
0787 jdx++;
0788 break;
0789 } else {
0790 for (int i = 0; i < 3; i++) {
0791 louts[0][i] = lovts[0][i];
0792 louts[1][i] = lovts[1][i];
0793 }
0794 }
0795 }
0796 idx = jdx - 1;
0797 }
0798
0799 if (sim_hit.isProducedBySecondary() && cIDs.size() < 2)
0800 if (denyExtension(sim_hit, bCur.z())) {
0801 isContinuation = hasContinuation = false;
0802 }
0803 for (int io = 0; io < 2; io++) {
0804 if ((io == 0 && !isContinuation) || (io == 1 && !hasContinuation))
0805 continue;
0806 int direction = io ? +1 : -1;
0807 extendHit(refID, cIDs, direction, lpini, lmom, lpend, lmend);
0808 }
0809
0810 flagUnexpected(header, 1, 0, sim_hit, lpini, lpend, lpos, lmom);
0811
0812 double DoF2 = 0, dir = 0;
0813 for (int i = 0; i < 3; i++) {
0814 double neu = (lpini[i] + lpend[i]) / 2, alt = lpos[i];
0815 lpos[i] = neu;
0816 double d = neu - alt;
0817 dir += d * lmom[i];
0818 DoF2 += d * d;
0819 }
0820
0821 time += ((dir > 0) ? 1 : ((dir < 0) ? -1 : 0)) * sqrt(DoF2) / dd4hep::c_light;
0822 if (level() >= algorithms::LogLevel::kDebug) {
0823 debug("--------------------");
0824 printSubHitList(input, subHitList);
0825 debug(" =");
0826
0827 Position locPos(lpos[0], lpos[1], lpos[2]);
0828 Position globPos = refVol.nominal().localToWorld(locPos);
0829 debug(" position = ({:7.2f},{:7.2f},{:7.2f}) [mm]", globPos.X() / mm, globPos.Y() / mm,
0830 globPos.Z() / mm);
0831 debug(" edep = {:.0f} [eV]", eDep / eV);
0832 debug(" time = {:.2f} [ns]", time);
0833 }
0834 return true;
0835 }
0836 void MPGDTrackerDigi::printSubHitList(const Input& input, std::vector<int>& subHitList) const {
0837 const auto [headers, sim_hits] = input;
0838 const double edmm = edm4eic::unit::mm;
0839 using edm4eic::unit::eV, edm4eic::unit::GeV;
0840 int ldx = 0;
0841 for (int kdx : subHitList) {
0842 const edm4hep::SimTrackerHit& sim_hp = sim_hits->at(kdx);
0843 CellID cIDk = sim_hp.getCellID();
0844 CellID hIDk = cIDk >> 32, vIDk = cIDk & m_volumeBits;
0845 if (ldx == 0) {
0846 debug("Hit cellID{:d} = 0x{:08x}, 0x{:08x}", ldx++, hIDk, vIDk);
0847 } else {
0848 debug(" + cellID{:d} = 0x{:08x}, 0x{:08x}", ldx++, hIDk, vIDk);
0849 }
0850 debug(" position = ({:7.2f},{:7.2f},{:7.2f}) [mm]", sim_hp.getPosition().x / edmm,
0851 sim_hp.getPosition().y / edmm, sim_hp.getPosition().z / edmm);
0852 debug(" xy_radius = {:.2f}",
0853 std::hypot(sim_hp.getPosition().x, sim_hp.getPosition().y) / edmm);
0854 debug(" momentum = ({:.2f}, {:.2f}, {:.2f}) [GeV]", sim_hp.getMomentum().x / GeV,
0855 sim_hp.getMomentum().y / GeV, sim_hp.getMomentum().z / GeV);
0856 debug(" edep = {:.0f} [eV]", sim_hp.getEDep() / eV);
0857 debug(" time = {:.2f} [ns]", sim_hp.getTime());
0858 }
0859 }
0860
0861 void getLocalPosMom(const edm4hep::SimTrackerHit& sim_hit, const TGeoHMatrix& toModule,
0862 double* lpos, double* lmom) {
0863 const edm4hep::Vector3d& pos = sim_hit.getPosition();
0864
0865 const double edmm = edm4eic::unit::mm, ed2dd = dd4hep::mm / edmm;
0866 const double gpos[3] = {pos.x * ed2dd, pos.y * ed2dd, pos.z * ed2dd};
0867 const edm4hep::Vector3f& mom = sim_hit.getMomentum();
0868 const double gmom[3] = {mom.x, mom.y, mom.z};
0869 toModule.MasterToLocal(gpos, lpos);
0870 toModule.MasterToLocalVect(gmom, lmom);
0871 }
0872
0873
0874
0875
0876
0877
0878
0879
0880
0881
0882
0883
0884
0885
0886
0887
0888
0889
0890 unsigned int MPGDTrackerDigi::cTraversing(const double* lpos, const double* lmom, double path,
0891 bool isSecondary,
0892 double rMin, double rMax,
0893 double dZ, double startPhi,
0894 double endPhi,
0895 double lintos[][3], double louts[][3], double* lpini,
0896 double* lpend) const {
0897 unsigned int status = 0;
0898 double Mx = lpos[0], My = lpos[1], Mz = lpos[2], M2 = Mx * Mx + My * My;
0899 double Px = lmom[0], Py = lmom[1], Pz = lmom[2];
0900
0901 double tIn = 0, tOut = 0;
0902 for (double phi : {startPhi, endPhi}) {
0903
0904 double Ux = cos(phi), Uy = sin(phi);
0905 double D = Px * Uy - Py * Ux;
0906 if (D) {
0907 double t = (My * Ux - Mx * Uy) / D;
0908 double Ex = Mx + t * Px, Ey = My + t * Py, Ez = Mz + t * Pz;
0909 double rE = sqrt(Ex * Ex + Ey * Ey), phiE = atan2(Ey, Ex);
0910
0911
0912 if (rMin < rE && rE < rMax && fabs(Ez) < dZ && fabs(phiE - phi) < 1) {
0913 if (t < 0) {
0914 status |= 0x10;
0915 tIn = t;
0916 } else {
0917 status |= 0x20;
0918 tOut = t;
0919 }
0920 }
0921 }
0922 }
0923
0924 double zLow = -dZ, zUp = +dZ;
0925 for (double Z : {zLow, zUp}) {
0926
0927 if (Pz) {
0928 double t = (Z - Mz) / Pz;
0929 double Ex = Mx + t * Px, Ey = My + t * Py, rE = sqrt(Ex * Ex + Ey * Ey);
0930 double phi = atan2(Ey, Ex);
0931 if (rMin < rE && rE < rMax && startPhi < phi && phi < endPhi) {
0932 if (t < 0) {
0933 if (!(status & 0x10) || ((status & 0x10) && t > tIn)) {
0934 status |= 0x10;
0935 tIn = t;
0936 }
0937 } else if (t > 0) {
0938 if (!(status & 0x20) || ((status & 0x20) && t < tOut)) {
0939 status |= 0x20;
0940 tOut = t;
0941 }
0942 }
0943 }
0944 }
0945 }
0946
0947 double ts[3 ][2 ] = {
0948 {0, 0}, {0, 0}, {tIn, tOut}};
0949 double a = Px * Px + Py * Py, b = Px * Mx + Py * My;
0950 for (int lu = 0; lu < 2; lu++) {
0951 double R;
0952 unsigned int statGene;
0953 if (lu == 1) {
0954 R = rMax;
0955 statGene = 0x4;
0956 } else {
0957 R = rMin;
0958 statGene = 0x1;
0959 }
0960 double c = M2 - R * R;
0961 if (!a) {
0962 if ((status & 0x30) != 0x30)
0963 status |= 0x1000;
0964 continue;
0965 } else if (!c) {
0966 status |= 0x2000;
0967 continue;
0968 } else {
0969 double det = b * b - a * c;
0970 if (det < 0) {
0971 if (lu == 1) {
0972 status |= 0x4000;
0973 continue;
0974 }
0975 } else {
0976 double sqdet = sqrt(det);
0977 for (int is = 0; is < 2; is++) {
0978 int s = 1 - 2 * is;
0979 double t = (-b + s * sqdet) / a;
0980 double Ix = Mx + t * Px, Iy = My + t * Py, Iz = Mz + t * Pz, phi = atan2(Iy, Ix);
0981 if (fabs(Iz) > dZ || phi < startPhi || endPhi < phi)
0982 continue;
0983 if (t < 0) {
0984
0985
0986
0987
0988 if (status & statGene) {
0989 double tPrv = ts[lu][0];
0990 if (t > tPrv) {
0991 ts[lu][0] = t;
0992 ts[lu][1] = tPrv;
0993 }
0994 status |= statGene << 1;
0995 } else {
0996 ts[lu][0] = t;
0997 status |= statGene;
0998 }
0999 } else {
1000 if (status & statGene << 1) {
1001 double tPrv = ts[lu][1];
1002 if (t < tPrv) {
1003 ts[lu][1] = t;
1004 ts[lu][0] = tPrv;
1005 }
1006 status |= statGene;
1007 } else {
1008 ts[lu][1] = t;
1009 status |= statGene << 1;
1010 }
1011 }
1012 }
1013 }
1014 }
1015 }
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033 bool canReEnter = (status & 0x3) == 0x3;
1034 double norm = sqrt(a + Pz * Pz);
1035 if (canReEnter) {
1036 bool doesReEnter = true;
1037 for (int i12 = 0; i12 < 2; i12++) {
1038 double t = ts[0][i12];
1039 int s = t > 0 ? +1 : -1;
1040 const double tolerance = 20 * dd4hep::um;
1041 if (path / 2 - s * t * norm < tolerance)
1042 doesReEnter = false;
1043 }
1044 if (doesReEnter) {
1045 status &= ~0x3;
1046 canReEnter = false;
1047 }
1048 }
1049 double rHit = sqrt(M2);
1050 if (rHit < rMin && !canReEnter) {
1051 unsigned int statvs = status;
1052 if (statvs & 0x1) {
1053 status &= ~0x1;
1054 status |= 0x2;
1055 ts[0][1] = -ts[0][0];
1056 }
1057 if (statvs & 0x2) {
1058 status &= ~0x2;
1059 status |= 0x1;
1060 ts[0][0] = -ts[0][1];
1061 }
1062 } else if (rHit > rMax) {
1063 unsigned int statvs = status;
1064 if (statvs & 0x4) {
1065 status &= ~0x4;
1066 status |= 0x8;
1067 ts[1][1] = -ts[1][0];
1068 }
1069 if (statvs & 0x8) {
1070 status &= ~0x8;
1071 status |= 0x4;
1072 ts[1][0] = -ts[1][1];
1073 }
1074 }
1075 for (int lu = 0; lu < 2; lu++) {
1076 unsigned int statGene = lu ? 0x4 : 0x1;
1077 if (status & statGene) {
1078 double t = ts[lu][0];
1079 if (t < 0) {
1080 if (status & 0x10) {
1081 if (lu == 1 || !canReEnter) {
1082 status |= 0x10000;
1083 status &= ~statGene;
1084 } else {
1085 if (t < tIn)
1086 status |= 0x10000;
1087 }
1088 }
1089 } else {
1090 if (status & 0x20) {
1091 if (lu == 1 || !canReEnter) {
1092 status |= 0x20000;
1093 status &= ~statGene;
1094 } else {
1095 if (t > tOut)
1096 status |= 0x20000;
1097 }
1098 }
1099 }
1100 }
1101 if (status & statGene << 1) {
1102 double t = ts[lu][1];
1103 if (t < 0) {
1104 if (status & 0x10) {
1105 if (lu == 1 || !canReEnter) {
1106 status |= 0x40000;
1107 status &= ~(statGene << 1);
1108 } else {
1109 if (t < tIn)
1110 status |= 0x40000;
1111 }
1112 }
1113 } else {
1114 if (status & 0x20) {
1115 if (lu == 1 || !canReEnter) {
1116 status |= 0x80000;
1117 status &= ~(statGene << 1);
1118 } else {
1119 if (t > tOut)
1120 status |= 0x80000;
1121 }
1122 }
1123 }
1124 }
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135 if (canReEnter)
1136 status |= 0x100;
1137 double at = path / 2 / norm;
1138 unsigned int statws = 0;
1139 for (int is = 0; is < 2; is++) {
1140 int s = 1 - 2 * is;
1141 double Ix = s * at * Px, Iy = s * at * Py, Iz = s * at * Pz;
1142 for (int lu = 0; lu < 2; lu++) {
1143 unsigned int statvs = lu ? 0x4 : 0x1;
1144 for (int io = 0; io < 2; io++) {
1145 statvs <<= io;
1146 if (status & statvs) {
1147 double t = ts[lu][io];
1148 if (t * s < 0)
1149 continue;
1150 double dIx = t * Px - Ix, dIy = t * Py - Iy, dIz = t * Pz - Iz;
1151 double dist = sqrt(dIx * dIx + dIy * dIy + dIz * dIz);
1152
1153 double tolerance = m_toleranceFactor(norm) * 20 * dd4hep::um;
1154 if (dist < tolerance)
1155 statws |= statvs;
1156 }
1157 }
1158 }
1159 }
1160 if (!(statws & 0x5))
1161 status &= ~0x5;
1162 if (!(statws & 0xa))
1163 status &= ~0xa;
1164
1165
1166
1167 if (((status & 0x5) == 0x1 || (status & 0x5) == 0x4) && !isSecondary) {
1168 double tIn = (status & 0x1) ? ts[0][0] : ts[1][0];
1169 lpini[0] = Mx + tIn * Px;
1170 lpini[1] = My + tIn * Py;
1171 lpini[2] = Mz + tIn * Pz;
1172 } else {
1173 lpini[0] = Mx - at * Px;
1174 lpini[1] = My - at * Py;
1175 lpini[2] = Mz - at * Pz;
1176 }
1177 if (((status & 0xa) == 0x2 || (status & 0xa) == 0x8) && !isSecondary) {
1178 double tOut = (status & 0x2) ? ts[0][1] : ts[1][1];
1179 lpend[0] = Mx + tOut * Px;
1180 lpend[1] = My + tOut * Py;
1181 lpend[2] = Mz + tOut * Pz;
1182 } else {
1183 lpend[0] = Mx + at * Px;
1184 lpend[1] = My + at * Py;
1185 lpend[2] = Mz + at * Pz;
1186 }
1187
1188 for (int lu = 0; lu < 2; lu++) {
1189 unsigned int statvs = lu ? 0x4 : 0x1;
1190 double tIn = ts[lu][0], tOut = ts[lu][1];
1191 if (status & statvs) {
1192 lintos[lu][0] = Mx + tIn * Px;
1193 lintos[lu][1] = My + tIn * Py;
1194 lintos[lu][2] = Mz + tIn * Pz;
1195 }
1196 statvs <<= 1;
1197 if (status & statvs) {
1198 louts[lu][0] = Mx + tOut * Px;
1199 louts[lu][1] = My + tOut * Py;
1200 louts[lu][2] = Mz + tOut * Pz;
1201 }
1202 }
1203 return status;
1204 }
1205 unsigned int MPGDTrackerDigi::bTraversing(const double* lpos, const double* lmom, double ref2Cur,
1206 double path,
1207 bool isSecondary,
1208 double dZ,
1209 double dX, double dY,
1210 double lintos[][3], double louts[][3], double* lpini,
1211 double* lpend) const {
1212 unsigned int status = 0;
1213 double Mx = lpos[0], My = lpos[1], Mxy[2] = {Mx, My};
1214 double Px = lmom[0], Py = lmom[1], Pxy[2] = {Px, Py};
1215 double Mz = lpos[2] + ref2Cur, Pz = lmom[2];
1216
1217 double tIn = 0, tOut = 0;
1218 double xyLow[2] = {-dX, -dY}, xyUp[2] = {+dX, +dY};
1219 for (int xy = 0; xy < 2; xy++) {
1220 int yx = 1 - xy;
1221 double a_Low = xyLow[xy], a_Up = xyUp[xy], Ma = Mxy[xy], Pa = Pxy[xy];
1222 double b_Low = xyLow[yx], b_Up = xyUp[yx], Mb = Mxy[yx], Pb = Pxy[yx];
1223 for (double A : {a_Low, a_Up}) {
1224
1225 if (Pa) {
1226 double t = (A - Ma) / Pa;
1227 double Eb = Mb + t * Pb, Ez = Mz + t * Pz;
1228 if (b_Low < Eb && Eb < b_Up && fabs(Ez) < dZ) {
1229 if (t < 0) {
1230 if (!(status & 0x10) || ((status & 0x10) && t > tIn)) {
1231 status |= 0x10;
1232 tIn = t;
1233 }
1234 } else if (t > 0) {
1235 if (!(status & 0x20) || ((status & 0x20) && t < tOut)) {
1236 status |= 0x20;
1237 tOut = t;
1238 }
1239 }
1240 }
1241 }
1242 }
1243 }
1244
1245 for (int lu = 0; lu < 2; lu++) {
1246 int s = 2 * lu - 1;
1247 double Z = s * dZ;
1248 unsigned int statGene = lu ? 0x4 : 0x1;
1249
1250 if (Pz) {
1251 double t = (Z - Mz) / Pz;
1252 if (t < 0) {
1253 if (!(status & 0x10) || ((status & 0x10) && t > tIn)) {
1254 status |= statGene;
1255 tIn = t;
1256 }
1257 } else if (t > 0) {
1258 if (!(status & 0x20) || ((status & 0x20) && t < tOut)) {
1259 status |= statGene << 1;
1260 tOut = t;
1261 }
1262 }
1263 }
1264 }
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274 double norm = sqrt(Px * Px + Py * Py + Pz * Pz), at = path / 2 / norm;
1275 unsigned int statws = 0;
1276 for (int is = 0; is < 2; is++) {
1277 int s = 1 - 2 * is;
1278 double Ix = s * at * Px, Iy = s * at * Py, Iz = s * at * Pz;
1279 for (int lu = 0; lu < 2; lu++) {
1280 unsigned int statvs = lu ? 0x4 : 0x1;
1281 for (int io = 0; io < 2; io++) {
1282 statvs <<= io;
1283 if (status & statvs) {
1284 double t = io ? tOut : tIn;
1285 if (t * s < 0)
1286 continue;
1287 double dIx = t * Px - Ix, dIy = t * Py - Iy, dIz = t * Pz - Iz;
1288 double dist = sqrt(dIx * dIx + dIy * dIy + dIz * dIz);
1289
1290 double tolerance = m_toleranceFactor(norm) * 20 * dd4hep::um;
1291 if (dist < tolerance)
1292 statws |= statvs;
1293 }
1294 }
1295 }
1296 }
1297 if (!(statws & 0x5))
1298 status &= ~0x5;
1299 if (!(statws & 0xa))
1300 status &= ~0xa;
1301
1302 Mz -= ref2Cur;
1303
1304
1305 if ((status & 0x5) && !isSecondary) {
1306 lpini[0] = Mx + tIn * Px;
1307 lpini[1] = My + tIn * Py;
1308 lpini[2] = Mz + tIn * Pz;
1309 } else {
1310 lpini[0] = Mx - at * Px;
1311 lpini[1] = My - at * Py;
1312 lpini[2] = Mz - at * Pz;
1313 }
1314 if ((status & 0xa) && !isSecondary) {
1315 lpend[0] = Mx + tOut * Px;
1316 lpend[1] = My + tOut * Py;
1317 lpend[2] = Mz + tOut * Pz;
1318 } else {
1319 lpend[0] = Mx + at * Px;
1320 lpend[1] = My + at * Py;
1321 lpend[2] = Mz + at * Pz;
1322 }
1323
1324 for (int lu = 0; lu < 2; lu++) {
1325 unsigned int statvs = lu ? 0x4 : 0x1;
1326 if (status & statvs) {
1327 lintos[lu][0] = Mx + tIn * Px;
1328 lintos[lu][1] = My + tIn * Py;
1329 lintos[lu][2] = Mz + tIn * Pz;
1330 }
1331 statvs <<= 1;
1332 if (status & statvs) {
1333 louts[lu][0] = Mx + tOut * Px;
1334 louts[lu][1] = My + tOut * Py;
1335 louts[lu][2] = Mz + tOut * Pz;
1336 }
1337 }
1338 return status;
1339 }
1340
1341
1342 bool cExtrapolate(const double* lpos, const double* lmom,
1343 double rT,
1344 double* lext)
1345 {
1346 bool ok = false;
1347 double Mx = lpos[0], My = lpos[1], Mz = lpos[2], M2 = Mx * Mx + My * My;
1348 double Px = lmom[0], Py = lmom[1], Pz = lmom[2];
1349 double a = Px * Px + Py * Py, b = Px * Mx + Py * My, c = M2 - rT * rT;
1350 double tF = 0;
1351 if (!c)
1352 ok = true;
1353 else if (a) {
1354 double det = b * b - a * c;
1355 if (det >= 0) {
1356 double sqdet = sqrt(det);
1357 for (int is = 0; is < 2; is++) {
1358 int s = 1 - 2 * is;
1359 double t = (-b + s * sqdet) / a, norm = sqrt(a + Pz * Pz);
1360
1361 if (t * norm < -dd4hep::nm)
1362 continue;
1363 if (!ok ||
1364
1365 (ok && fabs(t) < fabs(tF))) {
1366 tF = t;
1367 ok = true;
1368 }
1369 }
1370 }
1371 }
1372 if (ok) {
1373 lext[0] = Mx + tF * Px;
1374 lext[1] = My + tF * Py;
1375 lext[2] = Mz + tF * Pz;
1376 }
1377 return ok;
1378 }
1379 bool bExtrapolate(const double* lpos, const double* lmom,
1380 double zT,
1381 double* lext)
1382 {
1383 bool ok = false;
1384 double Mx = lpos[0], My = lpos[1], Mz = lpos[2];
1385 double Px = lmom[0], Py = lmom[1], Pz = lmom[2], norm = sqrt(Px * Px + Py * Py + Pz * Pz);
1386 double tF = 0;
1387 if (Pz) {
1388 tF = (zT - Mz) / Pz;
1389
1390 ok = tF * norm > -dd4hep::nm;
1391 }
1392 if (ok) {
1393 lext[0] = Mx + tF * Px;
1394 lext[1] = My + tF * Py;
1395 lext[2] = Mz + tF * Pz;
1396 }
1397 return ok;
1398 }
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409 unsigned int MPGDTrackerDigi::cExtension(double const* lpos, double const* lmom,
1410 double rT, int direction,
1411 double dZ, double startPhi,
1412 double endPhi,
1413 double* lext) const {
1414 unsigned int status = 0;
1415 double Mx = lpos[0], My = lpos[1], Mz = lpos[2];
1416 double Px = lmom[0], Py = lmom[1], Pz = lmom[2], norm = sqrt(Px * Px + Py * Py + Pz * Pz);
1417
1418
1419 const double margin = 10 * dd4hep::um;
1420 double t = direction * margin / norm;
1421 Mx += t * Px;
1422 My += t * Py;
1423 Mz += t * Pz;
1424 double M2 = Mx * Mx + My * My, rIni = sqrt(M2), rLow, rUp;
1425 if (rIni < rT) {
1426 rLow = rIni;
1427 rUp = rT;
1428 } else {
1429 rLow = rT;
1430 rUp = rIni;
1431 }
1432
1433 double tF = 0;
1434 for (double phi : {startPhi, endPhi}) {
1435
1436 double Ux = cos(phi), Uy = sin(phi);
1437 double D = Px * Uy - Py * Ux;
1438 if (D) {
1439 double t = (My * Ux - Mx * Uy) / D;
1440 if (t * direction < 0)
1441 continue;
1442 double Ex = Mx + t * Px, Ey = My + t * Py, Ez = Mz + t * Pz;
1443 double rE = sqrt(Ex * Ex + Ey * Ey), phiE = atan2(Ey, Ex);
1444
1445 if (rLow < rE && rE < rUp && fabs(Ez) < dZ && fabs(phiE - phi) < 1) {
1446 status |= 0x1;
1447 tF = t;
1448 }
1449 }
1450 }
1451
1452 double zLow = -dZ, zUp = +dZ;
1453 for (double Z : {zLow, zUp}) {
1454
1455 if (Pz) {
1456 double t = (Z - Mz) / Pz;
1457 if (t * direction < 0)
1458 continue;
1459 double Ex = Mx + t * Px, Ey = My + t * Py, rE = sqrt(Ex * Ex + Ey * Ey);
1460 double phi = atan2(Ey, Ex);
1461 if (rLow < rE && rE < rUp && startPhi < phi && phi < endPhi) {
1462 if (t < 0) {
1463 if (!status || (status && t > tF)) {
1464 status |= 0x1;
1465 tF = t;
1466 }
1467 } else if (t > 0) {
1468 if (!status || (status && t < tF)) {
1469 status |= 0x1;
1470 tF = t;
1471 }
1472 }
1473 }
1474 }
1475 }
1476
1477 if (!status) {
1478 double a = Px * Px + Py * Py, b = Px * Mx + Py * My, c = M2 - rT * rT;
1479 if (!a) {
1480 status |= 0x1000;
1481 } else if (!c) {
1482 status |= 0x2000;
1483 } else {
1484 double det = b * b - a * c;
1485 if (det >= 0) {
1486 double sqdet = sqrt(det);
1487 for (int is = 0; is < 2; is++) {
1488 int s = 1 - 2 * is;
1489 double t = (-b + s * sqdet) / a;
1490 if (t * direction < 0)
1491 continue;
1492 double Ix = Mx + t * Px, Iy = My + t * Py, Iz = Mz + t * Pz, phi = atan2(Iy, Ix);
1493 if (fabs(Iz) > dZ || phi < startPhi || endPhi < phi)
1494 continue;
1495 if (!(status & 0x1) ||
1496
1497 ((status & 0x1) && fabs(t) < fabs(tF))) {
1498 tF = t;
1499 status |= 0x1;
1500 }
1501 }
1502 }
1503 }
1504 }
1505 if (status & 0x1) {
1506 lext[0] = Mx + tF * Px;
1507 lext[1] = My + tF * Py;
1508 lext[2] = Mz + tF * Pz;
1509 }
1510 return status;
1511 }
1512 unsigned int MPGDTrackerDigi::bExtension(const double* lpos, const double* lmom,
1513 double zT, int direction,
1514 double dX, double dY,
1515 double* lext) const {
1516 unsigned int status = 0;
1517 double Mx = lpos[0], My = lpos[1], Mxy[2] = {Mx, My};
1518 double Px = lmom[0], Py = lmom[1], Pxy[2] = {Px, Py};
1519 double Mz = lpos[2], Pz = lmom[2];
1520 double norm = sqrt(Px * Px + Py * Py + Pz * Pz);
1521
1522
1523 const double margin = 10 * dd4hep::um;
1524 double t = direction * margin / norm;
1525 Mx += t * Px;
1526 My += t * Py;
1527 Mz += t * Pz;
1528 double &zIni = Mz, zLow, zUp;
1529 if (zIni < zT) {
1530 zLow = zIni;
1531 zUp = zT;
1532 } else {
1533 zLow = zT;
1534 zUp = zIni;
1535 }
1536
1537 double tF = 0;
1538 double xyLow[2] = {-dX, -dY}, xyUp[2] = {+dX, +dY};
1539 for (int xy = 0; xy < 2; xy++) {
1540 int yx = 1 - xy;
1541 double a_Low = xyLow[xy], a_Up = xyUp[xy], Ma = Mxy[xy], Pa = Pxy[xy];
1542 double b_Low = xyLow[yx], b_Up = xyUp[yx], Mb = Mxy[yx], Pb = Pxy[yx];
1543 for (double A : {a_Low, a_Up}) {
1544
1545 if (Pa) {
1546 double t = (A - Ma) / Pa;
1547 if (t * direction < 0)
1548 continue;
1549 double Eb = Mb + t * Pb, Ez = Mz + t * Pz;
1550 if (zLow < Ez && Ez < zUp && b_Low < Eb && Eb < b_Up) {
1551 if (!status || (status && fabs(t) < fabs(tF))) {
1552 status |= 0x1;
1553 tF = t;
1554 }
1555 }
1556 }
1557 }
1558 }
1559
1560 if (!status) {
1561 if (Pz) {
1562 tF = (zT - Mz) / Pz;
1563 if (tF * direction > 0)
1564 status = 0x1;
1565 }
1566 }
1567 if (status) {
1568 lext[0] = Mx + tF * Px;
1569 lext[1] = My + tF * Py;
1570 lext[2] = Mz + tF * Pz;
1571 }
1572 return status;
1573 }
1574
1575 double getRef2Cur(DetElement refVol, DetElement curVol) {
1576
1577
1578 const TGeoHMatrix toRefVol = refVol.nominal().worldTransformation();
1579 const TGeoHMatrix toCurVol = curVol.nominal().worldTransformation();
1580 const double* TRef = toRefVol.GetTranslation();
1581 const double* TCur = toCurVol.GetTranslation();
1582
1583 double gdT[3];
1584 for (int i = 0; i < 3; i++)
1585 gdT[i] = TRef[i] - TCur[i];
1586 double ldT[3];
1587 toRefVol.MasterToLocalVect(gdT, ldT);
1588 return ldT[2];
1589 }
1590
1591 std::string inconsistency(const edm4hep::EventHeader& event, unsigned int status, CellID cID,
1592 const double* lpos, const double* lmom) {
1593 using edm4eic::unit::GeV, dd4hep::mm;
1594 return fmt::format("Event {}#{}, SimHit 0x{:016x} @ {:.2f},{:.2f},{:.2f} mm, P = "
1595 "{:.2f},{:.2f},{:.2f} GeV inconsistency 0x{:x}",
1596 event.getRunNumber(), event.getEventNumber(), cID, lpos[0] / mm, lpos[1] / mm,
1597 lpos[2] / mm, lmom[0] / GeV, lmom[1] / GeV, lmom[2] / GeV, status);
1598 }
1599 std::string oddity(const edm4hep::EventHeader& event, unsigned int status, double dist, CellID cID,
1600 const double* lpos, const double* lmom, CellID cJD, const double* lpoj,
1601 const double* lmoj) {
1602 using edm4eic::unit::GeV, dd4hep::mm;
1603 return fmt::format("Event {}#{}, Bizarre SimHit sequence: 0x{:016x} @ {:.4f},{:.4f},{:.4f} mm, P "
1604 "= {:.2f},{:.2f},{:.2f} GeV and 0x{:016x} @ {:.4f},{:.4f},{:.4f} mm, P = "
1605 "{:.2f},{:.2f},{:.2f} GeV: status 0x{:x}, distance {:.4f}",
1606 event.getRunNumber(), event.getEventNumber(), cID, lpos[0] / mm, lpos[1] / mm,
1607 lpos[2] / mm, lmom[0] / GeV, lmom[1] / GeV, lmom[2] / GeV, cJD, lpoj[0] / mm,
1608 lpoj[1] / mm, lpoj[2] / mm, lmoj[0] / GeV, lmoj[1] / GeV, lmoj[2] / GeV,
1609 status, dist);
1610 }
1611
1612 bool MPGDTrackerDigi::samePMO(const edm4hep::SimTrackerHit& sim_hit,
1613 const edm4hep::SimTrackerHit& sim_hjt) const {
1614
1615
1616
1617
1618 #if EDM4HEP_BUILD_VERSION >= EDM4HEP_VERSION(0, 99, 0)
1619 bool sameParticle = sim_hjt.getParticle() == sim_hit.getParticle();
1620 #else
1621 bool sameParticle = sim_hjt.getMCParticle() == sim_hit.getMCParticle();
1622 #endif
1623
1624 CellID vID = sim_hit.getCellID() & m_volumeBits;
1625 CellID refID = vID & m_moduleBits;
1626 CellID vJD = sim_hjt.getCellID() & m_volumeBits;
1627 CellID refJD = vJD & m_moduleBits;
1628 bool sameModule = refJD == refID;
1629
1630
1631
1632 bool isSecondary = sim_hit.isProducedBySecondary();
1633 bool jsSecondary = sim_hjt.isProducedBySecondary();
1634 bool sameOrigin = jsSecondary == isSecondary;
1635 return sameParticle && sameModule && sameOrigin;
1636 }
1637
1638 double outInDistance(int shape, int orientation, double lintos[][3], double louts[][3],
1639 double* lmom, double* lmoj) {
1640
1641 bool ok;
1642 double lExt[3];
1643 double lmOI[3];
1644 for (int i = 0; i < 3; i++)
1645 lmOI[i] = (lmom[i] + lmoj[i]) / 2;
1646 double *lOut, *lInto;
1647 if (orientation > 0) {
1648 lOut = louts[1];
1649 lInto = lintos[0];
1650 } else if (orientation < 0) {
1651 lOut = louts[0];
1652 lInto = lintos[1];
1653 } else {
1654 lOut = louts[0];
1655 lInto = lintos[0];
1656 }
1657 if (shape == 0) {
1658 double rInto = sqrt(lInto[0] * lInto[0] + lInto[1] * lInto[1]);
1659 ok = cExtrapolate(lOut, lmOI, rInto, lExt);
1660 } else {
1661 ok = bExtrapolate(lOut, lmOI, lInto[2], lExt);
1662 }
1663 if (ok) {
1664 double dist2 = 0;
1665 for (int i = 0; i < 3; i++) {
1666 double d = lExt[i] - lInto[i];
1667 dist2 += d * d;
1668 }
1669 return sqrt(dist2);
1670 } else
1671 return -1;
1672 }
1673
1674 unsigned int MPGDTrackerDigi::extendHit(CellID refID, std::vector<std::uint64_t>& cIDs,
1675 int direction, double* lpini, double* lmini, double* lpend,
1676 double* lmend) const {
1677 unsigned int status = 0;
1678 const VolumeManager& volman = m_detector->volumeManager();
1679 DetElement refVol = volman.lookupDetElement(refID);
1680 const auto& shape = refVol.solid();
1681 double *lpoE, *lmoE;
1682 if (direction < 0) {
1683 lpoE = lpini;
1684 lmoE = lmini;
1685 } else {
1686 lpoE = lpend;
1687 lmoE = lmend;
1688 }
1689
1690
1691
1692
1693
1694
1695
1696 for (int rankE : {0, 4}) {
1697 CellID vIDE = refID | m_stripIDs[rankE];
1698 int alreadyThere = 0;
1699 for (int i = 0; i < (int)cIDs.size(); i++) {
1700 if ((cIDs[i] & m_volumeBits) == vIDE) {
1701 alreadyThere = 1;
1702 break;
1703 }
1704 }
1705 if (alreadyThere)
1706 continue;
1707 if (std::find(cIDs.begin(), cIDs.end(), vIDE) != cIDs.end())
1708 continue;
1709 DetElement volE = volman.lookupDetElement(vIDE);
1710 double lext[3];
1711 if (std::string_view{shape.type()} == "TGeoTubeSeg") {
1712 const Tube& tExt = volE.solid();
1713 double R = rankE == 0 ? tExt.rMin() : tExt.rMax();
1714 double startPhi = tExt.startPhi() * radian;
1715 startPhi -= 2 * TMath::Pi();
1716 double endPhi = tExt.endPhi() * radian;
1717 endPhi -= 2 * TMath::Pi();
1718 double dZ = tExt.dZ();
1719 status = cExtension(lpoE, lmoE, R, direction, dZ, startPhi, endPhi, lext);
1720 } else if (std::string_view{shape.type()} == "TGeoBBox") {
1721 double ref2E = getRef2Cur(refVol, volE);
1722 const Box& bExt = volE.solid();
1723 double Z = rankE == 0 ? -bExt.z() : +bExt.z();
1724 Z -= ref2E;
1725 double dX = bExt.x(), dY = bExt.y();
1726 status = bExtension(lpoE, lmoE, Z, direction, dX, dY, lext);
1727 } else {
1728 critical(R"(Bad input data: CellID {:x} has invalid shape "{}")", refID, shape.type());
1729 throw std::runtime_error(R"(Inconsistency: Inappropriate SimHits fed to "MPGDTrackerDigi".)");
1730 }
1731 if (status != 0x1)
1732 continue;
1733 if (direction < 0) {
1734 for (int i = 0; i < 3; i++)
1735 lpini[i] = lext[i];
1736 } else {
1737 for (int i = 0; i < 3; i++)
1738 lpend[i] = lext[i];
1739 }
1740 break;
1741 }
1742 return status;
1743 }
1744
1745 bool MPGDTrackerDigi::denyExtension(const edm4hep::SimTrackerHit& sim_hit, double depth) const {
1746
1747
1748
1749 CellID vID = sim_hit.getCellID() & m_volumeBits;
1750 bool isHelperVolume = m_stripRank(vID) != 0 && m_stripRank(vID) != 4;
1751
1752
1753 const double fraction = .10;
1754 const double edmm = edm4eic::unit::mm, ed2dd = dd4hep::mm / edmm;
1755 bool smallPathLength = sim_hit.getPathLength() * ed2dd < fraction * depth;
1756 return isHelperVolume || smallPathLength;
1757 }
1758
1759 void MPGDTrackerDigi::flagUnexpected(const edm4hep::EventHeader& event, int shape, double expected,
1760 const edm4hep::SimTrackerHit& sim_hit, double* lpini,
1761 double* lpend, double* lpos, double* lmom) const {
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772 double Rnew2 = 0, Znew, diff2 = 0;
1773 for (int i = 0; i < 3; i++) {
1774 double neu = (lpini[i] + lpend[i]) / 2, alt = lpos[i];
1775 double d = neu - alt;
1776 diff2 += d * d;
1777 if (i != 2)
1778 Rnew2 += neu * neu;
1779 if (i == 2)
1780 Znew = neu;
1781 }
1782 double found = shape ? Znew : sqrt(Rnew2), residual = found - expected;
1783 bool isSecondary = sim_hit.isProducedBySecondary();
1784 bool isPrimary =
1785 !isSecondary && sqrt(lmom[0] * lmom[0] + lmom[1] * lmom[1] + lmom[2] * lmom[2]) > .1 * GeV;
1786 if ((fabs(residual) > .000001 && isPrimary) || (sqrt(diff2) > .000001 && isSecondary)) {
1787 debug("Event {}#{}, SimHit 0x{:016x} origin {:d}: d{:c} = {:.5f} diff = {:.5f}",
1788 event.getRunNumber(), event.getEventNumber(), sim_hit.getCellID(), isSecondary,
1789 shape ? 'Z' : 'R', residual, sqrt(diff2));
1790 }
1791 }
1792
1793 }