LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
NeutrinoIdTool.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
12 
13 #include "Helpers/MCParticleHelper.h"
14 
20 
22 
23 using namespace pandora;
24 
25 namespace lar_content
26 {
27 
28 template <typename T>
30  m_useTrainingMode(false),
31  m_selectNuanceCode(false),
32  m_nuance(-std::numeric_limits<int>::max()),
33  m_minPurity(0.9f),
34  m_minCompleteness(0.9f),
35  m_minProbability(0.0f),
36  m_defaultProbability(0.0f),
37  m_maxNeutrinos(1),
38  m_persistFeatures(false),
39  m_filePathEnvironmentVariable("FW_SEARCH_PATH")
40 {
41 }
42 
43 //------------------------------------------------------------------------------------------------------------------------------------------
44 
45 template <typename T>
46 void NeutrinoIdTool<T>::SelectOutputPfos(const Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
47  const SliceHypotheses &crSliceHypotheses, PfoList &selectedPfos)
48 {
49  if (nuSliceHypotheses.size() != crSliceHypotheses.size())
50  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
51 
52  const unsigned int nSlices(nuSliceHypotheses.size());
53  if (nSlices == 0)
54  return;
55 
56  SliceFeaturesVector sliceFeaturesVector;
57  this->GetSliceFeatures(this, nuSliceHypotheses, crSliceHypotheses, sliceFeaturesVector);
58 
60  {
61  // ATTN in training mode, just return everything as a cosmic-ray
62  this->SelectAllPfos(pAlgorithm, crSliceHypotheses, selectedPfos);
63 
64  unsigned int bestSliceIndex(std::numeric_limits<unsigned int>::max());
65  if (!this->GetBestMCSliceIndex(pAlgorithm, nuSliceHypotheses, crSliceHypotheses, bestSliceIndex))
66  return;
67 
68  for (unsigned int sliceIndex = 0; sliceIndex < nSlices; ++sliceIndex)
69  {
70  const SliceFeatures &features(sliceFeaturesVector.at(sliceIndex));
71  if (!features.IsFeatureVectorAvailable())
72  continue;
73 
74  LArMvaHelper::MvaFeatureVector featureVector;
75  features.GetFeatureVector(featureVector);
76  LArMvaHelper::ProduceTrainingExample(m_trainingOutputFile, sliceIndex == bestSliceIndex, featureVector);
77  }
78 
79  return;
80  }
81 
82  this->SelectPfosByProbability(pAlgorithm, nuSliceHypotheses, crSliceHypotheses, sliceFeaturesVector, selectedPfos);
83 }
84 
85 //------------------------------------------------------------------------------------------------------------------------------------------
86 
87 template <typename T>
88 void NeutrinoIdTool<T>::GetSliceFeatures(const NeutrinoIdTool<T> *const pTool, const SliceHypotheses &nuSliceHypotheses,
89  const SliceHypotheses &crSliceHypotheses, SliceFeaturesVector &sliceFeaturesVector) const
90 {
91  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
92  sliceFeaturesVector.push_back(SliceFeatures(nuSliceHypotheses.at(sliceIndex), crSliceHypotheses.at(sliceIndex), pTool));
93 }
94 
95 //------------------------------------------------------------------------------------------------------------------------------------------
96 
97 template <typename T>
98 bool NeutrinoIdTool<T>::GetBestMCSliceIndex(const Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
99  const SliceHypotheses &crSliceHypotheses, unsigned int &bestSliceIndex) const
100 {
101  unsigned int nHitsInBestSlice(0), nNuHitsInBestSlice(0);
102 
103  // Get all hits in all slices to find true number of mc hits
104  const CaloHitList *pAllReconstructedCaloHitList(nullptr);
105  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pAllReconstructedCaloHitList));
106 
107  const MCParticleList *pMCParticleList(nullptr);
108  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pMCParticleList));
109 
110  // Obtain map: [mc particle -> primary mc particle]
111  LArMCParticleHelper::MCRelationMap mcToPrimaryMCMap;
112  LArMCParticleHelper::GetMCPrimaryMap(pMCParticleList, mcToPrimaryMCMap);
113 
114  // Remove non-reconstructable hits, e.g. those downstream of a neutron
115  CaloHitList reconstructableCaloHitList;
117  LArMCParticleHelper::SelectCaloHits(pAllReconstructedCaloHitList, mcToPrimaryMCMap, reconstructableCaloHitList,
118  parameters.m_selectInputHits, parameters.m_maxPhotonPropagation);
119 
120  const int nuNHitsTotal(this->CountNeutrinoInducedHits(reconstructableCaloHitList));
121  const CaloHitSet reconstructableCaloHitSet(reconstructableCaloHitList.begin(), reconstructableCaloHitList.end());
122 
123  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
124  {
125  CaloHitList reconstructedCaloHitList;
126  this->Collect2DHits(crSliceHypotheses.at(sliceIndex), reconstructedCaloHitList, reconstructableCaloHitSet);
127 
128  for (const ParticleFlowObject *const pNeutrino : nuSliceHypotheses.at(sliceIndex))
129  {
130  const PfoList &nuFinalStates(pNeutrino->GetDaughterPfoList());
131  this->Collect2DHits(nuFinalStates, reconstructedCaloHitList, reconstructableCaloHitSet);
132  }
133 
134  const unsigned int nNuHits(this->CountNeutrinoInducedHits(reconstructedCaloHitList));
135 
136  if (nNuHits > nNuHitsInBestSlice)
137  {
138  nNuHitsInBestSlice = nNuHits;
139  nHitsInBestSlice = reconstructedCaloHitList.size();
140  bestSliceIndex = sliceIndex;
141  }
142  }
143 
144  // ATTN for events with no neutrino induced hits, default neutrino purity and completeness to zero
145  const float purity(nHitsInBestSlice > 0 ? static_cast<float>(nNuHitsInBestSlice) / static_cast<float>(nHitsInBestSlice) : 0.f);
146  const float completeness(nuNHitsTotal > 0 ? static_cast<float>(nNuHitsInBestSlice) / static_cast<float>(nuNHitsTotal) : 0.f);
147  return this->PassesQualityCuts(pAlgorithm, purity, completeness);
148 }
149 
150 //------------------------------------------------------------------------------------------------------------------------------------------
151 
152 template <typename T>
153 bool NeutrinoIdTool<T>::PassesQualityCuts(const Algorithm *const pAlgorithm, const float purity, const float completeness) const
154 {
155  if (purity < m_minPurity || completeness < m_minCompleteness)
156  return false;
157  if (m_selectNuanceCode && (this->GetNuanceCode(pAlgorithm) != m_nuance))
158  return false;
159 
160  return true;
161 }
162 
163 //------------------------------------------------------------------------------------------------------------------------------------------
164 
165 template <typename T>
166 void NeutrinoIdTool<T>::Collect2DHits(const PfoList &pfos, CaloHitList &reconstructedCaloHitList, const CaloHitSet &reconstructableCaloHitSet) const
167 {
168  CaloHitList collectedHits;
169  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_U, collectedHits);
170  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_V, collectedHits);
171  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_W, collectedHits);
172 
173  for (const CaloHit *const pCaloHit : collectedHits)
174  {
175  const CaloHit *const pParentHit = static_cast<const CaloHit *>(pCaloHit->GetParentAddress());
176  if (!reconstructableCaloHitSet.count(pParentHit))
177  continue;
178 
179  // Ensure no hits have been double counted
180  if (std::find(reconstructedCaloHitList.begin(), reconstructedCaloHitList.end(), pParentHit) == reconstructedCaloHitList.end())
181  reconstructedCaloHitList.push_back(pParentHit);
182  }
183 }
184 
185 //------------------------------------------------------------------------------------------------------------------------------------------
186 
187 template <typename T>
188 unsigned int NeutrinoIdTool<T>::CountNeutrinoInducedHits(const CaloHitList &caloHitList) const
189 {
190  unsigned int nNuHits(0);
191  for (const CaloHit *const pCaloHit : caloHitList)
192  {
193  try
194  {
195  if (LArMCParticleHelper::IsNeutrino(LArMCParticleHelper::GetParentMCParticle(MCParticleHelper::GetMainMCParticle(pCaloHit))))
196  nNuHits++;
197  }
198  catch (const StatusCodeException &)
199  {
200  }
201  }
202 
203  return nNuHits;
204 }
205 
206 //------------------------------------------------------------------------------------------------------------------------------------------
207 
208 template <typename T>
209 int NeutrinoIdTool<T>::GetNuanceCode(const Algorithm *const pAlgorithm) const
210 {
211  const MCParticleList *pMCParticleList = nullptr;
212  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pMCParticleList));
213 
214  MCParticleVector trueNeutrinos;
215  LArMCParticleHelper::GetTrueNeutrinos(pMCParticleList, trueNeutrinos);
216 
217  if (trueNeutrinos.size() != 1)
218  {
219  std::cout << "NeutrinoIdTool::GetNuanceCode - Error: number of true neutrinos in event must be exactly one" << std::endl;
220  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
221  }
222 
223  return LArMCParticleHelper::GetNuanceCode(trueNeutrinos.front());
224 }
225 
226 //------------------------------------------------------------------------------------------------------------------------------------------
227 
228 template <typename T>
229 void NeutrinoIdTool<T>::SelectAllPfos(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &hypotheses, PfoList &selectedPfos) const
230 {
231  for (const PfoList &pfos : hypotheses)
232  {
233  for (const ParticleFlowObject *const pPfo : pfos)
234  {
235  object_creation::ParticleFlowObject::Metadata metadata;
236  metadata.m_propertiesToAdd["NuScore"] = -1.f;
237  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
238  }
239 
240  this->SelectPfos(pfos, selectedPfos);
241  }
242 }
243 
244 //------------------------------------------------------------------------------------------------------------------------------------------
245 
246 template <typename T>
247 void NeutrinoIdTool<T>::SelectPfosByProbability(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
248  const SliceHypotheses &crSliceHypotheses, const SliceFeaturesVector &sliceFeaturesVector, PfoList &selectedPfos) const
249 {
250  // Calculate the probability of each slice that passes the minimum probability cut
251  std::vector<UintFloatPair> sliceIndexProbabilityPairs;
252  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
253  {
254  const float nuProbability(sliceFeaturesVector.at(sliceIndex).GetNeutrinoProbability(m_mva, m_defaultProbability));
255 
256  for (const ParticleFlowObject *const pPfo : crSliceHypotheses.at(sliceIndex))
257  {
258  object_creation::ParticleFlowObject::Metadata metadata;
259  metadata.m_propertiesToAdd["NuScore"] = nuProbability;
260 
261  if (m_persistFeatures && sliceFeaturesVector.at(sliceIndex).IsFeatureVectorAvailable())
262  {
263  LArMvaHelper::DoubleMap featureMap;
264  sliceFeaturesVector.at(sliceIndex).GetFeatureMap(featureMap);
265 
266  for (auto const &[name, value] : featureMap)
267  metadata.m_propertiesToAdd[name] = value;
268  }
269 
270  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
271  }
272 
273  for (const ParticleFlowObject *const pPfo : nuSliceHypotheses.at(sliceIndex))
274  {
275  object_creation::ParticleFlowObject::Metadata metadata;
276  metadata.m_propertiesToAdd["NuScore"] = nuProbability;
277 
278  if (m_persistFeatures && sliceFeaturesVector.at(sliceIndex).IsFeatureVectorAvailable())
279  {
280  LArMvaHelper::DoubleMap featureMap;
281  sliceFeaturesVector.at(sliceIndex).GetFeatureMap(featureMap);
282 
283  for (auto const &[name, value] : featureMap)
284  metadata.m_propertiesToAdd[name] = value;
285  }
286 
287  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
288  }
289 
290  if (nuProbability < m_minProbability)
291  {
292  this->SelectPfos(crSliceHypotheses.at(sliceIndex), selectedPfos);
293  continue;
294  }
295 
296  sliceIndexProbabilityPairs.push_back(UintFloatPair(sliceIndex, nuProbability));
297  }
298 
299  // Sort the slices by probability
300  std::sort(sliceIndexProbabilityPairs.begin(), sliceIndexProbabilityPairs.end(),
301  [](const UintFloatPair &a, const UintFloatPair &b) { return (a.second > b.second); });
302 
303  // Select the first m_maxNeutrinos as neutrinos, and the rest as cosmic
304  unsigned int nNuSlices(0);
305  for (const UintFloatPair &slice : sliceIndexProbabilityPairs)
306  {
307  if (nNuSlices < m_maxNeutrinos)
308  {
309  this->SelectPfos(nuSliceHypotheses.at(slice.first), selectedPfos);
310  nNuSlices++;
311  continue;
312  }
313 
314  this->SelectPfos(crSliceHypotheses.at(slice.first), selectedPfos);
315  }
316 }
317 
318 //------------------------------------------------------------------------------------------------------------------------------------------
319 
320 template <typename T>
321 void NeutrinoIdTool<T>::SelectPfos(const PfoList &pfos, PfoList &selectedPfos) const
322 {
323  selectedPfos.insert(selectedPfos.end(), pfos.begin(), pfos.end());
324 }
325 
326 //------------------------------------------------------------------------------------------------------------------------------------------
327 //------------------------------------------------------------------------------------------------------------------------------------------
328 
329 // TODO think about how to make this function cleaner when features are more established
330 template <typename T>
331 NeutrinoIdTool<T>::SliceFeatures::SliceFeatures(const PfoList &nuPfos, const PfoList &crPfos, const NeutrinoIdTool<T> *const pTool) :
332  m_isAvailable(false),
333  m_pTool(pTool)
334 {
335  try
336  {
337  const ParticleFlowObject *const pNeutrino(this->GetNeutrino(nuPfos));
338  const CartesianVector &nuVertex(LArPfoHelper::GetVertex(pNeutrino)->GetPosition());
339  const PfoList &nuFinalStates(pNeutrino->GetDaughterPfoList());
340 
341  // Neutrino features
342  CartesianVector nuWeightedDirTotal(0.f, 0.f, 0.f);
343  unsigned int nuNHitsUsedTotal(0);
344  unsigned int nuNHitsTotal(0);
345  CartesianPointVector nuAllSpacePoints;
346  for (const ParticleFlowObject *const pPfo : nuFinalStates)
347  {
348  CartesianPointVector spacePoints;
349  this->GetSpacePoints(pPfo, spacePoints);
350 
351  nuAllSpacePoints.insert(nuAllSpacePoints.end(), spacePoints.begin(), spacePoints.end());
352  nuNHitsTotal += spacePoints.size();
353 
354  if (spacePoints.size() < 5)
355  continue;
356 
357  const CartesianVector dir(this->GetDirectionFromVertex(spacePoints, nuVertex));
358  nuWeightedDirTotal += dir * static_cast<float>(spacePoints.size());
359  nuNHitsUsedTotal += spacePoints.size();
360  }
361 
362  if (nuNHitsUsedTotal == 0)
363  return;
364  const CartesianVector nuWeightedDir(nuWeightedDirTotal * (1.f / static_cast<float>(nuNHitsUsedTotal)));
365 
366  CartesianPointVector pointsInSphere;
367  this->GetPointsInSphere(nuAllSpacePoints, nuVertex, 10, pointsInSphere);
368 
369  CartesianVector centroid(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
370  LArPcaHelper::EigenValues eigenValues(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
371  LArPcaHelper::EigenVectors eigenVectors;
372  LArPcaHelper::RunPca(pointsInSphere, centroid, eigenValues, eigenVectors);
373 
374  const float nuNFinalStatePfos(static_cast<float>(nuFinalStates.size()));
375  const float nuVertexY(nuVertex.GetY());
376  const float nuWeightedDirZ(nuWeightedDir.GetZ());
377  const float nuNSpacePointsInSphere(static_cast<float>(pointsInSphere.size()));
378 
379  if (eigenValues.GetX() <= std::numeric_limits<float>::epsilon())
380  return;
381  const float nuEigenRatioInSphere(eigenValues.GetY() / eigenValues.GetX());
382 
383  // Cosmic-ray features
384  unsigned int nCRHitsMax(0);
385  unsigned int nCRHitsTotal(0);
386  float crLongestTrackDirY(std::numeric_limits<float>::max());
387  float crLongestTrackDeflection(-std::numeric_limits<float>::max());
388 
389  for (const ParticleFlowObject *const pPfo : crPfos)
390  {
391  CartesianPointVector spacePoints;
392  this->GetSpacePoints(pPfo, spacePoints);
393 
394  nCRHitsTotal += spacePoints.size();
395 
396  if (spacePoints.size() < 5)
397  continue;
398 
399  if (spacePoints.size() > nCRHitsMax)
400  {
401  nCRHitsMax = spacePoints.size();
402  const CartesianVector upperDir(this->GetUpperDirection(spacePoints));
403  const CartesianVector lowerDir(this->GetLowerDirection(spacePoints));
404 
405  crLongestTrackDirY = upperDir.GetY();
406  crLongestTrackDeflection = 1.f - upperDir.GetDotProduct(lowerDir * (-1.f));
407  }
408  }
409 
410  if (nCRHitsMax == 0)
411  return;
412  if (nCRHitsTotal == 0)
413  return;
414 
415  const float crFracHitsInLongestTrack = static_cast<float>(nCRHitsMax) / static_cast<float>(nCRHitsTotal);
416 
417  // Push the features to the feature vector
418  m_featureVector.push_back(nuNFinalStatePfos);
419  m_featureVector.push_back(nuNHitsTotal);
420  m_featureVector.push_back(nuVertexY);
421  m_featureVector.push_back(nuWeightedDirZ);
422  m_featureVector.push_back(nuNSpacePointsInSphere);
423  m_featureVector.push_back(nuEigenRatioInSphere);
424  m_featureVector.push_back(crLongestTrackDirY);
425  m_featureVector.push_back(crLongestTrackDeflection);
426  m_featureVector.push_back(crFracHitsInLongestTrack);
427  m_featureVector.push_back(nCRHitsMax);
428 
429  m_featureMap["NuNFinalStatePfos"] = nuNFinalStatePfos;
430  m_featureMap["NuNHitsTotal"] = nuNHitsTotal;
431  m_featureMap["NuVertexY"] = nuVertexY;
432  m_featureMap["NuWeightedDirZ"] = nuWeightedDirZ;
433  m_featureMap["NuNSpacePointsInSphere"] = nuNSpacePointsInSphere;
434  m_featureMap["NuEigenRatioInSphere"] = nuEigenRatioInSphere;
435  m_featureMap["CRLongestTrackDirY"] = crLongestTrackDirY;
436  m_featureMap["CRLongestTrackDeflection"] = crLongestTrackDeflection;
437  m_featureMap["CRFracHitsInLongestTrack"] = crFracHitsInLongestTrack;
438  m_featureMap["CRNHitsMax"] = nCRHitsMax;
439 
440  m_isAvailable = true;
441  }
442  catch (StatusCodeException &)
443  {
444  return;
445  }
446 }
447 
448 //------------------------------------------------------------------------------------------------------------------------------------------
449 
450 template <typename T>
452 {
453  return m_isAvailable;
454 }
455 
456 //------------------------------------------------------------------------------------------------------------------------------------------
457 
458 template <typename T>
460 {
461  if (!m_isAvailable)
462  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
463 
464  featureVector.insert(featureVector.end(), m_featureVector.begin(), m_featureVector.end());
465 }
466 
467 //------------------------------------------------------------------------------------------------------------------------------------------
468 
469 template <typename T>
471 {
472  if (!m_isAvailable)
473  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
474 
475  featureMap.insert(m_featureMap.begin(), m_featureMap.end());
476 }
477 
478 //------------------------------------------------------------------------------------------------------------------------------------------
479 
480 template <typename T>
481 float NeutrinoIdTool<T>::SliceFeatures::GetNeutrinoProbability(const T &t, const float defaultProbability) const
482 {
483  // ATTN if one or more of the features can not be calculated, then give the slice a default score
484  if (!this->IsFeatureVectorAvailable())
485  return defaultProbability;
486 
487  LArMvaHelper::MvaFeatureVector featureVector;
488  this->GetFeatureVector(featureVector);
489  return LArMvaHelper::CalculateProbability(t, featureVector);
490 }
491 
492 //------------------------------------------------------------------------------------------------------------------------------------------
493 
494 template <typename T>
495 const ParticleFlowObject *NeutrinoIdTool<T>::SliceFeatures::GetNeutrino(const PfoList &nuPfos) const
496 {
497  // ATTN we should only ever have one neutrino reconstructed per slice
498  if (nuPfos.size() != 1)
499  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
500 
501  return nuPfos.front();
502 }
503 
504 //------------------------------------------------------------------------------------------------------------------------------------------
505 
506 template <typename T>
507 void NeutrinoIdTool<T>::SliceFeatures::GetSpacePoints(const ParticleFlowObject *const pPfo, CartesianPointVector &spacePoints) const
508 {
509  ClusterList clusters3D;
510  LArPfoHelper::GetThreeDClusterList(pPfo, clusters3D);
511 
512  if (clusters3D.size() > 1)
513  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
514 
515  if (clusters3D.empty())
516  return;
517 
518  CaloHitList caloHits;
519  clusters3D.front()->GetOrderedCaloHitList().FillCaloHitList(caloHits);
520 
521  for (const CaloHit *const pCaloHit : caloHits)
522  spacePoints.push_back(pCaloHit->GetPositionVector());
523 }
524 
525 //------------------------------------------------------------------------------------------------------------------------------------------
526 
527 template <typename T>
528 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetDirectionFromVertex(const CartesianPointVector &spacePoints, const CartesianVector &vertex) const
529 {
530  return this->GetDirection(spacePoints,
531  [&](const CartesianVector &pointA, const CartesianVector &pointB)
532  { return ((pointA - vertex).GetMagnitude() < (pointB - vertex).GetMagnitude()); });
533 }
534 
535 //------------------------------------------------------------------------------------------------------------------------------------------
536 
537 template <typename T>
538 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetUpperDirection(const CartesianPointVector &spacePoints) const
539 {
540  return this->GetDirection(
541  spacePoints, [&](const CartesianVector &pointA, const CartesianVector &pointB) { return (pointA.GetY() > pointB.GetY()); });
542 }
543 
544 //------------------------------------------------------------------------------------------------------------------------------------------
545 
546 template <typename T>
547 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetLowerDirection(const CartesianPointVector &spacePoints) const
548 {
549  return this->GetDirection(
550  spacePoints, [&](const CartesianVector &pointA, const CartesianVector &pointB) { return (pointA.GetY() < pointB.GetY()); });
551 }
552 
553 //------------------------------------------------------------------------------------------------------------------------------------------
554 
555 template <typename T>
556 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetDirection(const CartesianPointVector &spacePoints,
557  std::function<bool(const CartesianVector &pointA, const CartesianVector &pointB)> fShouldChooseA) const
558 {
559  // ATTN If wire w pitches vary between TPCs, exception will be raised in initialisation of lar pseudolayer plugin
560  const LArTPC *const pFirstLArTPC(m_pTool->GetPandora().GetGeometry()->GetLArTPCMap().begin()->second);
561  const float layerPitch(pFirstLArTPC->GetWirePitchW());
562 
563  const ThreeDSlidingFitResult fit(&spacePoints, 5, layerPitch);
564  const CartesianVector endMin(fit.GetGlobalMinLayerPosition());
565  const CartesianVector endMax(fit.GetGlobalMaxLayerPosition());
566  const CartesianVector dirMin(fit.GetGlobalMinLayerDirection());
567  const CartesianVector dirMax(fit.GetGlobalMaxLayerDirection());
568 
569  const bool isMinStart(fShouldChooseA(endMin, endMax));
570  const CartesianVector startPoint(isMinStart ? endMin : endMax);
571  const CartesianVector endPoint(isMinStart ? endMax : endMin);
572  const CartesianVector startDir(isMinStart ? dirMin : dirMax);
573 
574  const bool shouldFlip((endPoint - startPoint).GetUnitVector().GetDotProduct(startDir) < 0.f);
575  return (shouldFlip ? startDir * (-1.f) : startDir);
576 }
577 
578 //------------------------------------------------------------------------------------------------------------------------------------------
579 
580 template <typename T>
581 void NeutrinoIdTool<T>::SliceFeatures::GetPointsInSphere(const CartesianPointVector &spacePoints, const CartesianVector &vertex,
582  const float radius, CartesianPointVector &spacePointsInSphere) const
583 {
584  for (const CartesianVector &point : spacePoints)
585  {
586  if ((point - vertex).GetMagnitudeSquared() <= radius * radius)
587  spacePointsInSphere.push_back(point);
588  }
589 }
590 
591 //------------------------------------------------------------------------------------------------------------------------------------------
592 
593 template <typename T>
594 StatusCode NeutrinoIdTool<T>::ReadSettings(const TiXmlHandle xmlHandle)
595 {
596  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "UseTrainingMode", m_useTrainingMode));
597 
598  if (m_useTrainingMode)
599  {
600  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "TrainingOutputFileName", m_trainingOutputFile));
601  }
602 
603  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumPurity", m_minPurity));
604 
605  PANDORA_RETURN_RESULT_IF_AND_IF(
606  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumCompleteness", m_minCompleteness));
607 
608  PANDORA_RETURN_RESULT_IF_AND_IF(
609  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SelectNuanceCode", m_selectNuanceCode));
610 
611  if (m_selectNuanceCode)
612  {
613  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "NuanceCode", m_nuance));
614  }
615 
616  PANDORA_RETURN_RESULT_IF_AND_IF(
617  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumNeutrinoProbability", m_minProbability));
618 
619  PANDORA_RETURN_RESULT_IF_AND_IF(
620  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "DefaultProbability", m_defaultProbability));
621 
622  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaximumNeutrinos", m_maxNeutrinos));
623 
624  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "PersistFeatures", m_persistFeatures));
625 
626  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
627  XmlHelper::ReadValue(xmlHandle, "FilePathEnvironmentVariable", m_filePathEnvironmentVariable));
628 
629  if (!m_useTrainingMode)
630  {
631  std::string mvaName;
632  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "MvaName", mvaName));
633 
634  std::string mvaFileName;
635  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "MvaFileName", mvaFileName));
636 
637  const std::string fullMvaFileName(LArFileHelper::FindFileInPath(mvaFileName, m_filePathEnvironmentVariable));
638  m_mva.Initialize(fullMvaFileName, mvaName);
639  }
640 
641  return STATUS_CODE_SUCCESS;
642 }
643 
646 
647 } // namespace lar_content
float m_defaultProbability
Default probability set if score could not be calculated.
bool PassesQualityCuts(const pandora::Algorithm *const pAlgorithm, const float purity, const float completeness) const
Determine if the event passes the selection cuts for training and has the required NUANCE code...
std::vector< pandora::PfoList > SliceHypotheses
Header file for the pfo helper class.
Header file for the neutrino id tool class.
bool m_selectInputHits
whether to select input hits
MvaTypes::MvaFeatureVector MvaFeatureVector
Definition: LArMvaHelper.h:75
bool GetBestMCSliceIndex(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses, const SliceHypotheses &crSliceHypotheses, unsigned int &bestSliceIndex) const
Get the slice with the most neutrino induced hits using Monte-Carlo information.
SliceFeatures(const pandora::PfoList &nuPfos, const pandora::PfoList &crPfos, const NeutrinoIdTool *const pTool)
Constructor.
static pandora::StatusCode ProduceTrainingExample(const std::string &trainingOutputFile, const bool result, TCONTAINER &&featureContainer)
Produce a training example with the given features and result.
Definition: LArMvaHelper.h:285
void GetFeatureMap(LArMvaHelper::DoubleMap &featureMap) const
Get the feature map for the MVA.
bool IsFeatureVectorAvailable() const
Check if all features were calculable.
float m_minProbability
Minimum probability required to classify a slice as the neutrino.
int GetNuanceCode(const pandora::Algorithm *const pAlgorithm) const
Use the current MCParticle list to get the nuance code of the neutrino in the event.
pandora::CartesianVector EigenValues
Definition: LArPcaHelper.h:24
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
const NeutrinoIdTool *const m_pTool
The tool that owns this.
static const pandora::Vertex * GetVertex(const pandora::ParticleFlowObject *const pPfo)
Get the pfo vertex.
void SelectOutputPfos(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses, const SliceHypotheses &crSliceHypotheses, pandora::PfoList &selectedPfos)
Select which reconstruction hypotheses to use; neutrino outcomes or cosmic-ray muon outcomes for each...
void SelectAllPfos(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &hypotheses, pandora::PfoList &selectedPfos) const
Select all pfos under the same hypothesis.
STL namespace.
LArMvaHelper::DoubleMap m_featureMap
A map between MVA features and their names.
Header file for the principal curve analysis helper class.
static void GetMCPrimaryMap(const pandora::MCParticleList *const pMCParticleList, MCRelationMap &mcPrimaryMap)
Get mapping from individual mc particles (in a provided list) and their primary parent mc particles...
static unsigned int GetNuanceCode(const pandora::MCParticle *const pMCParticle)
Get the nuance code of an MCParticle.
float m_minCompleteness
Minimum completeness of the best slice to use event for training.
float m_maxPhotonPropagation
the maximum photon propagation length
TFile f
Definition: plotHisto.C:6
std::string m_filePathEnvironmentVariable
The environment variable providing a list of paths to mva files.
void Collect2DHits(const pandora::PfoList &pfos, pandora::CaloHitList &reconstructedCaloHitList, const pandora::CaloHitSet &reconstructableCaloHitSet) const
Collect all 2D hits in a supplied list of Pfos and push them on to an existing hit list...
bool m_selectNuanceCode
Should select training events by nuance code.
unsigned int m_maxNeutrinos
The maximum number of neutrinos to select in any one event.
unsigned int CountNeutrinoInducedHits(const pandora::CaloHitList &caloHitList) const
Count the number of neutrino induced hits in a given list using MC information.
std::pair< unsigned int, float > UintFloatPair
void GetFeatureVector(LArMvaHelper::MvaFeatureVector &featureVector) const
Get the feature vector for the MVA.
pandora::CartesianVector GetLowerDirection(const pandora::CartesianPointVector &spacePoints) const
Use a sliding fit to get the lower direction of a collection of spacepoints.
void GetSliceFeatures(const NeutrinoIdTool *const pTool, const SliceHypotheses &nuSliceHypotheses, const SliceHypotheses &crSliceHypotheses, SliceFeaturesVector &sliceFeaturesVector) const
Get the features of each slice.
std::map< std::string, double > DoubleMap
Definition: LArMvaHelper.h:76
Header file for the lar monte carlo particle helper helper class.
int m_nuance
Nuance code to select for training.
bool m_isAvailable
Is the feature vector available.
void SelectPfos(const pandora::PfoList &pfos, pandora::PfoList &selectedPfos) const
Add the given pfos to the selected Pfo list.
bool m_useTrainingMode
Should use training mode. If true, training examples will be written to the output file...
Float_t radius
Definition: plot.C:23
float m_minPurity
Minimum purity of the best slice to use event for training.
LArMvaHelper::MvaFeatureVector m_featureVector
The MVA feature vector.
const pandora::ParticleFlowObject * GetNeutrino(const pandora::PfoList &nuPfos) const
Get the recontructed neutrino the input list of neutrino Pfos.
std::vector< art::Ptr< simb::MCParticle > > MCParticleVector
static const pandora::MCParticle * GetParentMCParticle(const pandora::MCParticle *const pMCParticle)
Get the parent mc particle.
float GetNeutrinoProbability(const T &t, const float defaultProbability) const
Get the probability that this slice contains a neutrino interaction.
static std::string FindFileInPath(const std::string &unqualifiedFileName, const std::string &environmentVariable, const std::string &delimiter=":")
Find the fully-qualified file name by searching through a list of delimiter-separated paths in a name...
Header file for the file helper class.
static void GetThreeDClusterList(const pandora::ParticleFlowObject *const pPfo, pandora::ClusterList &clusterList)
Get the list of 3D clusters from an input pfo.
void SelectPfosByProbability(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses, const SliceHypotheses &crSliceHypotheses, const SliceFeaturesVector &sliceFeaturesVector, pandora::PfoList &selectedPfos) const
Select pfos based on the probability that their slice contains a neutrino interaction.
double value
Definition: spectrum.C:18
std::vector< SliceFeatures > SliceFeaturesVector
Header file for the lar three dimensional sliding fit result class.
bool m_persistFeatures
If true, the mva features will be persisted in the metadata.
NeutrinoIdTool class.
pandora::CartesianVector GetDirectionFromVertex(const pandora::CartesianPointVector &spacePoints, const pandora::CartesianVector &vertex) const
Use a sliding fit to get the direction of a collection of spacepoint near a vertex position...
pandora::CartesianVector GetUpperDirection(const pandora::CartesianPointVector &spacePoints) const
Use a sliding fit to get the upper direction of a collection of spacepoints.
pandora::CartesianVector GetDirection(const pandora::CartesianPointVector &spacePoints, std::function< bool(const pandora::CartesianVector &pointA, const pandora::CartesianVector &pointB)> fShouldChooseA) const
Use a sliding fit to get the direction of a collection of spacepoints.
static void RunPca(const T &t, pandora::CartesianVector &centroid, EigenValues &outputEigenValues, EigenVectors &outputEigenVectors)
Run principal component analysis using input calo hits (TPC_VIEW_U,V,W or TPC_3D; all treated as 3D p...
static void GetTrueNeutrinos(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &trueNeutrinos)
Get neutrino MC particles from an input MC particle list.
TDirectory * dir
Definition: macro.C:5
std::vector< pandora::CartesianVector > EigenVectors
Definition: LArPcaHelper.h:25
void GetPointsInSphere(const pandora::CartesianPointVector &spacePoints, const pandora::CartesianVector &vertex, const float radius, pandora::CartesianPointVector &spacePointsInSphere) const
Get a vector of spacepoints within a given radius of a vertex point.
void GetSpacePoints(const pandora::ParticleFlowObject *const pPfo, pandora::CartesianPointVector &spacePoints) const
Get the 3D space points in a given pfo.
std::string m_trainingOutputFile
Output file name for training examples.
static double CalculateProbability(const MvaInterface &classifier, TCONTAINER &&featureContainer)
Use the trained mva to calculate a classification probability for an example.
Definition: LArMvaHelper.h:369
static void GetCaloHits(const pandora::PfoList &pfoList, const pandora::HitType &hitType, pandora::CaloHitList &caloHitList)
Get a list of calo hits of a particular hit type from a list of pfos.
std::unordered_map< const pandora::MCParticle *, const pandora::MCParticle * > MCRelationMap
static bool IsNeutrino(const pandora::MCParticle *const pMCParticle)
Whether a mc particle is a neutrino or antineutrino.
static void SelectCaloHits(const pandora::CaloHitList *const pCaloHitList, const MCRelationMap &mcToTargetMCMap, pandora::CaloHitList &selectedCaloHitList, const bool selectInputHits, const float maxPhotonPropagation)
Select a subset of calo hits representing those that represent "reconstructable" regions of the event...
vertex reconstruction