CMS 3D CMS Logo

TrackingMaterialProducer.cc
Go to the documentation of this file.
1 #include <iostream> // FIXME: switch to MessagLogger & friends
2 #include <vector>
3 #include <string>
4 #include <cassert>
5 #include <exception>
6 #include <tuple>
7 
13 
18 
20 
21 // GEANT4
22 #include "G4Step.hh"
23 #include "G4Track.hh"
24 #include "G4VSolid.hh"
25 #include "G4LogicalVolumeStore.hh"
26 #include "G4TouchableHistory.hh"
27 #include "G4VPhysicalVolume.hh"
28 #include "G4AffineTransform.hh"
29 
31 
32 // Uncomment the following #define directive to have the full list of
33 // volumes known to G4 printed to LogInfo("TrackingMaterialProducer")
34 
35 #define DEBUG_G4_VOLUMES
36 
37 using namespace CLHEP;
38 using edm::LogInfo;
39 
40 // missing from GEANT4 < 9.0 : G4LogicalVolumeStore::GetVolume( name )
41 static const G4LogicalVolume* GetVolume(const std::string& name) {
42  const G4LogicalVolumeStore* lvs = G4LogicalVolumeStore::GetInstance();
43 
44 #ifdef DEBUG_G4_VOLUMES
45  for (G4LogicalVolumeStore::const_iterator volume = lvs->begin(); volume != lvs->end(); ++volume)
46  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: G4 registered volumes " << (*volume)->GetName()
47  << std::endl;
48 #endif
49 
50  for (G4LogicalVolumeStore::const_iterator volume = lvs->begin(); volume != lvs->end(); ++volume) {
51  if ((const std::string&)(*volume)->GetName() == name)
52  return (*volume);
53  }
54  return nullptr;
55 }
56 
57 // missing from GEANT4 : G4TouchableHistory::GetTransform( depth )
58 static inline const G4AffineTransform& GetTransform(const G4TouchableHistory* touchable, int depth) {
59  return touchable->GetHistory()->GetTransform(touchable->GetHistory()->GetDepth() - depth);
60 }
61 
62 // navigate up the hierarchy of volumes until one with an attached sensitive detector is found
63 // return a tuple holding
64 // - pointer to the first (deepest) sensitive G4VPhysicalVolume
65 // - how may steps up in the hierarchy it is (0 is the starting volume)
66 // if no sensitive detector is found, return a NULL pointer and 0
67 
68 std::tuple<const G4VPhysicalVolume*, int> GetSensitiveVolume(const G4VTouchable* touchable) {
69  int depth = touchable->GetHistoryDepth();
70  for (int level = 0; level < depth; ++level) { // 0 is self
71  const G4VPhysicalVolume* volume = touchable->GetVolume(level);
72  if (volume->GetLogicalVolume()->GetSensitiveDetector() != nullptr) {
73  return std::make_tuple(volume, level);
74  }
75  }
76  return std::tuple<const G4VPhysicalVolume*, int>(nullptr, 0);
77 }
78 
79 //-------------------------------------------------------------------------
81  edm::ParameterSet config = iPSet.getParameter<edm::ParameterSet>("TrackingMaterialProducer");
82  m_selectedNames = config.getParameter<std::vector<std::string> >("SelectedVolumes");
83  m_primaryTracks = config.getParameter<bool>("PrimaryTracksOnly");
84  m_txtOutFile = config.getUntrackedParameter<std::string>("txtOutFile");
85  m_hgcalzfront = config.getParameter<double>("hgcalzfront");
86  m_tracks = nullptr;
87  m_track_volume = nullptr;
88 
89  produces<std::vector<MaterialAccountingTrack> >();
90  output_file_ = new TFile("radLen_vs_eta_fromProducer.root", "RECREATE");
91  output_file_->cd();
92  radLen_vs_eta_ = new TProfile("radLen", "radLen", 250., -5., 5., 0, 10.);
93 
94  //Check if HGCal volumes are selected
95  isHGCal = false;
96  //if (std::find(m_selectedNames.begin(), m_selectedNames.end(), "CALOECTSRear") != m_selectedNames.end()) {
97  if (std::find(m_selectedNames.begin(), m_selectedNames.end(), "HGCal") != m_selectedNames.end()) {
98  isHGCal = true;
99  }
100  //Check if HFNose volumes are selected
101  isHFNose = false;
102  if (std::find(m_selectedNames.begin(), m_selectedNames.end(), "HFNose") != m_selectedNames.end()) {
103  isHFNose = true;
104  }
105  if (isHGCal or isHFNose) {
106  outVolumeZpositionTxt.open(m_txtOutFile.c_str(), std::ios::out);
107  }
108 }
109 
110 //-------------------------------------------------------------------------
112 
113 //-------------------------------------------------------------------------
115  radLen_vs_eta_->Write();
116  output_file_->Close();
117 }
118 //-------------------------------------------------------------------------
120  // INFO
121  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: List of the selected volumes: " << std::endl;
122  for (std::vector<std::string>::const_iterator volume_name = m_selectedNames.begin();
123  volume_name != m_selectedNames.end();
124  ++volume_name) {
125  const G4LogicalVolume* volume = GetVolume(*volume_name);
126  if (volume) {
127  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: " << *volume_name << std::endl;
128  m_selectedVolumes.push_back(volume);
129  } else {
130  // FIXME: throw an exception ?
131  std::cerr << "TrackingMaterialProducer::update(const BeginOfJob*): WARNING: selected volume \"" << *volume_name
132  << "\" not found in geometry " << std::endl;
133  }
134  }
135 }
136 
137 //-------------------------------------------------------------------------
139  m_tracks = new std::vector<MaterialAccountingTrack>();
140 }
141 
142 //-------------------------------------------------------------------------
144  m_track.reset();
145  m_track_volume = nullptr;
146 
147  // prevent secondary tracks from propagating
148  G4Track* track = const_cast<G4Track*>((*event)());
149  if (m_primaryTracks and track->GetParentID() != 0) {
150  track->SetTrackStatus(fStopAndKill);
151  }
152 
153  //For the HGCal case:
154  //In the beginning of each track, the track will first hit an HGCAL volume and it will
155  //save the upper z volume boundary. So, the low boundary of the first
156  //volume is never saved. Here we give the low boundary of the first volume.
157  //This can be found by asking first to run not on 'HGCal' volume below but
158  //on 'CALOECTSRear', which at the moment of this writing it contains
159  //HGCalService, HGCal and thermal screen. You should run Fireworks to
160  //check if these naming conventions and volumes are valid in the future.
161  //Then, check the VolumesZPosition.txt file to see where CEService ends and
162  //put that number in hgcalzfront. Keep in mind to run on the desired volume above here:
163  //https://github.com/cms-sw/cmssw/blob/master/SimTracker/TrackerMaterialAnalysis/plugins/TrackingMaterialProducer.cc#L95
164  //and to replace the volume name of the material first hit at the file creation line below
165  if (isHGCal && track->GetTrackStatus() != fStopAndKill && fabs(track->GetMomentum().eta()) > outerHGCalEta &&
166  fabs(track->GetMomentum().eta()) < innerHGCalEta) {
167  if (track->GetMomentum().eta() > 0.) {
168  outVolumeZpositionTxt << "Air " << m_hgcalzfront << " " << 0 << " " << 0 << " " << 0 << " " << 0 << std::endl;
169  } else if (track->GetMomentum().eta() <= 0.) {
170  outVolumeZpositionTxt << "Air " << -m_hgcalzfront << " " << 0 << " " << 0 << " " << 0 << " " << 0 << std::endl;
171  }
172  }
173 
174  //For the HFnose case:
175  //restrict the outher radius to eta 3.3 since there is HGCAL shadowing
176  //restrict the innner radius to eta 4 since it's non projective
177 
178  if (isHFNose && track->GetTrackStatus() != fStopAndKill && fabs(track->GetMomentum().eta()) > outerHFnoseEta &&
179  fabs(track->GetMomentum().eta()) < innerHFnoseEta) {
180  if (track->GetMomentum().eta() > 0.) {
181  outVolumeZpositionTxt << "Polyethylene " << m_hgcalzfront << " " << 0 << " " << 0 << " " << 0 << " " << 0
182  << std::endl;
183  } else if (track->GetMomentum().eta() <= 0.) {
184  outVolumeZpositionTxt << "Polyethylene " << -m_hgcalzfront << " " << 0 << " " << 0 << " " << 0 << " " << 0
185  << std::endl;
186  }
187  }
188 }
189 
190 bool TrackingMaterialProducer::isSelectedFast(const G4TouchableHistory* touchable) {
191  for (int d = touchable->GetHistoryDepth() - 1; d >= 0; --d) {
192  if (std::find(m_selectedNames.begin(), m_selectedNames.end(), touchable->GetVolume(d)->GetName()) !=
193  m_selectedNames.end())
194  return true;
195  }
196  return false;
197 }
198 
199 //-------------------------------------------------------------------------
201  const G4TouchableHistory* touchable = static_cast<const G4TouchableHistory*>(step->GetTrack()->GetTouchable());
202  if (not isSelectedFast(touchable)) {
203  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer:\t[...] skipping "
204  << touchable->GetVolume()->GetName() << std::endl;
205  return;
206  }
207 
208  // material and step proterties
209  const G4Material* material = touchable->GetVolume()->GetLogicalVolume()->GetMaterial();
210  double length = step->GetStepLength() / cm; // mm -> cm
211  double X0 = material->GetRadlen() / cm; // mm -> cm
212  double Ne = material->GetElectronDensity() * cm3; // 1/mm3 -> 1/cm3
213  double Xi = Ne / 6.0221415e23 * 0.307075 / 2; // MeV / cm
214  double radiationLengths = length / X0; //
215  double energyLoss = length * Xi / 1000.; // GeV
216  //double energyLoss = step->GetDeltaEnergy()/MeV; should we use this??
217 
218  G4ThreeVector globalPosPre = step->GetPreStepPoint()->GetPosition();
219  G4ThreeVector globalPosPost = step->GetPostStepPoint()->GetPosition();
220  GlobalPoint globalPositionIn(globalPosPre.x() / cm, globalPosPre.y() / cm, globalPosPre.z() / cm); // mm -> cm
221  GlobalPoint globalPositionOut(globalPosPost.x() / cm, globalPosPost.y() / cm, globalPosPost.z() / cm); // mm -> cm
222 
223  G4StepPoint* prePoint = step->GetPreStepPoint();
224  G4StepPoint* postPoint = step->GetPostStepPoint();
225  const CLHEP::Hep3Vector& postPos = postPoint->GetPosition();
226  //Go below only in HGCal case
227  if (isHGCal or isHFNose) {
228  //A step never spans across boundaries: geometry or physics define the end points
229  //If the step is limited by a boundary, the post-step point stands on the
230  //boundary and it logically belongs to the next volume.
231  if (postPoint->GetStepStatus() == fGeomBoundary && fabs(postPoint->GetMomentum().eta()) > outerHGCalEta &&
232  fabs(postPoint->GetMomentum().eta()) < innerHGCalEta) {
233  //Post point position is the low z edge of the new volume, or the upper for the prepoint volume.
234  //So, premat - postz - posteta - postR - premattotalenergylossEtable - premattotalenergylossEfull
235  //Observe the two zeros at the end which in the past where set to emCalculator.GetDEDX and
236  //emCalculator.ComputeTotalDEDX but decided not to be used. Will save the structure though for the script.
237  outVolumeZpositionTxt << prePoint->GetMaterial()->GetName() << " " << postPos.z() << " "
238  << postPoint->GetMomentum().eta() << " "
239  << sqrt(postPos.x() * postPos.x() + postPos.y() * postPos.y()) << " " << 0 << " " << 0
240  << std::endl;
241  }
242 
243  if (postPoint->GetStepStatus() == fGeomBoundary && fabs(postPoint->GetMomentum().eta()) > outerHFnoseEta &&
244  fabs(postPoint->GetMomentum().eta()) < innerHFnoseEta) {
245  outVolumeZpositionTxt << prePoint->GetMaterial()->GetName() << " " << postPos.z() << " "
246  << postPoint->GetMomentum().eta() << " "
247  << sqrt(postPos.x() * postPos.x() + postPos.y() * postPos.y()) << " " << 0 << " " << 0
248  << std::endl;
249  }
250 
251  } //end of isHGCal or HFnose if
252 
253  // check for a sensitive detector
254  bool enter_sensitive = false;
255  bool leave_sensitive = false;
256  double cosThetaPre = 0.0;
257  double cosThetaPost = 0.0;
258  int level = 0;
259  const G4VPhysicalVolume* sensitive = nullptr;
261  std::tie(sensitive, level) = GetSensitiveVolume(touchable);
262  if (sensitive) {
263  const G4VSolid& solid = *touchable->GetSolid(level);
264  const G4AffineTransform& transform = GetTransform(touchable, level);
265  G4ThreeVector pos = transform.Inverse().TransformPoint(G4ThreeVector(0., 0., 0.));
266  position = GlobalPoint(pos.x() / cm, pos.y() / cm, pos.z() / cm); // mm -> cm
267 
268  G4ThreeVector localPosPre = transform.TransformPoint(globalPosPre);
269  EInside statusPre = solid.Inside(localPosPre);
270  if (statusPre == kSurface) {
271  enter_sensitive = true;
272  G4ThreeVector globalDirPre = step->GetPreStepPoint()->GetMomentumDirection();
273  G4ThreeVector localDirPre = transform.TransformAxis(globalDirPre);
274  G4ThreeVector normalPre = solid.SurfaceNormal(localPosPre);
275  cosThetaPre = normalPre.cosTheta(-localDirPre);
276  }
277 
278  G4ThreeVector localPosPost = transform.TransformPoint(globalPosPost);
279  EInside statusPost = solid.Inside(localPosPost);
280  if (statusPost == kSurface) {
281  leave_sensitive = true;
282  G4ThreeVector globalDirPost = step->GetPostStepPoint()->GetMomentumDirection();
283  G4ThreeVector localDirPost = transform.TransformAxis(globalDirPost);
284  G4ThreeVector normalPost = solid.SurfaceNormal(localPosPost);
285  cosThetaPost = normalPost.cosTheta(localDirPost);
286  }
287  }
288 
289  // update track accounting
290  if (enter_sensitive) {
291  if (m_track_volume != nullptr) {
292  edm::LogWarning("TrackingMaterialProducer") << "Entering volume " << sensitive << " while inside volume "
293  << m_track_volume << ". Something is inconsistent";
294  m_track.reset();
295  }
296  m_track_volume = sensitive;
297  m_track.enterDetector(position, cosThetaPre);
298  }
299  m_track.step(MaterialAccountingStep(length, radiationLengths, energyLoss, globalPositionIn, globalPositionOut));
300  if (leave_sensitive) {
301  if (m_track_volume != sensitive) {
302  edm::LogWarning("TrackingMaterialProducer") << "Leaving volume " << sensitive << " while inside volume "
303  << m_track_volume << ". Something is inconsistent";
304  m_track.reset();
305  } else
306  m_track.leaveDetector(cosThetaPost);
307  m_track_volume = nullptr;
308  }
309  if (sensitive)
310  LogInfo("TrackingMaterialProducer") << "Track was near sensitive volume " << sensitive->GetName() << std::endl;
311  else
312  LogInfo("TrackingMaterialProducer") << "Track was near non-sensitive volume " << touchable->GetVolume()->GetName()
313  << std::endl;
314  LogInfo("TrackingMaterialProducer") << "Step length: " << length << " cm\n"
315  << "globalPreStep(r,z): (" << globalPositionIn.perp() << ", "
316  << globalPositionIn.z() << ") cm\n"
317  << "globalPostStep(r,z): (" << globalPositionOut.perp() << ", "
318  << globalPositionOut.z() << ") cm\n"
319  << "position(r,z): (" << position.perp() << ", " << position.z() << ") cm\n"
320  << "Radiation lengths: " << radiationLengths << " \t\t(X0: " << X0
321  << " cm)\n"
322  << "Energy loss: " << energyLoss << " MeV \t(Xi: " << Xi
323  << " MeV/cm)\n"
324  << "Track was " << (enter_sensitive ? "entering " : "in none ")
325  << "sensitive volume\n"
326  << "Track was " << (leave_sensitive ? "leaving " : "in none ")
327  << "sensitive volume\n";
328 }
329 
330 //-------------------------------------------------------------------------
332  const G4Track* track = (*event)();
333  if (m_primaryTracks and track->GetParentID() != 0)
334  return;
335 
336  radLen_vs_eta_->Fill(track->GetMomentum().eta(), m_track.summary().radiationLengths());
337  m_tracks->push_back(m_track);
338 
339  // LogInfo
340  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: this track took " << m_track.steps().size()
341  << " steps, and passed through " << m_track.detectors().size()
342  << " sensitive detectors" << std::endl;
343  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: track length: " << m_track.summary().length()
344  << " cm" << std::endl;
345  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: radiation lengths: "
346  << m_track.summary().radiationLengths() << std::endl;
347  LogInfo("TrackingMaterialProducer") << "TrackingMaterialProducer: energy loss: "
348  << m_track.summary().energyLoss() << " MeV" << std::endl;
349 }
350 
351 //-------------------------------------------------------------------------
353  // transfer ownership to the Event
354  std::unique_ptr<std::vector<MaterialAccountingTrack> > tracks(m_tracks);
355  iEvent.put(std::move(tracks));
356  m_tracks = nullptr;
357 }
358 
359 //-------------------------------------------------------------------------
360 bool TrackingMaterialProducer::isSelected(const G4VTouchable* touchable) {
361  for (size_t i = 0; i < m_selectedVolumes.size(); ++i)
362  if (m_selectedVolumes[i]->IsAncestor(touchable->GetVolume()) or
363  m_selectedVolumes[i] == touchable->GetVolume()->GetLogicalVolume())
364  return true;
365 
366  return false;
367 }
368 
369 //-------------------------------------------------------------------------
370 // define as a plugin
#define DEFINE_SIMWATCHER(type)
T getParameter(std::string const &) const
Definition: ParameterSet.h:303
constexpr float Xi[136]
T z() const
Definition: PV3DBase.h:61
Global3DPoint GlobalPoint
Definition: GlobalPoint.h:10
Definition: config.py:1
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
TrackingMaterialProducer(const edm::ParameterSet &)
static const G4AffineTransform & GetTransform(const G4TouchableHistory *touchable, int depth)
int iEvent
Definition: GenABIO.cc:224
T sqrt(T t)
Definition: SSEVec.h:19
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
d
Definition: ztail.py:151
Log< level::Info, false > LogInfo
static const G4LogicalVolume * GetVolume(const std::string &name)
static int position[264][3]
Definition: ReadPGInfo.cc:289
step
Definition: StallMonitor.cc:98
Log< level::Warning, false > LogWarning
void produce(edm::Event &, const edm::EventSetup &) override
void update(const BeginOfJob *) override
This routine will be called when the appropriate signal arrives.
bool isSelected(const G4VTouchable *touch)
def move(src, dest)
Definition: eostools.py:511
bool isSelectedFast(const G4TouchableHistory *touch)
Definition: event.py:1
std::tuple< const G4VPhysicalVolume *, int > GetSensitiveVolume(const G4VTouchable *touchable)
unsigned transform(const HcalDetId &id, unsigned transformCode)