LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
SimulationDrawer.cxx
Go to the documentation of this file.
1 
7 #include <iomanip>
8 #include <algorithm>
9 
10 #include "TParticle.h"
11 #include "TLatex.h"
12 #include "TPolyMarker3D.h"
13 #include "TPolyMarker.h"
14 #include "TPolyLine.h"
15 #include "TPolyLine3D.h"
16 #include "TDatabasePDG.h"
17 
36 
37 //#include "larevt/SpaceChargeServices/SpaceChargeService.h"
38 //#include "larevt/SpaceCharge/SpaceChargeStandard.h"
39 
44 
45 namespace {
46  // Utility function to make uniform error messages.
47  void writeErrMsg(const char* fcn,
48  cet::exception const& e)
49  {
50  mf::LogWarning("SimulationDrawer") << "SimulationDrawer::" << fcn
51  << " failed with message:\n"
52  << e;
53  }
54 }
55 
56 namespace evd{
57 
59 {
60  // For now only draw cryostat=0.
62  minx = 1e9;
63  maxx = -1e9;
64  miny = 1e9;
65  maxy = -1e9;
66  minz = 1e9;
67  maxz = -1e9;
68 
69  for(size_t cryoIdx = 0; cryoIdx < geom->Ncryostats(); cryoIdx++)
70  {
71  const geo::CryostatGeo& cryoGeo = geom->Cryostat(cryoIdx);
72 
73  for (size_t tpcIdx = 0; tpcIdx < cryoGeo.NTPC(); tpcIdx++)
74  {
75  const geo::TPCGeo& tpc = cryoGeo.TPC(tpcIdx);
76 
77  std::cout << "Cryo/TPC idx: " << cryoIdx << "/" << tpcIdx << ", TPC center: " << tpc.GetCenter()[0] << ", " << tpc.GetCenter()[1] << ", " << tpc.GetCenter()[2] << std::endl;
78  std::cout << " TPC Active center: " << tpc.GetActiveVolumeCenter()[0] << ", " << tpc.GetActiveVolumeCenter()[1] << ", " << tpc.GetActiveVolumeCenter()[2] << ", H/W/L: " << tpc.ActiveHalfHeight() << "/" << tpc.ActiveHalfWidth() << "/" << tpc.ActiveLength() << std::endl;
79 
80  if (minx>tpc.GetCenter()[0]-tpc.HalfWidth())
81  minx = tpc.GetCenter()[0]-tpc.HalfWidth();
82  if (maxx<tpc.GetCenter()[0]+tpc.HalfWidth())
83  maxx = tpc.GetCenter()[0]+tpc.HalfWidth();
84  if (miny>tpc.GetCenter()[1]-tpc.HalfHeight())
85  miny = tpc.GetCenter()[1]-tpc.HalfHeight();
86  if (maxy<tpc.GetCenter()[1]+tpc.HalfHeight())
87  maxy = tpc.GetCenter()[1]+tpc.HalfHeight();
88  if (minz>tpc.GetCenter()[2]-tpc.Length()/2.)
89  minz = tpc.GetCenter()[2]-tpc.Length()/2.;
90  if (maxz<tpc.GetCenter()[2]+tpc.Length()/2.)
91  maxz = tpc.GetCenter()[2]+tpc.Length()/2.;
92 
93  std::cout << " minx/maxx: " << minx << "/" << maxx << ", miny/maxy: " << miny << "/" << maxy << ", minz/miny: " << minz << "/" << maxz << std::endl;
94  }
95  }
96 }
97 
98  //......................................................................
99 
101  {
102  }
103 
104  //......................................................................
105 
107  evdb::View2D* view)
108  {
109 
110  if( evt.isRealData() ) return;
111 
113  // Skip drawing if option is turned off
114  if (!drawopt->fShowMCTruthText) return;
115 
116  std::vector<const simb::MCTruth*> mctruth;
117  this->GetMCTruth(evt, mctruth);
118 
119  for (unsigned int i=0; i<mctruth.size(); ++i) {
120  std::string mctext;
121  bool firstin = true;
122  bool firstout = true;
123  std::string origin;
124  std::string incoming;
125  std::string outgoing;
126  // Label cosmic rays -- others are pretty obvious
127  if (mctruth[i]->Origin()==simb::kCosmicRay) origin = "c-ray: ";
128  int jmax = TMath::Min(20,mctruth[i]->NParticles());
129  for (int j=0; j<jmax; ++j) {
130  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
131  char buff[1024];
132  if (p.P()>0.05) {
133  sprintf(buff,"#color[%d]{%s #scale[0.75]{[%.1f GeV/c]}}",
136  p.P());
137  }
138  else {
139  sprintf(buff,"#color[%d]{%s}",
142  }
143  if (p.StatusCode()==0) {
144  if (firstin==false) incoming += " + ";
145  incoming += buff;
146  firstin = false;
147  }
148  if (p.StatusCode()==1) {
149  if (firstout==false) outgoing += " + ";
150  outgoing += buff;
151  firstout = false;
152  }
153  } // loop on j particles
154  if (origin=="" && incoming=="") {
155  mctext = outgoing;
156  }
157  else {
158  mctext = origin+incoming+" #rightarrow "+outgoing;
159  }
160  TLatex& latex = view->AddLatex(0.03, 0.2, mctext.c_str());
161  latex.SetTextSize(0.6);
162 
163  } // loop on i mctruth objects
164  }
165 
166  //......................................................................
167 
169  evdb::View2D* /*view*/)
170  {
171  if( evt.isRealData() ) return;
172 
174  // Skip drawing if option is turned off
175  if (!drawopt->fShowMCTruthText) return;
176 
177  std::vector<const simb::MCTruth*> mctruth;
178  this->GetMCTruth(evt, mctruth);
179  std::cout<<"\nMCTruth Ptcl trackID PDG P T Moth Process\n";
180  for (unsigned int i=0; i<mctruth.size(); ++i) {
181  for (int j=0; j<mctruth[i]->NParticles(); ++j) {
182  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
183  if(p.StatusCode() == 0 || p.StatusCode() == 1) {
184  int KE = 1000 * (p.E() - p.Mass());
185  std::cout<<std::right<<std::setw(7)<<i<<std::setw(5)<<j
186  <<std::setw(8)<<p.TrackId()
187  <<" "<<std::setw(14)<<Style::LatexName(p.PdgCode())
188  <<std::setw(7)<<int(1000 * p.P())
189  <<std::setw(7)<<KE<<std::setw(7)<<p.Mother()
190  <<" "<<p.Process()
191  <<"\n";
192  }
193 /*
194  std::cout << Style::LatexName(p.PdgCode())
195  << "\t\t" << p.E() << " GeV"
196  << "\t" << "(" << p.P() << " GeV/c)"
197  << std::endl;
198 */
199  } // loop on j particles in list
200  }
201  std::cout<<"Note: Momentum, P, and kinetic energy, T, in MeV/c\n";
202  } // MCTruthLongText
203 
204 
205  //......................................................................
206  //this is the method you would use to color code hits by the MC truth pdg value
208  evdb::View2D* view,
209  unsigned int plane)
210  {
211  if( evt.isRealData() ) return;
212 
214  // If the option is turned off, there's nothing to do
215  if (!drawopt->fShowMCTruthVectors) return;
216 
217  detinfo::DetectorProperties const* detprop = lar::providerFrom<detinfo::DetectorPropertiesService>();
218 
221  // get the x position of the plane in question
222  double xyz[3] = {0.};
223  double xyz2[3] = {0.};
224 
225  // Unpack and draw the MC vectors
226  std::vector<const simb::MCTruth*> mctruth;
227  this->GetMCTruth(evt, mctruth);
228 
229  for (size_t i = 0; i < mctruth.size(); ++i) {
230  if (mctruth[i]->Origin() == simb::kCosmicRay) continue;
231  for (int j = 0; j < mctruth[i]->NParticles(); ++j) {
232  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
233 
234  // Skip all but incoming and out-going particles
235  if (!(p.StatusCode()==0 || p.StatusCode()==1)) continue;
236 
237  double r = p.P()*10.0; // Scale length so 10 cm = 1 GeV/c
238 
239  if (r < 0.1) continue; // Skip very short particles
240  if (p.StatusCode() == 0) r = -r; // Flip for incoming particles
241 
242  xyz[0] = p.Vx();
243  xyz[1] = p.Vy();
244  xyz[2] = p.Vz();
245  xyz2[0] = xyz[0] + r * p.Px()/p.P();
246  xyz2[1] = xyz[1] + r * p.Py()/p.P();
247  xyz2[2] = xyz[2] + r * p.Pz()/p.P();
248 
249  double w1 = geo->WireCoordinate(xyz[1], xyz[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
250  double w2 = geo->WireCoordinate(xyz2[1], xyz2[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
251 
252  double time = detprop->ConvertXToTicks(xyz[0]+p.T()*detprop->DriftVelocity()*1e-3, (int)plane, rawopt->fTPC, rawopt->fCryostat);
253  double time2 = detprop->ConvertXToTicks(xyz2[0]+p.T()*detprop->DriftVelocity()*1e-3, (int)plane, rawopt->fTPC, rawopt->fCryostat);
254 
255  if(rawopt->fAxisOrientation < 1){
256  TLine& l = view->AddLine(w1, time, w2, time2);
258  }
259  else{
260  TLine& l = view->AddLine(time, w1, time2, w2);
262  }
263 
264  } // loop on j particles in list
265  } // loop on truths
266 
267  }
268 
269  //......................................................................
270  //this method draws the true particle trajectories in 3D
272  evdb::View3D* view)
273 {
274  if( evt.isRealData() ) return;
275 
277  // If the option is turned off, there's nothing to do
278  if (!drawopt->fShowMCTruthTrajectories) return;
279 
280  // learn whether we want to draw the scintillation light too;
281  // in fact, we don't care whether it's scintillation or not,
282  // but it is photons with very low energy, close to the optical range
283  bool const fDrawLight = drawopt->fShowScintillationLight;
284 
285  // Space charge service...
286 // const spacecharge::SpaceCharge* spaceCharge = lar::providerFrom<spacecharge::SpaceChargeService>();
287 
288  // geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
289  detinfo::DetectorProperties const* theDetector = lar::providerFrom<detinfo::DetectorPropertiesService>();
290  detinfo::DetectorClocks const* detClocks = lar::providerFrom<detinfo::DetectorClocksService>();
292 
293  // get the particles from the Geant4 step
294  std::vector<const simb::MCParticle*> plist;
295  this->GetParticle(evt, plist);
296 
297  // Useful variables
298  double xMinimum(-1.*(maxx-minx));
299  double xMaximum( 2.*(maxx-minx));
300 
301  double xMinFromTicks = theDetector->ConvertTicksToX(0,0,0,0);
302  double xMaxFromTicks = theDetector->ConvertTicksToX(theDetector->NumberTimeSamples(),0,0,0);
303 
304  std::cout << "# samples: " << theDetector->NumberTimeSamples() << ", min/max X: " << xMinFromTicks << "/" << xMaxFromTicks << ", xMinimum/xMaximum: " << xMinimum << "/" << xMaximum << std::endl;
305 
306  // Define a couple of colors for neutrals and if we gray it out...
307  Color_t const scintillationColor = kMagenta;
308  int neutralColor(12);
309  int grayedColor(15);
310  int neutrinoColor(38);
311 
312  // Use the LArVoxelList to get the true energy deposition locations as opposed to using MCTrajectories
314 
315  mf::LogDebug("SimulationDrawer") << "Starting loop over " << plist.size() << " McParticles, voxel list size is " << voxels.size() << std::endl;
316 
317  // Using the voxel information can be slow (see previous implementation of this code).
318  // In order to speed things up we have modified the strategy:
319  // 1) Make one pass through the list of voxels
320  // 2) For each voxel, keep track of the MCParticle contributing energy to it and it's position
321  // which is done by keeping a map between the MCParticle and a vector of positions
322  // 3) Then loop through the map to draw the particle trajectories.
323  // One caveat is the need for MCParticles... and the voxels contain the track ids. So we'll need one
324  // more loop to make a map of track id's and MCParticles.
325 
326  // First up is to build the map between track id's and associated MCParticles so we can recover when looping over voxels
327  std::map<int, const simb::MCParticle*> trackToMcParticleMap;
328 
329  // Should we display the trajectories too?
330  constexpr unsigned int MaxParticles = 100000000;
331  double minPartEnergy(0.01);
332 
333  auto const* pDatabasePDG = TDatabasePDG::Instance();
334 
335  unsigned int nDrawnParticles = 0U;
336  for (const simb::MCParticle* mcPart: plist) {
337  trackToMcParticleMap[mcPart->TrackId()] = mcPart;
338 
339  // Quick loop through to draw trajectories...
340  if (!drawopt->fShowMCTruthTrajectories) continue;
341 
342  // Is there an associated McTrajectory?
343  const simb::MCTrajectory& mcTraj = mcPart->Trajectory();
344  if (mcTraj.empty()) continue;
345 
346  int const pdgCode(mcPart->PdgCode());
347  int const colorIdx
348  { drawopt->fShowMCTruthColors? evd::Style::ColorFromPDG(mcPart->PdgCode()): grayedColor };
349  TParticlePDG const* partPDG(pDatabasePDG->GetParticle(pdgCode));
350  double const partCharge = partPDG ? partPDG->Charge() : 0.;
351  double const partEnergy = mcPart->E();
352 
353  // is this a photon with less than 100 eV of energy?
354  // (LArG4 scintillation photons are "Rootini", PDG code 0... *burp*)
355  bool const isScintillationLight = ((pdgCode == 22) || (pdgCode == 0)) && (partEnergy < 1e-7);
356 
357  if (isScintillationLight) {
358  // we do not apply any threshold, but we draw it only if requested
359  if (!fDrawLight) continue;
360  }
361  else { // apply the "normal" threshold
362  if (partEnergy < minPartEnergy) continue;
363  }
364 
365  if (nDrawnParticles++ >= MaxParticles) {
366  if (nDrawnParticles == MaxParticles) {
367  mf::LogWarning("SimulationDrawer")
368  << "Only " << nDrawnParticles << "/" << plist.size()
369  << " MCParticle's will be displayed.";
370  }
371  continue; // too many!
372  }
373 
374  /*
375  * The following is meant to get the correct offset for drawing the particle trajectory
376  * In particular, the cosmic rays will not be correctly placed without this
377  *
378  * More specifically, the true location of the cosmic ray betrays the
379  * expectation of the reader.
380  * When we look at an event, we expect that if two particles are close
381  * by, their observables (in our case, ionization tracks) will be as
382  * close. The event display projects into a static picture everything
383  * that happens at any time. This is not a big deal in a fast detector,
384  * where the time window can be short, but has consequences of mixing
385  * activity at different times for a LArTPC.
386  * The slowness of TPCs is a major problem for physics. Here we are
387  * facing a minor spin-off of that problem: if a pion is produced at a
388  * certain position x by the neutrino interaction, and then one
389  * millisecond later a cosmic rays passes at the same location, the
390  * display will show both at the same position x, but the detection
391  * will be delayed by the number of ticks the TDC counted in that one
392  * millisecond. Reconstruction will not be fooled by the close distance
393  * of the two tracks, but the display leads us to expect that confusion
394  * /should/ arise.
395  *
396  */
397  //double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T())+theDetector->GetXTicksOffset(0,0,0)-theDetector->TriggerOffset());
398  //double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T())+theDetector->GetXTicksOffset(0,0,0));
399  double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T()));
400  //double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T()));
401  double xOffset(0.); //(theDetector->ConvertTicksToX(g4Ticks, 0, 0, 0));
402  bool xOffsetNotSet(true);
403  // collect the points from this particle
404  int const numTrajPoints = mcTraj.size();
405 
406  std::vector<double> hitPositions;
407  hitPositions.reserve(3*numTrajPoints);
408  std::size_t hitCount = 0U;
409 
410 
411  for(int hitIdx = 0; hitIdx < numTrajPoints; hitIdx++)
412  {
413  double xPos = mcTraj.X(hitIdx);
414  double yPos = mcTraj.Y(hitIdx);
415  double zPos = mcTraj.Z(hitIdx);
416 
417  // If the original simulated hit did not occur in the TPC volume then don't draw it
418  if (xPos < minx || xPos > maxx || yPos < miny || yPos > maxy|| zPos < minz || zPos > maxz) continue;
419 
420  // Check xOffset state and set if necessary
421  if (xOffsetNotSet)
422  {
423  try
424  {
425  auto const* pTPC = geom->PositionToTPCptr(geo::Point_t(xPos, yPos, zPos));
426  if (!pTPC) continue;
427 
428  geo::PlaneID const firstPlane { pTPC->ID(), 0 };
429  xMinFromTicks = theDetector->ConvertTicksToX(0, firstPlane);
430  xMaxFromTicks = theDetector->ConvertTicksToX(theDetector->NumberTimeSamples(), firstPlane);
431 
432  if (xMaxFromTicks < xMinFromTicks) std::swap(xMinFromTicks,xMaxFromTicks);
433 
434  xOffset = xMinFromTicks - theDetector->ConvertTicksToX(g4Ticks, firstPlane);
435  xOffsetNotSet = false;
436  xOffset = 0.; // ?!?
437  }
438  catch(...) {continue;}
439  }
440 
441  // Now move the hit position to correspond to the timing
442  xPos += xOffset;
443 
444  // Check fiducial limits
445  if (xPos > xMinFromTicks && xPos < xMaxFromTicks)
446  {
447  // Check for space charge offsets
448 // if (spaceCharge->EnableSimEfieldSCE())
449 // {
450 // std::vector<double> offsetVec = spaceCharge->GetPosOffsets(xPos,yPos,zPos);
451 // xPos += offsetVec[0] - 0.7;
452 // yPos -= offsetVec[1];
453 // zPos -= offsetVec[2];
454 // }
455 
456  hitPositions.push_back(xPos);
457  hitPositions.push_back(yPos);
458  hitPositions.push_back(zPos);
459  ++hitCount;
460  }
461  } // for hitIdx
462 
463 
464  TPolyLine3D& pl(view->AddPolyLine3D(1, colorIdx, 1, 1));
465 
466  // Draw neutrals as a gray dotted line to help fade into background a bit...
467  if (isScintillationLight) {
468  pl.SetLineColor(scintillationColor);
469  pl.SetLineStyle(kSolid);
470  pl.SetLineWidth(1);
471  }
472  else if (partCharge == 0.)
473  {
474  pl.SetLineColor(neutralColor);
475  pl.SetLineStyle(kDotted);
476  pl.SetLineWidth(1);
477  }
478  pl.SetPolyLine(hitCount, hitPositions.data(), "");
479  } // for mcPart
480 
481 
482  // Now we set up and build the map between MCParticles and a vector of positions obtained from the voxels
483  std::map<const simb::MCParticle*, std::vector<std::vector<double> > > partToPosMap;
484 
486  for(vxitr = voxels.begin(); vxitr != voxels.end(); vxitr++)
487  {
488  const sim::LArVoxelData &vxd = (*vxitr).second;
489 
490  for(size_t partIdx = 0; partIdx < vxd.NumberParticles(); partIdx++)
491  {
492  if(vxd.Energy(partIdx) > drawopt->fMinEnergyDeposition)
493  {
494  int trackId = vxd.TrackID(partIdx);
495 
496  // It can be in some instances that mcPart here could be zero.
497  const simb::MCParticle* mcPart = trackToMcParticleMap[trackId];
498 
499  partToPosMap[mcPart].push_back(std::vector<double>(3));
500 
501  partToPosMap[mcPart].back()[0] = vxd.VoxelID().X();
502  partToPosMap[mcPart].back()[1] = vxd.VoxelID().Y();
503  partToPosMap[mcPart].back()[2] = vxd.VoxelID().Z();
504  }
505  } // end if this track id is in the current voxel
506  }// end loop over voxels
507 
508  // Finally ready for the main event! Simply loop through the map between MCParticle and positions to
509  // draw the trajectories
510  std::map<const simb::MCParticle*, std::vector<std::vector<double> > >::iterator partToPosMapItr;
511 
512  for(partToPosMapItr = partToPosMap.begin(); partToPosMapItr != partToPosMap.end(); partToPosMapItr++)
513  {
514  // Recover the McParticle, we'll need to access several data members so may as well dereference it
515  const simb::MCParticle* mcPart = partToPosMapItr->first;
516 
517  // Apparently, it can happen that we get a null pointer here or maybe no points to plot
518  if (!mcPart || partToPosMapItr->second.empty()) continue;
519 
520  // The following is meant to get the correct offset for drawing the particle trajectory
521  // In particular, the cosmic rays will not be correctly placed without this
522  //double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T())+theDetector->GetXTicksOffset(0,0,0)-theDetector->TriggerOffset());
523  //double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T())+theDetector->GetXTicksOffset(0,0,0));
524  double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T()));
525 // double g4Ticks(detClocks->TPCG4Time2Tick(mcPart->T()));
526  double xOffset(0.); //theDetector->ConvertTicksToX(g4Ticks, 0, 0, 0));
527  bool xOffsetNotSet(true);
528 
529  int colorIdx(evd::Style::ColorFromPDG(mcPart->PdgCode()));
530  int markerIdx(kFullDotSmall);
531  int markerSize(2);
532 
533  if (!drawopt->fShowMCTruthFullSize)
534  {
535  colorIdx = grayedColor;
536  markerIdx = kDot;
537  markerSize = 1;
538  }
539 
540  std::unique_ptr<double[]> hitPositions(new double[3*partToPosMapItr->second.size()]);
541  int hitCount(0);
542 
543  // Now loop over points and add to trajectory
544  for(size_t posIdx = 0; posIdx < partToPosMapItr->second.size(); posIdx++)
545  {
546  const std::vector<double>& posVec = partToPosMapItr->second[posIdx];
547 
548  // Check xOffset state and set if necessary
549  if (xOffsetNotSet)
550  {
551  double hitLocation[] = {posVec[0],posVec[1],posVec[2]};
552 
553  try
554  {
555  const geo::CryostatGeo& cryoGeo = geom->PositionToCryostat(hitLocation);
556  int tpcIdx = cryoGeo.FindTPCAtPosition(hitLocation, 1.);
557 
558  xMinFromTicks = theDetector->ConvertTicksToX(0,0,tpcIdx,cryoGeo.ID().Cryostat);
559  xMaxFromTicks = theDetector->ConvertTicksToX(theDetector->NumberTimeSamples(),0,tpcIdx,cryoGeo.ID().Cryostat);
560 
561  if (xMaxFromTicks < xMinFromTicks) std::swap(xMinFromTicks,xMaxFromTicks);
562 
563  xOffset = xMinFromTicks - theDetector->ConvertTicksToX(g4Ticks, 0, tpcIdx, cryoGeo.ID().Cryostat);
564  xOffsetNotSet = false;
565  xOffset = 0.;
566  }
567  catch(...) {continue;}
568  }
569 
570  double xCoord = posVec[0] + xOffset;
571 
572  if (xCoord > xMinFromTicks && xCoord < xMaxFromTicks)
573  {
574  hitPositions[3*hitCount ] = xCoord;
575  hitPositions[3*hitCount + 1] = posVec[1];
576  hitPositions[3*hitCount + 2] = posVec[2];
577  hitCount++;
578  }
579  }
580 
581  TPolyMarker3D& pm = view->AddPolyMarker3D(1, colorIdx, markerIdx, markerSize);
582  pm.SetPolyMarker(hitCount, hitPositions.get(), markerIdx);
583  }
584 
585  // Finally, let's see if we can draw the incoming particle from the MCTruth information
586  std::vector<const simb::MCTruth*> mctruth;
587  this->GetMCTruth(evt, mctruth);
588 
589  // Loop through the MCTruth vector
590  for (unsigned int idx = 0; idx < mctruth.size(); idx++)
591  {
592  // Go through each MCTruth object in the list
593  for (int particleIdx = 0; particleIdx < mctruth[idx]->NParticles(); particleIdx++)
594  {
595  const simb::MCParticle& mcPart = mctruth[idx]->GetParticle(particleIdx);
596 
597  // A negative mother id indicates the "primary" particle
598  if(mcPart.Mother() == -1 && mcPart.StatusCode() == 0)
599  {
600  mf::LogDebug("SimulationDrawer") << mcPart << std::endl;
601 
602  // Get position vector
603  TVector3 particlePosition(mcPart.Vx(),mcPart.Vy(),mcPart.Vz());
604 
605  // Get direction vector (in opposite direction)
606  TVector3 oppPartDir(-mcPart.Px(),-mcPart.Py(),-mcPart.Pz());
607 
608  if (oppPartDir.Mag2() > 0.) oppPartDir.SetMag(1.);
609 
610  double arcLenToDraw = -particlePosition.Z() / oppPartDir.CosTheta();
611 
612  // No point in drawing if arc length is zero (e.g. Ar nucleus)
613  if (arcLenToDraw > 0.)
614  {
615  // Draw the line, use an off color to be unique
616  TPolyLine3D& pl(view->AddPolyLine3D(2, neutrinoColor, 1, 2));
617 
618  pl.SetPoint(0,particlePosition.X(),particlePosition.Y(),particlePosition.Z());
619 
620  particlePosition += std::min(arcLenToDraw + 10.,1000.) * oppPartDir;
621 
622  pl.SetPoint(1,particlePosition.X(),particlePosition.Y(),particlePosition.Z());
623  }
624  }
625  // The particles we want to draw will be early in the list so break out if we didn't find them
626  else break;
627  } // loop on particles in list
628  }
629 
630  return;
631 }
632 
633  //......................................................................
634  //this method draws the true particle trajectories in 3D Ortho view.
637  double msize,
638  evdb::View2D* view)
639  {
640  if( evt.isRealData() ) return;
641 
643 
644  // If the option is turned off, there's nothing to do
645  if (!drawopt->fShowMCTruthTrajectories) return;
646 
647  geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
648  detinfo::DetectorProperties const* theDetector = lar::providerFrom<detinfo::DetectorPropertiesService>();
649  detinfo::DetectorClocks const* detClocks = lar::providerFrom<detinfo::DetectorClocksService>();
650 
651  // get the particles from the Geant4 step
652  std::vector<const simb::MCParticle*> plist;
653  this->GetParticle(evt, plist);
654 
655  // Useful variables
656 
657  //double detHalfWidth(geom->DetHalfWidth());
658  double xMinimum(-1.*(maxx-minx));
659  double xMaximum( 2.*(maxx-minx));
660 
661 // double detHalfHeight(geom->DetHalfHeight());
662 // double zMinimum(0.);
663 // double zMaximum(geom->DetLength());
664 
665  // Use the LArVoxelList to get the true energy deposition locations as opposed to using MCTrajectories
667 
668  mf::LogDebug("SimulationDrawer") << "Starting loop over " << plist.size() << " McParticles, voxel list size is " << voxels.size() << std::endl;
669 
670  // Using the voxel information can be slow (see previous implementation of this code).
671  // In order to speed things up we have modified the strategy:
672  // 1) Make one pass through the list of voxels
673  // 2) For each voxel, keep track of the MCParticle contributing energy to it and it's position
674  // which is done by keeping a map between the MCParticle and a vector of positions
675  // 3) Then loop through the map to draw the particle trajectories.
676  // One caveat is the need for MCParticles... and the voxels contain the track ids. So we'll need one
677  // more loop to make a map of track id's and MCParticles.
678 
679  // First up is to build the map between track id's and associated MCParticles so we can recover when looping over voxels
680  std::map<int, const simb::MCParticle*> trackToMcParticleMap;
681 
682  // Should we display the trajectories too?
683  bool displayMcTrajectories(true);
684  double minPartEnergy(0.025);
685 
686  double tpcminx = 1.0; double tpcmaxx = -1.0;
687  double xOffset = 0.0; double g4Ticks = 0.0;
688  double coeff = 0.0; double readoutwindowsize = 0.0;
689  double vtx[3] = {0.0, 0.0, 0.0};
690  for(size_t p = 0; p < plist.size(); ++p)
691  {
692  trackToMcParticleMap[plist[p]->TrackId()] = plist[p];
693 
694  // Quick loop through to drawn trajectories...
695  if (displayMcTrajectories)
696  {
697  // Is there an associated McTrajectory?
698  const simb::MCParticle* mcPart = plist[p];
699  const simb::MCTrajectory& mcTraj = mcPart->Trajectory();
700 
701  int pdgCode(mcPart->PdgCode());
702  TParticlePDG* partPDG(TDatabasePDG::Instance()->GetParticle(pdgCode));
703  double partCharge = partPDG ? partPDG->Charge() : 0.;
704  double partEnergy = mcPart->E();
705 
706  if (!mcTraj.empty() && partEnergy > minPartEnergy && mcPart->TrackId() < 100000000)
707  {
708  // collect the points from this particle
709  int numTrajPoints = mcTraj.size();
710 
711  std::unique_ptr<double[]> hitPosX(new double[numTrajPoints]);
712  std::unique_ptr<double[]> hitPosY(new double[numTrajPoints]);
713  std::unique_ptr<double[]> hitPosZ(new double[numTrajPoints]);
714  int hitCount(0);
715 
716  double xPos = mcTraj.X(0);
717  double yPos = mcTraj.Y(0);
718  double zPos = mcTraj.Z(0);
719 
720  tpcminx = 1.0; tpcmaxx = -1.0;
721  xOffset = 0.0; g4Ticks = 0.0;
722  vtx[0] = 0.0; vtx[1] = 0.0; vtx[2] = 0.0;
723  coeff = 0.0; readoutwindowsize = 0.0;
724  for(int hitIdx = 0; hitIdx < numTrajPoints; hitIdx++)
725  {
726  xPos = mcTraj.X(hitIdx);
727  yPos = mcTraj.Y(hitIdx);
728  zPos = mcTraj.Z(hitIdx);
729 
730  // If the original simulated hit did not occur in the TPC volume then don't draw it
731  if (xPos < minx || xPos > maxx || yPos < miny || yPos > maxy|| zPos < minz || zPos > maxz) continue;
732 
733  if ((xPos < tpcminx) || (xPos > tpcmaxx))
734  {
735  vtx[0] = xPos; vtx[1] = yPos; vtx[2] = zPos;
736  geo::TPCID tpcid = geom->FindTPCAtPosition(vtx);
737  unsigned int cryo = geom->FindCryostatAtPosition(vtx);
738 
739  if (tpcid.isValid)
740  {
741  unsigned int tpc = tpcid.TPC;
742  const geo::TPCGeo& tpcgeo = geom->GetElement(tpcid);
743  tpcminx = tpcgeo.MinX(); tpcmaxx = tpcgeo.MaxX();
744 
745  coeff = theDetector->GetXTicksCoefficient(tpc, cryo);
746  readoutwindowsize = theDetector->ConvertTicksToX(theDetector->ReadOutWindowSize(), 0, tpc, cryo);
747 
748  // The following is meant to get the correct offset for drawing the particle trajectory
749  // In particular, the cosmic rays will not be correctly placed without this
750  g4Ticks = detClocks->TPCG4Time2Tick(mcPart->T())
751  +theDetector->GetXTicksOffset(0, tpc, cryo)
752  -theDetector->TriggerOffset();
753 
754  xOffset = theDetector->ConvertTicksToX(g4Ticks, 0, tpc, cryo);
755  }
756  else { xOffset = 0; tpcminx = 1.0; tpcmaxx = -1.0; coeff = 0.0; readoutwindowsize = 0.0;}
757  }
758 
759  // Now move the hit position to correspond to the timing
760  xPos += xOffset;
761 
762  bool inreadoutwindow = false;
763  if (coeff < 0)
764  {
765  if ((xPos > readoutwindowsize) && (xPos < tpcmaxx)) inreadoutwindow = true;
766  }
767  else if (coeff > 0)
768  {
769  if ((xPos > tpcminx) && (xPos < readoutwindowsize)) inreadoutwindow = true;
770  }
771 
772  if (!inreadoutwindow) continue;
773 
774  // Check fiducial limits
775  if (xPos > xMinimum && xPos < xMaximum)
776  {
777  hitPosX[hitCount] = xPos;
778  hitPosY[hitCount] = yPos;
779  hitPosZ[hitCount] = zPos;
780  hitCount++;
781  }
782 
783  }
784 
785  TPolyLine& pl = view->AddPolyLine(1, evd::Style::ColorFromPDG(mcPart->PdgCode()), 1, 1); //kFullCircle, msize);
786 
787  // Draw neutrals as a gray dotted line to help fade into background a bit...
788  if (partCharge == 0.)
789  {
790  pl.SetLineColor(13);
791  pl.SetLineStyle(3);
792  pl.SetLineWidth(1);
793  }
794 
795  if(proj == evd::kXY)
796  pl.SetPolyLine(hitCount, hitPosX.get(), hitPosY.get(), "");
797  else if(proj == evd::kXZ)
798  pl.SetPolyLine(hitCount, hitPosZ.get(), hitPosX.get(), "");
799  else if(proj == evd::kYZ)
800  pl.SetPolyLine(hitCount, hitPosZ.get(), hitPosY.get(), "");
801  }
802  }
803  }
804 
805  // Now we set up and build the map between MCParticles and a vector of positions obtained from the voxels
806  std::map<const simb::MCParticle*, std::vector<std::vector<double> > > partToPosMap;
807 
809  for(vxitr = voxels.begin(); vxitr != voxels.end(); vxitr++)
810  {
811  const sim::LArVoxelData &vxd = (*vxitr).second;
812 
813  for(size_t partIdx = 0; partIdx < vxd.NumberParticles(); partIdx++)
814  {
815  if(vxd.Energy(partIdx) > drawopt->fMinEnergyDeposition)
816  {
817  int trackId = vxd.TrackID(partIdx);
818 
819  // It can be in some instances that mcPart here could be zero.
820  const simb::MCParticle* mcPart = trackToMcParticleMap[trackId];
821 
822  partToPosMap[mcPart].push_back(std::vector<double>(3));
823 
824  partToPosMap[mcPart].back()[0] = vxd.VoxelID().X();
825  partToPosMap[mcPart].back()[1] = vxd.VoxelID().Y();
826  partToPosMap[mcPart].back()[2] = vxd.VoxelID().Z();
827  }
828  } // end if this track id is in the current voxel
829  }// end loop over voxels
830 
831  // Finally ready for the main event! Simply loop through the map between MCParticle and positions to
832  // draw the trajectories
833  std::map<const simb::MCParticle*, std::vector<std::vector<double> > >::iterator partToPosMapItr;
834 
835  for(partToPosMapItr = partToPosMap.begin(); partToPosMapItr != partToPosMap.end(); partToPosMapItr++)
836  {
837  // Recover the McParticle, we'll need to access several data members so may as well dereference it
838  const simb::MCParticle* mcPart = partToPosMapItr->first;
839 
840  // Apparently, it can happen that we get a null pointer here or maybe no points to plot
841  if (!mcPart || partToPosMapItr->second.empty()) continue;
842 
843  tpcminx = 1.0; tpcmaxx = -1.0;
844  xOffset = 0.0; g4Ticks = 0.0;
845  std::vector< std::array<double, 3> > posVecCorr;
846  posVecCorr.reserve(partToPosMapItr->second.size());
847  coeff = 0.0; readoutwindowsize = 0.0;
848 
849  // Now loop over points and add to trajectory
850  for(size_t posIdx = 0; posIdx < partToPosMapItr->second.size(); posIdx++)
851  {
852  const std::vector<double>& posVec = partToPosMapItr->second[posIdx];
853 
854  if ((posVec[0] < tpcminx) || (posVec[0] > tpcmaxx))
855  {
856  vtx[0] = posVec[0]; vtx[1] = posVec[1]; vtx[2] = posVec[2];
857  geo::TPCID tpcid = geom->FindTPCAtPosition(vtx);
858  unsigned int cryo = geom->FindCryostatAtPosition(vtx);
859 
860  if (tpcid.isValid)
861  {
862  unsigned int tpc = tpcid.TPC;
863 
864  const geo::TPCGeo& tpcgeo = geom->GetElement(tpcid);
865  tpcminx = tpcgeo.MinX(); tpcmaxx = tpcgeo.MaxX();
866 
867  coeff = theDetector->GetXTicksCoefficient(tpc, cryo);
868  readoutwindowsize = theDetector->ConvertTicksToX(theDetector->ReadOutWindowSize(), 0, tpc, cryo);
869  // The following is meant to get the correct offset for drawing the particle trajectory
870  // In particular, the cosmic rays will not be correctly placed without this
871  g4Ticks = detClocks->TPCG4Time2Tick(mcPart->T())
872  +theDetector->GetXTicksOffset(0, tpc, cryo)
873  -theDetector->TriggerOffset();
874 
875  xOffset = theDetector->ConvertTicksToX(g4Ticks, 0, tpc, cryo);
876  }
877  else { xOffset = 0; tpcminx = 1.0; tpcmaxx = -1.0; coeff = 0.0; readoutwindowsize = 0.0; }
878  }
879 
880  double xCoord = posVec[0] + xOffset;
881 
882  bool inreadoutwindow = false;
883  if (coeff < 0)
884  {
885  if ((xCoord > readoutwindowsize) && (xCoord < tpcmaxx)) inreadoutwindow = true;
886  }
887  else if (coeff > 0)
888  {
889  if ((xCoord > tpcminx) && (xCoord < readoutwindowsize)) inreadoutwindow = true;
890  }
891 
892  if (inreadoutwindow && (xCoord > xMinimum && xCoord < xMaximum))
893  {
894  posVecCorr.push_back({{xCoord, posVec[1], posVec[2] }});
895  }
896  }
897 
898  TPolyMarker& pm = view->AddPolyMarker(posVecCorr.size(), evd::Style::ColorFromPDG(mcPart->PdgCode()), kFullDotMedium, 2); //kFullCircle, msize);
899 
900  for (size_t p = 0; p < posVecCorr.size(); ++p)
901  {
902  if(proj == evd::kXY)
903  pm.SetPoint(p, posVecCorr[p][0], posVecCorr[p][1]);
904  else if(proj == evd::kXZ)
905  pm.SetPoint(p, posVecCorr[p][2], posVecCorr[p][0]);
906  else if(proj == evd::kYZ)
907  pm.SetPoint(p, posVecCorr[p][2], posVecCorr[p][1]);
908  }
909  }
910 
911  return;
912  }
913 
914  //......................................................................
916  std::vector<const simb::MCParticle*>& plist)
917  {
918  plist.clear();
919 
920  if( evt.isRealData() ) return 0;
921 
923 
924  std::vector<const simb::MCParticle*> temp;
925 
927  // use get by Type because there should only be one collection of these in the event
928  try{
929  evt.getView(drawopt->fG4ModuleLabel, plcol);
930  for(unsigned int i = 0; i < plcol.vals().size(); ++i){
931  temp.push_back(plcol.vals().at(i));
932  }
933  temp.swap(plist);
934  }
935  catch(cet::exception& e){
936  writeErrMsg("GetRawDigits", e);
937  }
938 
939  return plist.size();
940 
941  }
942 
943  //......................................................................
944 
946  std::vector<const simb::MCTruth*>& mcvec)
947  {
948  mcvec.clear();
949 
950  if( evt.isRealData() ) return 0;
951 
952  std::vector<const simb::MCTruth*> temp;
953 
954  std::vector< art::Handle< std::vector<simb::MCTruth> > > mctcol;
955  // use get by Type because there should only be one collection of these in the event
956  try{
957  evt.getManyByType(mctcol);
958  for(size_t mctc = 0; mctc < mctcol.size(); ++mctc){
959  art::Handle< std::vector<simb::MCTruth> > mclistHandle = mctcol[mctc];
960 
961  for(size_t i = 0; i < mclistHandle->size(); ++i){
962  temp.push_back(&(mclistHandle->at(i)));
963  }
964  }
965  temp.swap(mcvec);
966  }
967  catch(cet::exception& e){
968  writeErrMsg("GetMCTruth", e);
969  }
970 
971  return mcvec.size();
972  }
973 
974 
975  //......................................................................
976 
977  void SimulationDrawer::HiLite(int trkId, bool dohilite)
978  {
979  fHighlite[trkId] = dohilite;
980  }
981 
982 }// namespace
double E(const int i=0) const
Definition: MCParticle.h:237
double Z(const size_type i) const
Definition: MCTrajectory.h:154
double X(const size_type i) const
Definition: MCTrajectory.h:152
int PdgCode() const
Definition: MCParticle.h:216
Point GetActiveVolumeCenter() const
Returns the center of the TPC active volume in world coordinates [cm].
Definition: TPCGeo.h:247
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
double Py(const int i=0) const
Definition: MCParticle.h:235
Utilities related to art service access.
void MCTruthLongText(const art::Event &evt, evdb::View2D *view)
CryostatGeo const & GetElement(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
unsigned int fTPC
TPC number to draw, typically set by TWQProjectionView.
void HiLite(int trkId, bool hlt=true)
TPolyLine & AddPolyLine(int n, int c, int w, int s)
Definition: View2D.cxx:210
std::string fG4ModuleLabel
module label producing sim::SimChannel objects
size_type size() const
Definition: LArVoxelList.h:140
Container of LAr voxel information.
double ActiveHalfHeight() const
Half height (associated with y coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:91
const simb::MCTrajectory & Trajectory() const
Definition: MCParticle.h:257
int Mother() const
Definition: MCParticle.h:217
TLine & AddLine(double x1, double y1, double x2, double y2)
Definition: View2D.cxx:187
double Mass() const
Definition: MCParticle.h:243
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:90
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
bool isValid
Whether this ID points to a valid element.
Definition: geo_types.h:129
Geometry information for a single TPC.
Definition: TPCGeo.h:37
double Px(const int i=0) const
Definition: MCParticle.h:234
static sim::LArVoxelList GetLArVoxelList(const art::Event &evt, std::string moduleLabel)
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:130
int GetMCTruth(const art::Event &evt, std::vector< const simb::MCTruth * > &mctruth)
int StatusCode() const
Definition: MCParticle.h:215
double MaxX() const
Returns the world x coordinate of the end of the box.
Definition: BoxBoundedGeo.h:93
A collection of drawable 2-D objects.
static void FromPDG(TLine &line, int pdgcode)
Definition: Style.cxx:138
bool isRealData() const
Definition: Event.h:83
Geometry information for a single cryostat.
Definition: CryostatGeo.h:36
OrthoProj_t
Definition: OrthoProj.h:12
unsigned int Ncryostats() const
Returns the number of cryostats in the detector.
std::string Process() const
Definition: MCParticle.h:219
void MCTruth3D(const art::Event &evt, evdb::View3D *view)
Particle class.
bool empty() const
Definition: MCTrajectory.h:170
iterator begin()
Definition: LArVoxelList.h:131
int TrackId() const
Definition: MCParticle.h:214
virtual double ConvertXToTicks(double X, int p, int t, int c) const =0
double Length() const
Length is associated with z coordinate [cm].
Definition: TPCGeo.h:107
list_type::const_iterator const_iterator
Definition: LArVoxelList.h:79
geo::TPCID FindTPCAtPosition(double const worldLoc[3]) const
Returns the ID of the TPC at specified location.
TPolyLine3D & AddPolyLine3D(int n, int c, int w, int s)
Definition: View3D.cxx:105
std::size_t getView(std::string const &moduleLabel, std::string const &productInstanceName, std::vector< ELEMENT const * > &result) const
Definition: DataViewImpl.h:474
double Y(const size_type i) const
Definition: MCTrajectory.h:153
Encapsulates the information we want store for a voxel.
LArSoft includes.
unsigned int fCryostat
Cryostat number to draw, typically set by TWQProjectionView.
double Y() const
Definition: LArVoxelID.cxx:79
double ActiveHalfWidth() const
Half width (associated with x coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:87
int GetParticle(const art::Event &evt, std::vector< const simb::MCParticle * > &plist)
double P(const int i=0) const
Definition: MCParticle.h:238
TPolyMarker & AddPolyMarker(int n, int c, int st, double sz)
Definition: View2D.cxx:157
static int ColorFromPDG(int pdgcode)
Definition: Style.cxx:65
double T(const int i=0) const
Definition: MCParticle.h:228
void getManyByType(std::vector< Handle< PROD >> &results) const
Definition: DataViewImpl.h:446
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:155
size_type NumberParticles() const
Definition: LArVoxelData.h:202
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
Definition: fwd.h:47
virtual unsigned int NumberTimeSamples() const =0
The data type to uniquely identify a TPC.
Definition: geo_types.h:195
Description of geometry of one entire detector.
TLatex & AddLatex(double x, double y, const char *text)
Definition: View2D.cxx:308
double ActiveLength() const
Length (associated with z coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:95
double Z() const
Definition: LArVoxelID.cxx:86
bool fShowScintillationLight
Whether to draw low energy light (default: no).
void MCTruthShortText(const art::Event &evt, evdb::View2D *view)
Conversion of times between different formats and references.
double HalfHeight() const
Height is associated with y coordinate [cm].
Definition: TPCGeo.h:103
double X() const
Definition: LArVoxelID.cxx:72
virtual double ConvertTicksToX(double ticks, int p, int t, int c) const =0
geo::TPCID::TPCID_t FindTPCAtPosition(double const worldLoc[3], double const wiggle) const
Returns the index of the TPC at specified location.
double Vx(const int i=0) const
Definition: MCParticle.h:225
mapped_type Energy() const
Definition: LArVoxelData.h:209
Encapsulate the construction of a single detector plane.
size_type size() const
Definition: MCTrajectory.h:169
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Int_t min
Definition: plot.C:26
virtual double DriftVelocity(double efield=0., double temperature=0.) const =0
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
static const char * LatexName(int pdgcode)
Convert PDG code to a latex string (root-style)
Definition: Style.cxx:12
Float_t proj
Definition: plot.C:34
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
double Pz(const int i=0) const
Definition: MCParticle.h:236
Render the objects from the Simulation package.
int fAxisOrientation
0 = TDC values on y-axis, wire number on x-axis, 1 = swapped
double Vz(const int i=0) const
Definition: MCParticle.h:227
void MCTruthOrtho(const art::Event &evt, evd::OrthoProj_t proj, double msize, evdb::View2D *view)
void MCTruthVectors2D(const art::Event &evt, evdb::View2D *view, unsigned int plane)
sim::LArVoxelID VoxelID() const
Definition: LArVoxelData.h:201
A collection of 3D drawable objects.
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:203
Float_t e
Definition: plot.C:34
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:187
Namespace collecting geometry-related classes utilities.
geo::CryostatID::CryostatID_t FindCryostatAtPosition(geo::Point_t const &worldLoc) const
Returns the index of the cryostat at specified location.
std::map< int, bool > fHighlite
double Vy(const int i=0) const
Definition: MCParticle.h:226
art framework interface to geometry description
double HalfWidth() const
Width is associated with x coordinate [cm].
Definition: TPCGeo.h:99
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:230
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
Cosmic rays.
Definition: MCTruth.h:22
collection_type & vals()
Definition: View.h:34
TPolyMarker3D & AddPolyMarker3D(int n, int c, int st, double sz)
Definition: View3D.cxx:75
Encapsulate the construction of a single detector plane.
const key_type & TrackID(const size_type) const
geo::CryostatID const & ID() const
Returns the identifier of this cryostat.
Definition: CryostatGeo.h:116
Point GetCenter() const
Returns the center of the TPC volume in world coordinates [cm].
Definition: TPCGeo.h:693