Belle II Software development
TrackExtrapolateG4e.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9/* Own header. */
10#include <tracking/trackExtrapolateG4e/TrackExtrapolateG4e.h>
11
12/* Basf2 headers. */
13#include <ecl/geometry/ECLGeometryPar.h>
14#include <framework/datastore/StoreArray.h>
15#include <framework/datastore/StoreObjPtr.h>
16#include <framework/geometry/BFieldManager.h>
17#include <framework/logging/Logger.h>
18#include <genfit/Exception.h>
19#include <klm/dataobjects/bklm/BKLMElementNumbers.h>
20#include <klm/dataobjects/bklm/BKLMStatus.h>
21#include <klm/bklm/geometry/GeometryPar.h>
22#include <klm/bklm/geometry/Module.h>
23#include <klm/dataobjects/KLMChannelIndex.h>
24#include <klm/dataobjects/KLMMuidLikelihood.h>
25#include <klm/dataobjects/KLMMuidHit.h>
26#include <klm/dataobjects/eklm/EKLMAlignmentHit.h>
27#include <klm/muid/MuidBuilder.h>
28#include <klm/muid/MuidElementNumbers.h>
29#include <mdst/dataobjects/ECLCluster.h>
30#include <mdst/dataobjects/KLMCluster.h>
31#include <mdst/dataobjects/Track.h>
32#include <simulation/kernel/ExtCylSurfaceTarget.h>
33#include <simulation/kernel/ExtManager.h>
34
35/* CLHEP headers. */
36#include <CLHEP/Matrix/Vector.h>
37#include <CLHEP/Units/PhysicalConstants.h>
38#include <CLHEP/Units/SystemOfUnits.h>
39
40/* Geant4 headers. */
41#include <G4ErrorFreeTrajState.hh>
42#include <G4ErrorMatrix.hh>
43#include <G4ErrorPropagatorData.hh>
44#include <G4ErrorSymMatrix.hh>
45#include <G4ParticleTable.hh>
46#include <G4PhysicalVolumeStore.hh>
47#include <G4Point3D.hh>
48#include <G4StateManager.hh>
49#include <G4Step.hh>
50#include <G4StepPoint.hh>
51#include <G4ThreeVector.hh>
52#include <G4TouchableHandle.hh>
53#include <G4Track.hh>
54#include <G4UImanager.hh>
55#include <G4VPhysicalVolume.hh>
56#include <G4VTouchable.hh>
57
58/* C++ headers. */
59#include <algorithm>
60#include <cmath>
61#include <iostream>
62
63#define TWOPI (2.0*M_PI)
64#define PI_8 (0.125*M_PI)
65#define DEPTH_RPC 9
66#define DEPTH_SCINT 11
67
68using namespace Belle2;
69
71
78
80 m_ExtInitialized(false), // initialized later
81 m_MuidInitialized(false), // initialized later
82 m_MeanDt(0.0), // initialized later
83 m_MaxDt(0.0), // initialized later
84 m_MagneticField(0.0), // initialized later
85 m_MaxDistSqInVariances(0.0), // initialized later
86 m_MaxKLMTrackClusterDistance(0.0), // initialized later
87 m_MaxECLTrackClusterDistance(0.0), // initialized later
88 m_MinPt(0.0), // initialized later
89 m_MinKE(0.0), // initialized later
90 m_ExtMgr(nullptr), // initialized later
91 m_HypothesesExt(nullptr), // initialized later
92 m_HypothesesMuid(nullptr), // initialized later
93 m_DefaultHypotheses(nullptr), // initialized later
94 m_EnterExit(nullptr), // initialized later
95 m_BKLMVolumes(nullptr), // initialized later
96 m_TargetExt(nullptr), // initialized later
97 m_TargetMuid(nullptr), // initialized later
98 m_MinRadiusSq(0.0), // initialized later
99 m_OffsetZ(0.0), // initialized later
100 m_BarrelNSector(0), // initialized later
101 m_BarrelMaxR(0.0), // initialized later
102 m_BarrelMinR(0.0), // initialized later
103 m_BarrelHalfLength(0.0), // initialized later
104 m_OutermostActiveBarrelLayer(0), // initialized later
105 m_BarrelScintVariance(0.0), // initialized later
106 m_EndcapMaxR(0.0), // initialized later
107 m_EndcapMinR(0.0), // initialized later
108 m_EndcapMiddleZ(0.0), // initialized later
109 m_EndcapHalfLength(0.0), // initialized later
110 m_OutermostActiveForwardEndcapLayer(0), // initialized later
111 m_OutermostActiveBackwardEndcapLayer(0), // initialized later
112 m_EndcapScintVariance(0.0), // initialized later
113 m_eklmTransformData(nullptr) // initialized later
114{
115 for (int j = 0; j < BKLMElementNumbers::getMaximalLayerNumber() + 1; ++j) {
117 m_BarrelZStripVariance[j] = 0.0;
119 m_EndcapModuleMiddleZ[j] = 0.0;
120 }
121 for (int s = 0; s < BKLMElementNumbers::getMaximalSectorNumber() + 1; ++s) {
122 for (int j = 0; j < BKLMElementNumbers::getMaximalLayerNumber() + 1; ++j) {
123 m_BarrelModuleMiddleRadius[0][s][j] = 0.0;
124 m_BarrelModuleMiddleRadius[1][s][j] = 0.0;
125 }
126 m_BarrelSectorPerp[s] = G4ThreeVector(0.0, 0.0, 0.0);
127 m_BarrelSectorPhi[s] = G4ThreeVector(0.0, 0.0, 0.0);
128 }
131}
132
136
137// Initialize for EXT
138void TrackExtrapolateG4e::initialize(double minPt, double minKE,
139 std::vector<Const::ChargedStable>& hypotheses)
140{
141 m_ExtInitialized = true;
142
143 // Define required objects, register the new ones and relations
144 m_recoTracks.isRequired();
145 m_tracks.isRequired();
146 m_extHits.registerInDataStore();
147 m_tracks.registerRelationTo(m_extHits);
148
149 // Save the magnetic field z component (gauss) at the origin
150 m_MagneticField = BFieldManager::getField(0, 0, 0).Z() / Unit::T * CLHEP::tesla / CLHEP::gauss;
151
152 // Convert user cutoff values to geant4 units
153 m_MinPt = std::max(0.0, minPt) * CLHEP::GeV;
154 m_MinKE = std::max(0.0, minKE) * CLHEP::GeV;
155
156 // Save pointer to the list of particle hypotheses for EXT extrapolation
157 m_HypothesesExt = &hypotheses;
158
159 // Define the list of volumes that will have their entry and/or
160 // exit points stored during the extrapolation.
162
163 // Store the address of the ExtManager (used later)
165
166 // Set up the EXT-specific geometry (might have already been done by MUID)
167 if (m_TargetExt == nullptr) {
168 if (!m_COILGeometryPar.isValid())
169 B2FATAL("Coil geometry data are not available.");
170 double offsetZ = m_COILGeometryPar->getGlobalOffsetZ();
171 double rMinCoil = m_COILGeometryPar->getCryoRmin();
172 double halfLength = m_COILGeometryPar->getCryoLength();
173 m_COILGeometryPar.addCallback([this, &offsetZ, &rMinCoil, &halfLength]() {
174 offsetZ = m_COILGeometryPar->getGlobalOffsetZ();
175 rMinCoil = m_COILGeometryPar->getCryoRmin();
176 halfLength = m_COILGeometryPar->getCryoLength();
177 });
178 m_TargetExt = new Simulation::ExtCylSurfaceTarget(rMinCoil, offsetZ - halfLength, offsetZ + halfLength);
179 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetExt);
180 }
181 if (!m_BeamPipeGeo.isValid())
182 B2FATAL("Beam pipe geometry data are not available.");
183 double beampipeRadius = m_BeamPipeGeo->getParameter("Lv2OutBe.R2") * CLHEP::cm; // mm
184 m_BeamPipeGeo.addCallback([this, &beampipeRadius]() {
185 beampipeRadius = m_BeamPipeGeo->getParameter("Lv2OutBe.R2") * CLHEP::cm;
186 });
187 m_MinRadiusSq = beampipeRadius * beampipeRadius; // mm^2
188}
189
190// Initialize for MUID
191void TrackExtrapolateG4e::initialize(double meanDt, double maxDt, double maxKLMTrackHitDistance,
192 double maxKLMTrackClusterDistance, double maxECLTrackClusterDistance,
193 double minPt, double minKE, bool addHitsToRecoTrack,
194 std::vector<Const::ChargedStable>& hypotheses)
195{
196 m_MuidInitialized = true;
197 m_addHitsToRecoTrack = addHitsToRecoTrack;
198
199 // Define required objects, register the new ones and relations
200 m_eclClusters.isRequired();
201 m_klmHit2ds.isRequired();
202 m_klmClusters.isRequired();
203 m_recoTracks.isRequired();
204 m_tracks.isRequired();
205 m_extHits.registerInDataStore();
206 m_klmMuidLikelihoods.registerInDataStore();
207 m_klmMuidHits.registerInDataStore();
208 m_trackClusterSeparations.registerInDataStore();
209 m_tracks.registerRelationTo(m_extHits);
210 m_tracks.registerRelationTo(m_klmMuidLikelihoods);
211 m_tracks.registerRelationTo(m_klmMuidHits);
212 m_tracks.registerRelationTo(m_klmHit2ds);
213 m_tracks.registerRelationTo(m_trackClusterSeparations);
214 m_tracks.registerRelationTo(m_klmClusters);
215 m_klmClusters.registerRelationTo(m_trackClusterSeparations);
216 m_eclClusters.registerRelationTo(m_extHits);
218
219 // Save the in-time cut's central value and width for valid hits
220 m_MeanDt = meanDt;
221 m_MaxDt = maxDt;
222
223 // Save the magnetic field z component (gauss) at the origin
224 m_MagneticField = BFieldManager::getField(0, 0, 0).Z() / Unit::T * CLHEP::tesla / CLHEP::gauss;
225
226 // Convert from sigma to variance for hit-position uncertainty
227 m_MaxDistSqInVariances = maxKLMTrackHitDistance * maxKLMTrackHitDistance;
228
229 // Convert user cutoff values to geant4 units
230 m_MaxKLMTrackClusterDistance = std::max(0.0, maxKLMTrackClusterDistance) * CLHEP::cm; // mm
231 m_MaxECLTrackClusterDistance = std::max(0.0, maxECLTrackClusterDistance) * CLHEP::cm; // mm
232 m_MinPt = std::max(0.0, minPt) * CLHEP::GeV;
233 m_MinKE = std::max(0.0, minKE) * CLHEP::GeV;
234
235 // Save pointer to the list of particle hypotheses for EXT extrapolation
236 m_HypothesesMuid = &hypotheses;
237
238 // Define the list of volumes that will have their entry and/or
239 // exit points stored during the extrapolation.
241
242 // Store the address of the ExtManager (used later)
244
245 // Set up the EXT-specific geometry (might have already been done by EXT)
246 if (m_TargetExt == nullptr) {
247 if (!m_COILGeometryPar.isValid())
248 B2FATAL("Coil geometry data are not available.");
249 double offsetZ = m_COILGeometryPar->getGlobalOffsetZ();
250 double rMinCoil = m_COILGeometryPar->getCryoRmin();
251 double halfLength = m_COILGeometryPar->getCryoLength();
252 m_COILGeometryPar.addCallback([this, &offsetZ, &rMinCoil, &halfLength]() {
253 offsetZ = m_COILGeometryPar->getGlobalOffsetZ();
254 rMinCoil = m_COILGeometryPar->getCryoRmin();
255 halfLength = m_COILGeometryPar->getCryoLength();
256 });
257 m_TargetExt = new Simulation::ExtCylSurfaceTarget(rMinCoil, offsetZ - halfLength, offsetZ + halfLength);
258 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetExt);
259 }
260 if (!m_BeamPipeGeo.isValid())
261 B2FATAL("Beam pipe geometry data are not available.");
262 double beampipeRadius = m_BeamPipeGeo->getParameter("Lv2OutBe.R2") * CLHEP::cm; // mm
263 m_BeamPipeGeo.addCallback([this, &beampipeRadius]() {
264 beampipeRadius = m_BeamPipeGeo->getParameter("Lv2OutBe.R2") * CLHEP::cm;
265 });
266 m_MinRadiusSq = beampipeRadius * beampipeRadius; // mm^2
267
268 // Set up the MUID-specific geometry
270 const EKLM::GeometryData& eklmGeometry = EKLM::GeometryData::Instance();
271 m_BarrelHalfLength = bklmGeometry->getHalfLength() * CLHEP::cm; // in G4 units (mm)
272 m_EndcapHalfLength = 0.5 * eklmGeometry.getSectionPosition()->getLength(); // in G4 units (mm)
273 m_OffsetZ = bklmGeometry->getOffsetZ() * CLHEP::cm; // in G4 units (mm)
274 double minZ = m_OffsetZ - (m_BarrelHalfLength + 2.0 * m_EndcapHalfLength);
275 double maxZ = m_OffsetZ + (m_BarrelHalfLength + 2.0 * m_EndcapHalfLength);
276 m_BarrelNSector = bklmGeometry->getNSector();
277 m_BarrelMaxR = bklmGeometry->getOuterRadius() * CLHEP::cm / std::cos(M_PI / m_BarrelNSector); // in G4 units (mm)
279 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetMuid);
280
281 m_BarrelHalfLength /= CLHEP::cm; // now in G4e units (cm)
282 m_EndcapHalfLength /= CLHEP::cm; // now in G4e units (cm)
283 m_OffsetZ /= CLHEP::cm; // now in G4e units (cm)
284 m_BarrelMinR = bklmGeometry->getGap1InnerRadius(); // in G4e units (cm)
285 m_BarrelMaxR /= CLHEP::cm; // now in G4e units (cm)
286 m_EndcapMinR = eklmGeometry.getSectionPosition()->getInnerR() / CLHEP::cm; // in G4e units (cm)
287 m_EndcapMaxR = eklmGeometry.getSectionPosition()->getOuterR() / CLHEP::cm; // in G4e units (cm)
289
290 // Measurement uncertainties and acceptance windows
291 double width = eklmGeometry.getStripGeometry()->getWidth() / CLHEP::cm; // in G4e units (cm)
292 m_EndcapScintVariance = width * width / 12.0;
293 width = bklmGeometry->getScintHalfWidth() * 2.0; // in G4e units (cm)
294 m_BarrelScintVariance = width * width / 12.0;
295 for (int layer = 1; layer <= BKLMElementNumbers::getMaximalLayerNumber(); ++layer) {
296 const bklm::Module* module =
297 bklmGeometry->findModule(BKLMElementNumbers::c_ForwardSection, 1, layer);
298 width = module->getPhiStripWidth(); // in G4e units (cm)
299 m_BarrelPhiStripVariance[layer - 1] = width * width / 12.0;
300 width = module->getZStripWidth(); // in G4e units (cm)
301 m_BarrelZStripVariance[layer - 1] = width * width / 12.0;
302 }
303
304 // KLM geometry (for associating KLM hit with extrapolated crossing point)
306 for (int sector = 1; sector <= BKLMElementNumbers::getMaximalSectorNumber(); ++sector) {
307 double phi = M_PI_4 * (sector - 1);
308 m_BarrelSectorPerp[sector - 1].set(std::cos(phi), std::sin(phi), 0.0);
309 m_BarrelSectorPhi[sector - 1].set(-std::sin(phi), std::cos(phi), 0.0);
310 }
312 for (KLMChannelIndex& klmLayer : klmLayers) {
313 if (klmLayer.getSubdetector() == KLMElementNumbers::c_BKLM)
314 m_BarrelModuleMiddleRadius[klmLayer.getSection()][klmLayer.getSector() - 1][klmLayer.getLayer() - 1] =
315 bklmGeometry->getActiveMiddleRadius(klmLayer.getSection(), klmLayer.getSector(), klmLayer.getLayer()); // in G4e units (cm)
316 }
317 double dz(eklmGeometry.getLayerShiftZ() / CLHEP::cm); // in G4e units (cm)
318 double z0((eklmGeometry.getSectionPosition()->getZ()
319 + eklmGeometry.getLayerShiftZ()
320 - 0.5 * eklmGeometry.getSectionPosition()->getLength()
321 - 0.5 * eklmGeometry.getLayerPosition()->getLength()) / CLHEP::cm); // in G4e units (cm)
323 1; // zero-based counting
325 1; // zero-based counting
326 for (int layer = 1; layer <= EKLMElementNumbers::getMaximalLayerNumber(); ++layer) {
327 m_EndcapModuleMiddleZ[layer - 1] = z0 + dz * (layer - 1); // in G4e units (cm)
328 }
329
331}
332
334{
335 B2DEBUG(20, (byMuid ? "muid" : "ext"));
336 if (byMuid) {
337 if (!m_klmChannelStatus.isValid())
338 B2FATAL("KLM channel status data are not available.");
339 if (!m_klmStripEfficiency.isValid())
340 B2FATAL("KLM strip efficiency data are not available.");
341 if (!m_klmLikelihoodParameters.isValid())
342 B2FATAL("KLM likelihood parameters are not available.");
343 std::vector<int> muidPdgCodes = MuidElementNumbers::getPDGVector();
344 if (!m_MuidBuilderMap.empty()) {
345 if (m_klmLikelihoodParameters.hasChanged()) { /* Clear m_MuidBuilderMap if KLMLikelihoodParameters payload changed. */
346 for (auto const& muidBuilder : m_MuidBuilderMap)
347 delete muidBuilder.second;
348 m_MuidBuilderMap.clear();
349 } else /* Return if m_MuidBuilderMap is already initialized. */
350 return;
351 }
352 for (int pdg : muidPdgCodes)
353 m_MuidBuilderMap.insert(std::pair<int, MuidBuilder*>(pdg, new MuidBuilder(pdg)));
354 }
355}
356
358{
359
360 // Put geant4 in proper state (in case this module is in a separate process)
361 if (G4StateManager::GetStateManager()->GetCurrentState() == G4State_Idle) {
362 G4StateManager::GetStateManager()->SetNewState(G4State_GeomClosed);
363 }
364
365 G4ThreeVector directionAtIP, positionG4e, momentumG4e;
366 G4ErrorTrajErr covG4e(5); // initialized to zeroes
367
368 // Loop over the reconstructed tracks
369 // Do extrapolation for each hypothesis of each reconstructed track.
370 if (byMuid) { // event() called by Muid module
371 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetMuid);
372 std::vector<std::pair<ECLCluster*, G4ThreeVector> > eclClusterInfo(m_eclClusters.getEntries());
373 for (int c = 0; c < m_eclClusters.getEntries(); ++c) {
374 eclClusterInfo[c].first = m_eclClusters[c];
375 eclClusterInfo[c].second = G4ThreeVector(m_eclClusters[c]->getClusterPosition().X(),
376 m_eclClusters[c]->getClusterPosition().Y(),
377 m_eclClusters[c]->getClusterPosition().Z()) * CLHEP::cm;
378 }
379 std::vector<std::pair<KLMCluster*, G4ThreeVector> > klmClusterInfo(m_klmClusters.getEntries());
380 for (int c = 0; c < m_klmClusters.getEntries(); ++c) {
381 klmClusterInfo[c].first = m_klmClusters[c];
382 klmClusterInfo[c].second = G4ThreeVector(m_klmClusters[c]->getClusterPosition().X(),
383 m_klmClusters[c]->getClusterPosition().Y(),
384 m_klmClusters[c]->getClusterPosition().Z()) * CLHEP::cm;
385 }
386 // Keep track of (re-)use of BKLMHit2ds
387 std::vector<std::map<const Track*, double> > bklmHitUsed(m_klmHit2ds.getEntries());
388 for (auto& b2track : m_tracks) {
389 for (const auto& hypothesis : *m_HypothesesMuid) {
390 int pdgCode = hypothesis.getPDGCode();
391 if (hypothesis == Const::electron || hypothesis == Const::muon)
392 pdgCode = -pdgCode;
393 G4ErrorFreeTrajState g4eState("g4e_mu+", G4ThreeVector(), G4ThreeVector()); // will be updated
394 ExtState extState = getStartPoint(b2track, pdgCode, g4eState);
395 swim(extState, g4eState, &eclClusterInfo, &klmClusterInfo, &bklmHitUsed);
396 } // Muid hypothesis loop
397 } // Muid track loop
398 } else { // event() called by Ext module
399 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetExt);
400 for (auto& b2track : m_tracks) {
401 for (const auto& hypothesis : *m_HypothesesExt) {
402 int pdgCode = hypothesis.getPDGCode();
403 if (hypothesis == Const::electron || hypothesis == Const::muon) pdgCode = -pdgCode;
404 G4ErrorFreeTrajState g4eState("g4e_mu+", G4ThreeVector(), G4ThreeVector()); // will be updated
405 ExtState extState = getStartPoint(b2track, pdgCode, g4eState);
406 swim(extState, g4eState);
407 } // Ext hypothesis loop
408 } // Ext track loop
409 } // byMuid
410
411 if (byMuid) {
413 }
414}
415
417{
418}
419
421{
422 if (m_DefaultHypotheses != nullptr)
423 delete m_DefaultHypotheses;
424 if (byMuid) {
425 delete m_TargetMuid;
426 for (auto const& muidBuilder : m_MuidBuilderMap)
427 delete muidBuilder.second;
428 }
429 if (m_TargetExt != nullptr) {
430 delete m_TargetExt;
431 m_TargetExt = nullptr;
432 }
433 if (m_EnterExit != nullptr) {
434 delete m_EnterExit;
435 delete m_BKLMVolumes;
436 m_ExtMgr->RunTermination();
437 m_EnterExit = nullptr;
438 m_BKLMVolumes = nullptr;
439 }
440}
441
442void TrackExtrapolateG4e::extrapolate(int pdgCode, // signed for charge
443 double tof, // in ns (from IP to position)
444 // DIVOT bool isCosmic, // true for back-extrapolation
445 const G4ThreeVector& position, // in cm (genfit2 units)
446 const G4ThreeVector& momentum, // in GeV/c (genfit2 units)
447 const G4ErrorSymMatrix& covariance) // (6x6) using cm, GeV/c (genfit2 units)
448{
449 bool isCosmic = false; // DIVOT
450 if ((!m_ExtInitialized) && (!m_MuidInitialized)) {
451 // No EXT nor MUID module in analysis path ==> mimic ext::initialize() with reasonable defaults.
452 // The default values are taken from the EXT module's parameter definitions.
454 extMgr->Initialize("Ext", "default", 0.0, 0.25, false, 0, std::vector<std::string>());
455 // Redefine geant4e step length, magnetic field step limitation (fraction of local curvature radius),
456 // and kinetic energy loss limitation (maximum fractional energy loss) by communicating with
457 // the geant4 UI. (Commands were defined in ExtMessenger when physics list was set up.)
458 // *NOTE* If module muid runs after this, its G4UImanager commands will override these.
459 G4UImanager::GetUIpointer()->ApplyCommand("/geant4e/limits/stepLength 250 mm");
460 G4UImanager::GetUIpointer()->ApplyCommand("/geant4e/limits/magField 0.001");
461 G4UImanager::GetUIpointer()->ApplyCommand("/geant4e/limits/energyLoss 0.05");
462 m_DefaultHypotheses = new std::vector<Const::ChargedStable>; // not used
463 initialize(0.1, 0.002, *m_DefaultHypotheses);
464 }
465
466 // Put geant4 in proper state (in case this module is in a separate process)
467 if (G4StateManager::GetStateManager()->GetCurrentState() == G4State_Idle) {
468 G4StateManager::GetStateManager()->SetNewState(G4State_GeomClosed);
469 }
470
471 G4ErrorPropagatorData::GetErrorPropagatorData()->SetTarget(m_TargetExt);
472
473 // Do extrapolation for selected hypothesis (pion, electron, muon, kaon, proton,
474 // deuteron) for the selected track until calorimeter exit.
475
476 G4ThreeVector positionG4e = position * CLHEP::cm; // convert from genfit2 units (cm) to geant4 units (mm)
477 G4ThreeVector momentumG4e = momentum * CLHEP::GeV; // convert from genfit2 units (GeV/c) to geant4 units (MeV/c)
478 if (isCosmic)
479 momentumG4e = -momentumG4e;
480 G4ErrorSymMatrix covarianceG4e(5, 0); // in Geant4e units (GeV/c, cm)
481 fromPhasespaceToG4e(momentum, covariance, covarianceG4e);
482 G4String nameG4e("g4e_" + G4ParticleTable::GetParticleTable()->FindParticle(pdgCode)->GetParticleName());
483 G4ErrorFreeTrajState g4eState(nameG4e, positionG4e, momentumG4e, covarianceG4e);
484 ExtState extState = { nullptr, pdgCode, isCosmic, tof, 0.0, // for EXT and MUID
485 momentumG4e.unit(), 0.0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false // for MUID only
486 };
487 swim(extState, g4eState);
488}
489
490// Swim one track for MUID until it stops or leaves the KLM-bounding cylinder
491void TrackExtrapolateG4e::swim(ExtState& extState, G4ErrorFreeTrajState& g4eState,
492 const std::vector<std::pair<ECLCluster*, G4ThreeVector> >* eclClusterInfo,
493 const std::vector<std::pair<KLMCluster*, G4ThreeVector> >* klmClusterInfo,
494 std::vector<std::map<const Track*, double> >* bklmHitUsed)
495{
496 if (extState.pdgCode == 0)
497 return;
498 if (g4eState.GetMomentum().perp() <= m_MinPt)
499 return;
500 if (m_TargetMuid->GetDistanceFromPoint(g4eState.GetPosition()) < 0.0)
501 return;
502 G4ParticleDefinition* particle = G4ParticleTable::GetParticleTable()->FindParticle(extState.pdgCode);
503 double mass = particle->GetPDGMass();
504 double minPSq = (mass + m_MinKE) * (mass + m_MinKE) - mass * mass;
505 // Create structures for ECLCluster-Track matching
506 std::vector<double> eclClusterDistance;
507 std::vector<ExtHit> eclHit1, eclHit2, eclHit3;
508 if (eclClusterInfo != nullptr) {
509 eclClusterDistance.resize(eclClusterInfo->size(), 1.0E10); // "positive infinity"
510 ExtHit tempExtHit(.0, extState.pdgCode, Const::EDetector::ECL, 0, EXT_FIRST,
511 extState.isCosmic,
512 G4ThreeVector(), G4ThreeVector(), G4ErrorSymMatrix(6));
513 eclHit1.resize(eclClusterInfo->size(), tempExtHit);
514 eclHit2.resize(eclClusterInfo->size(), tempExtHit);
515 eclHit3.resize(eclClusterInfo->size(), tempExtHit);
516 }
517 // Create structures for KLMCluster-Track matching
518 std::vector<TrackClusterSeparation> klmHit;
519 if (klmClusterInfo != nullptr) {
520 klmHit.resize(klmClusterInfo->size()); // initialize each to huge distance
521 }
522 KLMMuidLikelihood* klmMuidLikelihood = m_klmMuidLikelihoods.appendNew(); // rest of this object will be filled later
523 klmMuidLikelihood->setPDGCode(extState.pdgCode);
524 if (extState.track != nullptr)
525 extState.track->addRelationTo(klmMuidLikelihood);
526 G4ErrorMode propagationMode = (extState.isCosmic ? G4ErrorMode_PropBackwards : G4ErrorMode_PropForwards);
527 m_ExtMgr->InitTrackPropagation(propagationMode);
528 while (true) {
529 const G4int errCode = m_ExtMgr->PropagateOneStep(&g4eState, propagationMode);
530 G4Track* track = g4eState.GetG4Track();
531 const G4Step* step = track->GetStep();
532 const G4StepPoint* preStepPoint = step->GetPreStepPoint();
533 const G4StepPoint* postStepPoint = step->GetPostStepPoint();
534 G4TouchableHandle preTouch = preStepPoint->GetTouchableHandle();
535 G4VPhysicalVolume* pVol = preTouch->GetVolume();
536 const G4int preStatus = preStepPoint->GetStepStatus();
537 const G4int postStatus = postStepPoint->GetStepStatus();
538 G4ThreeVector pos = track->GetPosition(); // this is at postStepPoint
539 G4ThreeVector mom = track->GetMomentum(); // ditto
540 // Ignore the zero-length step by PropagateOneStep() at each boundary
541 if (extState.isCosmic) mom = -mom;
542 if (step->GetStepLength() > 0.0) {
543 double dt = step->GetDeltaTime();
544 double dl = step->GetStepLength() / track->GetMaterial()->GetRadlen();
545 if (preStatus == fGeomBoundary) { // first step in this volume?
546 if (m_TargetExt->GetDistanceFromPoint(pos) < 0.0) { // only hits outside ECL during muid
547 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
548 createExtHit(EXT_ENTER, extState, g4eState, preStepPoint, preTouch);
549 }
550 }
551 }
552 if (extState.isCosmic) {
553 extState.tof -= dt;
554 extState.length -= dl;
555 } else {
556 extState.tof += dt;
557 extState.length += dl;
558 }
559 if (postStatus == fGeomBoundary) { // last step in this volume?
560 if (m_TargetExt->GetDistanceFromPoint(pos) < 0.0) { // only hits outside ECL during muid
561 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
562 createExtHit(EXT_EXIT, extState, g4eState, postStepPoint, preTouch);
563 }
564 }
565 }
566 if (createMuidHit(extState, g4eState, klmMuidLikelihood, bklmHitUsed)) {
567 // Force geant4e to update its G4Track from the Kalman-updated state
568 m_ExtMgr->GetPropagator()->SetStepN(0);
569 }
570 if (eclClusterInfo != nullptr) {
571 for (unsigned int c = 0; c < eclClusterInfo->size(); ++c) {
572 G4ThreeVector eclPos((*eclClusterInfo)[c].second);
573 G4ThreeVector prePos(preStepPoint->GetPosition());
574 G4ThreeVector diff(prePos - eclPos);
575 double distance = diff.mag();
576 if (distance < m_MaxECLTrackClusterDistance) {
577 // fallback ECLNEAR in case no ECLCROSS is found
578 if (distance < eclClusterDistance[c]) {
579 eclClusterDistance[c] = distance;
580 G4ErrorSymMatrix covariance(6, 0);
581 fromG4eToPhasespace(g4eState, covariance);
582 eclHit3[c].update(EXT_ECLNEAR, extState.tof, pos / CLHEP::cm, mom / CLHEP::GeV, covariance);
583 }
584 // find position of crossing of the track with the ECLCluster's sphere
585 if (eclHit1[c].getStatus() == EXT_FIRST) {
586 if (pos.mag2() >= eclPos.mag2()) {
587 double r = eclPos.mag();
588 double preD = prePos.mag() - r;
589 double postD = pos.mag() - r;
590 double f = postD / (postD - preD);
591 G4ThreeVector midPos = pos + (prePos - pos) * f;
592 double tof = extState.tof + dt * f * (extState.isCosmic ? +1 : -1); // in ns, at end of step
593 G4ErrorSymMatrix covariance(6, 0);
594 fromG4eToPhasespace(g4eState, covariance);
595 eclHit1[c].update(EXT_ECLCROSS, tof, midPos / CLHEP::cm, mom / CLHEP::GeV, covariance);
596 }
597 }
598 }
599 // find closest distance to the radial line to the ECLCluster
600 if (eclHit2[c].getStatus() == EXT_FIRST) {
601 G4ThreeVector delta(pos - prePos);
602 G4ThreeVector perp(eclPos.cross(delta));
603 double perpMag2 = perp.mag2();
604 if (perpMag2 > 1.0E-10) {
605 double dist = std::fabs(diff * perp) / std::sqrt(perpMag2);
606 if (dist < m_MaxECLTrackClusterDistance) {
607 double f = eclPos * (prePos.cross(perp)) / perpMag2;
608 if ((f > -0.5) && (f <= 1.0)) {
609 G4ThreeVector midPos(prePos + f * delta);
610 double length = extState.length + dl * (1.0 - f) * (extState.isCosmic ? +1 : -1);
611 G4ErrorSymMatrix covariance(6, 0);
612 fromG4eToPhasespace(g4eState, covariance);
613 eclHit2[c].update(EXT_ECLDL, length, midPos / CLHEP::cm, mom / CLHEP::GeV, covariance);
614 }
615 }
616 }
617 }
618 }
619 }
620 if (klmClusterInfo != nullptr) {
621 for (unsigned int c = 0; c < klmClusterInfo->size(); ++c) {
622 G4ThreeVector klmPos = (*klmClusterInfo)[c].second;
623 G4ThreeVector separation = klmPos - pos;
624 double distance = separation.mag();
625 if (distance < klmHit[c].getDistance()) {
626 klmHit[c].setDistance(distance);
627 klmHit[c].setTrackClusterAngle(mom.angle(separation));
628 klmHit[c].setTrackClusterSeparationAngle(mom.angle(klmPos));
629 klmHit[c].setTrackRotationAngle(extState.directionAtIP.angle(mom));
630 klmHit[c].setTrackClusterInitialSeparationAngle(extState.directionAtIP.angle(klmPos));
631 }
632 }
633 }
634 }
635 // Post-step momentum too low?
636 if (errCode || (mom.mag2() < minPSq)) {
637 break;
638 }
639 // Detect escapes from the imaginary target cylinder.
640 if (m_TargetMuid->GetDistanceFromPoint(pos) < 0.0) {
641 break;
642 }
643 // Stop extrapolating as soon as the track curls inward too much
644 if (pos.perp2() < m_MinRadiusSq) {
645 break;
646 }
647 } // track-extrapolation "infinite" loop
648
649 m_ExtMgr->EventTermination(propagationMode);
650
651 finishTrack(extState, klmMuidLikelihood, (g4eState.GetPosition().z() > m_OffsetZ));
652
653 if (eclClusterInfo != nullptr) {
654 for (unsigned int c = 0; c < eclClusterInfo->size(); ++c) {
655 if (eclHit1[c].getStatus() != EXT_FIRST) {
656 ExtHit* h = m_extHits.appendNew(eclHit1[c]);
657 (*eclClusterInfo)[c].first->addRelationTo(h);
658 if (extState.track != nullptr) {
659 extState.track->addRelationTo(h);
660 }
661 }
662 if (eclHit2[c].getStatus() != EXT_FIRST) {
663 ExtHit* h = m_extHits.appendNew(eclHit2[c]);
664 (*eclClusterInfo)[c].first->addRelationTo(h);
665 if (extState.track != nullptr) {
666 extState.track->addRelationTo(h);
667 }
668 }
669 if (eclHit3[c].getStatus() != EXT_FIRST) {
670 ExtHit* h = m_extHits.appendNew(eclHit3[c]);
671 (*eclClusterInfo)[c].first->addRelationTo(h);
672 if (extState.track != nullptr) {
673 extState.track->addRelationTo(h);
674 }
675 }
676 }
677 }
678
679 if (klmClusterInfo != nullptr) {
680 // here we set a relation only to the closest KLMCluster
681 // and we don't set any relation if the distance is too large
682 double minDistance = m_MaxKLMTrackClusterDistance;
683 unsigned int closestCluster = 0;
684 for (unsigned int c = 0; c < klmClusterInfo->size(); ++c) {
685 if (klmHit[c].getDistance() > 1.0E9) {
686 continue;
687 }
688 TrackClusterSeparation* h = m_trackClusterSeparations.appendNew(klmHit[c]);
689 if (h != nullptr) {
690 (*klmClusterInfo)[c].first->addRelationTo(h); // relation KLMCluster to TrackClusterSeparation
691 if (extState.track != nullptr) {
692 extState.track->addRelationTo(h); // relation Track to TrackClusterSeparation
693 }
694 }
695 if (klmHit[c].getDistance() < minDistance) {
696 closestCluster = c;
697 minDistance = klmHit[c].getDistance();
698 }
699 }
700 if (minDistance < m_MaxKLMTrackClusterDistance) {
701 // set the relation Track to KLMCluster, using the distance as weight
702 // but for being consistent with the basf2 conventions, store the distance in cm
703 if (extState.track != nullptr) {
704 extState.track->addRelationTo((*klmClusterInfo)[closestCluster].first, 1. / (minDistance / CLHEP::cm));
705 }
706 }
707 }
708}
709
711{
712 for (auto& klmCluster : m_klmClusters) {
713 const auto& trackClusterSeparations = klmCluster.getRelationsWith<TrackClusterSeparation>();
714
715 // If there are no TrackClusterSeparation objects related to this cluster, set NaN
716 if (trackClusterSeparations.size() == 0) {
717 klmCluster.setClusterTrackSeparation(Const::doubleNaN);
718 klmCluster.setClusterTrackSeparationAngle(Const::doubleNaN);
719 klmCluster.setClusterTrackRotationAngle(Const::doubleNaN);
720 continue;
721 }
722
723 // Look for the closest track by comparing the TrackClusterSeparation objects
724 const auto closestSeparationIterator = std::min_element(trackClusterSeparations.begin(), trackClusterSeparations.end(),
725 [](const auto & a, const auto & b) {
726 return a.getDistance() < b.getDistance();
727 });
728
729 // We found the closest track, let's set the cluster properties accordingly
730 const auto& closestSeparation = *closestSeparationIterator;
731 klmCluster.setClusterTrackSeparation(closestSeparation.getDistance() / CLHEP::cm);
732 klmCluster.setClusterTrackSeparationAngle(closestSeparation.getTrackClusterSeparationAngle());
733 klmCluster.setClusterTrackRotationAngle(closestSeparation.getTrackRotationAngle());
734 }
735}
736
737// Swim one track for EXT until it stops or leaves the ECL-bounding cylinder
738void TrackExtrapolateG4e::swim(ExtState& extState, G4ErrorFreeTrajState& g4eState)
739{
740 if (extState.pdgCode == 0)
741 return;
742 if (g4eState.GetMomentum().perp() <= m_MinPt)
743 return;
744 if (m_TargetExt->GetDistanceFromPoint(g4eState.GetPosition()) < 0.0)
745 return;
746 G4ParticleDefinition* particle = G4ParticleTable::GetParticleTable()->FindParticle(extState.pdgCode);
747 double mass = particle->GetPDGMass();
748 double minPSq = (mass + m_MinKE) * (mass + m_MinKE) - mass * mass;
749 G4ErrorMode propagationMode = (extState.isCosmic ? G4ErrorMode_PropBackwards : G4ErrorMode_PropForwards);
750 m_ExtMgr->InitTrackPropagation(propagationMode);
751 while (true) {
752 const G4int errCode = m_ExtMgr->PropagateOneStep(&g4eState, propagationMode);
753 G4Track* track = g4eState.GetG4Track();
754 const G4Step* step = track->GetStep();
755 const G4StepPoint* preStepPoint = step->GetPreStepPoint();
756 const G4StepPoint* postStepPoint = step->GetPostStepPoint();
757 G4TouchableHandle preTouch = preStepPoint->GetTouchableHandle();
758 G4VPhysicalVolume* pVol = preTouch->GetVolume();
759 const G4int preStatus = preStepPoint->GetStepStatus();
760 const G4int postStatus = postStepPoint->GetStepStatus();
761 G4ThreeVector pos = track->GetPosition(); // this is at postStepPoint
762 G4ThreeVector mom = track->GetMomentum(); // ditto
763 // First step on this track?
764 if (extState.isCosmic)
765 mom = -mom;
766 if (preStatus == fUndefined) {
767 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
768 createExtHit(EXT_FIRST, extState, g4eState, preStepPoint, preTouch);
769 }
770 }
771 // Ignore the zero-length step by PropagateOneStep() at each boundary
772 if (step->GetStepLength() > 0.0) {
773 double dt = step->GetDeltaTime();
774 double dl = step->GetStepLength() / track->GetMaterial()->GetRadlen();
775 if (preStatus == fGeomBoundary) { // first step in this volume?
776 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
777 createExtHit(EXT_ENTER, extState, g4eState, preStepPoint, preTouch);
778 }
779 }
780 if (extState.isCosmic) {
781 extState.tof -= dt;
782 extState.length -= dl;
783 } else {
784 extState.tof += dt;
785 extState.length += dl;
786 }
787 // Last step in this volume?
788 if (postStatus == fGeomBoundary) {
789 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
790 createExtHit(EXT_EXIT, extState, g4eState, postStepPoint, preTouch);
791 }
792 }
793 }
794 // Post-step momentum too low?
795 if (errCode || (mom.mag2() < minPSq)) {
796 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
797 createExtHit(EXT_STOP, extState, g4eState, postStepPoint, preTouch);
798 }
799 break;
800 }
801 // Detect escapes from the imaginary target cylinder.
802 if (m_TargetExt->GetDistanceFromPoint(pos) < 0.0) {
803 if (m_EnterExit->find(pVol) != m_EnterExit->end()) {
804 createExtHit(EXT_ESCAPE, extState, g4eState, postStepPoint, preTouch);
805 }
806 break;
807 }
808 // Stop extrapolating as soon as the track curls inward too much
809 if (pos.perp2() < m_MinRadiusSq) {
810 break;
811 }
812 } // track-extrapolation "infinite" loop
813
814 m_ExtMgr->EventTermination(propagationMode);
815
816}
817
818// Register the list of volumes for which entry/exit point is to be saved during extrapolation
820{
821 G4PhysicalVolumeStore* pvStore = G4PhysicalVolumeStore::GetInstance();
822 if (pvStore->size() == 0) {
823 B2FATAL("No geometry defined. Please create the geometry first.");
824 }
825 if (m_EnterExit != nullptr) // Only do this once
826 return;
827 m_EnterExit = new std::map<G4VPhysicalVolume*, enum VolTypes>;
828 m_BKLMVolumes = new std::vector<G4VPhysicalVolume*>;
829 for (std::vector<G4VPhysicalVolume*>::iterator iVol = pvStore->begin();
830 iVol != pvStore->end(); ++iVol) {
831 const G4String name = (*iVol)->GetName();
832 // Do not store ExtHits in CDC, so let's start from TOP and ARICH.
833 // TOP doesn't have one envelope; it has 16 "TOPModule"s
834 if (name.find("TOPModule") != std::string::npos) {
835 (*m_EnterExit)[*iVol] = VOLTYPE_TOP1;
836 }
837 // TOP quartz bar (=sensitive)
838 else if (name.find("_TOPPrism_") != std::string::npos ||
839 name.find("_TOPBarSegment") != std::string::npos ||
840 name.find("_TOPMirrorSegment") != std::string::npos) {
841 (*m_EnterExit)[*iVol] = VOLTYPE_TOP2;
842 }
843 // TOP quartz glue (not sensitive?)
844 else if (name.find("TOPBarSegment1Glue") != std::string::npos ||
845 name.find("TOPBarSegment2Glue") != std::string::npos ||
846 name.find("TOPMirrorSegmentGlue") != std::string::npos) {
847 (*m_EnterExit)[*iVol] = VOLTYPE_TOP3;
848 // ARICH volumes
849 } else if (name == "ARICH.AerogelSupportPlate") {
850 (*m_EnterExit)[*iVol] = VOLTYPE_ARICH1;
851 } else if (name == "ARICH.AerogelImgPlate") {
852 (*m_EnterExit)[*iVol] = VOLTYPE_ARICH2;
853 } else if (name.find("ARICH.HAPDWindow") != std::string::npos) {
854 (*m_EnterExit)[*iVol] = VOLTYPE_ARICH3;
855 }
856 // ECL crystal
857 else if (name.find("lv_barrel_crystal_") != std::string::npos ||
858 name.find("lv_forward_crystal_") != std::string::npos ||
859 name.find("lv_backward_crystal_") != std::string::npos) {
860 (*m_EnterExit)[*iVol] = VOLTYPE_ECL;
861 }
862 // Barrel KLM: BKLM.Layer**GasPhysical for RPCs or BKLM.Layer**ChimneyGasPhysical for RPCs
863 // BKLM.ScintActiveType*Physical for scintillator strips
864 else if (name.compare(0, 5, "BKLM.") == 0) {
865 if (name.find("GasPhysical") != std::string::npos) {
866 (*m_EnterExit)[*iVol] = VOLTYPE_BKLM1;
867 } else if (name.find("ScintActiveType") != std::string::npos) {
868 (*m_EnterExit)[*iVol] = VOLTYPE_BKLM2;
869 } else if ((name.find("ScintType") != std::string::npos) ||
870 (name.find("ElectrodePhysical") != std::string::npos)) {
871 m_BKLMVolumes->push_back(*iVol);
872 }
873 }
874 // Endcap KLM: StripSensitive_*
875 else if (name.compare(0, 14, "StripSensitive") == 0) {
876 (*m_EnterExit)[*iVol] = VOLTYPE_EKLM;
877 }
878 }
879
880}
881
882// Convert the physical volume to integer(-like) identifiers
883void TrackExtrapolateG4e::getVolumeID(const G4TouchableHandle& touch, Const::EDetector& detID, int& copyID)
884{
885
886 // default values
887 detID = Const::EDetector::invalidDetector;
888 copyID = 0;
889
890 G4VPhysicalVolume* pv = touch->GetVolume(0);
891 std::map<G4VPhysicalVolume*, enum VolTypes>::iterator it = m_EnterExit->find(pv);
892 if (it == m_EnterExit->end())
893 return;
894
895 switch (it->second) {
896 case VOLTYPE_CDC:
897 detID = Const::EDetector::CDC;
898 copyID = pv->GetCopyNo();
899 return;
900 case VOLTYPE_TOP1:
901 detID = Const::EDetector::TOP;
902 copyID = -(pv->GetCopyNo()); // negative to distinguish module and quartz hits
903 return;
904 case VOLTYPE_TOP2:
905 detID = Const::EDetector::TOP;
906 if (touch->GetHistoryDepth() >= 1)
907 copyID = touch->GetVolume(1)->GetCopyNo();
908 return;
909 case VOLTYPE_TOP3:
910 detID = Const::EDetector::TOP;
911 if (touch->GetHistoryDepth() >= 2)
912 copyID = touch->GetVolume(2)->GetCopyNo();
913 return;
914 case VOLTYPE_ARICH1:
915 detID = Const::EDetector::ARICH;
916 copyID = 12345;
917 return;
918 case VOLTYPE_ARICH2:
919 detID = Const::EDetector::ARICH;
920 copyID = 6789;
921 return;
922 case VOLTYPE_ARICH3:
923 detID = Const::EDetector::ARICH;
924 if (touch->GetHistoryDepth() >= 2)
925 copyID = touch->GetVolume(2)->GetCopyNo();
926 return;
927 case VOLTYPE_ECL:
928 detID = Const::EDetector::ECL;
930 return;
931 case VOLTYPE_BKLM1: // BKLM RPCs
932 detID = Const::EDetector::BKLM;
933 if (touch->GetHistoryDepth() == DEPTH_RPC) {
934 // int plane = touch->GetCopyNumber(0);
935 int layer = touch->GetCopyNumber(4);
936 int sector = touch->GetCopyNumber(6);
937 int section = touch->GetCopyNumber(7);
938 copyID = BKLMElementNumbers::moduleNumber(section, sector, layer);
939 }
940 return;
941 case VOLTYPE_BKLM2: // BKLM scints
942 detID = Const::EDetector::BKLM;
943 if (touch->GetHistoryDepth() == DEPTH_SCINT) {
944 int strip = touch->GetCopyNumber(1);
945 int plane = (touch->GetCopyNumber(2) == BKLM_INNER) ?
948 int layer = touch->GetCopyNumber(6);
949 int sector = touch->GetCopyNumber(8);
950 int section = touch->GetCopyNumber(9);
952 section, sector, layer, plane, strip);
953 BKLMStatus::setMaximalStrip(copyID, strip);
954 }
955 return;
956 case VOLTYPE_EKLM:
957 detID = Const::EDetector::EKLM;
959 touch->GetVolume(7)->GetCopyNo(),
960 touch->GetVolume(6)->GetCopyNo(),
961 touch->GetVolume(5)->GetCopyNo(),
962 touch->GetVolume(4)->GetCopyNo(),
963 touch->GetVolume(1)->GetCopyNo());
964 return;
965 }
966
967}
968
969ExtState TrackExtrapolateG4e::getStartPoint(const Track& b2track, int pdgCode, G4ErrorFreeTrajState& g4eState)
970{
971 ExtState extState = {&b2track, pdgCode, false, 0.0, 0.0, // for EXT and MUID
972 G4ThreeVector(0, 0, 1), 0.0, 0, 0, 0, -1, -1, -1, -1, 0, 0, false // for MUID only
973 };
974 RecoTrack* recoTrack = b2track.getRelatedTo<RecoTrack>();
975 if (recoTrack == nullptr) {
976 B2WARNING("Track without associated RecoTrack: skipping extrapolation for this track.");
977 extState.pdgCode = 0; // prevent start of extrapolation in swim()
978 return extState;
979 }
980 const genfit::AbsTrackRep* trackRep = recoTrack->getCardinalRepresentation();
981 // check for a valid track fit
982 if (!recoTrack->wasFitSuccessful(trackRep)) {
983 B2WARNING("RecoTrack fit failed for cardinal representation: skipping extrapolation for this track.");
984 extState.pdgCode = 0; // prevent start of extrapolation in swim()
985 return extState;
986 }
987 int charge = int(trackRep->getPDGCharge());
988 if (charge != 0) {
989 extState.pdgCode *= charge;
990 } else {
991 charge = 1; // should never happen but persist if it does
992 }
993 TVector3 firstPosition, firstMomentum, lastPosition, lastMomentum; // initialized to zeroes
994 TMatrixDSym firstCov(6), lastCov(6); // initialized to zeroes
995 try {
996 const genfit::MeasuredStateOnPlane& firstState = recoTrack->getMeasuredStateOnPlaneFromFirstHit(trackRep);
997 trackRep->getPosMomCov(firstState, firstPosition, firstMomentum, firstCov);
998 const genfit::MeasuredStateOnPlane& lastState = recoTrack->getMeasuredStateOnPlaneFromLastHit(trackRep);
999 trackRep->getPosMomCov(lastState, lastPosition, lastMomentum, lastCov);
1000 // in genfit units (cm, GeV/c)
1001 extState.tof = lastState.getTime(); // DIVOT: must be revised when IP profile (reconstructed beam spot) become available!
1002 if (lastPosition.Mag2() < firstPosition.Mag2()) {
1003 firstPosition = lastPosition;
1004 firstMomentum = -lastMomentum;
1005 firstCov = lastCov;
1006 trackRep->getPosMomCov(firstState, lastPosition, lastMomentum, lastCov);
1007 lastMomentum *= -1.0; // extrapolate backwards instead of forwards
1008 extState.isCosmic = true;
1009 extState.tof = firstState.getTime(); // DIVOT: must be revised when IP profile (reconstructed beam spot) become available!
1010 }
1011
1012 G4ParticleDefinition* particle = G4ParticleTable::GetParticleTable()->FindParticle(extState.pdgCode);
1013 if (extState.pdgCode != trackRep->getPDG()) {
1014 double pSq = lastMomentum.Mag2();
1015 double mass = particle->GetPDGMass() / CLHEP::GeV;
1016 extState.tof *= std::sqrt((pSq + mass * mass) / (pSq + lastState.getMass() * lastState.getMass()));
1017 }
1018
1019 extState.directionAtIP.set(firstMomentum.Unit().X(), firstMomentum.Unit().Y(), firstMomentum.Unit().Z());
1020 if (m_MagneticField != 0.0) { // in gauss
1021 double radius = (firstMomentum.Perp() * CLHEP::GeV / CLHEP::eV) / (CLHEP::c_light * charge * m_MagneticField); // in cm
1022 double centerPhi = extState.directionAtIP.phi() - M_PI_2;
1023 double centerX = firstPosition.X() + radius * std::cos(centerPhi);
1024 double centerY = firstPosition.Y() + radius * std::sin(centerPhi);
1025 double pocaPhi = atan2(charge * centerY, charge * centerX) + M_PI;
1026 double ipPerp = extState.directionAtIP.perp();
1027 if (ipPerp > 0.0) {
1028 extState.directionAtIP.setX(+std::sin(pocaPhi) * ipPerp);
1029 extState.directionAtIP.setY(-std::cos(pocaPhi) * ipPerp);
1030 }
1031 } else {
1032 // No field: replace flaky covariance matrix with a diagonal one measured in 1.5T field
1033 // for a 10 GeV/c track ... and replace momentum magnitude with fixed 10 GeV/c
1034 lastCov *= 0.0;
1035 lastCov[0][0] = 5.0E-5;
1036 lastCov[1][1] = 1.0E-7;
1037 lastCov[2][2] = 5.0E-4;
1038 lastCov[3][3] = 3.5E-3;
1039 lastCov[4][4] = 3.5E-3;
1040 lastMomentum = lastMomentum.Unit() * 10.0;
1041 }
1042
1043 G4ThreeVector posG4e(lastPosition.X() * CLHEP::cm, lastPosition.Y() * CLHEP::cm,
1044 lastPosition.Z() * CLHEP::cm); // in Geant4 units (mm)
1045 G4ThreeVector momG4e(lastMomentum.X() * CLHEP::GeV, lastMomentum.Y() * CLHEP::GeV,
1046 lastMomentum.Z() * CLHEP::GeV); // in Geant4 units (MeV/c)
1047 G4ErrorSymMatrix covG4e(5, 0); // in Geant4e units (GeV/c, cm)
1048 fromPhasespaceToG4e(lastMomentum, lastCov, covG4e); // in Geant4e units (GeV/c, cm)
1049 g4eState.SetData("g4e_" + particle->GetParticleName(), posG4e, momG4e);
1050 g4eState.SetParameters(posG4e, momG4e); // compute private-state parameters from momG4e
1051 g4eState.SetError(covG4e);
1052 } catch (const genfit::Exception&) {
1053 B2WARNING("genfit::MeasuredStateOnPlane() exception: skipping extrapolation for this track. initial momentum = ("
1054 << firstMomentum.X() << "," << firstMomentum.Y() << "," << firstMomentum.Z() << ")");
1055 extState.pdgCode = 0; // prevent start of extrapolation in swim()
1056 }
1057
1058 return extState;
1059}
1060
1061
1062void TrackExtrapolateG4e::fromG4eToPhasespace(const G4ErrorFreeTrajState& g4eState, G4ErrorSymMatrix& covariance)
1063{
1064
1065 // Convert Geant4e covariance matrix with parameters 1/p, lambda, phi, yT, zT (in GeV/c, radians, cm)
1066 // to phase-space covariance matrix with parameters x, y, z, px, py, pz (in GeV/c, cm)
1067 // (1/p) = 1/sqrt( px^2 + py^2 + pz^2 )
1068 // phi = atan( py / px )
1069 // lambda = asin( pz / sqrt( px^2 + py^2 + pz^2 )
1070 // xT = x * cos(lambda) * cos(phi) + y * cos(lambda) * sin(phi) + z * sin(lambda)
1071 // yT = -x * sin(phi) + y * cos(phi)
1072 // zT = -x * sin(lambda) * cos(phi) - y * sin(lambda) * sin(phi) + z * cos(lambda)
1073
1074 G4ErrorFreeTrajParam param = g4eState.GetParameters();
1075 double p = 1.0 / (param.GetInvP() * CLHEP::GeV); // in GeV/c
1076 double pSq = p * p;
1077 double lambda = param.GetLambda(); // in radians
1078 double sinLambda = std::sin(lambda);
1079 double cosLambda = std::cos(lambda);
1080 double phi = param.GetPhi(); // in radians
1081 double sinPhi = std::sin(phi);
1082 double cosPhi = std::cos(phi);
1083
1084 // Transformation Jacobian 6x5 from Geant4e 5x5 to phase-space 6x6
1085
1086 G4ErrorMatrix jacobian(6, 5, 0); // All entries are initialized to 0
1087
1088 jacobian(4, 1) = -pSq * cosLambda * cosPhi; // @(px)/@(1/p)
1089 jacobian(5, 1) = -pSq * cosLambda * sinPhi; // @(py)/@(1/p)
1090 jacobian(6, 1) = -pSq * sinLambda; // @(pz)/@(1/p)
1091
1092 jacobian(4, 2) = -p * sinLambda * cosPhi; // @(px)/@(lambda)
1093 jacobian(5, 2) = -p * sinLambda * sinPhi; // @(py)/@(lambda)
1094 jacobian(6, 2) = p * cosLambda; // @(pz)/@(lambda)
1095
1096 jacobian(4, 3) = -p * cosLambda * sinPhi; // @(px)/@(phi)
1097 jacobian(5, 3) = p * cosLambda * cosPhi; // @(py)/@(phi)
1098
1099 jacobian(1, 4) = -sinPhi; // @(x)/@(yT)
1100 jacobian(2, 4) = cosPhi; // @(y)/@(yT)
1101
1102 jacobian(1, 5) = -sinLambda * cosPhi; // @(x)/@(zT)
1103 jacobian(2, 5) = -sinLambda * sinPhi; // @(y)/@(zT)
1104 jacobian(3, 5) = cosLambda; // @(z)/@(zT)
1105
1106 G4ErrorTrajErr g4eCov = g4eState.GetError();
1107 covariance.assign(g4eCov.similarity(jacobian));
1108
1109}
1110
1111void TrackExtrapolateG4e::fromPhasespaceToG4e(const TVector3& momentum, const TMatrixDSym& covariance, G4ErrorTrajErr& covG4e)
1112{
1113
1114 // Convert phase-space covariance matrix with parameters x, y, z, px, py, pz (in genfit2 units: cm, GeV/c)
1115 // to Geant4e covariance matrix with parameters 1/p, lambda, phi, yT, zT (in geant4e units: GeV/c, radians, cm)
1116 // xT = x * cos(lambda) * cos(phi) + y * cos(lambda) * sin(phi) + z * sin(lambda)
1117 // yT = -x * sin(phi) + y * cos(phi)
1118 // zT = -x * sin(lambda) * cos(phi) - y * sin(lambda) * sin(phi) + z * cos(lambda)
1119 // (1/p) = 1/sqrt( px^2 + py^2 + pz^2 )
1120 // phi = atan( py / px )
1121 // lambda = asin( pz / sqrt( px^2 + py^2 + pz^2 )
1122
1123 G4ErrorSymMatrix temp(6, 0);
1124 for (int k = 0; k < 6; ++k) {
1125 for (int j = k; j < 6; ++j) {
1126 temp[j][k] = covariance[j][k];
1127 }
1128 }
1129
1130 double pInvSq = 1.0 / momentum.Mag2();
1131 double pInv = std::sqrt(pInvSq);
1132 double pPerpInv = 1.0 / momentum.Perp();
1133 double sinLambda = momentum.CosTheta();
1134 double cosLambda = std::sqrt(1.0 - sinLambda * sinLambda);
1135 double phi = momentum.Phi();
1136 double cosPhi = std::cos(phi);
1137 double sinPhi = std::sin(phi);
1138
1139 // Transformation Jacobian 5x6 from phase-space 6x6 to Geant4e 5x5
1140 G4ErrorMatrix jacobian(5, 6, 0); // All entries are initialized to 0
1141
1142 jacobian(1, 4) = -pInvSq * cosLambda * cosPhi; // @(1/p)/@(px)
1143 jacobian(1, 5) = -pInvSq * cosLambda * sinPhi; // @(1/p)/@(py)
1144 jacobian(1, 6) = -pInvSq * sinLambda; // @(1/p)/@(pz)
1145
1146 jacobian(2, 4) = -pInv * sinLambda * cosPhi; // @(lambda)/@(px)
1147 jacobian(2, 5) = -pInv * sinLambda * sinPhi; // @(lambda)/@(py)
1148 jacobian(2, 6) = pInv * cosLambda; // @(lambda)/@(pz)
1149
1150 jacobian(3, 4) = -pPerpInv * sinPhi; // @(phi)/@(px)
1151 jacobian(3, 5) = pPerpInv * cosPhi; // @(phi)/@(py)
1152
1153 jacobian(4, 1) = -sinPhi; // @(yT)/@(x)
1154 jacobian(4, 2) = cosPhi; // @(yT)/@(y)
1155
1156 jacobian(5, 1) = -sinLambda * cosPhi; // @(zT)/@(x)
1157 jacobian(5, 2) = -sinLambda * sinPhi; // @(zT)/@(y)
1158 jacobian(5, 3) = cosLambda; // @(zT)/@(z)
1159
1160 covG4e = temp.similarity(jacobian);
1161
1162}
1163
1164void TrackExtrapolateG4e::fromPhasespaceToG4e(const G4ThreeVector& momentum, const G4ErrorSymMatrix& covariance,
1165 G4ErrorTrajErr& covG4e)
1166{
1167
1168 // Convert phase-space covariance matrix with parameters x, y, z, px, py, pz (in genfit2 units: cm, GeV/c)
1169 // to Geant4e covariance matrix with parameters 1/p, lambda, phi, yT, zT (in geant4e units: GeV/c, radians, cm)
1170 // xT = x * cos(lambda) * cos(phi) + y * cos(lambda) * sin(phi) + z * sin(lambda)
1171 // yT = -x * sin(phi) + y * cos(phi)
1172 // zT = -x * sin(lambda) * cos(phi) - y * sin(lambda) * sin(phi) + z * cos(lambda)
1173 // (1/p) = 1/sqrt( px^2 + py^2 + pz^2 )
1174 // phi = atan( py / px )
1175 // lambda = asin( pz / sqrt( px^2 + py^2 + pz^2 )
1176
1177 G4ErrorSymMatrix temp(covariance);
1178
1179 double pInvSq = 1.0 / momentum.mag2();
1180 double pInv = std::sqrt(pInvSq);
1181 double pPerpInv = 1.0 / momentum.perp();
1182 double sinLambda = momentum.cosTheta();
1183 double cosLambda = std::sqrt(1.0 - sinLambda * sinLambda);
1184 double phi = momentum.phi();
1185 double cosPhi = std::cos(phi);
1186 double sinPhi = std::sin(phi);
1187
1188 // Transformation Jacobian 5x6 from phase-space 6x6 to Geant4e 5x5
1189 G4ErrorMatrix jacobian(5, 6, 0);
1190
1191 jacobian(1, 4) = -pInvSq * cosLambda * cosPhi; // @(1/p)/@(px)
1192 jacobian(1, 5) = -pInvSq * cosLambda * sinPhi; // @(1/p)/@(py)
1193 jacobian(1, 6) = -pInvSq * sinLambda; // @(1/p)/@(pz)
1194
1195 jacobian(2, 4) = -pInv * sinLambda * cosPhi; // @(lambda)/@(px)
1196 jacobian(2, 5) = -pInv * sinLambda * sinPhi; // @(lambda)/@(py)
1197 jacobian(2, 6) = pInv * cosLambda; // @(lambda)/@(pz)
1198
1199 jacobian(3, 4) = -pPerpInv * sinPhi; // @(phi)/@(px)
1200 jacobian(3, 5) = pPerpInv * cosPhi; // @(phi)/@(py)
1201
1202 jacobian(4, 1) = -sinPhi; // @(yT)/@(x)
1203 jacobian(4, 2) = cosPhi; // @(yT)/@(y)
1204
1205 jacobian(5, 1) = -sinLambda * cosPhi; // @(zT)/@(x)
1206 jacobian(5, 2) = -sinLambda * sinPhi; // @(zT)/@(y)
1207 jacobian(5, 3) = cosLambda; // @(zT)/@(z)
1208 covG4e = temp.similarity(jacobian);
1209
1210}
1211
1212// write another volume-entry or volume-exit point on extrapolated track
1214 const G4ErrorFreeTrajState& g4eState,
1215 const G4StepPoint* stepPoint, const G4TouchableHandle& touch)
1216{
1217 Const::EDetector detID(Const::EDetector::invalidDetector);
1218 int copyID(0);
1219 getVolumeID(touch, detID, copyID);
1220 G4ThreeVector pos(stepPoint->GetPosition() / CLHEP::cm);
1221 G4ThreeVector mom(stepPoint->GetMomentum() / CLHEP::GeV);
1222 if (extState.isCosmic)
1223 mom = -mom;
1224 G4ErrorSymMatrix covariance(6, 0);
1225 fromG4eToPhasespace(g4eState, covariance);
1226 ExtHit* extHit = m_extHits.appendNew(extState.tof, extState.pdgCode, detID, copyID, status,
1227 extState.isCosmic,
1228 pos, mom, covariance);
1229 // If called standalone, there will be no associated track
1230 if (extState.track != nullptr)
1231 extState.track->addRelationTo(extHit);
1232}
1233
1234// Write another volume-entry point on track.
1235// The track state will be modified here by the Kalman fitter.
1236
1237bool TrackExtrapolateG4e::createMuidHit(ExtState& extState, G4ErrorFreeTrajState& g4eState, KLMMuidLikelihood* klmMuidLikelihood,
1238 std::vector<std::map<const Track*, double> >* bklmHitUsed)
1239{
1240
1241 Intersection intersection;
1242 intersection.hit = -1;
1243 intersection.chi2 = -1.0;
1244 intersection.position = g4eState.GetPosition() / CLHEP::cm;
1245 intersection.momentum = g4eState.GetMomentum() / CLHEP::GeV;
1246 G4ThreeVector prePos = g4eState.GetG4Track()->GetStep()->GetPreStepPoint()->GetPosition() / CLHEP::cm;
1247 G4ThreeVector oldPosition(prePos.x(), prePos.y(), prePos.z());
1248 double r = intersection.position.perp();
1249 double z = std::fabs(intersection.position.z() - m_OffsetZ);
1250
1251 // Is the track in the barrel?
1252 if ((r > m_BarrelMinR) && (r < m_BarrelMaxR) && (z < m_BarrelHalfLength)) {
1253 // Did the track cross the inner midplane of a detector module?
1254 if (findBarrelIntersection(extState, oldPosition, intersection)) {
1255 fromG4eToPhasespace(g4eState, intersection.covariance);
1256 if (findMatchingBarrelHit(intersection, extState.track)) {
1257 (*bklmHitUsed)[intersection.hit].insert(std::pair<const Track*, double>(extState.track, intersection.chi2));
1258 extState.extLayerPattern |= (0x00000001 << intersection.layer);
1259 float layerBarrelEfficiency = 1.;
1260 for (int plane = 0; plane <= BKLMElementNumbers::getMaximalPlaneNumber(); plane++) {
1261 layerBarrelEfficiency *= m_klmStripEfficiency->getBarrelEfficiency(
1263 intersection.sector + 1, intersection.layer + 1, plane, 1);
1264 }
1265 klmMuidLikelihood->setExtBKLMEfficiencyValue(intersection.layer, layerBarrelEfficiency);
1266 if (extState.lastBarrelExtLayer < intersection.layer) {
1267 extState.lastBarrelExtLayer = intersection.layer;
1268 }
1269 extState.hitLayerPattern |= (0x00000001 << intersection.layer);
1270 if (extState.lastBarrelHitLayer < intersection.layer) {
1271 extState.lastBarrelHitLayer = intersection.layer;
1272 }
1273 // If the updated point is outside the barrel, discard it and the Kalman-fitter adjustment
1274 r = intersection.position.perp();
1275 z = std::fabs(intersection.position.z() - m_OffsetZ);
1276 if ((r <= m_BarrelMinR) || (r >= m_BarrelMaxR) || (z >= m_BarrelHalfLength)) {
1277 intersection.chi2 = -1.0;
1278 }
1279 } else {
1280 // Record a no-hit track crossing if this step is strictly within a barrel sensitive volume
1281 std::vector<G4VPhysicalVolume*>::iterator j = find(m_BKLMVolumes->begin(), m_BKLMVolumes->end(),
1282 g4eState.GetG4Track()->GetVolume());
1283 if (j != m_BKLMVolumes->end()) {
1284 bool isDead = true; // by default, the nearest orthogonal strips are dead
1285 int section = intersection.isForward ?
1288 int sector = intersection.sector + 1; // from 0-based to 1-based enumeration
1289 int layer = intersection.layer + 1; // from 0-based to 1-based enumeration
1290 const bklm::Module* m = bklm::GeometryPar::instance()->findModule(section, sector, layer); // uses 1-based enumeration
1291 if (m) {
1292 const CLHEP::Hep3Vector localPosition = m->globalToLocal(intersection.position); // uses and returns position in cm
1293 int zStrip = m->getZStripNumber(localPosition);
1294 int phiStrip = m->getPhiStripNumber(localPosition);
1295 if (zStrip >= 0 && phiStrip >= 0) {
1296 uint16_t channel1, channel2;
1297 channel1 = m_klmElementNumbers->channelNumberBKLM(
1298 section, sector, layer,
1300 channel2 = m_klmElementNumbers->channelNumberBKLM(
1301 section, sector, layer,
1303 enum KLMChannelStatus::ChannelStatus status1, status2;
1304 status1 = m_klmChannelStatus->getChannelStatus(channel1);
1305 status2 = m_klmChannelStatus->getChannelStatus(channel2);
1306 if (status1 == KLMChannelStatus::c_Unknown ||
1307 status2 == KLMChannelStatus::c_Unknown)
1308 B2ERROR("No KLM channel status data."
1309 << LogVar("Section", section)
1310 << LogVar("Sector", sector)
1311 << LogVar("Layer", layer)
1312 << LogVar("Z strip", zStrip)
1313 << LogVar("Phi strip", phiStrip));
1314 isDead = (status1 == KLMChannelStatus::c_Dead ||
1315 status2 == KLMChannelStatus::c_Dead);
1316 }
1317 }
1318 if (!isDead) {
1319 extState.extLayerPattern |= (0x00000001 << intersection.layer); // valid extrapolation-crossing of the layer but no matching hit
1320 float layerBarrelEfficiency = 1.;
1321 for (int plane = 0; plane <= BKLMElementNumbers::getMaximalPlaneNumber(); plane++) {
1322 layerBarrelEfficiency *= m_klmStripEfficiency->getBarrelEfficiency(
1324 intersection.sector + 1, intersection.layer + 1, plane, 1);
1325 }
1326 klmMuidLikelihood->setExtBKLMEfficiencyValue(intersection.layer, layerBarrelEfficiency);
1327 } else {
1328 klmMuidLikelihood->setExtBKLMEfficiencyValue(intersection.layer, 0);
1329 }
1330 if (extState.lastBarrelExtLayer < intersection.layer) {
1331 extState.lastBarrelExtLayer = intersection.layer;
1332 }
1333 }
1334 }
1335 }
1336 }
1337
1338 // Is the track in the endcap?
1339 if ((r > m_EndcapMinR) && (std::fabs(z - m_EndcapMiddleZ) < m_EndcapHalfLength)) {
1340 // Did the track cross the inner midplane of a detector module?
1341 if (findEndcapIntersection(extState, oldPosition, intersection)) {
1342 fromG4eToPhasespace(g4eState, intersection.covariance);
1343 if (findMatchingEndcapHit(intersection, extState.track)) {
1344 extState.extLayerPattern |= (0x00008000 << intersection.layer);
1345 float layerEndcapEfficiency = 1.;
1346 for (int plane = 1; plane <= EKLMElementNumbers::getMaximalPlaneNumber(); plane++) {
1347 layerEndcapEfficiency *= m_klmStripEfficiency->getEndcapEfficiency(
1349 intersection.sector + 1, intersection.layer + 1, plane, 1);
1350 }
1351 klmMuidLikelihood->setExtEKLMEfficiencyValue(intersection.layer, layerEndcapEfficiency);
1352 if (extState.lastEndcapExtLayer < intersection.layer) {
1353 extState.lastEndcapExtLayer = intersection.layer;
1354 }
1355 extState.hitLayerPattern |= (0x00008000 << intersection.layer);
1356 if (extState.lastEndcapHitLayer < intersection.layer) {
1357 extState.lastEndcapHitLayer = intersection.layer;
1358 }
1359 // If the updated point is outside the endcap, discard it and the Kalman-fitter adjustment
1360 r = intersection.position.perp();
1361 z = std::fabs(intersection.position.z() - m_OffsetZ);
1362 if ((r <= m_EndcapMinR) || (r >= m_EndcapMaxR) || (std::fabs(z - m_EndcapMiddleZ) >= m_EndcapHalfLength)) {
1363 intersection.chi2 = -1.0;
1364 }
1365 } else {
1366 bool isDead = true;
1367 int result, strip1, strip2;
1368 result = m_eklmTransformData->getStripsByIntersection(
1369 intersection.position, &strip1, &strip2);
1370 if (result == 0) {
1371 uint16_t channel1, channel2;
1372 channel1 = m_klmElementNumbers->channelNumberEKLM(strip1);
1373 channel2 = m_klmElementNumbers->channelNumberEKLM(strip2);
1374 enum KLMChannelStatus::ChannelStatus status1, status2;
1375 status1 = m_klmChannelStatus->getChannelStatus(channel1);
1376 status2 = m_klmChannelStatus->getChannelStatus(channel2);
1377 if (status1 == KLMChannelStatus::c_Unknown ||
1378 status2 == KLMChannelStatus::c_Unknown)
1379 B2ERROR("Incomplete KLM channel status data.");
1380 isDead = (status1 == KLMChannelStatus::c_Dead ||
1381 status2 == KLMChannelStatus::c_Dead);
1382 }
1383 if (!isDead) {
1384 extState.extLayerPattern |= (0x00008000 << intersection.layer); // valid extrapolation-crossing of the layer but no matching hit
1385 float layerEndcapEfficiency = 1.;
1386 for (int plane = 1; plane <= EKLMElementNumbers::getMaximalPlaneNumber(); plane++) {
1387 layerEndcapEfficiency *= m_klmStripEfficiency->getEndcapEfficiency(
1389 intersection.sector + 1, intersection.layer + 1, plane, 1);
1390 }
1391 klmMuidLikelihood->setExtEKLMEfficiencyValue(intersection.layer, layerEndcapEfficiency);
1392 } else {
1393 klmMuidLikelihood->setExtEKLMEfficiencyValue(intersection.layer, 0);
1394 }
1395 if (extState.lastEndcapExtLayer < intersection.layer) {
1396 extState.lastEndcapExtLayer = intersection.layer;
1397 }
1398 }
1399 }
1400 }
1401
1402 // Create a new MuidHit and RelationEntry between it and the track.
1403 // Adjust geant4e's position, momentum and covariance based on matching hit and tell caller to update the geant4e state.
1404 if (intersection.chi2 >= 0.0) {
1405 ROOT::Math::XYZVector tpos(intersection.position.x(),
1406 intersection.position.y(),
1407 intersection.position.z());
1408 ROOT::Math::XYZVector tposAtHitPlane(intersection.positionAtHitPlane.x(),
1409 intersection.positionAtHitPlane.y(),
1410 intersection.positionAtHitPlane.z());
1411 KLMMuidHit* klmMuidHit = m_klmMuidHits.appendNew(extState.pdgCode,
1412 intersection.inBarrel, intersection.isForward,
1413 intersection.sector, intersection.layer,
1414 tpos, tposAtHitPlane,
1415 extState.tof, intersection.time,
1416 intersection.chi2);
1417 if (extState.track != nullptr) { extState.track->addRelationTo(klmMuidHit); }
1418 G4Point3D newPos(intersection.position.x() * CLHEP::cm,
1419 intersection.position.y() * CLHEP::cm,
1420 intersection.position.z() * CLHEP::cm);
1421 g4eState.SetPosition(newPos);
1422 G4Vector3D newMom(intersection.momentum.x() * CLHEP::GeV,
1423 intersection.momentum.y() * CLHEP::GeV,
1424 intersection.momentum.z() * CLHEP::GeV);
1425 g4eState.SetMomentum(newMom);
1426 G4ErrorTrajErr covG4e;
1427 fromPhasespaceToG4e(intersection.momentum, intersection.covariance, covG4e);
1428 g4eState.SetError(covG4e);
1429 extState.chi2 += intersection.chi2;
1430 extState.nPoint += 2; // two (orthogonal) independent hits per detector layer
1431 return true;
1432 }
1433
1434 // Tell caller that the geant4e state was not modified.
1435 return false;
1436
1437}
1438
1439bool TrackExtrapolateG4e::findBarrelIntersection(ExtState& extState, const G4ThreeVector& oldPosition, Intersection& intersection)
1440{
1441 // Be generous: allow outward-moving intersection to be in the dead space between
1442 // largest sensitive-volume Z and m_BarrelHalfLength, not necessarily in a geant4 sensitive volume
1443 if (std::fabs(intersection.position.z() - m_OffsetZ) > m_BarrelHalfLength)
1444 return false;
1445 double phi = intersection.position.phi();
1446 if (phi < 0.0)
1447 phi += TWOPI;
1448 if (phi > TWOPI - PI_8)
1449 phi -= TWOPI;
1450 int sector = (int)((phi + PI_8) / M_PI_4);
1451 int section = intersection.position.z() > m_OffsetZ ?
1454 double oldR = oldPosition * m_BarrelSectorPerp[sector];
1455 double newR = intersection.position * m_BarrelSectorPerp[sector];
1456 for (int layer = extState.firstBarrelLayer; layer <= m_OutermostActiveBarrelLayer; ++layer) {
1457 if (newR < m_BarrelModuleMiddleRadius[section][sector][layer]) break;
1458 if (oldR <= m_BarrelModuleMiddleRadius[section][sector][layer]) {
1459 extState.firstBarrelLayer = layer + 1; // ratchet outward for next call's loop starting value
1460 if (extState.firstBarrelLayer > m_OutermostActiveBarrelLayer) extState.escaped = true;
1461 intersection.inBarrel = true;
1462 intersection.isForward = intersection.position.z() > m_OffsetZ;
1463 intersection.layer = layer;
1464 intersection.sector = sector;
1465 return true;
1466 }
1467 }
1468 return false;
1469}
1470
1471bool TrackExtrapolateG4e::findEndcapIntersection(ExtState& extState, const G4ThreeVector& oldPosition, Intersection& intersection)
1472{
1473 // Be generous: allow intersection to be in the dead space between m_EndcapMinR and innermost
1474 // sensitive-volume radius or between outermost sensitive-volume radius and m_EndcapMaxR,
1475 // not necessarily in a geant4 sensitive volume
1476 if (oldPosition.perp() > m_EndcapMaxR)
1477 return false;
1478 if (intersection.position.perp() < m_EndcapMinR)
1479 return false;
1480 double oldZ = std::fabs(oldPosition.z() - m_OffsetZ);
1481 double newZ = std::fabs(intersection.position.z() - m_OffsetZ);
1482 bool isForward = intersection.position.z() > m_OffsetZ;
1483 int outermostLayer = isForward ? m_OutermostActiveForwardEndcapLayer
1485 for (int layer = extState.firstEndcapLayer; layer <= outermostLayer; ++layer) {
1486 if (newZ < m_EndcapModuleMiddleZ[layer])
1487 break;
1488 if (oldZ <= m_EndcapModuleMiddleZ[layer]) {
1489 extState.firstEndcapLayer = layer + 1; // ratchet outward for next call's loop starting value
1490 if (extState.firstEndcapLayer > outermostLayer)
1491 extState.escaped = true;
1492 intersection.inBarrel = false;
1493 intersection.isForward = isForward;
1494 intersection.layer = layer;
1495 intersection.sector = m_eklmTransformData->getSectorByPosition(
1496 isForward ? 2 : 1, intersection.position) - 1;
1497 return true;
1498 }
1499 }
1500 return false;
1501}
1502
1504
1505{
1506 G4ThreeVector extPos0(intersection.position);
1507 double diffBestMagSq = 1.0E60;
1508 int bestHit = -1;
1509 int matchingLayer = intersection.layer + 1;
1510 G4ThreeVector n(m_BarrelSectorPerp[intersection.sector]);
1511 for (int h = 0; h < m_klmHit2ds.getEntries(); ++h) {
1512 KLMHit2d* hit = m_klmHit2ds[h];
1513 if (hit->getSubdetector() != KLMElementNumbers::c_BKLM)
1514 continue;
1515 if (hit->getLayer() != matchingLayer)
1516 continue;
1517 if (hit->isOutOfTime())
1518 continue;
1519 if (std::fabs(hit->getTime() - m_MeanDt) > m_MaxDt)
1520 continue;
1521 G4ThreeVector diff(hit->getPositionX() - intersection.position.x(),
1522 hit->getPositionY() - intersection.position.y(),
1523 hit->getPositionZ() - intersection.position.z());
1524 double dn = diff * n; // in cm
1525 if (std::fabs(dn) < 2.0) {
1526 // Hit and extrapolated point are in the same sector
1527 diff -= n * dn;
1528 if (diff.mag2() < diffBestMagSq) {
1529 diffBestMagSq = diff.mag2();
1530 bestHit = h;
1531 extPos0 = intersection.position;
1532 }
1533 } else {
1534 // Accept a nearby hit in adjacent sector
1535 if (std::fabs(dn) > 50.0)
1536 continue;
1537 int sector = hit->getSector() - 1;
1538 int dSector = abs(intersection.sector - sector);
1539 if ((dSector != +1) && (dSector != m_BarrelNSector - 1))
1540 continue;
1541 // Use the normal vector of the adjacent (hit's) sector
1542 G4ThreeVector nHit(m_BarrelSectorPerp[sector]);
1543 int section = intersection.isForward ?
1546 double dn2 = intersection.position * nHit - m_BarrelModuleMiddleRadius[section][sector][intersection.layer];
1547 dn = diff * nHit + dn2;
1548 if (std::fabs(dn) > 1.0)
1549 continue;
1550 // Project extrapolated track to the hit's plane in the adjacent sector
1551 G4ThreeVector extDir(intersection.momentum.unit());
1552 double extDirA = extDir * nHit;
1553 if (std::fabs(extDirA) < 1.0E-6)
1554 continue;
1555 G4ThreeVector projection = extDir * (dn2 / extDirA);
1556 if (projection.mag() > 15.0)
1557 continue;
1558 diff += projection - nHit * dn;
1559 if (diff.mag2() < diffBestMagSq) {
1560 diffBestMagSq = diff.mag2();
1561 bestHit = h;
1562 extPos0 = intersection.position - projection;
1563 }
1564 }
1565 }
1566
1567 if (bestHit >= 0) {
1568 KLMHit2d* hit = m_klmHit2ds[bestHit];
1569 intersection.isForward = (hit->getSection() == 1);
1570 intersection.sector = hit->getSector() - 1;
1571 intersection.time = hit->getTime();
1572 double localVariance[2] = {m_BarrelScintVariance, m_BarrelScintVariance};
1573 if (hit->inRPC()) {
1574 int nStrips = hit->getPhiStripMax() - hit->getPhiStripMin() + 1;
1575 double dn = nStrips - 1.5;
1576 double factor = std::pow((0.9 + 0.4 * dn * dn), 1.5) * 0.60; // measured-in-Belle resolution
1577 localVariance[0] = m_BarrelPhiStripVariance[intersection.layer] * factor;
1578 nStrips = hit->getZStripMax() - hit->getZStripMin() + 1;
1579 dn = nStrips - 1.5;
1580 factor = std::pow((0.9 + 0.4 * dn * dn), 1.5) * 0.55; // measured-in-Belle resolution
1581 localVariance[1] = m_BarrelZStripVariance[intersection.layer] * factor;
1582 } else {
1583 int nStrips = hit->getPhiStripMax() - hit->getPhiStripMin() + 1;
1584 localVariance[0] *= (nStrips * nStrips); // variance inflated for multi-strip hit
1585 nStrips = hit->getZStripMax() - hit->getZStripMin() + 1;
1586 localVariance[1] *= (nStrips * nStrips); // variance inflated for multi-strip hit
1587 }
1588 G4ThreeVector hitPos(hit->getPositionX(), hit->getPositionY(),
1589 hit->getPositionZ());
1590 adjustIntersection(intersection, localVariance, hitPos, extPos0);
1591 if (intersection.chi2 >= 0.0) {
1592 intersection.hit = bestHit;
1593 hit->isOnTrack(true);
1594 if (track != nullptr) {
1595 track->addRelationTo(hit, intersection.chi2);
1596 RecoTrack* recoTrack = track->getRelatedTo<RecoTrack>();
1598 recoTrack->addBKLMHit(hit, recoTrack->getNumberOfTotalHits() + 1);
1599 }
1600 }
1601 }
1602 }
1603 return intersection.chi2 >= 0.0;
1604
1605}
1606
1608{
1609 double diffBestMagSq = 1.0E60;
1610 int bestHit = -1;
1611 int matchingLayer = intersection.layer + 1;
1612 int matchingEndcap = (intersection.isForward ? 2 : 1);
1613 G4ThreeVector n(0.0, 0.0, (intersection.isForward ? 1.0 : -1.0));
1614 for (int h = 0; h < m_klmHit2ds.getEntries(); ++h) {
1615 KLMHit2d* hit = m_klmHit2ds[h];
1616 if (hit->getSubdetector() != KLMElementNumbers::c_EKLM)
1617 continue;
1618 if (hit->getLayer() != matchingLayer)
1619 continue;
1620 if (hit->getSection() != matchingEndcap)
1621 continue;
1622 // DIVOT no such function for EKLM!
1623 // if (hit->isOutOfTime()) continue;
1624 if (std::fabs(hit->getTime() - m_MeanDt) > m_MaxDt)
1625 continue;
1626 G4ThreeVector diff(hit->getPositionX() - intersection.position.x(),
1627 hit->getPositionY() - intersection.position.y(),
1628 hit->getPositionZ() - intersection.position.z());
1629 double dn = diff * n; // in cm
1630 if (std::fabs(dn) > 2.0)
1631 continue;
1632 diff -= n * dn;
1633 if (diff.mag2() < diffBestMagSq) {
1634 diffBestMagSq = diff.mag2();
1635 bestHit = h;
1636 }
1637 }
1638
1639 if (bestHit >= 0) {
1640 KLMHit2d* hit = m_klmHit2ds[bestHit];
1641 intersection.hit = bestHit;
1642 intersection.isForward = (hit->getSection() == 2);
1643 intersection.sector = hit->getSector() - 1;
1644 intersection.time = hit->getTime();
1645 double localVariance[2] = {m_EndcapScintVariance, m_EndcapScintVariance};
1646 int nStrips = hit->getXStripMax() - hit->getXStripMin() + 1;
1647 localVariance[0] *= (nStrips * nStrips); // variance inflated for multi-strip hit
1648 nStrips = hit->getYStripMax() - hit->getYStripMin() + 1;
1649 localVariance[1] *= (nStrips * nStrips); // variance inflated for multi-strip hit
1650 G4ThreeVector hitPos(hit->getPositionX(), hit->getPositionY(), hit->getPositionZ());
1651 adjustIntersection(intersection, localVariance, hitPos, intersection.position);
1652 if (intersection.chi2 >= 0.0) {
1653 // DIVOT no such function for EKLM!
1654 // hit->isOnTrack(true);
1655 if (track != nullptr) {
1656 track->addRelationTo(hit, intersection.chi2);
1657 RecoTrack* recoTrack = track->getRelatedTo<RecoTrack>();
1659 for (const EKLMAlignmentHit& alignmentHit : hit->getRelationsFrom<EKLMAlignmentHit>()) {
1660 recoTrack->addEKLMHit(&alignmentHit, recoTrack->getNumberOfTotalHits() + 1);
1661 }
1662 }
1663 }
1664 }
1665 }
1666 return intersection.chi2 >= 0.0;
1667
1668}
1669
1670void TrackExtrapolateG4e::adjustIntersection(Intersection& intersection, const double localVariance[2],
1671 const G4ThreeVector& hitPos, const G4ThreeVector& extPos0)
1672{
1673 // Use the gain matrix formalism to get the corrected track parameters.
1674 // R. Fruhwirth, Application of Kalman Filtering, NIM A262 (1987) 444-450
1675 // Equations (7)
1676 // x_k^{k-1} = extPar[] 6 elements before filtering
1677 // C_k^{k-1} = extCov[] 6x6 symmetric before filtering
1678 // r_k^{k-1} = residual[] 2 elements before filtering
1679 // h_k = 2x6 projects cartesian coordinates to measurement-plane coordinates
1680 // H_k = @h_k/@x = jacobian[] 2x6 Jacobian of projection to measurement plane
1681 // R_k^{k-1} = correction[] 2x2 before Invert()
1682 // G_k = R^(-1) = correction[] 2x2 after Invert()
1683 // K_k = gain[] 6x2 Kalman gain matrix
1684 // x_k = extPar[] 6 elements after filtering
1685 // C_k = extCov[] 6x6 symmetric after filtering
1686 // r_k = residual[] 2 elements after filtering
1687 // Use the relation K*H*C = (C*H^T*R^-1)*H*C = C*(H^T*R^-1*H)*C^T
1688
1689 // In most cases, extPos0 is the same as intersection.position. They differ only when
1690 // the nearest BKLM hit is in the sector adjacent to that of intersection.position.
1691 G4ThreeVector extPos(extPos0);
1692 G4ThreeVector extMom(intersection.momentum);
1693 G4ThreeVector extDir(extMom.unit());
1694 G4ThreeVector diffPos(hitPos - extPos);
1695 G4ErrorSymMatrix extCov(intersection.covariance);
1696 // Track parameters (x,y,z,px,py,pz) before correction
1697 G4ErrorMatrix extPar(6, 1); // initialized to all zeroes
1698 extPar[0][0] = extPos.x();
1699 extPar[1][0] = extPos.y();
1700 extPar[2][0] = extPos.z();
1701 extPar[3][0] = extMom.x();
1702 extPar[4][0] = extMom.y();
1703 extPar[5][0] = extMom.z();
1704 G4ThreeVector nA; // unit vector normal to the readout plane
1705 G4ThreeVector nB; // unit vector along phi- or x-readout direction (for barrel or endcap)
1706 G4ThreeVector nC; // unit vector along z- or y-readout direction (for barrel or endcap)
1707 if (intersection.inBarrel) {
1708 nA = m_BarrelSectorPerp[intersection.sector];
1709 nB = m_BarrelSectorPhi[intersection.sector];
1710 nC = G4ThreeVector(0.0, 0.0, 1.0);
1711 } else {
1712 double out = (intersection.isForward ? 1.0 : -1.0);
1713 nA = G4ThreeVector(0.0, 0.0, out);
1714 nB = G4ThreeVector(out, 0.0, 0.0);
1715 nC = G4ThreeVector(0.0, out, 0.0);
1716 }
1717 // Don't adjust the extrapolation if the track is nearly tangent to the readout plane.
1718 double extDirA = extDir * nA;
1719 if (std::fabs(extDirA) < 1.0E-6)
1720 return;
1721 double extDirBA = extDir * nB / extDirA;
1722 double extDirCA = extDir * nC / extDirA;
1723 // Move the extrapolated coordinate (at most a tiny amount!) to the plane of the hit.
1724 // If the moved point is outside the KLM, don't do Kalman filtering.
1725 G4ThreeVector move = extDir * ((diffPos * nA) / extDirA);
1726 extPos += move;
1727 diffPos -= move;
1728 intersection.positionAtHitPlane = extPos;
1729 // Projection jacobian onto the nB-nC measurement plane
1730 G4ErrorMatrix jacobian(2, 6); // initialized to all zeroes
1731 jacobian[0][0] = nB.x() - nA.x() * extDirBA;
1732 jacobian[0][1] = nB.y() - nA.y() * extDirBA;
1733 jacobian[0][2] = nB.z() - nA.z() * extDirBA;
1734 jacobian[1][0] = nC.x() - nA.x() * extDirCA;
1735 jacobian[1][1] = nC.y() - nA.y() * extDirCA;
1736 jacobian[1][2] = nC.z() - nA.z() * extDirCA;
1737 // Residuals of EXT track and KLM hit on the nB-nC measurement plane
1738 G4ErrorMatrix residual(2, 1); // initialized to all zeroes
1739 residual[0][0] = diffPos.x() * jacobian[0][0] + diffPos.y() * jacobian[0][1] + diffPos.z() * jacobian[0][2];
1740 residual[1][0] = diffPos.x() * jacobian[1][0] + diffPos.y() * jacobian[1][1] + diffPos.z() * jacobian[1][2];
1741 // Measurement errors in the detector plane
1742 G4ErrorSymMatrix hitCov(2, 0); // initialized to all zeroes
1743 hitCov[0][0] = localVariance[0];
1744 hitCov[1][1] = localVariance[1];
1745 // No magnetic field: increase the hit uncertainty
1746 if (m_MagneticField == 0.0) {
1747 hitCov[0][0] *= 10.0;
1748 hitCov[1][1] *= 10.0;
1749 }
1750 // Now get the correction matrix: combined covariance of EXT and KLM hit.
1751 // 1st dimension = nB, 2nd dimension = nC.
1752 G4ErrorSymMatrix correction(extCov.similarity(jacobian) + hitCov);
1753 // Ignore the best hit if it is too far from the extrapolated-track intersection in the hit's plane
1754 if (residual[0][0] * residual[0][0] > correction[0][0] * m_MaxDistSqInVariances)
1755 return;
1756 if (residual[1][0] * residual[1][0] > correction[1][1] * m_MaxDistSqInVariances)
1757 return;
1758 int fail = 0;
1759 correction.invert(fail);
1760 if (fail != 0)
1761 return;
1762 // Matrix inversion succeeded and is reasonable.
1763 // Evaluate chi-squared increment assuming that the Kalman filter
1764 // won't be able to adjust the extrapolated track's position (fall-back).
1765 intersection.chi2 = (correction.similarityT(residual))[0][0];
1766 // Do the Kalman filtering
1767 G4ErrorMatrix gain((extCov * jacobian.T()) * correction);
1768 G4ErrorSymMatrix HRH(correction.similarityT(jacobian));
1769 extCov -= HRH.similarity(extCov);
1770 extPar += gain * residual;
1771 extPos.set(extPar[0][0], extPar[1][0], extPar[2][0]);
1772 extMom.set(extPar[3][0], extPar[4][0], extPar[5][0]);
1773 // Calculate the chi-squared increment using the Kalman-filtered state
1774 correction = hitCov - extCov.similarity(jacobian);
1775 correction.invert(fail);
1776 if (fail != 0)
1777 return;
1778 diffPos = hitPos - extPos;
1779 residual[0][0] = diffPos.x() * jacobian[0][0] + diffPos.y() * jacobian[0][1] + diffPos.z() * jacobian[0][2];
1780 residual[1][0] = diffPos.x() * jacobian[1][0] + diffPos.y() * jacobian[1][1] + diffPos.z() * jacobian[1][2];
1781 intersection.chi2 = (correction.similarityT(residual))[0][0];
1782 // Update the position, momentum and covariance of the point
1783 // Project the corrected extrapolation to the plane of the original
1784 // extrapolation's intersection.position. (Note: intersection.position is the same as
1785 // extPos0 in all cases except when nearest BKLM hit is in adjacent
1786 // sector, for which extPos0 is a projected position to the hit's plane.)
1787 // Also, leave the momentum magnitude unchanged.
1788 intersection.position = extPos + extDir * (((intersection.position - extPos) * nA) / extDirA);
1789 intersection.momentum = intersection.momentum.mag() * extMom.unit();
1790 intersection.covariance = extCov;
1791}
1792
1793void TrackExtrapolateG4e::finishTrack(const ExtState& extState, KLMMuidLikelihood* klmMuidLikelihood, bool isForward)
1794{
1795 /* Done with this track: compute KLM likelihoods and fill the relative dataobject. */
1796 int lastExtLayer = extState.lastBarrelExtLayer + extState.lastEndcapExtLayer + 1;
1798 isForward, extState.escaped, extState.lastBarrelExtLayer, extState.lastEndcapExtLayer);
1799 klmMuidLikelihood->setOutcome(outcome);
1800 klmMuidLikelihood->setIsForward(isForward);
1801 klmMuidLikelihood->setBarrelExtLayer(extState.lastBarrelExtLayer);
1802 klmMuidLikelihood->setEndcapExtLayer(extState.lastEndcapExtLayer);
1803 klmMuidLikelihood->setBarrelHitLayer(extState.lastBarrelHitLayer);
1804 klmMuidLikelihood->setEndcapHitLayer(extState.lastEndcapHitLayer);
1805 klmMuidLikelihood->setExtLayer(lastExtLayer);
1806 klmMuidLikelihood->setHitLayer(((extState.lastEndcapHitLayer == -1) ?
1807 extState.lastBarrelHitLayer :
1808 extState.lastBarrelExtLayer + extState.lastEndcapHitLayer + 1));
1809 klmMuidLikelihood->setChiSquared(extState.chi2);
1810 klmMuidLikelihood->setDegreesOfFreedom(extState.nPoint);
1811 klmMuidLikelihood->setExtLayerPattern(extState.extLayerPattern);
1812 klmMuidLikelihood->setHitLayerPattern(extState.hitLayerPattern);
1813 /* Do KLM likelihood calculation. */
1814 if (outcome != MuidElementNumbers::c_NotReached) { /* Extrapolation reached KLM sensitive volume. */
1815 double denom = 0.0;
1816 int charge = klmMuidLikelihood->getCharge();
1817 std::vector<int> signedPdgVector = MuidElementNumbers::getPDGVector(charge);
1818 std::map<int, double> mapPdgPDF;
1819 for (int pdg : signedPdgVector) {
1820 auto search = m_MuidBuilderMap.find(pdg);
1821 if (search == m_MuidBuilderMap.end())
1822 B2FATAL("Something went wrong: PDF for PDG code " << pdg << " not found!");
1823 double pdf = (search->second)->getPDF(klmMuidLikelihood);
1824 denom += pdf;
1825 mapPdgPDF.insert(std::pair<int, double>(std::abs(pdg), pdf));
1826 }
1827 if (denom < 1.0E-20)
1828 klmMuidLikelihood->setJunkPDFValue(true); /* Anomaly: should be very rare. */
1829 else {
1830 for (auto const& [pdg, pdf] : mapPdgPDF) {
1831 klmMuidLikelihood->setPDFValue(pdf, std::abs(pdg));
1832 if (pdf > 0.0)
1833 klmMuidLikelihood->setLogL(std::log(pdf), std::abs(pdg));
1834 }
1835 }
1836 }
1837}
static KLMChannelNumber channelNumber(int section, int sector, int layer, int plane, int strip)
Get channel number.
static constexpr int getMaximalLayerNumber()
Get maximal layer number (1-based).
static constexpr int getMaximalSectorNumber()
Get maximal sector number (1-based).
static KLMModuleNumber moduleNumber(int section, int sector, int layer, bool fatalError=true)
Get module number.
static constexpr int getMaximalPlaneNumber()
Get maximal plane number (0-based).
static void setMaximalStrip(int &module, int strip)
Set maximal strip number.
Definition BKLMStatus.h:49
static const ChargedStable muon
muon particle
Definition Const.h:660
EDetector
Enum for identifying the detector components (detector and subdetector).
Definition Const.h:42
static const double doubleNaN
quiet_NaN
Definition Const.h:703
static const ChargedStable electron
electron particle
Definition Const.h:659
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
int ECLVolumeToCellID(const G4VTouchable *)
Get Cell Id (LEP: new way)
This dataobject is used only for EKLM alignment.
static const EKLMElementNumbers & Instance()
Instantiation.
int stripNumber(int section, int layer, int sector, int plane, int strip) const
Get strip number.
static constexpr int getMaximalLayerNumber()
Get maximal layer number.
static constexpr int getMaximalPlaneNumber()
Get maximal plane number.
double getOuterR() const
Get outer radius.
double getInnerR() const
Get inner radius.
double getZ() const
Get Z coordinate.
double getLength() const
Get length.
double getWidth() const
Get width.
const ElementPosition * getLayerPosition() const
Get position data for layers.
double getLayerShiftZ() const
Get Z distance between two layers.
const StripGeometry * getStripGeometry() const
Get strip geometry data.
const ElementPosition * getSectionPosition() const
Get position data for sections.
EKLM geometry data.
static const GeometryData & Instance(enum DataSource dataSource=c_Database, const GearDir *gearDir=nullptr)
Instantiation.
static const TransformDataGlobalAligned & Instance()
Instantiation.
Store one Ext hit as a ROOT object.
Definition ExtHit.h:31
KLM channel index.
@ c_Dead
Dead channel (no signal).
@ c_Unknown
Unknown status (no data).
static const KLMElementNumbers & Instance()
Instantiation.
KLM 2d hit.
Definition KLMHit2d.h:33
Store one muon-identification hit in the KLM as a ROOT object.
Definition KLMMuidHit.h:24
Class to store the likelihoods from KLM with additional information related to the extrapolation.
void setExtLayer(int layer)
Set the outermost EKLM layer crossed in the extrapolation.
void setExtLayerPattern(unsigned int pattern)
Set the pattern of the layers crossed in the extrapolation.
void setLogL(double logL, int pdg)
Set the log-likelihood.
void setExtEKLMEfficiencyValue(int layer, float efficiency)
Set the efficiency of a given EKLM layer.
void setExtBKLMEfficiencyValue(int layer, float efficiency)
Set the efficiency of a given BKLM layer.
void setChiSquared(double chiSquared)
Set the chi-squared of the extrapolation.
void setHitLayerPattern(unsigned int pattern)
Set the pattern of the layers actually crossed by the track.
void setBarrelExtLayer(int layer)
Set the outermost BKLM layer crossed in the extrapolation.
void setIsForward(bool isForward)
Set if this extrapolation is in forward or backward B/EKLM.
void setJunkPDFValue(bool flag)
Set the junk flag (1 if junk, 0 if not).
void setPDGCode(int pdg)
Set the PDG code of the particle hypothesis used during the extrapolation.
void setDegreesOfFreedom(int dof)
Set the number of degrees of freedom (= 2 times the number of KLM hits) for the chi-square computatio...
int getCharge() const
Get the charge of the particle hypothesis used during the extrapolation.
void setPDFValue(double pdfValue, int pdg)
Set the normalized PDF.
void setEndcapExtLayer(int layer)
Set the outermost EKLM layer crossed in the extrapolation.
void setBarrelHitLayer(int layer)
Set the outermost BKLM layer actually crossed by the track.
void setOutcome(unsigned int outcome)
Set the outcome of this extrapolation.
void setHitLayer(int layer)
Set the outermost KLM layer actually crossed by the track.
void setEndcapHitLayer(int layer)
Set the outermost EKLM layer actually crossed by the track.
Build the Muid likelihoods starting from the hit pattern and the transverse scattering in the KLM.
Definition MuidBuilder.h:29
static unsigned int calculateExtrapolationOutcome(bool isForward, bool escaped, int lastBarrelLayer, int lastEndcapLayer)
Calculate the track extrapolation outcome.
static std::vector< int > getPDGVector()
Get a vector with all the hypothesis PDG codes used for Muid.
This is the Reconstruction Event-Data Model Track.
Definition RecoTrack.h:79
bool addBKLMHit(const UsedBKLMHit *bklmHit, const unsigned int sortingParameter, OriginTrackFinder foundByTrackFinder=OriginTrackFinder::c_undefinedTrackFinder)
Adds a bklm hit with the given information to the reco track.
Definition RecoTrack.h:286
bool wasFitSuccessful(const genfit::AbsTrackRep *representation=nullptr) const
Returns true if the last fit with the given representation was successful.
Definition RecoTrack.cc:336
bool addEKLMHit(const UsedEKLMHit *eklmHit, const unsigned int sortingParameter, OriginTrackFinder foundByTrackFinder=OriginTrackFinder::c_undefinedTrackFinder)
Adds an eklm hit with the given information to the reco track.
Definition RecoTrack.h:300
genfit::AbsTrackRep * getCardinalRepresentation() const
Get a pointer to the cardinal track representation. You are not allowed to modify or delete it!
Definition RecoTrack.h:631
const genfit::MeasuredStateOnPlane & getMeasuredStateOnPlaneFromLastHit(const genfit::AbsTrackRep *representation=nullptr) const
Return genfit's MeasuredStateOnPlane for the last hit in a fit useful for extrapolation of measuremen...
Definition RecoTrack.cc:619
static void registerRequiredRelations(StoreArray< RecoTrack > &recoTracks, std::string const &pxdHitsStoreArrayName="", std::string const &svdHitsStoreArrayName="", std::string const &cdcHitsStoreArrayName="", std::string const &bklmHitsStoreArrayName="", std::string const &eklmHitsStoreArrayName="", std::string const &recoHitInformationStoreArrayName="")
Convenience method which registers all relations required to fully use a RecoTrack.
Definition RecoTrack.cc:53
const genfit::MeasuredStateOnPlane & getMeasuredStateOnPlaneFromFirstHit(const genfit::AbsTrackRep *representation=nullptr) const
Return genfit's MeasuredStateOnPlane for the first hit in a fit useful for extrapolation of measureme...
Definition RecoTrack.cc:605
unsigned int getNumberOfTotalHits() const
Return the number of cdc + svd + pxd + bklm + eklm hits.
Definition RecoTrack.h:436
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
TO * getRelatedTo(const std::string &name="", const std::string &namedRelation="") const
Get the object to which this object has a relation.
Defines a closed cylinder for the geant4e "target", the surface that encloses the volume within which...
It is the main interface for the user to define the setup and start the propagation.
Definition ExtManager.h:50
static ExtManager * GetManager()
Get pointer to the instance of this singleton class (create if needed)
Definition ExtManager.cc:72
void Initialize(const char[], const std::string &, double, double, bool, int, const std::vector< std::string > &)
Initialize Geant4 and Geant4e.
Store one Track-KLMCluster separation as a ROOT object.
geant4e-based track extrapolation.
double m_BarrelScintVariance
BKLM scintillator strip position variance (cm^2)
double m_EndcapHalfLength
half-length (cm) of either endcap
double m_MaxKLMTrackClusterDistance
user-defined maximum distance (mm) between KLMCluster and associated track (for KLID)
Simulation::ExtCylSurfaceTarget * m_TargetMuid
virtual "target" cylinder for MUID (boundary beyond which extrapolation ends)
ExtState getStartPoint(const Track &, int, G4ErrorFreeTrajState &)
Get the start point for a new reconstructed track with specific PDG hypothesis.
bool createMuidHit(ExtState &, G4ErrorFreeTrajState &, KLMMuidLikelihood *, std::vector< std::map< const Track *, double > > *)
Create another MUID extrapolation hit for a track candidate.
int m_OutermostActiveForwardEndcapLayer
outermost forward-endcap layer that is active for muon identification (user-defined)
static TrackExtrapolateG4e * getInstance()
Get the singleton's address.
void beginRun(bool flag)
Perform beginning-of-run actions.
void initialize(double minPt, double minKE, std::vector< Const::ChargedStable > &hypotheses)
Initialize for track extrapolation by the EXT module.
double m_BarrelMaxR
maximum radius (cm) of the barrel
G4ThreeVector m_BarrelSectorPhi[BKLMElementNumbers::getMaximalSectorNumber()+1]
azimuthal unit vector of each barrel sector
StoreArray< KLMMuidLikelihood > m_klmMuidLikelihoods
KLM muid likelihoods.
int m_OutermostActiveBackwardEndcapLayer
outermost backward-endcap layer that is active for muon identification (user-defined)
static TrackExtrapolateG4e * m_Singleton
Stores pointer to the singleton class.
std::vector< Const::ChargedStable > * m_DefaultHypotheses
Default ChargedStable hypotheses (needed as call argument but not used)
const std::vector< Const::ChargedStable > * m_HypothesesExt
ChargedStable hypotheses for EXT.
double m_MagneticField
Magnetic field z component (gauss) at origin.
double m_BarrelHalfLength
half-length (cm) of the barrel
StoreArray< KLMCluster > m_klmClusters
KLM clusters.
double m_MaxDt
Coincidence window half-width for in-time KLM hits (ns)
double m_BarrelMinR
minimum radius (cm) of the barrel
bool findEndcapIntersection(ExtState &, const G4ThreeVector &, Intersection &)
Find the intersection point of the track with the crossed EKLM plane.
void endRun(bool flag)
Perform end-of-run actions.
void finishTrack(const ExtState &, KLMMuidLikelihood *, bool)
Complete muon identification after end of track extrapolation.
double m_BarrelModuleMiddleRadius[2][BKLMElementNumbers::getMaximalSectorNumber()+1][BKLMElementNumbers::getMaximalLayerNumber()+1]
hit-plane radius (cm) at closest distance to IP of each barrel end | sector | layer
double m_BarrelPhiStripVariance[BKLMElementNumbers::getMaximalLayerNumber()+1]
BKLM RPC phi-measuring strip position variance (cm^2) by layer.
double m_MinRadiusSq
Minimum squared radius (cm) outside of which extrapolation will continue.
StoreArray< KLMMuidHit > m_klmMuidHits
KLM muid hits.
void terminate(bool flag)
Terminates this singleton.
void createExtHit(ExtHitStatus, const ExtState &, const G4ErrorFreeTrajState &, const G4StepPoint *, const G4TouchableHandle &)
Create another EXT extrapolation hit for a track candidate.
bool findMatchingEndcapHit(Intersection &, const Track *)
Find the matching EKLM 2D hit nearest the intersection point of the track with the crossed EKLM plane...
double m_EndcapMiddleZ
midpoint along z (cm) of the forward endcap from the KLM midpoint
const EKLMElementNumbers * m_eklmElementNumbers
EKLM element numbers.
const std::vector< Const::ChargedStable > * m_HypothesesMuid
ChargedStable hypotheses for MUID.
void fromPhasespaceToG4e(const G4ThreeVector &, const G4ErrorSymMatrix &, G4ErrorTrajErr &)
Convert the phasespace covariance to geant4e covariance.
double m_OffsetZ
offset (cm) along z axis of KLM midpoint from IP
const KLMElementNumbers * m_klmElementNumbers
KLM element numbers.
std::vector< G4VPhysicalVolume * > * m_BKLMVolumes
Pointers to BKLM geant4 sensitive (physical) volumes.
int m_OutermostActiveBarrelLayer
outermost barrel layer that is active for muon identification (user-defined)
G4ThreeVector m_BarrelSectorPerp[BKLMElementNumbers::getMaximalSectorNumber()+1]
normal unit vector of each barrel sector
bool findMatchingBarrelHit(Intersection &, const Track *)
Find the matching BKLM 2D hit nearest the intersection point of the track with the crossed BKLM plane...
double m_EndcapModuleMiddleZ[BKLMElementNumbers::getMaximalLayerNumber()+1]
hit-plane z (cm) of each IP layer relative to KLM midpoint
void getVolumeID(const G4TouchableHandle &, Const::EDetector &, int &)
Get the physical volume information for a geant4 physical volume.
DBObjPtr< KLMLikelihoodParameters > m_klmLikelihoodParameters
Conditions-database object for KLM likelihood parameters.
double m_MaxECLTrackClusterDistance
user-defined maximum distance (mm) between ECLCluster and associated track (for EID)
double m_EndcapScintVariance
EKLM scintillator strip position variance (cm^2)
StoreArray< TrackClusterSeparation > m_trackClusterSeparations
Track cluster separation.
StoreArray< Track > m_tracks
Tracks.
void event(bool flag)
Performs track extrapolation for all tracks in one event.
double m_MinPt
Minimum transverse momentum in MeV/c for extrapolation to be started.
StoreArray< ECLCluster > m_eclClusters
ECL clusters.
bool m_addHitsToRecoTrack
Parameter to add the found hits also to the reco tracks or not. Is turned off by default.
int m_BarrelNSector
Number of barrel sectors.
StoreArray< KLMHit2d > m_klmHit2ds
KLM 2d hits.
std::map< G4VPhysicalVolume *, enum VolTypes > * m_EnterExit
Pointers to geant4 physical volumes whose entry/exit points will be saved.
bool m_MuidInitialized
Flag to indicate that MUID initialize() has been called.
StoreArray< RecoTrack > m_recoTracks
Reco tracks.
double m_EndcapMinR
minimum radius (cm) of the endcaps
StoreArray< ExtHit > m_extHits
Ext hits.
std::map< int, MuidBuilder * > m_MuidBuilderMap
PDF for the charged final state particle hypotheses.
void extrapolate(int pdgCode, double tof, const G4ThreeVector &position, const G4ThreeVector &momentum, const G4ErrorSymMatrix &covariance)
Performs track extrapolation for a single track (specified in genfit2 units).
TrackExtrapolateG4e()
constructor is hidden; user calls TrackExtrapolateG4e::getInstance() instead
double m_MeanDt
Mean hit - trigger time (ns)
double m_EndcapMaxR
maximum radius (cm) of the endcaps
Simulation::ExtCylSurfaceTarget * m_TargetExt
virtual "target" cylinder for EXT (boundary beyond which extrapolation ends)
bool findBarrelIntersection(ExtState &, const G4ThreeVector &, Intersection &)
Find the intersection point of the track with the crossed BKLM plane.
void findClosestTrackToKLMClusters()
Find the closest Track to each KLMCluster and fill the corresponding fields of KLMCluster objects.
bool m_ExtInitialized
Flag to indicate that EXT initialize() has been called.
DBObjPtr< KLMChannelStatus > m_klmChannelStatus
Conditions-database object for KLM channel status.
double m_MinKE
Minimum kinetic energy in MeV for extrapolation to continue.
void registerVolumes()
Register the list of geant4 physical volumes whose entry/exit points will be saved during extrapolati...
void fromG4eToPhasespace(const G4ErrorFreeTrajState &, G4ErrorSymMatrix &)
Convert the geant4e 5x5 covariance to phasespace 6x6 covariance.
Simulation::ExtManager * m_ExtMgr
Pointer to the ExtManager singleton.
DBObjPtr< KLMStripEfficiency > m_klmStripEfficiency
Conditions-database object for KLM strip efficiency.
DBObjPtr< COILGeometryPar > m_COILGeometryPar
Conditions-database object for COIL geometry.
double m_MaxDistSqInVariances
user-defined maximum squared-distance (in number of variances) for matching hit to extrapolation
double m_BarrelZStripVariance[BKLMElementNumbers::getMaximalLayerNumber()+1]
BKLM RPC z-measuring strip position variance (cm^2) by layer.
void swim(ExtState &, G4ErrorFreeTrajState &, const std::vector< std::pair< ECLCluster *, G4ThreeVector > > *, const std::vector< std::pair< KLMCluster *, G4ThreeVector > > *, std::vector< std::map< const Track *, double > > *)
Swim a single track (MUID) until it stops or leaves the target cylinder.
void adjustIntersection(Intersection &, const double *, const G4ThreeVector &, const G4ThreeVector &)
Nudge the track using the matching hit.
const EKLM::TransformDataGlobalAligned * m_eklmTransformData
EKLM transformation data.
DBObjPtr< BeamPipeGeo > m_BeamPipeGeo
Conditions-database object for beam pipe geometry.
Class that bundles various TrackFitResults.
Definition Track.h:25
static const double T
[tesla]
Definition Unit.h:120
Provides BKLM geometry parameters for simulation, reconstruction etc (from Gearbox or DataBase)
Definition GeometryPar.h:37
const Module * findModule(int section, int sector, int layer) const
Get the pointer to the definition of a module.
double getScintHalfWidth(void) const
Get the height of the entire volume of a scintillator strip (including TiO2 coating)
double getGap1InnerRadius(void) const
Get the radius of the inner tangent circle of gap 0 (innermost)
double getOuterRadius(void) const
Get the radius of the inscribed circle of the outer polygon.
double getOffsetZ(void) const
Get the global shift along a of the entire BKLM.
int getNSector(void) const
Get the number of sectors of the BKLM.
double getHalfLength(void) const
Get the half-length along z of the BKLM.
double getActiveMiddleRadius(int section, int sector, int layer) const
Get the radial midpoint of the detector module's active volume of specified layer.
static GeometryPar * instance(void)
Static method to get a reference to the singleton GeometryPar instance.
Define the geometry of a BKLM module Each sector [octant] contains Modules.
Definition Module.h:76
Class to store variables with their name which were sent to the logging service.
static void getField(const double *pos, double *field)
return the magnetic field at a given position.
ExtHitStatus
Define state of extrapolation for each recorded hit.
Definition ExtHit.h:26
@ VOLTYPE_ARICH2
ARICH Img plate.
@ VOLTYPE_TOP2
TOP quartz.
@ VOLTYPE_TOP3
TOP glue.
@ VOLTYPE_ARICH3
ARICH HAPD window.
@ VOLTYPE_BKLM2
BKLM scintillator.
@ VOLTYPE_EKLM
EKLM.
@ VOLTYPE_BKLM1
BKLM RPC.
@ VOLTYPE_ARICH1
ARICH aerogel.
@ VOLTYPE_TOP1
TOP container.
Abstract base class for different kinds of events.
Data structure to define extrapolation state.
int lastEndcapExtLayer
MUID: outermost endcap layer crossed by the extrapolated track.
double chi2
MUID: accumulated chi-squared of all in-plane transverse deviations between extrapolation and matchin...
G4ThreeVector directionAtIP
MUID: initial direction of track, used for KLID.
int nPoint
MUID: accumulated number of points with matching 2D hits.
int lastEndcapHitLayer
MUID: outermost endcap layer with a matching hit.
bool isCosmic
True for back-propagation of a cosmic ray.
int firstEndcapLayer
MUID: outermost barrel layer encountered by the extrapolated track in the prior steps.
int extLayerPattern
MUID: accumulated bit pattern of layers crossed by the extrapolated track.
int pdgCode
Particle hypothesis that is being extrapolated.
double length
Length from start of extrapolation (rad lengths), updated during extrapolation.
bool escaped
MUID: flag to indicate that the extrapolated track escaped from the KLM.
int firstBarrelLayer
MUID: outermost barrel layer encountered by the extrapolated track in the prior steps.
int lastBarrelHitLayer
MUID: outermost barrel layer with a matching hit.
int hitLayerPattern
MUID: accumulated bit pattern of layers with matching hits.
int lastBarrelExtLayer
MUID: outermost barrel layer crossed by the extrapolated track.
double tof
Time of flight from IP (ns), updated during extrapolation.
const Track * track
Pointer to the reconstructed track.
intersection of muid-extrapolated track with a KLM layer
int sector
sector number (0..7 for barrel, 0..3 for endcap) of this point
G4ThreeVector momentum
extrapolated-track momentum (GeV/c) at this intersection
double chi2
chi-squared value of transverse deviation between extrapolated and measured hit positions
bool inBarrel
flag to indicate if this point is in the barrel (true) or endcap (false)
int hit
index in {B,E}KLMHit2ds of matching hit
G4ThreeVector positionAtHitPlane
extrapolated-track position (cm) projected to the 2D hit's midplane
G4ThreeVector position
extrapolated-track global position (cm) of this intersection
G4ErrorSymMatrix covariance
extrapolated-track phase-space covariance matrix at this intersection
bool isForward
flag to indicate if this point is in the forward (true) or backward (false) end
double time
time (ns) of matching BKLMHit2d
int layer
layer number (0..14 for barrel, 0..13 for endcap) of this point