All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DlVertexingAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoradlcontent/LArVertex/DlVertexingAlgorithm.cc
3  *
4  * @brief Implementation of the deep learning vertexing algorithm.
5  *
6  * $Log: $
7  */
8 
9 #include <chrono>
10 #include <cmath>
11 
12 #include <torch/script.h>
13 #include <torch/torch.h>
14 
19 
21 
22 using namespace pandora;
23 using namespace lar_content;
24 
25 namespace lar_dl_content
26 {
27 
28 DlVertexingAlgorithm::DlVertexingAlgorithm() :
29  m_trainingMode{false},
30  m_trainingOutputFile{""},
31  m_event{-1},
32  m_pass{1},
33  m_nClasses{0},
34  m_height{256},
35  m_width{256},
36  m_maxHitAdc{500.f},
37  m_regionSize{32.f},
38  m_visualise{false},
39  m_writeTree{false},
40  m_rng(static_cast<std::mt19937::result_type>(std::chrono::high_resolution_clock::now().time_since_epoch().count()))
41 {
42 }
43 
45 {
46  if (m_writeTree)
47  {
48  try
49  {
50  PANDORA_MONITORING_API(SaveTree(this->GetPandora(), m_rootTreeName, m_rootFileName, "RECREATE"));
51  }
52  catch (StatusCodeException e)
53  {
54  std::cout << "VertexAssessmentAlgorithm: Unable to write to ROOT tree" << std::endl;
55  }
56  }
57 }
58 
59 //-----------------------------------------------------------------------------------------------------------------------------------------
60 
62 {
63  if (m_trainingMode)
64  return this->PrepareTrainingSample();
65  else
66  return this->Infer();
67 
68  return STATUS_CODE_SUCCESS;
69 }
70 
72 {
74  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->GetMCToHitsMap(mcToHitsMap));
75  MCParticleList hierarchy;
76  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->CompleteMCHierarchy(mcToHitsMap, hierarchy));
77  std::normal_distribution<> gauss(0.f, 15.f);
78 
79  for (const std::string &listname : m_caloHitListNames)
80  {
81  const CaloHitList *pCaloHitList(nullptr);
82  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*this, listname, pCaloHitList));
83  if (pCaloHitList->empty())
84  continue;
85  m_rng.seed(static_cast<std::mt19937::result_type>(pCaloHitList->size()));
86 
87  HitType view{pCaloHitList->front()->GetHitType()};
88  const bool isU{view == TPC_VIEW_U}, isV{view == TPC_VIEW_V}, isW{view == TPC_VIEW_W};
89  if (!(isU || isV || isW))
90  return STATUS_CODE_NOT_ALLOWED;
91 
92  CartesianPointVector vertices;
93  for (const MCParticle *mc : hierarchy)
94  {
95  if (LArMCParticleHelper::IsNeutrino(mc))
96  vertices.push_back(mc->GetVertex());
97  }
98  if (vertices.empty())
99  continue;
100  const CartesianVector &vertex{vertices.front()};
101  const std::string trainingFilename{m_trainingOutputFile + "_" + listname + ".csv"};
102  const unsigned long nVertices{1};
103  unsigned long nHits{0};
104  const unsigned int nuance{LArMCParticleHelper::GetNuanceCode(hierarchy.front())};
105 
106  // Vertices
107  const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
108  const double xVtx{vertex.GetX()};
109  double zVtx{0.};
110  if (isW)
111  zVtx = transform->YZtoW(vertex.GetY(), vertex.GetZ());
112  else if (isV)
113  zVtx = transform->YZtoV(vertex.GetY(), vertex.GetZ());
114  else
115  zVtx = transform->YZtoU(vertex.GetY(), vertex.GetZ());
116 
117  // Calo hits
118  double xMin{0.f}, xMax{0.f}, zMin{0.f}, zMax{0.f};
119  // If we're on a refinement pass, constrain the region of interest
120  if (m_pass > 1)
121  {
122  const double xJitter{gauss(m_rng)}, zJitter{gauss(m_rng)};
123  xMin = (xVtx + xJitter) - m_regionSize;
124  xMax = (xVtx + xJitter) + m_regionSize;
125  zMin = (zVtx + zJitter) - m_regionSize;
126  zMax = (zVtx + zJitter) + m_regionSize;
127  }
128 
129  LArMvaHelper::MvaFeatureVector featureVector;
130  featureVector.emplace_back(static_cast<double>(nuance));
131  featureVector.emplace_back(static_cast<double>(nVertices));
132  featureVector.emplace_back(xVtx);
133  featureVector.emplace_back(zVtx);
134 
135  for (const CaloHit *pCaloHit : *pCaloHitList)
136  {
137  const float x{pCaloHit->GetPositionVector().GetX()}, z{pCaloHit->GetPositionVector().GetZ()}, adc{pCaloHit->GetInputEnergy()};
138  // If on a refinement pass, drop hits outside the region of interest
139  if (m_pass > 1 && (x < xMin || x > xMax || z < zMin || z > zMax))
140  continue;
141  featureVector.emplace_back(static_cast<double>(x));
142  featureVector.emplace_back(static_cast<double>(z));
143  featureVector.emplace_back(static_cast<double>(adc));
144  ++nHits;
145  }
146  featureVector.insert(featureVector.begin() + 2, static_cast<double>(nHits));
147  // Only write out the feature vector if there were enough hits in the (possibly perturbed) region of interest
148  if (nHits > 10)
149  LArMvaHelper::ProduceTrainingExample(trainingFilename, true, featureVector);
150  }
151 
152  return STATUS_CODE_SUCCESS;
153 }
154 
155 //-----------------------------------------------------------------------------------------------------------------------------------------
156 
158 {
159  if (m_pass == 1)
160  ++m_event;
161  CartesianPointVector vertexCandidatesU, vertexCandidatesV, vertexCandidatesW;
162  for (const std::string listName : m_caloHitListNames)
163  {
164  const CaloHitList *pCaloHitList{nullptr};
165  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*this, listName, pCaloHitList));
166 
167  HitType view{pCaloHitList->front()->GetHitType()};
168  const bool isU{view == TPC_VIEW_U}, isV{view == TPC_VIEW_V}, isW{view == TPC_VIEW_W};
169  if (!isU && !isV && !isW)
170  return STATUS_CODE_NOT_ALLOWED;
171 
173  PixelVector pixelVector;
174  this->MakeNetworkInputFromHits(*pCaloHitList, input, pixelVector);
175 
176  // Run the input through the trained model
178  inputs.push_back(input);
180  if (isU)
181  LArDLHelper::Forward(m_modelU, inputs, output);
182  else if (isV)
183  LArDLHelper::Forward(m_modelV, inputs, output);
184  else
185  LArDLHelper::Forward(m_modelW, inputs, output);
186 
187  int colOffset{0}, rowOffset{0}, canvasWidth{m_width}, canvasHeight{m_height};
188  this->GetCanvasParameters(output, pixelVector, colOffset, rowOffset, canvasWidth, canvasHeight);
189 
190  float **canvas{new float *[canvasHeight]};
191  for (int row = 0; row < canvasHeight; ++row)
192  canvas[row] = new float[canvasWidth]{};
193 
194  // we want the maximum value in the num_classes dimension (1) for every pixel
195  auto classes{torch::argmax(output, 1)};
196  // the argmax result is a 1 x height x width tensor where each element is a class id
197  auto classesAccessor{classes.accessor<long, 3>()};
198  const double scaleFactor{std::sqrt(m_height * m_height + m_width * m_width)};
199  std::map<int, bool> haveSeenMap;
200  for (const auto [row, col] : pixelVector)
201  {
202  const auto cls{classesAccessor[0][row][col]};
203  if (cls > 0 && cls < m_nClasses)
204  {
205  const int inner{static_cast<int>(std::round(std::ceil(scaleFactor * m_thresholds[cls - 1])))};
206  const int outer{static_cast<int>(std::round(std::ceil(scaleFactor * m_thresholds[cls])))};
207  this->DrawRing(canvas, row + rowOffset, col + colOffset, inner, outer, 1.f / (outer * outer - inner * inner));
208  }
209  }
210 
211  CartesianPointVector positionVector;
212  this->MakeWirePlaneCoordinatesFromCanvas(*pCaloHitList, canvas, canvasWidth, canvasHeight, colOffset, rowOffset, positionVector);
213  if (isU)
214  vertexCandidatesU.emplace_back(positionVector.front());
215  else if (isV)
216  vertexCandidatesV.emplace_back(positionVector.front());
217  else
218  vertexCandidatesW.emplace_back(positionVector.front());
219 
220  if (m_visualise)
221  {
222  PANDORA_MONITORING_API(SetEveDisplayParameters(this->GetPandora(), true, DETECTOR_VIEW_XZ, -1.f, 1.f, 1.f));
223  try
224  {
225  float x{0.f}, u{0.f}, v{0.f}, w{0.f};
226  this->GetTrueVertexPosition(x, u, v, w);
227  if (isU)
228  {
229  const CartesianVector trueVertex(x, 0.f, u);
230  PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex, "U(true)", BLUE, 3));
231  }
232  else if (isV)
233  {
234  const CartesianVector trueVertex(x, 0.f, v);
235  PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex, "V(true)", BLUE, 3));
236  }
237  else if (isW)
238  {
239  const CartesianVector trueVertex(x, 0.f, w);
240  PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &trueVertex, "W(true)", BLUE, 3));
241  }
242  }
243  catch (StatusCodeException &e)
244  {
245  std::cerr << "DlVertexingAlgorithm: Warning. Couldn't find true vertex." << std::endl;
246  }
247  for (const auto pos : positionVector)
248  {
249  std::string label{isU ? "U" : isV ? "V" : "W"};
250  PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &pos, label, RED, 3));
251  }
252  PANDORA_MONITORING_API(ViewEvent(this->GetPandora()));
253  }
254 
255  for (int row = 0; row < canvasHeight; ++row)
256  delete[] canvas[row];
257  delete[] canvas;
258  }
259 
260  int nEmptyLists{0};
261  if (vertexCandidatesU.empty())
262  ++nEmptyLists;
263  if (vertexCandidatesV.empty())
264  ++nEmptyLists;
265  if (vertexCandidatesW.empty())
266  ++nEmptyLists;
267  std::vector<VertexTuple> vertexTuples;
268  CartesianPointVector candidates3D;
269  if (nEmptyLists == 0)
270  {
271  vertexTuples.emplace_back(VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesV.front(), vertexCandidatesW.front()));
272  }
273  else if (nEmptyLists == 1)
274  {
275  if (vertexCandidatesU.empty())
276  { // V and W available
277  vertexTuples.emplace_back(VertexTuple(this->GetPandora(), vertexCandidatesV.front(), vertexCandidatesW.front(), TPC_VIEW_V, TPC_VIEW_W));
278  }
279  else if (vertexCandidatesV.empty())
280  { // U and W available
281  vertexTuples.emplace_back(VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesW.front(), TPC_VIEW_U, TPC_VIEW_W));
282  }
283  else
284  { // U and V available
285  vertexTuples.emplace_back(VertexTuple(this->GetPandora(), vertexCandidatesU.front(), vertexCandidatesV.front(), TPC_VIEW_U, TPC_VIEW_W));
286  }
287  }
288  else
289  { // Not enough views to reconstruct a 3D vertex
290  std::cout << "Insufficient 2D vertices to reconstruct a 3D vertex" << std::endl;
291  return STATUS_CODE_NOT_FOUND;
292  }
293 
294  if (m_visualise)
295  {
296  PANDORA_MONITORING_API(AddMarkerToVisualization(this->GetPandora(), &vertexTuples.front().GetPosition(), "candidate", GREEN, 1));
297  }
298 
299  if (!vertexTuples.empty())
300  {
301  const CartesianVector &vertex{vertexTuples.front().GetPosition()};
302  CartesianPointVector vertexCandidates;
303  vertexCandidates.emplace_back(vertex);
304  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->MakeCandidateVertexList(vertexCandidates));
305  }
306  else
307  {
308  std::cout << "Insufficient 2D vertices to reconstruct a 3D vertex" << std::endl;
309  return STATUS_CODE_NOT_FOUND;
310  }
311 
312  if (m_visualise)
313  {
314  PANDORA_MONITORING_API(ViewEvent(this->GetPandora()));
315  }
316 
317  return STATUS_CODE_SUCCESS;
318 }
319 
320 //-----------------------------------------------------------------------------------------------------------------------------------------
321 
323  const pandora::CaloHitList &caloHits, LArDLHelper::TorchInput &networkInput, PixelVector &pixelVector) const
324 {
325  // Determine the range of coordinates for the view
326  float xMin{0.f}, xMax{0.f}, zMin{0.f}, zMax{0.f};
327  GetHitRegion(caloHits, xMin, xMax, zMin, zMax);
328 
329  // Determine the bin edges - need double precision here for consistency with Python binning
330  std::vector<double> xBinEdges(m_width + 1);
331  std::vector<double> zBinEdges(m_height + 1);
332  xBinEdges[0] = xMin - PY_EPSILON;
333  const double dx = ((xMax + PY_EPSILON) - (xMin - PY_EPSILON)) / m_width;
334  for (int i = 1; i < m_width + 1; ++i)
335  xBinEdges[i] = xBinEdges[i - 1] + dx;
336  zBinEdges[0] = zMin - PY_EPSILON;
337  const double dz = ((zMax + PY_EPSILON) - (zMin - PY_EPSILON)) / m_height;
338  for (int i = 1; i < m_height + 1; ++i)
339  zBinEdges[i] = zBinEdges[i - 1] + dz;
340 
341  LArDLHelper::InitialiseInput({1, 1, m_height, m_width}, networkInput);
342  auto accessor = networkInput.accessor<float, 4>();
343 
344  float maxValue{0.f};
345  for (const CaloHit *pCaloHit : caloHits)
346  {
347  const float x{pCaloHit->GetPositionVector().GetX()};
348  const float z{pCaloHit->GetPositionVector().GetZ()};
349  if (m_pass > 1)
350  {
351  if (x < xMin || x > xMax || z < zMin || z > zMax)
352  continue;
353  }
354  const float adc{pCaloHit->GetInputEnergy() < m_maxHitAdc ? pCaloHit->GetInputEnergy() : m_maxHitAdc};
355  const int pixelX{static_cast<int>(std::floor((x - xBinEdges[0]) / dx))};
356  const int pixelZ{(m_height - 1) - static_cast<int>(std::floor((z - zBinEdges[0]) / dz))};
357  accessor[0][0][pixelZ][pixelX] += adc;
358  if (accessor[0][0][pixelZ][pixelX] > maxValue)
359  maxValue = accessor[0][0][pixelZ][pixelX];
360  }
361  if (maxValue > 0)
362  {
363  for (int row = 0; row < m_height; ++row)
364  {
365  for (int col = 0; col < m_width; ++col)
366  {
367  const float value{accessor[0][0][row][col]};
368  accessor[0][0][row][col] = value / maxValue;
369  if (value > 0)
370  pixelVector.emplace_back(std::make_pair(row, col));
371  }
372  }
373  }
374 
375  return STATUS_CODE_SUCCESS;
376 }
377 
378 //-----------------------------------------------------------------------------------------------------------------------------------------
379 
381  const CaloHitList &caloHits, const PixelVector &pixelVector, CartesianPointVector &positionVector) const
382 {
383  // Determine the range of coordinates for the view
384  float xMin{0.f}, xMax{0.f}, zMin{0.f}, zMax{0.f};
385  GetHitRegion(caloHits, xMin, xMax, zMin, zMax);
386 
387  // Determine the bin size - need double precision here for consistency with Python binning
388  const double dx{((xMax + PY_EPSILON) - (xMin - PY_EPSILON)) / m_width};
389  xMin -= PY_EPSILON;
390  const double dz{((zMax + PY_EPSILON) - (zMin - PY_EPSILON)) / m_height};
391  zMin -= PY_EPSILON;
392  // Original hit mapping applies a floor operation, so add half a pixel width to get pixel centre
393  const float xShift{static_cast<float>(dx * 0.5f)};
394  const float zShift{static_cast<float>(dz * 0.5f)};
395 
396  for (const auto [row, col] : pixelVector)
397  {
398  const float x{static_cast<float>(col * dx + xMin + xShift)};
399  const float z{static_cast<float>(dz * ((m_height - 1) - row) + zMin + zShift)};
400  CartesianVector pt(x, 0.f, z);
401  positionVector.emplace_back(pt);
402  }
403 
404  return STATUS_CODE_SUCCESS;
405 }
406 
407 //-----------------------------------------------------------------------------------------------------------------------------------------
408 
409 StatusCode DlVertexingAlgorithm::MakeWirePlaneCoordinatesFromCanvas(const CaloHitList &caloHits, float **canvas, const int canvasWidth,
410  const int canvasHeight, const int columnOffset, const int rowOffset, CartesianPointVector &positionVector) const
411 {
412  // Determine the range of coordinates for the view
413  float xMin{0.f}, xMax{0.f}, zMin{0.f}, zMax{0.f};
414  GetHitRegion(caloHits, xMin, xMax, zMin, zMax);
415 
416  // Determine the bin size - need double precision here for consistency with Python binning
417  const double dx{((xMax + PY_EPSILON) - (xMin - PY_EPSILON)) / m_width};
418  xMin -= PY_EPSILON;
419  const double dz{((zMax + PY_EPSILON) - (zMin - PY_EPSILON)) / m_height};
420  zMin -= PY_EPSILON;
421  // Original hit mapping applies a floor operation, so add half a pixel width to get pixel centre
422  const float xShift{static_cast<float>(dx * 0.5f)};
423  const float zShift{static_cast<float>(dz * 0.5f)};
424 
425  float best{-1.f};
426  int rowBest{0}, colBest{0};
427  for (int row = 0; row < canvasHeight; ++row)
428  for (int col = 0; col < canvasWidth; ++col)
429  if (canvas[row][col] > 0 && canvas[row][col] > best)
430  {
431  best = canvas[row][col];
432  rowBest = row;
433  colBest = col;
434  }
435 
436  const float x{static_cast<float>((colBest - columnOffset) * dx + xMin + xShift)};
437  const float z{static_cast<float>(dz * ((m_height - 1) - (rowBest - rowOffset)) + zMin + zShift)};
438  CartesianVector pt(x, 0.f, z);
439  positionVector.emplace_back(pt);
440 
441  return STATUS_CODE_SUCCESS;
442 }
443 
444 //-----------------------------------------------------------------------------------------------------------------------------------------
445 
447  int &colOffset, int &rowOffset, int &width, int &height) const
448 {
449  const double scaleFactor{std::sqrt(m_height * m_height + m_width * m_width)};
450  // output is a 1 x num_classes x height x width tensor
451  // we want the maximum value in the num_classes dimension (1) for every pixel
452  auto classes{torch::argmax(networkOutput, 1)};
453  // the argmax result is a 1 x height x width tensor where each element is a class id
454  auto classesAccessor{classes.accessor<long, 3>()};
455  int colOffsetMin{0}, colOffsetMax{0}, rowOffsetMin{0}, rowOffsetMax{0};
456  for (const auto [row, col] : pixelVector)
457  {
458  const auto cls{classesAccessor[0][row][col]};
459  const double threshold{m_thresholds[cls]};
460  if (threshold > 0. && threshold < 1.)
461  {
462  const int distance = static_cast<int>(std::round(std::ceil(scaleFactor * threshold)));
463  if ((row - distance) < rowOffsetMin)
464  rowOffsetMin = row - distance;
465  if ((row + distance) > rowOffsetMax)
466  rowOffsetMax = row + distance;
467  if ((col - distance) < colOffsetMin)
468  colOffsetMin = col - distance;
469  if ((col + distance) > colOffsetMax)
470  colOffsetMax = col + distance;
471  }
472  }
473  colOffset = colOffsetMin < 0 ? -colOffsetMin : 0;
474  rowOffset = rowOffsetMin < 0 ? -rowOffsetMin : 0;
475  width = std::max(colOffsetMax + colOffset + 1, m_width);
476  height = std::max(rowOffsetMax + rowOffset + 1, m_height);
477 }
478 
479 //-----------------------------------------------------------------------------------------------------------------------------------------
480 
481 void DlVertexingAlgorithm::DrawRing(float **canvas, const int row, const int col, const int inner, const int outer, const float weight) const
482 {
483  // Set the starting position for each circle bounding the ring
484  int c1{inner}, r1{0}, c2{outer}, r2{0};
485  int inner2{inner * inner}, outer2{outer * outer};
486  while (c2 >= r2)
487  {
488  // Set the output pixel location
489  int rp2{r2}, cp2{c2};
490  // We're still within the octant for the inner ring, so use the inner pixel location (see Update comment below)
491  // Note also that the inner row is always the same as the outer row, so no need to define rp1
492  int cp1{c1};
493  if (c1 <= r1)
494  { // We've completed the arc of the inner ring already, so just move radially out from here (see Update comment below)
495  cp1 = r2;
496  }
497  // Fill the pixels from inner to outer in the current row and their mirror pixels in the other octants
498  for (int c = cp1; c <= cp2; ++c)
499  {
500  canvas[row + rp2][col + c] += weight;
501  if (rp2 != c)
502  canvas[row + c][col + rp2] += weight;
503  if (rp2 != 0 && cp2 != 0)
504  {
505  canvas[row - rp2][col - c] += weight;
506  if (rp2 != c)
507  canvas[row - c][col - rp2] += weight;
508  }
509  if (rp2 != 0)
510  {
511  canvas[row - rp2][col + c] += weight;
512  if (rp2 != c)
513  canvas[row + c][col - rp2] += weight;
514  }
515  if (cp2 != 0)
516  {
517  canvas[row + rp2][col - c] += weight;
518  if (rp2 != c)
519  canvas[row - c][col + rp2] += weight;
520  }
521  }
522  // Only update the inner location while it remains in the octant (outer ring also remains in the octant of course, but the logic of
523  // the update means that the inner ring can leave its octant before the outer ring is complete, so we need to stop that)
524  if (c1 > r1)
525  this->Update(inner2, c1, r1);
526  // Update the outer location - increase the row position with every step, decrease the column position if conditions are met
527  this->Update(outer2, c2, r2);
528  }
529 }
530 
531 //-----------------------------------------------------------------------------------------------------------------------------------------
532 
533 void DlVertexingAlgorithm::Update(const int radius2, int &col, int &row) const
534 {
535  // Bresenham midpoint circle algorithm to determine if we should update the column position
536  // This obscure looking block of code uses bit shifts and integer arithmetic to perform this check as efficiently as possible
537  const int a{1 - (col << 2)};
538  const int b{col * col + row * row - radius2 + (row << 2) + 1};
539  const int c{(a << 2) * b + a * a};
540  if (c < 0)
541  {
542  --col;
543  ++row;
544  }
545  else
546  ++row;
547 }
548 
549 //-----------------------------------------------------------------------------------------------------------------------------------------
550 
552 {
553  const CaloHitList *pCaloHitList2D(nullptr);
554  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*this, "CaloHitList2D", pCaloHitList2D));
555  const MCParticleList *pMCParticleList(nullptr);
556  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*this, pMCParticleList));
557 
559  parameters.m_maxPhotonPropagation = std::numeric_limits<float>::max();
560  LArMCParticleHelper::SelectReconstructableMCParticles(
561  pMCParticleList, pCaloHitList2D, parameters, LArMCParticleHelper::IsBeamNeutrinoFinalState, mcToHitsMap);
562 
563  return STATUS_CODE_SUCCESS;
564 }
565 
566 //-----------------------------------------------------------------------------------------------------------------------------------------
567 
568 StatusCode DlVertexingAlgorithm::CompleteMCHierarchy(const LArMCParticleHelper::MCContributionMap &mcToHitsMap, MCParticleList &mcHierarchy) const
569 {
570  try
571  {
572  for (const auto [mc, hits] : mcToHitsMap)
573  {
574  (void)hits;
575  mcHierarchy.push_back(mc);
576  LArMCParticleHelper::GetAllAncestorMCParticles(mc, mcHierarchy);
577  }
578  }
579  catch (const StatusCodeException &e)
580  {
581  return e.GetStatusCode();
582  }
583 
584  // Move the neutrino to the front of the list
585  auto pivot =
586  std::find_if(mcHierarchy.begin(), mcHierarchy.end(), [](const MCParticle *mc) -> bool { return LArMCParticleHelper::IsNeutrino(mc); });
587  (void)pivot;
588  if (pivot != mcHierarchy.end())
589  std::rotate(mcHierarchy.begin(), pivot, std::next(pivot));
590  else
591  return STATUS_CODE_NOT_FOUND;
592 
593  return STATUS_CODE_SUCCESS;
594 }
595 
596 //-----------------------------------------------------------------------------------------------------------------------------------------
597 
598 void DlVertexingAlgorithm::GetHitRegion(const CaloHitList &caloHitList, float &xMin, float &xMax, float &zMin, float &zMax) const
599 {
600  xMin = std::numeric_limits<float>::max();
601  xMax = -std::numeric_limits<float>::max();
602  zMin = std::numeric_limits<float>::max();
603  zMax = -std::numeric_limits<float>::max();
604  // Find the range of x and z values in the view
605  for (const CaloHit *pCaloHit : caloHitList)
606  {
607  const float x{pCaloHit->GetPositionVector().GetX()};
608  const float z{pCaloHit->GetPositionVector().GetZ()};
609  xMin = std::min(x, xMin);
610  xMax = std::max(x, xMax);
611  zMin = std::min(z, zMin);
612  zMax = std::max(z, zMax);
613  }
614 
615  if (m_pass > 1)
616  {
617  // Constrain the hits to the allowed region if needed
618  const VertexList *pVertexList(nullptr);
619  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetList(*this, m_inputVertexListName, pVertexList));
620  if (pVertexList->empty() || caloHitList.empty())
621  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
622  const CartesianVector &centroid{pVertexList->front()->GetPosition()};
623  const HitType view{caloHitList.front()->GetHitType()};
624  float xCentroid{centroid.GetX()}, zCentroid{0.f};
625  const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
626  switch (view)
627  {
628  case TPC_VIEW_U:
629  zCentroid = transform->YZtoU(centroid.GetY(), centroid.GetZ());
630  break;
631  case TPC_VIEW_V:
632  zCentroid = transform->YZtoV(centroid.GetY(), centroid.GetZ());
633  break;
634  case TPC_VIEW_W:
635  zCentroid = transform->YZtoW(centroid.GetY(), centroid.GetZ());
636  break;
637  default:
638  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
639  break;
640  }
641 
642  xMin = std::max(xMin, xCentroid - m_regionSize);
643  xMax = std::min(xMax, xCentroid + m_regionSize);
644  zMin = std::max(zMin, zCentroid - m_regionSize);
645  zMax = std::min(zMax, zCentroid + m_regionSize);
646  }
647 
648  // Avoid unreasonable rescaling of very small hit regions
649  const float xRange{xMax - xMin}, zRange{zMax - zMin};
650  if (2.f * xRange < m_width)
651  {
652  const float padding{0.5f * (0.5f * m_width - xRange)};
653  xMin -= padding;
654  xMax += padding;
655  }
656  if (2.f * zRange < m_height)
657  {
658  const float padding{0.5f * (0.5f * m_height - zRange)};
659  zMin -= padding;
660  zMax += padding;
661  }
662 }
663 
664 //-----------------------------------------------------------------------------------------------------------------------------------------
665 
666 StatusCode DlVertexingAlgorithm::MakeCandidateVertexList(const CartesianPointVector &positions)
667 {
668  const VertexList *pVertexList{nullptr};
669  std::string temporaryListName;
670  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::CreateTemporaryListAndSetCurrent(*this, pVertexList, temporaryListName));
671 
672  for (const CartesianVector position : positions)
673  {
675  parameters.m_position = position;
676  parameters.m_vertexLabel = VERTEX_INTERACTION;
677  parameters.m_vertexType = VERTEX_3D;
678 
679  const Vertex *pVertex(nullptr);
680  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::Vertex::Create(*this, parameters, pVertex));
681  }
682  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::SaveList<Vertex>(*this, m_outputVertexListName));
683  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ReplaceCurrentList<Vertex>(*this, m_outputVertexListName));
684 
685  return STATUS_CODE_SUCCESS;
686 }
687 
688 //-----------------------------------------------------------------------------------------------------------------------------------------
689 
690 void DlVertexingAlgorithm::GetTrueVertexPosition(float &x, float &y, float &z) const
691 {
692  const CartesianVector &trueVertex{this->GetTrueVertex()};
693  x = trueVertex.GetX();
694  y = trueVertex.GetY();
695  z = trueVertex.GetZ();
696 }
697 
698 //-----------------------------------------------------------------------------------------------------------------------------------------
699 
700 void DlVertexingAlgorithm::GetTrueVertexPosition(float &x, float &u, float &v, float &w) const
701 {
702  const CartesianVector &trueVertex{this->GetTrueVertex()};
703  const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
704  x = trueVertex.GetX();
705  u = static_cast<float>(transform->YZtoU(trueVertex.GetY(), trueVertex.GetZ()));
706  v = static_cast<float>(transform->YZtoV(trueVertex.GetY(), trueVertex.GetZ()));
707  w = static_cast<float>(transform->YZtoW(trueVertex.GetY(), trueVertex.GetZ()));
708 }
709 
710 //-----------------------------------------------------------------------------------------------------------------------------------------
711 
712 const CartesianVector &DlVertexingAlgorithm::GetTrueVertex() const
713 {
714  const MCParticleList *pMCParticleList{nullptr};
715  if (STATUS_CODE_SUCCESS == PandoraContentApi::GetCurrentList(*this, pMCParticleList) && pMCParticleList)
716  {
717  MCParticleVector primaries;
718  LArMCParticleHelper::GetPrimaryMCParticleList(pMCParticleList, primaries);
719  if (!primaries.empty())
720  {
721  const MCParticle *primary{primaries.front()};
722  const MCParticleList &parents{primary->GetParentList()};
723  if (parents.size() == 1)
724  {
725  const MCParticle *trueNeutrino{parents.front()};
726  if (LArMCParticleHelper::IsNeutrino(trueNeutrino))
727  return primaries.front()->GetVertex();
728  }
729  }
730  }
731 
732  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
733 }
734 
735 void DlVertexingAlgorithm::PopulateRootTree(const std::vector<VertexTuple> &vertexTuples, const pandora::CartesianPointVector &vertexCandidatesU,
736  const pandora::CartesianPointVector &vertexCandidatesV, const pandora::CartesianPointVector &vertexCandidatesW) const
737 {
738  if (m_writeTree)
739  {
740  const MCParticleList *pMCParticleList{nullptr};
741  if (STATUS_CODE_SUCCESS == PandoraContentApi::GetCurrentList(*this, pMCParticleList))
742  {
743  if (pMCParticleList)
744  {
746  MCParticleVector primaries;
747  LArMCParticleHelper::GetPrimaryMCParticleList(pMCParticleList, primaries);
748  if (!primaries.empty())
749  {
750  const MCParticle *primary{primaries.front()};
751  const MCParticleList &parents{primary->GetParentList()};
752  if (parents.size() == 1)
753  {
754  const MCParticle *trueNeutrino{parents.front()};
755  if (LArMCParticleHelper::IsNeutrino(trueNeutrino))
756  {
757  const LArTransformationPlugin *transform{this->GetPandora().GetPlugins()->GetLArTransformationPlugin()};
758  const CartesianVector &trueVertex{primaries.front()->GetVertex()};
759  if (LArVertexHelper::IsInFiducialVolume(this->GetPandora(), trueVertex, "dune_fd_hd"))
760  {
761  const CartesianVector &recoVertex{vertexTuples.front().GetPosition()};
762  const float tx{trueVertex.GetX()};
763  const float tu{static_cast<float>(transform->YZtoU(trueVertex.GetY(), trueVertex.GetZ()))};
764  const float tv{static_cast<float>(transform->YZtoV(trueVertex.GetY(), trueVertex.GetZ()))};
765  const float tw{static_cast<float>(transform->YZtoW(trueVertex.GetY(), trueVertex.GetZ()))};
766  const float rx_u{vertexCandidatesU.front().GetX()};
767  const float ru{vertexCandidatesU.front().GetZ()};
768  const float rx_v{vertexCandidatesV.front().GetX()};
769  const float rv{vertexCandidatesV.front().GetZ()};
770  const float rx_w{vertexCandidatesW.front().GetX()};
771  const float rw{vertexCandidatesW.front().GetZ()};
772  const float dr_u{std::sqrt((rx_u - tx) * (rx_u - tx) + (ru - tu) * (ru - tu))};
773  const float dr_v{std::sqrt((rx_v - tx) * (rx_v - tx) + (rv - tv) * (rv - tv))};
774  const float dr_w{std::sqrt((rx_w - tx) * (rx_w - tx) + (rw - tw) * (rw - tw))};
775  const CartesianVector &dv{recoVertex - trueVertex};
776  const float dr{dv.GetMagnitude()};
777  const float dx{dv.GetX()}, dy{dv.GetY()}, dz{dv.GetZ()};
778  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "event", m_event));
779  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "pass", m_pass));
780  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dr_u", dr_u));
781  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dr_v", dr_v));
782  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dr_w", dr_w));
783  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dr", dr));
784  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dx", dx));
785  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dy", dy));
786  PANDORA_MONITORING_API(SetTreeVariable(this->GetPandora(), m_rootTreeName, "dz", dz));
787  PANDORA_MONITORING_API(FillTree(this->GetPandora(), m_rootTreeName));
788  }
789  }
790  }
791  }
792  }
793  }
794  }
795 }
796 
797 //-----------------------------------------------------------------------------------------------------------------------------------------
798 
799 StatusCode DlVertexingAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
800 {
801  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "TrainingMode", m_trainingMode));
802  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "Visualise", m_visualise));
803  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "Pass", m_pass));
804  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ImageHeight", m_height));
805  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ImageWidth", m_width));
806  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadVectorOfValues(xmlHandle, "DistanceThresholds", m_thresholds));
807  m_nClasses = m_thresholds.size() - 1;
808 
809  if (m_trainingMode)
810  {
811  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "TrainingOutputFileName", m_trainingOutputFile));
812  }
813  else
814  {
815  std::string modelName;
816  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "ModelFileNameU", modelName));
817  modelName = LArFileHelper::FindFileInPath(modelName, "FW_SEARCH_PATH");
818  LArDLHelper::LoadModel(modelName, m_modelU);
819  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "ModelFileNameV", modelName));
820  modelName = LArFileHelper::FindFileInPath(modelName, "FW_SEARCH_PATH");
821  LArDLHelper::LoadModel(modelName, m_modelV);
822  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "ModelFileNameW", modelName));
823  modelName = LArFileHelper::FindFileInPath(modelName, "FW_SEARCH_PATH");
824  LArDLHelper::LoadModel(modelName, m_modelW);
825  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "MaxHitAdc", m_maxHitAdc));
826  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "WriteTree", m_writeTree));
827  if (m_writeTree)
828  {
829  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "RootTreeName", m_rootTreeName));
830  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "RootFileName", m_rootFileName));
831  }
832 
833  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "OutputVertexListName", m_outputVertexListName));
834  if (m_pass > 1)
835  {
836  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "InputVertexListName", m_inputVertexListName));
837  }
838  }
839 
840  PANDORA_RETURN_RESULT_IF_AND_IF(
841  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadVectorOfValues(xmlHandle, "CaloHitListNames", m_caloHitListNames));
842 
843  return STATUS_CODE_SUCCESS;
844 }
845 
846 //-----------------------------------------------------------------------------------------------------------------------------------------
847 //-----------------------------------------------------------------------------------------------------------------------------------------
848 
850  const Pandora &pandora, const CartesianVector &vertexU, const CartesianVector &vertexV, const CartesianVector &vertexW) :
851  m_pos{0.f, 0.f, 0.f},
852  m_chi2{0.f}
853 {
854  LArGeometryHelper::MergeThreePositions3D(pandora, TPC_VIEW_U, TPC_VIEW_V, TPC_VIEW_W, vertexU, vertexV, vertexW, m_pos, m_chi2);
855  if (m_chi2 > 1.f)
856  {
857  CartesianVector vertexUV(0.f, 0.f, 0.f);
858  float chi2UV{0.f};
859  LArGeometryHelper::MergeTwoPositions3D(pandora, TPC_VIEW_U, TPC_VIEW_V, vertexU, vertexV, vertexUV, chi2UV);
860 
861  CartesianVector vertexUW(0.f, 0.f, 0.f);
862  float chi2UW{0.f};
863  LArGeometryHelper::MergeTwoPositions3D(pandora, TPC_VIEW_U, TPC_VIEW_W, vertexU, vertexW, vertexUW, chi2UW);
864 
865  CartesianVector vertexVW(0.f, 0.f, 0.f);
866  float chi2VW{0.f};
867  LArGeometryHelper::MergeTwoPositions3D(pandora, TPC_VIEW_V, TPC_VIEW_W, vertexV, vertexW, vertexVW, chi2VW);
868 
869  if (chi2UV < m_chi2)
870  {
871  m_pos = vertexUV;
872  m_chi2 = chi2UV;
873  }
874  if (chi2UW < m_chi2)
875  {
876  m_pos = vertexUW;
877  m_chi2 = chi2UW;
878  }
879  if (chi2VW < m_chi2)
880  {
881  m_pos = vertexVW;
882  m_chi2 = chi2VW;
883  }
884  }
885  // std::cout << "Merging 3, position (" << m_pos.GetX() << ", " << m_pos.GetY() << ", " << m_pos.GetZ() << ") with chi2 " << m_chi2 <<
886  // std::endl;
887 }
888 
889 //-----------------------------------------------------------------------------------------------------------------------------------------
890 
892  const Pandora &pandora, const CartesianVector &vertex1, const CartesianVector &vertex2, const HitType view1, const HitType view2) :
893  m_pos{0.f, 0.f, 0.f},
894  m_chi2{0.f}
895 {
896  LArGeometryHelper::MergeTwoPositions3D(pandora, view1, view2, vertex1, vertex2, m_pos, m_chi2);
897  std::cout << "Merging 2, position (" << m_pos.GetX() << ", " << m_pos.GetY() << ", " << m_pos.GetZ() << ") with chi2 " << m_chi2 << std::endl;
898 }
899 
900 //-----------------------------------------------------------------------------------------------------------------------------------------
901 
903 {
904  return m_pos;
905 }
906 
907 //-----------------------------------------------------------------------------------------------------------------------------------------
908 
910 {
911  return m_chi2;
912 }
913 
914 //-----------------------------------------------------------------------------------------------------------------------------------------
915 
917 {
918  const float x{m_pos.GetX()}, y{m_pos.GetY()}, z{m_pos.GetZ()};
919  return "3D pos: (" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ") X2 = " + std::to_string(m_chi2);
920 }
921 
922 } // namespace lar_dl_content
process_name vertex
Definition: cheaterreco.fcl:51
pandora::CartesianVector m_pos
Calculated 3D position.
LArDLHelper::TorchModel m_modelU
The model for the U view.
process_name opflash particleana ie ie ie z
const pandora::CartesianVector & GetTrueVertex() const
Retrieve the true neutrino vertex.
void GetHitRegion(const pandora::CaloHitList &caloHitList, float &xMin, float &xMax, float &zMin, float &zMax) const
std::vector< double > m_thresholds
Distance class thresholds.
std::unordered_map< const pandora::MCParticle *, pandora::CaloHitList > MCContributionMap
pandora::StatusCode MakeNetworkInputFromHits(const pandora::CaloHitList &caloHits, LArDLHelper::TorchInput &networkInput, PixelVector &pixelVector) const
MvaTypes::MvaFeatureVector MvaFeatureVector
Definition: LArMvaHelper.h:72
static constexpr Sample_t transform(Sample_t sample)
void Update(const int radius, int &col, int &row) const
Update the coordinates along the loci of a circle. When drawing the ring we need an efficient means t...
process_name opflash particleana ie x
BEGIN_PROLOG could also be cerr
pandora::StatusCode GetMCToHitsMap(LArMCParticleHelper::MCContributionMap &mcToHitsMap) const
bool m_visualise
Whether or not to visualise the candidate vertices.
int m_event
The current event number.
void PopulateRootTree(const std::vector< VertexTuple > &vertexTuples, const pandora::CartesianPointVector &vertexCandidatesU, const pandora::CartesianPointVector &vertexCandidatesV, const pandora::CartesianPointVector &vertexCandidatesW) const
Populate a root true with vertex information.
pandora::StringVector m_caloHitListNames
Names of input calo hit lists.
LArDLHelper::TorchModel m_modelV
The model for the V view.
float m_regionSize
The half width/height of the event region to consider in cm.
float m_maxHitAdc
Maximum ADC value to allow.
float m_maxPhotonPropagation
the maximum photon propagation length
pandora::StatusCode CompleteMCHierarchy(const LArMCParticleHelper::MCContributionMap &mcToHitsMap, pandora::MCParticleList &mcHierarchy) const
std::string m_outputVertexListName
Output vertex list name.
process_name gaushit a
void GetCanvasParameters(const LArDLHelper::TorchOutput &networkOutput, const PixelVector &pixelVector, int &columnOffset, int &rowOffset, int &width, int &height) const
Determines the parameters of the canvas for extracting the vertex location. The network predicts the ...
Header file for the geometry helper class.
std::mt19937 m_rng
The random number generator.
int m_nClasses
The number of distance classes.
process_name opflash particleana ie ie y
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
std::string m_rootTreeName
The ROOT tree name.
const double PY_EPSILON
The value of epsilon in Python.
bool m_writeTree
Whether or not to write validation details to a ROOT tree.
int m_height
The height of the images.
std::vector< art::Ptr< simb::MCParticle > > MCParticleVector
const pandora::CartesianVector & GetPosition() const
int m_pass
The pass of the train/infer step.
Header file for the file helper class.
j template void())
Definition: json.hpp:3108
pandora::StatusCode MakeWirePlaneCoordinatesFromPixels(const pandora::CaloHitList &caloHits, const PixelVector &pixelVector, pandora::CartesianPointVector &positionVector) const
static void Forward(TorchModel &model, const TorchInputVector &input, TorchOutput &output)
Run a deep learning model.
Definition: LArDLHelper.cc:41
void GetTrueVertexPosition(float &x, float &y, float &z) const
Retrieve the true neutrino vertex position.
std::string m_inputVertexListName
Input vertex list name if 2nd pass.
Header file for the vertex helper class.
fhicl::Table< sbnd::crt::CRTDetSimParams > Parameters
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
LArDLHelper::TorchModel m_modelW
The model for the W view.
required by fuzzyCluster table::sbnd_g4_services gaushitTruthMatch pandora
Definition: reco_sbnd.fcl:182
std::string m_trainingOutputFile
Output file name for training examples.
static pandora::StatusCode LoadModel(const std::string &filename, TorchModel &model)
Loads a deep learning model.
Definition: LArDLHelper.cc:16
std::string m_rootFileName
The ROOT file name.
std::string to_string(WindowPattern const &pattern)
BEGIN_PROLOG sequence::SlidingWindowTriggerPatternsOppositeWindows END_PROLOG simSlidingORM6O6 effSlidingORW output
pandora::StatusCode MakeCandidateVertexList(const pandora::CartesianPointVector &positions)
Create a vertex list from the candidate vertices.
do i e
temporary value
std::size_t count(Cont const &cont)
int m_width
The width of the images.
static void InitialiseInput(const at::IntArrayRef dimensions, TorchInput &tensor)
Create a torch input tensor.
Definition: LArDLHelper.cc:34
std::list< Vertex > VertexList
Definition: DCEL.h:182
BEGIN_PROLOG could also be cout
std::vector< torch::jit::IValue > TorchInputVector
Definition: LArDLHelper.h:27
VertexTuple(const pandora::Pandora &pandora, const pandora::CartesianVector &vertexU, const pandora::CartesianVector &vertexV, const pandora::CartesianVector &vertexW)
pandora::StatusCode MakeWirePlaneCoordinatesFromCanvas(const pandora::CaloHitList &caloHits, float **canvas, const int canvasWidth, const int canvasHeight, const int columnOffset, const int rowOffset, pandora::CartesianPointVector &positionVector) const
void DrawRing(float **canvas, const int row, const int col, const int inner, const int outer, const float weight) const
Add a filled ring to the specified canvas. The ring has an inner radius based on the minimum predicte...