All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SimulationDrawer.cxx
Go to the documentation of this file.
1 ///
2 /// \file SimulationDrawer.cxx
3 /// \brief Render the objects from the Simulation package
4 /// \author messier@indiana.edu
5 ///
6 
7 #include <algorithm>
8 #include <iomanip>
9 
10 #include "TDatabasePDG.h"
11 #include "TLatex.h"
12 #include "TLine.h"
13 #include "TPolyLine.h"
14 #include "TPolyLine3D.h"
15 #include "TPolyMarker.h"
16 #include "TPolyMarker3D.h"
17 
33 #include "nuevdb/EventDisplayBase/View2D.h"
34 #include "nuevdb/EventDisplayBase/View3D.h"
35 #include "nusimdata/SimulationBase/MCParticle.h"
36 #include "nusimdata/SimulationBase/MCTruth.h"
37 
38 #include "art/Framework/Principal/Event.h"
39 #include "art/Framework/Principal/DataViewImpl.h" // Missing from View.h
40 #include "art/Framework/Principal/View.h"
41 #include "art/Framework/Services/Registry/ServiceHandle.h"
42 #include "messagefacility/MessageLogger/MessageLogger.h"
43 
44 namespace {
45  // Utility function to make uniform error messages.
46  void
47  writeErrMsg(const char* fcn, cet::exception const& e)
48  {
49  mf::LogWarning("SimulationDrawer") << "SimulationDrawer::" << fcn << " failed with message:\n"
50  << e;
51  }
52 }
53 
54 namespace evd {
55 
57  {
58  // For now only draw cryostat=0.
59  art::ServiceHandle<geo::Geometry const> geom;
60  minx = 1e9;
61  maxx = -1e9;
62  miny = 1e9;
63  maxy = -1e9;
64  minz = 1e9;
65  maxz = -1e9;
66 
67  // This is looking to find the range of the complete active volume... this may not be the
68  // best way to do this...
69  for (size_t cryoIdx = 0; cryoIdx < geom->Ncryostats(); cryoIdx++) {
70  const geo::CryostatGeo& cryoGeo = geom->Cryostat(cryoIdx);
71 
72  for (size_t tpcIdx = 0; tpcIdx < cryoGeo.NTPC(); tpcIdx++) {
73  const geo::TPCGeo& tpc = cryoGeo.TPC(tpcIdx);
74 
75  mf::LogDebug("SimulationDrawer")
76  << "Cryo/TPC idx: " << cryoIdx << "/" << tpcIdx << ", TPC center: " << tpc.GetCenter()[0]
77  << ", " << tpc.GetCenter()[1] << ", " << tpc.GetCenter()[2] << std::endl;
78  mf::LogDebug("SimulationDrawer")
79  << " TPC Active center: " << tpc.GetActiveVolumeCenter()[0] << ", "
80  << tpc.GetActiveVolumeCenter()[1] << ", " << tpc.GetActiveVolumeCenter()[2]
81  << ", H/W/L: " << tpc.ActiveHalfHeight() << "/" << tpc.ActiveHalfWidth() << "/"
82  << tpc.ActiveLength() << std::endl;
83 
84  if (minx > tpc.GetCenter()[0] - tpc.HalfWidth())
85  minx = tpc.GetCenter()[0] - tpc.HalfWidth();
86  if (maxx < tpc.GetCenter()[0] + tpc.HalfWidth())
87  maxx = tpc.GetCenter()[0] + tpc.HalfWidth();
88  if (miny > tpc.GetCenter()[1] - tpc.HalfHeight())
89  miny = tpc.GetCenter()[1] - tpc.HalfHeight();
90  if (maxy < tpc.GetCenter()[1] + tpc.HalfHeight())
91  maxy = tpc.GetCenter()[1] + tpc.HalfHeight();
92  if (minz > tpc.GetCenter()[2] - tpc.Length() / 2.)
93  minz = tpc.GetCenter()[2] - tpc.Length() / 2.;
94  if (maxz < tpc.GetCenter()[2] + tpc.Length() / 2.)
95  maxz = tpc.GetCenter()[2] + tpc.Length() / 2.;
96 
97  mf::LogDebug("SimulationDrawer")
98  << " minx/maxx: " << minx << "/" << maxx << ", miny/maxy: " << miny << "/" << maxy
99  << ", minz/miny: " << minz << "/" << maxz << std::endl;
100  }
101  }
102  }
103 
104  //......................................................................
105 
107 
108  //......................................................................
109 
110  void
111  SimulationDrawer::MCTruthShortText(const art::Event& evt, evdb::View2D* view)
112  {
113 
114  if (evt.isRealData()) return;
115 
116  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
117  // Skip drawing if option is turned off
118  if (!drawopt->fShowMCTruthText) return;
119 
120  std::vector<const simb::MCTruth*> mctruth;
121  this->GetMCTruth(evt, mctruth);
122 
123  for (unsigned int i = 0; i < mctruth.size(); ++i) {
124  std::string mctext;
125  bool firstin = true;
126  bool firstout = true;
127  std::string origin;
128  std::string incoming;
129  std::string outgoing;
130  // Label cosmic rays -- others are pretty obvious
131  if (mctruth[i]->Origin() == simb::kCosmicRay) origin = "c-ray: ";
132  int jmax = TMath::Min(20, mctruth[i]->NParticles());
133  for (int j = 0; j < jmax; ++j) {
134  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
135  char buff[1024];
136  if (p.P() > 0.05) {
137  sprintf(buff,
138  "#color[%d]{%s #scale[0.75]{[%.1f GeV/c]}}",
139  Style::ColorFromPDG(p.PdgCode()),
140  Style::LatexName(p.PdgCode()),
141  p.P());
142  }
143  else {
144  sprintf(buff,
145  "#color[%d]{%s}",
146  Style::ColorFromPDG(p.PdgCode()),
147  Style::LatexName(p.PdgCode()));
148  }
149  if (p.StatusCode() == 0) {
150  if (firstin == false) incoming += " + ";
151  incoming += buff;
152  firstin = false;
153  }
154  if (p.StatusCode() == 1) {
155  if (firstout == false) outgoing += " + ";
156  outgoing += buff;
157  firstout = false;
158  }
159  } // loop on j particles
160  if (origin == "" && incoming == "") { mctext = outgoing; }
161  else {
162  mctext = origin + incoming + " #rightarrow " + outgoing;
163  }
164  TLatex& latex = view->AddLatex(0.03, 0.2, mctext.c_str());
165  latex.SetTextSize(0.6);
166 
167  } // loop on i mctruth objects
168  }
169 
170  //......................................................................
171 
172  void
173  SimulationDrawer::MCTruthLongText(const art::Event& evt, evdb::View2D* /*view*/)
174  {
175  if (evt.isRealData()) return;
176 
177  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
178  // Skip drawing if option is turned off
179  if (!drawopt->fShowMCTruthText) return;
180 
181  std::vector<const simb::MCTruth*> mctruth;
182  this->GetMCTruth(evt, mctruth);
183  std::cout << "\nMCTruth Ptcl trackID PDG P T Moth Process\n";
184  for (unsigned int i = 0; i < mctruth.size(); ++i) {
185  for (int j = 0; j < mctruth[i]->NParticles(); ++j) {
186  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
187  if (p.StatusCode() == 0 || p.StatusCode() == 1) {
188  int KE = 1000 * (p.E() - p.Mass());
189  std::cout << std::right << std::setw(7) << i << std::setw(5) << j << std::setw(8)
190  << p.TrackId() << " " << std::setw(14) << Style::LatexName(p.PdgCode())
191  << std::setw(7) << int(1000 * p.P()) << std::setw(7) << KE << std::setw(7)
192  << p.Mother() << " " << p.Process() << "\n";
193  }
194  /*
195  std::cout << Style::LatexName(p.PdgCode())
196  << "\t\t" << p.E() << " GeV"
197  << "\t" << "(" << p.P() << " GeV/c)"
198  << std::endl;
199 */
200  } // loop on j particles in list
201  }
202  std::cout << "Note: Momentum, P, and kinetic energy, T, in MeV/c\n";
203  } // MCTruthLongText
204 
205  //......................................................................
206  //this is the method you would use to color code hits by the MC truth pdg value
207  void
208  SimulationDrawer::MCTruthVectors2D(const art::Event& evt, evdb::View2D* view, unsigned int plane)
209  {
210  if (evt.isRealData()) return;
211 
212  const spacecharge::SpaceCharge* sce = lar::providerFrom<spacecharge::SpaceChargeService>();
213 
214  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
215  // If the option is turned off, there's nothing to do
216  if (drawopt->fShowMCTruthVectors > 3) {
217  std::cout << "Unsupported ShowMCTruthVectors option (> 2)\n";
218  return;
219  }
220 
221  art::ServiceHandle<geo::Geometry const> geo;
222  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
223  // get the x position of the plane in question
224  double xyz1[3] = {0.};
225  double xyz2[3] = {0.};
226 
227  // shift the truth by a fixed amount so it doesn't overlay the reco
228  double xShift = -2;
229 
230  static bool first = true;
231  if (first) {
232  std::cout
233  << "******** Show MCTruth (Genie) particles when ShowMCTruthVectors = 1 or 3 ******** \n";
234  std::cout << " MCTruth vectors corrected for space charge? " << sce->EnableCorrSCE()
235  << " and shifted by " << xShift << " cm in X\n";
236  std::cout << " Neutrons and photons drawn with dotted lines. \n";
237  std::cout << " Red = e+/-, nue, nuebar. Blue = mu+/-, numu, numubar. Green = tau+/-, nutau, "
238  "nutaubar.\n";
239  std::cout << " Yellow = photons. Magenta = pions, protons and nuetrons.\n";
240  std::cout << "******** Show MCParticle (Geant) decay photons (e.g. from pizeros) when "
241  "ShowMCTruthVectors = 2 or 3 ******** \n";
242  std::cout << " Photons > 50 MeV are drawn as dotted lines corrected for space charge and "
243  "are not shifted.\n";
244  std::cout << " Decay photon end points are drawn at 2 interaction lengths (44 cm) from the "
245  "start position.\n";
246  std::cout << " Color: Green = (50 < E_photon < 100 MeV), Blue = (100 MeV < E_photon < 200 "
247  "MeV), Red = (E_photon > 300 MeV).\n";
248  }
249 
250  bool showTruth = (drawopt->fShowMCTruthVectors == 1 || drawopt->fShowMCTruthVectors == 3);
251  bool showPhotons = (drawopt->fShowMCTruthVectors > 1);
252 
253  auto const detProp =
254  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(evt);
255 
256  if (showTruth) {
257  // Unpack and draw the MC vectors
258  std::vector<const simb::MCTruth*> mctruth;
259  this->GetMCTruth(evt, mctruth);
260 
261  for (size_t i = 0; i < mctruth.size(); ++i) {
262  if (mctruth[i]->Origin() == simb::kCosmicRay) continue;
263  for (int j = 0; j < mctruth[i]->NParticles(); ++j) {
264  const simb::MCParticle& p = mctruth[i]->GetParticle(j);
265 
266  // Skip all but incoming and out-going particles
267  if (!(p.StatusCode() == 0 || p.StatusCode() == 1)) continue;
268 
269  double r = p.P() * 10.0; // Scale length so 10 cm = 1 GeV/c
270 
271  if (p.StatusCode() == 0) r = -r; // Flip for incoming particles
272 
273  auto gptStart = geo::Point_t(p.Vx(), p.Vy(), p.Vz());
274  geo::Point_t sceOffset{0, 0, 0};
275  if (sce->EnableCorrSCE()) sceOffset = sce->GetPosOffsets(gptStart);
276  xyz1[0] = p.Vx() - sceOffset.X();
277  xyz1[1] = p.Vy() + sceOffset.Y();
278  xyz1[2] = p.Vz() + sceOffset.Z();
279  xyz2[0] = xyz1[0] + r * p.Px() / p.P();
280  xyz2[1] = xyz1[1] + r * p.Py() / p.P();
281  xyz2[2] = xyz1[2] + r * p.Pz() / p.P();
282 
283  double w1 =
284  geo->WireCoordinate(xyz1[1], xyz1[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
285  double w2 =
286  geo->WireCoordinate(xyz2[1], xyz2[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
287 
288  double time =
289  detProp.ConvertXToTicks(xyz1[0] + xShift, (int)plane, rawopt->fTPC, rawopt->fCryostat);
290  double time2 =
291  detProp.ConvertXToTicks(xyz2[0] + xShift, (int)plane, rawopt->fTPC, rawopt->fCryostat);
292 
293  if (rawopt->fAxisOrientation < 1) {
294  TLine& l = view->AddLine(w1, time, w2, time2);
295  evd::Style::FromPDG(l, p.PdgCode());
296  }
297  else {
298  TLine& l = view->AddLine(time, w1, time2, w2);
299  evd::Style::FromPDG(l, p.PdgCode());
300  }
301  //
302 
303  } // loop on j particles in list
304  } // loop on truths
305  } // showTruth
306 
307  if (showPhotons) {
308  // draw pizero photons with T > 30 MeV
309  art::ServiceHandle<cheat::ParticleInventoryService const> pi_serv;
310  sim::ParticleList const& plist = pi_serv->ParticleList();
311  if (plist.empty()) return;
312  // photon interaction length approximate
313  double r = 44;
314  for (sim::ParticleList::const_iterator ipart = plist.begin(); ipart != plist.end(); ++ipart) {
315  simb::MCParticle* p = (*ipart).second;
316  int trackID = p->TrackId();
317  art::Ptr<simb::MCTruth> theTruth = pi_serv->TrackIdToMCTruth_P(trackID);
318  if (theTruth->Origin() == simb::kCosmicRay) continue;
319  if (p->PdgCode() != 22) continue;
320  if (p->Process() != "Decay") continue;
321  int TMeV = 1000 * (p->E() - p->Mass());
322  if (TMeV < 30) continue;
323  auto gptStart = geo::Point_t(p->Vx(), p->Vy(), p->Vz());
324  geo::Point_t sceOffset{0, 0, 0};
325  if (sce->EnableCorrSCE()) sceOffset = sce->GetPosOffsets(gptStart);
326  xyz1[0] = p->Vx() - sceOffset.X();
327  xyz1[1] = p->Vy() + sceOffset.Y();
328  xyz1[2] = p->Vz() + sceOffset.Z();
329  xyz2[0] = xyz1[0] + r * p->Px() / p->P();
330  xyz2[1] = xyz1[1] + r * p->Py() / p->P();
331  xyz2[2] = xyz1[2] + r * p->Pz() / p->P();
332  double w1 =
333  geo->WireCoordinate(xyz1[1], xyz1[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
334  double t1 = detProp.ConvertXToTicks(xyz1[0], (int)plane, rawopt->fTPC, rawopt->fCryostat);
335  double w2 =
336  geo->WireCoordinate(xyz2[1], xyz2[2], (int)plane, rawopt->fTPC, rawopt->fCryostat);
337  double t2 = detProp.ConvertXToTicks(xyz2[0], (int)plane, rawopt->fTPC, rawopt->fCryostat);
338  TLine& l = view->AddLine(w1, t1, w2, t2);
339  l.SetLineWidth(2);
340  l.SetLineStyle(kDotted);
341  if (TMeV < 100) { l.SetLineColor(kGreen); }
342  else if (TMeV < 200) {
343  l.SetLineColor(kBlue);
344  }
345  else {
346  l.SetLineColor(kRed);
347  }
348  } // ipart
349  } // showPhotons
350 
351  first = false;
352 
353  } // MCTruthVectors2D
354 
355  //......................................................................
356  //this method draws the true particle trajectories in 3D
357  void
358  SimulationDrawer::MCTruth3D(const art::Event& evt, evdb::View3D* view)
359  {
360  if (evt.isRealData()) return;
361 
362  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
363  // If the option is turned off, there's nothing to do
364  if (!drawopt->fShowMCTruthTrajectories) return;
365 
366  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
367  auto const detProp =
368  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(evt, clockData);
369  art::ServiceHandle<geo::Geometry const> geom;
370 
371  // get the particles from the Geant4 step
372  std::vector<const simb::MCParticle*> plist;
373  this->GetParticle(evt, plist);
374 
375  // Define a couple of colors for neutrals and if we gray it out...
376  int neutralColor(12);
377  int grayedColor(15);
378  int neutrinoColor(38);
379 
380  // Use the LArVoxelList to get the true energy deposition locations as opposed to using MCTrajectories
381  const sim::LArVoxelList voxels =
382  sim::SimListUtils::GetLArVoxelList(evt, drawopt->fG4ModuleLabel.label());
383 
384  mf::LogDebug("SimulationDrawer")
385  << "Starting loop over " << plist.size() << " McParticles, voxel list size is "
386  << voxels.size() << std::endl;
387 
388  // Using the voxel information can be slow (see previous implementation of this code).
389  // In order to speed things up we have modified the strategy:
390  // 1) Make one pass through the list of voxels
391  // 2) For each voxel, keep track of the MCParticle contributing energy to it and it's position
392  // which is done by keeping a map between the MCParticle and a vector of positions
393  // 3) Then loop through the map to draw the particle trajectories.
394  // One caveat is the need for MCParticles... and the voxels contain the track ids. So we'll need one
395  // more loop to make a map of track id's and MCParticles.
396 
397  // First up is to build the map between track id's and associated MCParticles so we can recover when looping over voxels
398  std::map<int, const simb::MCParticle*> trackToMcParticleMap;
399 
400  // Should we display the trajectories too?
401  double minPartEnergy(0.01);
402 
403  for (size_t p = 0; p < plist.size(); ++p) {
404  trackToMcParticleMap[plist[p]->TrackId()] = plist[p];
405 
406  // Quick loop through to draw trajectories...
407  if (drawopt->fShowMCTruthTrajectories) {
408  // Is there an associated McTrajectory?
409  const simb::MCParticle* mcPart = plist[p];
410  const simb::MCTrajectory& mcTraj = mcPart->Trajectory();
411 
412  int pdgCode(mcPart->PdgCode());
413  int colorIdx(evd::Style::ColorFromPDG(mcPart->PdgCode()));
414  TParticlePDG* partPDG(TDatabasePDG::Instance()->GetParticle(pdgCode));
415  double partCharge = partPDG ? partPDG->Charge() : 0.;
416  double partEnergy = mcPart->E();
417 
418  if (!drawopt->fShowMCTruthColors) colorIdx = grayedColor;
419 
420  if (!mcTraj.empty() && partEnergy > minPartEnergy && mcPart->TrackId() < 100000000) {
421  double g4Ticks(clockData.TPCG4Time2Tick(mcPart->T()) - trigger_offset(clockData));
422  double xOffset = 0.;
423  double xPosMinTick = 0.;
424  double xPosMaxTick = std::numeric_limits<double>::max();
425 
426  // collect the points from this particle
427  int numTrajPoints = mcTraj.size();
428 
429  std::unique_ptr<double[]> hitPositions(new double[3 * numTrajPoints]);
430  int hitCount(0);
431 
432  for (int hitIdx = 0; hitIdx < numTrajPoints; hitIdx++) {
433  double xPos = mcTraj.X(hitIdx);
434  double yPos = mcTraj.Y(hitIdx);
435  double zPos = mcTraj.Z(hitIdx);
436 
437  // If we have cosmic rays then we need to get the offset which allows translating from
438  // when they were generated vs when they were tracked.
439  // Note that this also explicitly checks that they are in a TPC volume
440  geo::Point_t hitLocation(xPos, yPos, zPos);
441 
442  try {
443  geo::TPCID tpcID = geom->PositionToTPCID(hitLocation);
444  geo::PlaneID planeID(tpcID, 0);
445 
446  xPosMinTick = detProp.ConvertTicksToX(0, planeID);
447  xPosMaxTick = detProp.ConvertTicksToX(detProp.NumberTimeSamples(), planeID);
448  xOffset = detProp.ConvertTicksToX(g4Ticks, planeID) - xPosMinTick;
449 
450  if (xPosMaxTick < xPosMinTick) std::swap(xPosMinTick, xPosMaxTick);
451  }
452  catch (...) {
453  continue;
454  }
455 
456  // Now move the hit position to correspond to the timing
457  xPos += xOffset;
458 
459  // Check fiducial limits
460  if (xPos > xPosMinTick && xPos < xPosMaxTick) {
461  // Check for space charge offsets
462  // if (spaceCharge->EnableSimEfieldSCE())
463  // {
464  // std::vector<double> offsetVec = spaceCharge->GetPosOffsets(xPos,yPos,zPos);
465  // xPos += offsetVec[0] - 0.7;
466  // yPos -= offsetVec[1];
467  // zPos -= offsetVec[2];
468  // }
469 
470  hitPositions[3 * hitCount] = xPos;
471  hitPositions[3 * hitCount + 1] = yPos;
472  hitPositions[3 * hitCount + 2] = zPos;
473  hitCount++;
474  }
475  }
476 
477  TPolyLine3D& pl(view->AddPolyLine3D(1, colorIdx, 1, 1));
478 
479  // Draw neutrals as a gray dotted line to help fade into background a bit...
480  if (partCharge == 0.) {
481  pl.SetLineColor(neutralColor);
482  pl.SetLineStyle(3);
483  pl.SetLineWidth(1);
484  }
485  pl.SetPolyLine(hitCount, hitPositions.get(), "");
486  }
487  }
488  }
489 
490  // Now we set up and build the map between MCParticles and a vector of positions obtained from the voxels
491  std::map<const simb::MCParticle*, std::vector<std::vector<double>>> partToPosMap;
492 
494  for (vxitr = voxels.begin(); vxitr != voxels.end(); vxitr++) {
495  const sim::LArVoxelData& vxd = (*vxitr).second;
496 
497  for (size_t partIdx = 0; partIdx < vxd.NumberParticles(); partIdx++) {
498  if (vxd.Energy(partIdx) > drawopt->fMinEnergyDeposition) {
499  int trackId = vxd.TrackID(partIdx);
500 
501  // It can be in some instances that mcPart here could be zero.
502  const simb::MCParticle* mcPart = trackToMcParticleMap[trackId];
503 
504  partToPosMap[mcPart].push_back(std::vector<double>(3));
505 
506  partToPosMap[mcPart].back()[0] = vxd.VoxelID().X();
507  partToPosMap[mcPart].back()[1] = vxd.VoxelID().Y();
508  partToPosMap[mcPart].back()[2] = vxd.VoxelID().Z();
509  }
510  } // end if this track id is in the current voxel
511  } // end loop over voxels
512 
513  // Finally ready for the main event! Simply loop through the map between MCParticle and positions to
514  // draw the trajectories
515  std::map<const simb::MCParticle*, std::vector<std::vector<double>>>::iterator partToPosMapItr;
516 
517  for (partToPosMapItr = partToPosMap.begin(); partToPosMapItr != partToPosMap.end();
518  partToPosMapItr++) {
519  // Recover the McParticle, we'll need to access several data members so may as well dereference it
520  const simb::MCParticle* mcPart = partToPosMapItr->first;
521 
522  // Apparently, it can happen that we get a null pointer here or maybe no points to plot
523  if (!mcPart || partToPosMapItr->second.empty()) continue;
524 
525  double g4Ticks(clockData.TPCG4Time2Tick(mcPart->T()) - trigger_offset(clockData));
526  double xOffset = 0.;
527  double xPosMinTick = 0.;
528  double xPosMaxTick = std::numeric_limits<double>::max();
529 
530  int colorIdx(evd::Style::ColorFromPDG(mcPart->PdgCode()));
531  int markerIdx(kFullDotSmall);
532  int markerSize(2);
533 
534  if (!drawopt->fShowMCTruthFullSize) {
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  const std::vector<double>& posVec = partToPosMapItr->second[posIdx];
546 
547  // Check xOffset state and set if necessary
548  geo::Point_t hitLocation(posVec[0], posVec[1], posVec[2]);
549 
550  try {
551  geo::TPCID tpcID = geom->PositionToTPCID(hitLocation);
552  geo::PlaneID planeID(tpcID, 0);
553 
554  xPosMinTick = detProp.ConvertTicksToX(0, planeID);
555  xPosMaxTick = detProp.ConvertTicksToX(detProp.NumberTimeSamples(), planeID);
556  xOffset = detProp.ConvertTicksToX(g4Ticks, planeID) - xPosMinTick;
557 
558  if (xPosMaxTick < xPosMinTick) std::swap(xPosMinTick, xPosMaxTick);
559  }
560  catch (...) {
561  continue;
562  }
563 
564  double xCoord = posVec[0] + xOffset;
565 
566  // If a voxel records an energy deposit then must have been in the TPC
567  // But because things get shifted still need to cut off if outside drift
568  if (xCoord > xPosMinTick && xCoord < xPosMaxTick) {
569  hitPositions[3 * hitCount] = xCoord;
570  hitPositions[3 * hitCount + 1] = posVec[1];
571  hitPositions[3 * hitCount + 2] = posVec[2];
572  hitCount++;
573  }
574  }
575 
576  TPolyMarker3D& pm = view->AddPolyMarker3D(1, colorIdx, markerIdx, markerSize);
577  pm.SetPolyMarker(hitCount, hitPositions.get(), markerIdx);
578  }
579 
580  // Finally, let's see if we can draw the incoming particle from the MCTruth information
581  std::vector<const simb::MCTruth*> mctruth;
582  this->GetMCTruth(evt, mctruth);
583 
584  // Loop through the MCTruth vector
585  for (unsigned int idx = 0; idx < mctruth.size(); idx++) {
586  // Go through each MCTruth object in the list
587  for (int particleIdx = 0; particleIdx < mctruth[idx]->NParticles(); particleIdx++) {
588  const simb::MCParticle& mcPart = mctruth[idx]->GetParticle(particleIdx);
589 
590  // A negative mother id indicates the "primary" particle
591  if (mcPart.Mother() == -1 && mcPart.StatusCode() == 0) {
592  mf::LogDebug("SimulationDrawer") << mcPart << std::endl;
593 
594  // Get position vector
595  TVector3 particlePosition(mcPart.Vx(), mcPart.Vy(), mcPart.Vz());
596 
597  // Get direction vector (in opposite direction)
598  TVector3 oppPartDir(-mcPart.Px(), -mcPart.Py(), -mcPart.Pz());
599 
600  if (oppPartDir.Mag2() > 0.) oppPartDir.SetMag(1.);
601 
602  double arcLenToDraw = -particlePosition.Z() / oppPartDir.CosTheta();
603 
604  // No point in drawing if arc length is zero (e.g. Ar nucleus)
605  if (arcLenToDraw > 0.) {
606  // Draw the line, use an off color to be unique
607  TPolyLine3D& pl(view->AddPolyLine3D(2, neutrinoColor, 1, 2));
608 
609  pl.SetPoint(0, particlePosition.X(), particlePosition.Y(), particlePosition.Z());
610 
611  particlePosition += std::min(arcLenToDraw + 10., 1000.) * oppPartDir;
612 
613  pl.SetPoint(1, particlePosition.X(), particlePosition.Y(), particlePosition.Z());
614  }
615  }
616  // The particles we want to draw will be early in the list so break out if we didn't find them
617  else
618  break;
619  } // loop on particles in list
620  }
621 
622  return;
623  }
624 
625  //......................................................................
626  //this method draws the true particle trajectories in 3D Ortho view.
627  void
629  evd::OrthoProj_t proj,
630  double msize,
631  evdb::View2D* view)
632  {
633  if (evt.isRealData()) return;
634 
635  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
636 
637  // If the option is turned off, there's nothing to do
638  if (!drawopt->fShowMCTruthTrajectories) return;
639 
640  geo::GeometryCore const* geom = lar::providerFrom<geo::Geometry>();
641  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
642  auto const detProp =
643  art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(evt, clockData);
644 
645  // get the particles from the Geant4 step
646  std::vector<const simb::MCParticle*> plist;
647  this->GetParticle(evt, plist);
648 
649  // Useful variables
650 
651  double xMinimum(-1. * (maxx - minx));
652  double xMaximum(2. * (maxx - minx));
653 
654  // Use the LArVoxelList to get the true energy deposition locations as
655  // opposed to using MCTrajectories
656  // GetLarVoxelList should be adjusted to take an InputTag instead of strange.
657  const sim::LArVoxelList voxels =
658  sim::SimListUtils::GetLArVoxelList(evt, drawopt->fSimChannelLabel.encode());
659 
660  mf::LogDebug("SimulationDrawer")
661  << "Starting loop over " << plist.size() << " McParticles, voxel list size is "
662  << voxels.size() << std::endl;
663 
664  // Using the voxel information can be slow (see previous implementation of this code).
665  // In order to speed things up we have modified the strategy:
666  // 1) Make one pass through the list of voxels
667  // 2) For each voxel, keep track of the MCParticle contributing energy to it and it's position
668  // which is done by keeping a map between the MCParticle and a vector of positions
669  // 3) Then loop through the map to draw the particle trajectories.
670  // One caveat is the need for MCParticles... and the voxels contain the track ids. So we'll need one
671  // more loop to make a map of track id's and MCParticles.
672 
673  // First up is to build the map between track id's and associated MCParticles so we can recover when looping over voxels
674  std::map<int, const simb::MCParticle*> trackToMcParticleMap;
675 
676  // Should we display the trajectories too?
677  bool displayMcTrajectories(true);
678  double minPartEnergy(0.025);
679 
680  double tpcminx = 1.0;
681  double tpcmaxx = -1.0;
682  double xOffset = 0.0;
683  double g4Ticks = 0.0;
684  double coeff = 0.0;
685  double readoutwindowsize = 0.0;
686  double vtx[3] = {0.0, 0.0, 0.0};
687  for (size_t p = 0; p < plist.size(); ++p) {
688  trackToMcParticleMap[plist[p]->TrackId()] = plist[p];
689 
690  // Quick loop through to drawn trajectories...
691  if (displayMcTrajectories) {
692  // Is there an associated McTrajectory?
693  const simb::MCParticle* mcPart = plist[p];
694  const simb::MCTrajectory& mcTraj = mcPart->Trajectory();
695 
696  int pdgCode(mcPart->PdgCode());
697  TParticlePDG* partPDG(TDatabasePDG::Instance()->GetParticle(pdgCode));
698  double partCharge = partPDG ? partPDG->Charge() : 0.;
699  double partEnergy = mcPart->E();
700 
701  if (!mcTraj.empty() && partEnergy > minPartEnergy && mcPart->TrackId() < 100000000) {
702  // collect the points from this particle
703  int numTrajPoints = mcTraj.size();
704 
705  std::unique_ptr<double[]> hitPosX(new double[numTrajPoints]);
706  std::unique_ptr<double[]> hitPosY(new double[numTrajPoints]);
707  std::unique_ptr<double[]> hitPosZ(new double[numTrajPoints]);
708  int hitCount(0);
709 
710  double xPos = mcTraj.X(0);
711  double yPos = mcTraj.Y(0);
712  double zPos = mcTraj.Z(0);
713 
714  tpcminx = 1.0;
715  tpcmaxx = -1.0;
716  xOffset = 0.0;
717  g4Ticks = 0.0;
718  vtx[0] = 0.0;
719  vtx[1] = 0.0;
720  vtx[2] = 0.0;
721  coeff = 0.0;
722  readoutwindowsize = 0.0;
723  for (int hitIdx = 0; hitIdx < numTrajPoints; hitIdx++) {
724  xPos = mcTraj.X(hitIdx);
725  yPos = mcTraj.Y(hitIdx);
726  zPos = mcTraj.Z(hitIdx);
727 
728  // If the original simulated hit did not occur in the TPC volume then don't draw it
729  if (xPos < minx || xPos > maxx || yPos < miny || yPos > maxy || zPos < minz ||
730  zPos > maxz)
731  continue;
732 
733  if ((xPos < tpcminx) || (xPos > tpcmaxx)) {
734  vtx[0] = xPos;
735  vtx[1] = yPos;
736  vtx[2] = zPos;
737  geo::TPCID tpcid = geom->FindTPCAtPosition(vtx);
738  unsigned int cryo = geom->FindCryostatAtPosition(vtx);
739 
740  if (tpcid.isValid) {
741  unsigned int tpc = tpcid.TPC;
742  const geo::TPCGeo& tpcgeo = geom->GetElement(tpcid);
743  tpcminx = tpcgeo.MinX();
744  tpcmaxx = tpcgeo.MaxX();
745 
746  coeff = detProp.GetXTicksCoefficient(tpc, cryo);
747  readoutwindowsize =
748  detProp.ConvertTicksToX(detProp.ReadOutWindowSize(), 0, tpc, cryo);
749 
750  // The following is meant to get the correct offset for drawing
751  // the particle trajectory In particular, the cosmic rays will
752  // not be correctly placed without this
753  g4Ticks = clockData.TPCG4Time2Tick(mcPart->T()) +
754  detProp.GetXTicksOffset(0, tpc, cryo) - trigger_offset(clockData);
755 
756  xOffset = detProp.ConvertTicksToX(g4Ticks, 0, tpc, cryo);
757  }
758  else {
759  xOffset = 0;
760  tpcminx = 1.0;
761  tpcmaxx = -1.0;
762  coeff = 0.0;
763  readoutwindowsize = 0.0;
764  }
765  }
766 
767  // Now move the hit position to correspond to the timing
768  xPos += xOffset;
769 
770  bool inreadoutwindow = false;
771  if (coeff < 0) {
772  if ((xPos > readoutwindowsize) && (xPos < tpcmaxx)) inreadoutwindow = true;
773  }
774  else if (coeff > 0) {
775  if ((xPos > tpcminx) && (xPos < readoutwindowsize)) inreadoutwindow = true;
776  }
777 
778  if (!inreadoutwindow) continue;
779 
780  // Check fiducial limits
781  if (xPos > xMinimum && xPos < xMaximum) {
782  hitPosX[hitCount] = xPos;
783  hitPosY[hitCount] = yPos;
784  hitPosZ[hitCount] = zPos;
785  hitCount++;
786  }
787  }
788 
789  TPolyLine& pl = view->AddPolyLine(
790  1, evd::Style::ColorFromPDG(mcPart->PdgCode()), 1, 1); //kFullCircle, msize);
791 
792  // Draw neutrals as a gray dotted line to help fade into background a bit...
793  if (partCharge == 0.) {
794  pl.SetLineColor(13);
795  pl.SetLineStyle(3);
796  pl.SetLineWidth(1);
797  }
798 
799  if (proj == evd::kXY)
800  pl.SetPolyLine(hitCount, hitPosX.get(), hitPosY.get(), "");
801  else if (proj == evd::kXZ)
802  pl.SetPolyLine(hitCount, hitPosZ.get(), hitPosX.get(), "");
803  else if (proj == evd::kYZ)
804  pl.SetPolyLine(hitCount, hitPosZ.get(), hitPosY.get(), "");
805  }
806  }
807  }
808 
809  // Now we set up and build the map between MCParticles and a vector of positions obtained from the voxels
810  std::map<const simb::MCParticle*, std::vector<std::vector<double>>> partToPosMap;
811 
813  for (vxitr = voxels.begin(); vxitr != voxels.end(); vxitr++) {
814  const sim::LArVoxelData& vxd = (*vxitr).second;
815 
816  for (size_t partIdx = 0; partIdx < vxd.NumberParticles(); partIdx++) {
817  if (vxd.Energy(partIdx) > drawopt->fMinEnergyDeposition) {
818  int trackId = vxd.TrackID(partIdx);
819 
820  // It can be in some instances that mcPart here could be zero.
821  const simb::MCParticle* mcPart = trackToMcParticleMap[trackId];
822 
823  partToPosMap[mcPart].push_back(std::vector<double>(3));
824 
825  partToPosMap[mcPart].back()[0] = vxd.VoxelID().X();
826  partToPosMap[mcPart].back()[1] = vxd.VoxelID().Y();
827  partToPosMap[mcPart].back()[2] = vxd.VoxelID().Z();
828  }
829  } // end if this track id is in the current voxel
830  } // end loop over voxels
831 
832  // Finally ready for the main event! Simply loop through the map between MCParticle and positions to
833  // draw the trajectories
834  std::map<const simb::MCParticle*, std::vector<std::vector<double>>>::iterator partToPosMapItr;
835 
836  for (partToPosMapItr = partToPosMap.begin(); partToPosMapItr != partToPosMap.end();
837  partToPosMapItr++) {
838  // Recover the McParticle, we'll need to access several data members so may as well dereference it
839  const simb::MCParticle* mcPart = partToPosMapItr->first;
840 
841  // Apparently, it can happen that we get a null pointer here or maybe no points to plot
842  if (!mcPart || partToPosMapItr->second.empty()) continue;
843 
844  tpcminx = 1.0;
845  tpcmaxx = -1.0;
846  xOffset = 0.0;
847  g4Ticks = 0.0;
848  std::vector<std::array<double, 3>> posVecCorr;
849  posVecCorr.reserve(partToPosMapItr->second.size());
850  coeff = 0.0;
851  readoutwindowsize = 0.0;
852 
853  // Now loop over points and add to trajectory
854  for (size_t posIdx = 0; posIdx < partToPosMapItr->second.size(); posIdx++) {
855  const std::vector<double>& posVec = partToPosMapItr->second[posIdx];
856 
857  if ((posVec[0] < tpcminx) || (posVec[0] > tpcmaxx)) {
858  vtx[0] = posVec[0];
859  vtx[1] = posVec[1];
860  vtx[2] = posVec[2];
861  geo::TPCID tpcid = geom->FindTPCAtPosition(vtx);
862  unsigned int cryo = geom->FindCryostatAtPosition(vtx);
863 
864  if (tpcid.isValid) {
865  unsigned int tpc = tpcid.TPC;
866 
867  const geo::TPCGeo& tpcgeo = geom->GetElement(tpcid);
868  tpcminx = tpcgeo.MinX();
869  tpcmaxx = tpcgeo.MaxX();
870 
871  coeff = detProp.GetXTicksCoefficient(tpc, cryo);
872  readoutwindowsize = detProp.ConvertTicksToX(detProp.ReadOutWindowSize(), 0, tpc, cryo);
873  // The following is meant to get the correct offset for drawing the
874  // particle trajectory In particular, the cosmic rays will not be
875  // correctly placed without this
876  g4Ticks = clockData.TPCG4Time2Tick(mcPart->T()) +
877  detProp.GetXTicksOffset(0, tpc, cryo) - trigger_offset(clockData);
878 
879  xOffset = detProp.ConvertTicksToX(g4Ticks, 0, tpc, cryo);
880  }
881  else {
882  xOffset = 0;
883  tpcminx = 1.0;
884  tpcmaxx = -1.0;
885  coeff = 0.0;
886  readoutwindowsize = 0.0;
887  }
888  }
889 
890  double xCoord = posVec[0] + xOffset;
891 
892  bool inreadoutwindow = false;
893  if (coeff < 0) {
894  if ((xCoord > readoutwindowsize) && (xCoord < tpcmaxx)) inreadoutwindow = true;
895  }
896  else if (coeff > 0) {
897  if ((xCoord > tpcminx) && (xCoord < readoutwindowsize)) inreadoutwindow = true;
898  }
899 
900  if (inreadoutwindow && (xCoord > xMinimum && xCoord < xMaximum)) {
901  posVecCorr.push_back({{xCoord, posVec[1], posVec[2]}});
902  }
903  }
904 
905  TPolyMarker& pm = view->AddPolyMarker(posVecCorr.size(),
906  evd::Style::ColorFromPDG(mcPart->PdgCode()),
907  kFullDotMedium,
908  2); //kFullCircle, msize);
909 
910  for (size_t p = 0; p < posVecCorr.size(); ++p) {
911  if (proj == evd::kXY)
912  pm.SetPoint(p, posVecCorr[p][0], posVecCorr[p][1]);
913  else if (proj == evd::kXZ)
914  pm.SetPoint(p, posVecCorr[p][2], posVecCorr[p][0]);
915  else if (proj == evd::kYZ)
916  pm.SetPoint(p, posVecCorr[p][2], posVecCorr[p][1]);
917  }
918  }
919 
920  return;
921  }
922 
923  //......................................................................
924  int
925  SimulationDrawer::GetParticle(const art::Event& evt, std::vector<const simb::MCParticle*>& plist)
926  {
927  plist.clear();
928 
929  if (evt.isRealData()) return 0;
930 
931  art::ServiceHandle<evd::SimulationDrawingOptions const> drawopt;
932 
933  std::vector<const simb::MCParticle*> temp;
934 
935  art::View<simb::MCParticle> plcol;
936  // use get by Type because there should only be one collection of these in the event
937  try {
938  evt.getView(drawopt->fG4ModuleLabel, plcol);
939  for (unsigned int i = 0; i < plcol.vals().size(); ++i) {
940  temp.push_back(plcol.vals().at(i));
941  }
942  temp.swap(plist);
943  }
944  catch (cet::exception& e) {
945  writeErrMsg("GetRawDigits", e);
946  }
947 
948  return plist.size();
949  }
950 
951  //......................................................................
952 
953  int
954  SimulationDrawer::GetMCTruth(const art::Event& evt, std::vector<const simb::MCTruth*>& mcvec)
955  {
956  mcvec.clear();
957 
958  if (evt.isRealData()) return 0;
959 
960  std::vector<const simb::MCTruth*> temp;
961 
962  std::vector<art::Handle<std::vector<simb::MCTruth>>> mctcol;
963  // use get by Type because there should only be one collection of these in the event
964  try {
965  //evt.getManyByType(mctcol);
966  mctcol = evt.getMany<std::vector<simb::MCTruth>>();
967  for (size_t mctc = 0; mctc < mctcol.size(); ++mctc) {
968  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mctcol[mctc];
969 
970  for (size_t i = 0; i < mclistHandle->size(); ++i) {
971  temp.push_back(&(mclistHandle->at(i)));
972  }
973  }
974  temp.swap(mcvec);
975  }
976  catch (cet::exception& e) {
977  writeErrMsg("GetMCTruth", e);
978  }
979 
980  return mcvec.size();
981  }
982 
983  //......................................................................
984 
985  void
986  SimulationDrawer::HiLite(int trkId, bool dohilite)
987  {
988  fHighlite[trkId] = dohilite;
989  }
990 
991 } // namespace
992 ////////////////////////////////////////////////////////////////////////
std::map< int, bool > fHighlite
Point GetActiveVolumeCenter() const
Returns the center of the TPC active volume in world coordinates [cm].
Definition: TPCGeo.h:288
Utilities related to art service access.
void MCTruthLongText(const art::Event &evt, evdb::View2D *view)
CryostatGeo const & GetElement(geo::CryostatID const &cryoid) const
void HiLite(int trkId, bool hlt=true)
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:99
walls no right
Definition: selectors.fcl:105
pdgs p
Definition: selectors.fcl:22
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:88
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
bool isValid
Whether this ID points to a valid element.
Definition: geo_types.h:211
Geometry information for a single TPC.
Definition: TPCGeo.h:38
static sim::LArVoxelList GetLArVoxelList(const art::Event &evt, std::string moduleLabel)
int GetMCTruth(const art::Event &evt, std::vector< const simb::MCTruth * > &mctruth)
double MaxX() const
Returns the world x coordinate of the end of the box.
Definition: BoxBoundedGeo.h:91
static void FromPDG(TLine &line, int pdgcode)
Definition: Style.cxx:139
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
OrthoProj_t
Definition: OrthoProj.h:12
void MCTruth3D(const art::Event &evt, evdb::View3D *view)
iterator begin()
Definition: LArVoxelList.h:131
double Length() const
Length is associated with z coordinate [cm].
Definition: TPCGeo.h:115
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.
Encapsulates the information we want store for a voxel.
virtual bool EnableCorrSCE() const =0
double Y() const
Definition: LArVoxelID.cxx:79
double ActiveHalfWidth() const
Half width (associated with x coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:95
int GetParticle(const art::Event &evt, std::vector< const simb::MCParticle * > &plist)
static int ColorFromPDG(int pdgcode)
Definition: Style.cxx:65
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:181
size_type NumberParticles() const
Definition: LArVoxelData.h:196
const Cut kCosmicRay
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
Description of geometry of one entire detector.
double ActiveLength() const
Length (associated with z coordinate) of active TPC volume [cm].
Definition: TPCGeo.h:103
double Z() const
Definition: LArVoxelID.cxx:86
void MCTruthShortText(const art::Event &evt, evdb::View2D *view)
double HalfHeight() const
Height is associated with y coordinate [cm].
Definition: TPCGeo.h:111
double X() const
Definition: LArVoxelID.cxx:72
virtual geo::Vector_t GetPosOffsets(geo::Point_t const &point) const =0
mapped_type Energy() const
Definition: LArVoxelData.h:203
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Definition: CryostatGeo.cxx:93
static const char * LatexName(int pdgcode)
Convert PDG code to a latex string (root-style)
Definition: Style.cxx:12
Render the objects from the Simulation package.
do i e
int trigger_offset(DetectorClocksData const &data)
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:195
TCEvent evt
Definition: DataStructs.cxx:8
TPCID_t TPC
Index of the TPC within its cryostat.
Definition: geo_types.h:406
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:184
geo::CryostatID::CryostatID_t FindCryostatAtPosition(geo::Point_t const &worldLoc) const
Returns the index of the cryostat at specified location.
esac echo uname r
art framework interface to geometry description
BEGIN_PROLOG could also be cout
double HalfWidth() const
Width is associated with x coordinate [cm].
Definition: TPCGeo.h:107
auto const detProp
constexpr Point origin()
Returns a origin position with a point of the specified type.
Definition: geo_vectors.h:227
Encapsulate the construction of a single detector plane.
const key_type & TrackID(const size_type) const
Point GetCenter() const
Returns the center of the TPC volume in world coordinates [cm].
Definition: TPCGeo.h:779