All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NeutrinoIdTool.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArControlFlow/NeutrinoIdTool.cc
3  *
4  * @brief Implementation of the neutrino id tool class.
5  *
6  * $Log: $
7  */
8 
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_maxNeutrinos(1),
37  m_persistFeatures(false),
38  m_filePathEnvironmentVariable("FW_SEARCH_PATH")
39 {
40 }
41 
42 //------------------------------------------------------------------------------------------------------------------------------------------
43 
44 template <typename T>
45 void NeutrinoIdTool<T>::SelectOutputPfos(const Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
46  const SliceHypotheses &crSliceHypotheses, PfoList &selectedPfos)
47 {
48  if (nuSliceHypotheses.size() != crSliceHypotheses.size())
49  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
50 
51  const unsigned int nSlices(nuSliceHypotheses.size());
52  if (nSlices == 0)
53  return;
54 
55  SliceFeaturesVector sliceFeaturesVector;
56  this->GetSliceFeatures(this, nuSliceHypotheses, crSliceHypotheses, sliceFeaturesVector);
57 
58  if (m_useTrainingMode)
59  {
60  // ATTN in training mode, just return everything as a cosmic-ray
61  this->SelectAllPfos(pAlgorithm, crSliceHypotheses, selectedPfos);
62 
63  unsigned int bestSliceIndex(std::numeric_limits<unsigned int>::max());
64  if (!this->GetBestMCSliceIndex(pAlgorithm, nuSliceHypotheses, crSliceHypotheses, bestSliceIndex))
65  return;
66 
67  for (unsigned int sliceIndex = 0; sliceIndex < nSlices; ++sliceIndex)
68  {
69  const SliceFeatures &features(sliceFeaturesVector.at(sliceIndex));
70  if (!features.IsFeatureVectorAvailable())
71  continue;
72 
73  LArMvaHelper::MvaFeatureVector featureVector;
74  features.GetFeatureVector(featureVector);
75  LArMvaHelper::ProduceTrainingExample(m_trainingOutputFile, sliceIndex == bestSliceIndex, featureVector);
76  }
77 
78  return;
79  }
80 
81  this->SelectPfosByProbability(pAlgorithm, nuSliceHypotheses, crSliceHypotheses, sliceFeaturesVector, selectedPfos);
82 }
83 
84 //------------------------------------------------------------------------------------------------------------------------------------------
85 
86 template <typename T>
87 void NeutrinoIdTool<T>::GetSliceFeatures(const NeutrinoIdTool<T> *const pTool, const SliceHypotheses &nuSliceHypotheses,
88  const SliceHypotheses &crSliceHypotheses, SliceFeaturesVector &sliceFeaturesVector) const
89 {
90  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
91  sliceFeaturesVector.push_back(SliceFeatures(nuSliceHypotheses.at(sliceIndex), crSliceHypotheses.at(sliceIndex), pTool));
92 }
93 
94 //------------------------------------------------------------------------------------------------------------------------------------------
95 
96 template <typename T>
97 bool NeutrinoIdTool<T>::GetBestMCSliceIndex(const Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
98  const SliceHypotheses &crSliceHypotheses, unsigned int &bestSliceIndex) const
99 {
100  unsigned int nHitsInBestSlice(0), nNuHitsInBestSlice(0);
101 
102  // Get all hits in all slices to find true number of mc hits
103  const CaloHitList *pAllReconstructedCaloHitList(nullptr);
104  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pAllReconstructedCaloHitList));
105 
106  const MCParticleList *pMCParticleList(nullptr);
107  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pMCParticleList));
108 
109  // Obtain map: [mc particle -> primary mc particle]
110  LArMCParticleHelper::MCRelationMap mcToPrimaryMCMap;
111  LArMCParticleHelper::GetMCPrimaryMap(pMCParticleList, mcToPrimaryMCMap);
112 
113  // Remove non-reconstructable hits, e.g. those downstream of a neutron
114  CaloHitList reconstructableCaloHitList;
116  LArMCParticleHelper::SelectCaloHits(pAllReconstructedCaloHitList, mcToPrimaryMCMap, reconstructableCaloHitList,
117  parameters.m_selectInputHits, parameters.m_maxPhotonPropagation);
118 
119  const int nuNHitsTotal(this->CountNeutrinoInducedHits(reconstructableCaloHitList));
120  const CaloHitSet reconstructableCaloHitSet(reconstructableCaloHitList.begin(), reconstructableCaloHitList.end());
121 
122  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
123  {
124  CaloHitList reconstructedCaloHitList;
125  this->Collect2DHits(crSliceHypotheses.at(sliceIndex), reconstructedCaloHitList, reconstructableCaloHitSet);
126 
127  for (const ParticleFlowObject *const pNeutrino : nuSliceHypotheses.at(sliceIndex))
128  {
129  const PfoList &nuFinalStates(pNeutrino->GetDaughterPfoList());
130  this->Collect2DHits(nuFinalStates, reconstructedCaloHitList, reconstructableCaloHitSet);
131  }
132 
133  const unsigned int nNuHits(this->CountNeutrinoInducedHits(reconstructedCaloHitList));
134 
135  if (nNuHits > nNuHitsInBestSlice)
136  {
137  nNuHitsInBestSlice = nNuHits;
138  nHitsInBestSlice = reconstructedCaloHitList.size();
139  bestSliceIndex = sliceIndex;
140  }
141  }
142 
143  // ATTN for events with no neutrino induced hits, default neutrino purity and completeness to zero
144  const float purity(nHitsInBestSlice > 0 ? static_cast<float>(nNuHitsInBestSlice) / static_cast<float>(nHitsInBestSlice) : 0.f);
145  const float completeness(nuNHitsTotal > 0 ? static_cast<float>(nNuHitsInBestSlice) / static_cast<float>(nuNHitsTotal) : 0.f);
146  return this->PassesQualityCuts(pAlgorithm, purity, completeness);
147 }
148 
149 //------------------------------------------------------------------------------------------------------------------------------------------
150 
151 template <typename T>
152 bool NeutrinoIdTool<T>::PassesQualityCuts(const Algorithm *const pAlgorithm, const float purity, const float completeness) const
153 {
154  if (purity < m_minPurity || completeness < m_minCompleteness)
155  return false;
156  if (m_selectNuanceCode && (this->GetNuanceCode(pAlgorithm) != m_nuance))
157  return false;
158 
159  return true;
160 }
161 
162 //------------------------------------------------------------------------------------------------------------------------------------------
163 
164 template <typename T>
165 void NeutrinoIdTool<T>::Collect2DHits(const PfoList &pfos, CaloHitList &reconstructedCaloHitList, const CaloHitSet &reconstructableCaloHitSet) const
166 {
167  CaloHitList collectedHits;
168  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_U, collectedHits);
169  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_V, collectedHits);
170  LArPfoHelper::GetCaloHits(pfos, TPC_VIEW_W, collectedHits);
171 
172  for (const CaloHit *const pCaloHit : collectedHits)
173  {
174  const CaloHit *const pParentHit = static_cast<const CaloHit *>(pCaloHit->GetParentAddress());
175  if (!reconstructableCaloHitSet.count(pParentHit))
176  continue;
177 
178  // Ensure no hits have been double counted
179  if (std::find(reconstructedCaloHitList.begin(), reconstructedCaloHitList.end(), pParentHit) == reconstructedCaloHitList.end())
180  reconstructedCaloHitList.push_back(pParentHit);
181  }
182 }
183 
184 //------------------------------------------------------------------------------------------------------------------------------------------
185 
186 template <typename T>
187 unsigned int NeutrinoIdTool<T>::CountNeutrinoInducedHits(const CaloHitList &caloHitList) const
188 {
189  unsigned int nNuHits(0);
190  for (const CaloHit *const pCaloHit : caloHitList)
191  {
192  try
193  {
194  if (LArMCParticleHelper::IsNeutrino(LArMCParticleHelper::GetParentMCParticle(MCParticleHelper::GetMainMCParticle(pCaloHit))))
195  nNuHits++;
196  }
197  catch (const StatusCodeException &)
198  {
199  }
200  }
201 
202  return nNuHits;
203 }
204 
205 //------------------------------------------------------------------------------------------------------------------------------------------
206 
207 template <typename T>
208 int NeutrinoIdTool<T>::GetNuanceCode(const Algorithm *const pAlgorithm) const
209 {
210  const MCParticleList *pMCParticleList = nullptr;
211  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*pAlgorithm, pMCParticleList));
212 
213  MCParticleVector trueNeutrinos;
214  LArMCParticleHelper::GetTrueNeutrinos(pMCParticleList, trueNeutrinos);
215 
216  if (trueNeutrinos.size() != 1)
217  {
218  std::cout << "NeutrinoIdTool::GetNuanceCode - Error: number of true neutrinos in event must be exactly one" << std::endl;
219  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
220  }
221 
222  return LArMCParticleHelper::GetNuanceCode(trueNeutrinos.front());
223 }
224 
225 //------------------------------------------------------------------------------------------------------------------------------------------
226 
227 template <typename T>
228 void NeutrinoIdTool<T>::SelectAllPfos(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &hypotheses, PfoList &selectedPfos) const
229 {
230  for (const PfoList &pfos : hypotheses)
231  {
232  for (const ParticleFlowObject *const pPfo : pfos)
233  {
234  object_creation::ParticleFlowObject::Metadata metadata;
235  metadata.m_propertiesToAdd["NuScore"] = -1.f;
236  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
237  }
238 
239  this->SelectPfos(pfos, selectedPfos);
240  }
241 }
242 
243 //------------------------------------------------------------------------------------------------------------------------------------------
244 
245 template <typename T>
246 void NeutrinoIdTool<T>::SelectPfosByProbability(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &nuSliceHypotheses,
247  const SliceHypotheses &crSliceHypotheses, const SliceFeaturesVector &sliceFeaturesVector, PfoList &selectedPfos) const
248 {
249  // Calculate the probability of each slice that passes the minimum probability cut
250  std::vector<UintFloatPair> sliceIndexProbabilityPairs;
251  for (unsigned int sliceIndex = 0, nSlices = nuSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
252  {
253  const float nuProbability(sliceFeaturesVector.at(sliceIndex).GetNeutrinoProbability(m_mva));
254 
255  for (const ParticleFlowObject *const pPfo : crSliceHypotheses.at(sliceIndex))
256  {
257  object_creation::ParticleFlowObject::Metadata metadata;
258  metadata.m_propertiesToAdd["NuScore"] = nuProbability;
259 
260  if (m_persistFeatures)
261  {
262  LArMvaHelper::DoubleMap featureMap;
263  sliceFeaturesVector.at(sliceIndex).GetFeatureMap(featureMap);
264 
265  for (auto const &[name, value] : featureMap)
266  metadata.m_propertiesToAdd[name] = value;
267  }
268 
269  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
270  }
271 
272  for (const ParticleFlowObject *const pPfo : nuSliceHypotheses.at(sliceIndex))
273  {
274  object_creation::ParticleFlowObject::Metadata metadata;
275  metadata.m_propertiesToAdd["NuScore"] = nuProbability;
276 
277  if (m_persistFeatures)
278  {
279  LArMvaHelper::DoubleMap featureMap;
280  sliceFeaturesVector.at(sliceIndex).GetFeatureMap(featureMap);
281 
282  for (auto const &[name, value] : featureMap)
283  metadata.m_propertiesToAdd[name] = value;
284  }
285 
286  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
287  }
288 
289  if (nuProbability < m_minProbability)
290  {
291  this->SelectPfos(crSliceHypotheses.at(sliceIndex), selectedPfos);
292  continue;
293  }
294 
295  sliceIndexProbabilityPairs.push_back(UintFloatPair(sliceIndex, nuProbability));
296  }
297 
298  // Sort the slices by probability
299  std::sort(sliceIndexProbabilityPairs.begin(), sliceIndexProbabilityPairs.end(),
300  [](const UintFloatPair &a, const UintFloatPair &b) { return (a.second > b.second); });
301 
302  // Select the first m_maxNeutrinos as neutrinos, and the rest as cosmic
303  unsigned int nNuSlices(0);
304  for (const UintFloatPair &slice : sliceIndexProbabilityPairs)
305  {
306  if (nNuSlices < m_maxNeutrinos)
307  {
308  this->SelectPfos(nuSliceHypotheses.at(slice.first), selectedPfos);
309  nNuSlices++;
310  continue;
311  }
312 
313  this->SelectPfos(crSliceHypotheses.at(slice.first), selectedPfos);
314  }
315 }
316 
317 //------------------------------------------------------------------------------------------------------------------------------------------
318 
319 template <typename T>
320 void NeutrinoIdTool<T>::SelectPfos(const PfoList &pfos, PfoList &selectedPfos) const
321 {
322  selectedPfos.insert(selectedPfos.end(), pfos.begin(), pfos.end());
323 }
324 
325 //------------------------------------------------------------------------------------------------------------------------------------------
326 //------------------------------------------------------------------------------------------------------------------------------------------
327 
328 // TODO think about how to make this function cleaner when features are more established
329 template <typename T>
330 NeutrinoIdTool<T>::SliceFeatures::SliceFeatures(const PfoList &nuPfos, const PfoList &crPfos, const NeutrinoIdTool<T> *const pTool) :
331  m_isAvailable(false),
332  m_pTool(pTool)
333 {
334  try
335  {
336  const ParticleFlowObject *const pNeutrino(this->GetNeutrino(nuPfos));
337  const CartesianVector &nuVertex(LArPfoHelper::GetVertex(pNeutrino)->GetPosition());
338  const PfoList &nuFinalStates(pNeutrino->GetDaughterPfoList());
339 
340  // Neutrino features
341  CartesianVector nuWeightedDirTotal(0.f, 0.f, 0.f);
342  unsigned int nuNHitsUsedTotal(0);
343  unsigned int nuNHitsTotal(0);
344  CartesianPointVector nuAllSpacePoints;
345  for (const ParticleFlowObject *const pPfo : nuFinalStates)
346  {
347  CartesianPointVector spacePoints;
348  this->GetSpacePoints(pPfo, spacePoints);
349 
350  nuAllSpacePoints.insert(nuAllSpacePoints.end(), spacePoints.begin(), spacePoints.end());
351  nuNHitsTotal += spacePoints.size();
352 
353  if (spacePoints.size() < 5)
354  continue;
355 
356  const CartesianVector dir(this->GetDirectionFromVertex(spacePoints, nuVertex));
357  nuWeightedDirTotal += dir * static_cast<float>(spacePoints.size());
358  nuNHitsUsedTotal += spacePoints.size();
359  }
360 
361  if (nuNHitsUsedTotal == 0)
362  return;
363  const CartesianVector nuWeightedDir(nuWeightedDirTotal * (1.f / static_cast<float>(nuNHitsUsedTotal)));
364 
365  CartesianPointVector pointsInSphere;
366  this->GetPointsInSphere(nuAllSpacePoints, nuVertex, 10, pointsInSphere);
367 
368  CartesianVector centroid(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
369  LArPcaHelper::EigenValues eigenValues(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
370  LArPcaHelper::EigenVectors eigenVectors;
371  LArPcaHelper::RunPca(pointsInSphere, centroid, eigenValues, eigenVectors);
372 
373  const float nuNFinalStatePfos(static_cast<float>(nuFinalStates.size()));
374  const float nuVertexY(nuVertex.GetY());
375  const float nuWeightedDirZ(nuWeightedDir.GetZ());
376  const float nuNSpacePointsInSphere(static_cast<float>(pointsInSphere.size()));
377 
378  if (eigenValues.GetX() <= std::numeric_limits<float>::epsilon())
379  return;
380  const float nuEigenRatioInSphere(eigenValues.GetY() / eigenValues.GetX());
381 
382  // Cosmic-ray features
383  unsigned int nCRHitsMax(0);
384  unsigned int nCRHitsTotal(0);
385  float crLongestTrackDirY(std::numeric_limits<float>::max());
386  float crLongestTrackDeflection(-std::numeric_limits<float>::max());
387 
388  for (const ParticleFlowObject *const pPfo : crPfos)
389  {
390  CartesianPointVector spacePoints;
391  this->GetSpacePoints(pPfo, spacePoints);
392 
393  nCRHitsTotal += spacePoints.size();
394 
395  if (spacePoints.size() < 5)
396  continue;
397 
398  if (spacePoints.size() > nCRHitsMax)
399  {
400  nCRHitsMax = spacePoints.size();
401  const CartesianVector upperDir(this->GetUpperDirection(spacePoints));
402  const CartesianVector lowerDir(this->GetLowerDirection(spacePoints));
403 
404  crLongestTrackDirY = upperDir.GetY();
405  crLongestTrackDeflection = 1.f - upperDir.GetDotProduct(lowerDir * (-1.f));
406  }
407  }
408 
409  if (nCRHitsMax == 0)
410  return;
411  if (nCRHitsTotal == 0)
412  return;
413 
414  const float crFracHitsInLongestTrack = static_cast<float>(nCRHitsMax) / static_cast<float>(nCRHitsTotal);
415 
416  // Push the features to the feature vector
417  m_featureVector.push_back(nuNFinalStatePfos);
418  m_featureVector.push_back(nuNHitsTotal);
419  m_featureVector.push_back(nuVertexY);
420  m_featureVector.push_back(nuWeightedDirZ);
421  m_featureVector.push_back(nuNSpacePointsInSphere);
422  m_featureVector.push_back(nuEigenRatioInSphere);
423  m_featureVector.push_back(crLongestTrackDirY);
424  m_featureVector.push_back(crLongestTrackDeflection);
425  m_featureVector.push_back(crFracHitsInLongestTrack);
426  m_featureVector.push_back(nCRHitsMax);
427 
428  m_featureMap["NuNFinalStatePfos"] = nuNFinalStatePfos;
429  m_featureMap["NuNHitsTotal"] = nuNHitsTotal;
430  m_featureMap["NuVertexY"] = nuVertexY;
431  m_featureMap["NuWeightedDirZ"] = nuWeightedDirZ;
432  m_featureMap["NuNSpacePointsInSphere"] = nuNSpacePointsInSphere;
433  m_featureMap["NuEigenRatioInSphere"] = nuEigenRatioInSphere;
434  m_featureMap["CRLongestTrackDirY"] = crLongestTrackDirY;
435  m_featureMap["CRLongestTrackDeflection"] = crLongestTrackDeflection;
436  m_featureMap["CRFracHitsInLongestTrack"] = crFracHitsInLongestTrack;
437  m_featureMap["CRNHitsMax"] = nCRHitsMax;
438 
439  m_isAvailable = true;
440  }
441  catch (StatusCodeException &)
442  {
443  return;
444  }
445 }
446 
447 //------------------------------------------------------------------------------------------------------------------------------------------
448 
449 template <typename T>
451 {
452  return m_isAvailable;
453 }
454 
455 //------------------------------------------------------------------------------------------------------------------------------------------
456 
457 template <typename T>
459 {
460  if (!m_isAvailable)
461  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
462 
463  featureVector.insert(featureVector.end(), m_featureVector.begin(), m_featureVector.end());
464 }
465 
466 //------------------------------------------------------------------------------------------------------------------------------------------
467 
468 template <typename T>
470 {
471  if (!m_isAvailable)
472  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
473 
474  featureMap.insert(m_featureMap.begin(), m_featureMap.end());
475 }
476 
477 //------------------------------------------------------------------------------------------------------------------------------------------
478 
479 template <typename T>
481 {
482  // ATTN if one or more of the features can not be calculated, then default to calling the slice a cosmic ray
483  if (!this->IsFeatureVectorAvailable())
484  return 0.f;
485 
486  LArMvaHelper::MvaFeatureVector featureVector;
487  this->GetFeatureVector(featureVector);
488  return LArMvaHelper::CalculateProbability(t, featureVector);
489 }
490 
491 //------------------------------------------------------------------------------------------------------------------------------------------
492 
493 template <typename T>
494 const ParticleFlowObject *NeutrinoIdTool<T>::SliceFeatures::GetNeutrino(const PfoList &nuPfos) const
495 {
496  // ATTN we should only ever have one neutrino reconstructed per slice
497  if (nuPfos.size() != 1)
498  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
499 
500  return nuPfos.front();
501 }
502 
503 //------------------------------------------------------------------------------------------------------------------------------------------
504 
505 template <typename T>
506 void NeutrinoIdTool<T>::SliceFeatures::GetSpacePoints(const ParticleFlowObject *const pPfo, CartesianPointVector &spacePoints) const
507 {
508  ClusterList clusters3D;
509  LArPfoHelper::GetThreeDClusterList(pPfo, clusters3D);
510 
511  if (clusters3D.size() > 1)
512  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
513 
514  if (clusters3D.empty())
515  return;
516 
517  CaloHitList caloHits;
518  clusters3D.front()->GetOrderedCaloHitList().FillCaloHitList(caloHits);
519 
520  for (const CaloHit *const pCaloHit : caloHits)
521  spacePoints.push_back(pCaloHit->GetPositionVector());
522 }
523 
524 //------------------------------------------------------------------------------------------------------------------------------------------
525 
526 template <typename T>
527 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetDirectionFromVertex(const CartesianPointVector &spacePoints, const CartesianVector &vertex) const
528 {
529  return this->GetDirection(spacePoints, [&](const CartesianVector &pointA, const CartesianVector &pointB) {
530  return ((pointA - vertex).GetMagnitude() < (pointB - vertex).GetMagnitude());
531  });
532 }
533 
534 //------------------------------------------------------------------------------------------------------------------------------------------
535 
536 template <typename T>
537 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetUpperDirection(const CartesianPointVector &spacePoints) const
538 {
539  return this->GetDirection(
540  spacePoints, [&](const CartesianVector &pointA, const CartesianVector &pointB) { return (pointA.GetY() > pointB.GetY()); });
541 }
542 
543 //------------------------------------------------------------------------------------------------------------------------------------------
544 
545 template <typename T>
546 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetLowerDirection(const CartesianPointVector &spacePoints) const
547 {
548  return this->GetDirection(
549  spacePoints, [&](const CartesianVector &pointA, const CartesianVector &pointB) { return (pointA.GetY() < pointB.GetY()); });
550 }
551 
552 //------------------------------------------------------------------------------------------------------------------------------------------
553 
554 template <typename T>
555 CartesianVector NeutrinoIdTool<T>::SliceFeatures::GetDirection(const CartesianPointVector &spacePoints,
556  std::function<bool(const CartesianVector &pointA, const CartesianVector &pointB)> fShouldChooseA) const
557 {
558  // ATTN If wire w pitches vary between TPCs, exception will be raised in initialisation of lar pseudolayer plugin
559  const LArTPC *const pFirstLArTPC(m_pTool->GetPandora().GetGeometry()->GetLArTPCMap().begin()->second);
560  const float layerPitch(pFirstLArTPC->GetWirePitchW());
561 
562  const ThreeDSlidingFitResult fit(&spacePoints, 5, layerPitch);
563  const CartesianVector endMin(fit.GetGlobalMinLayerPosition());
564  const CartesianVector endMax(fit.GetGlobalMaxLayerPosition());
565  const CartesianVector dirMin(fit.GetGlobalMinLayerDirection());
566  const CartesianVector dirMax(fit.GetGlobalMaxLayerDirection());
567 
568  const bool isMinStart(fShouldChooseA(endMin, endMax));
569  const CartesianVector startPoint(isMinStart ? endMin : endMax);
570  const CartesianVector endPoint(isMinStart ? endMax : endMin);
571  const CartesianVector startDir(isMinStart ? dirMin : dirMax);
572 
573  const bool shouldFlip((endPoint - startPoint).GetUnitVector().GetDotProduct(startDir) < 0.f);
574  return (shouldFlip ? startDir * (-1.f) : startDir);
575 }
576 
577 //------------------------------------------------------------------------------------------------------------------------------------------
578 
579 template <typename T>
580 void NeutrinoIdTool<T>::SliceFeatures::GetPointsInSphere(const CartesianPointVector &spacePoints, const CartesianVector &vertex,
581  const float radius, CartesianPointVector &spacePointsInSphere) const
582 {
583  for (const CartesianVector &point : spacePoints)
584  {
585  if ((point - vertex).GetMagnitudeSquared() <= radius * radius)
586  spacePointsInSphere.push_back(point);
587  }
588 }
589 
590 //------------------------------------------------------------------------------------------------------------------------------------------
591 
592 template <typename T>
593 StatusCode NeutrinoIdTool<T>::ReadSettings(const TiXmlHandle xmlHandle)
594 {
595  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "UseTrainingMode", m_useTrainingMode));
596 
597  if (m_useTrainingMode)
598  {
599  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "TrainingOutputFileName", m_trainingOutputFile));
600  }
601 
602  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumPurity", m_minPurity));
603 
604  PANDORA_RETURN_RESULT_IF_AND_IF(
605  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumCompleteness", m_minCompleteness));
606 
607  PANDORA_RETURN_RESULT_IF_AND_IF(
608  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SelectNuanceCode", m_selectNuanceCode));
609 
610  if (m_selectNuanceCode)
611  {
612  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "NuanceCode", m_nuance));
613  }
614 
615  PANDORA_RETURN_RESULT_IF_AND_IF(
616  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinimumNeutrinoProbability", m_minProbability));
617 
618  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaximumNeutrinos", m_maxNeutrinos));
619 
620  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "PersistFeatures", m_persistFeatures));
621 
622  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
623  XmlHelper::ReadValue(xmlHandle, "FilePathEnvironmentVariable", m_filePathEnvironmentVariable));
624 
625  if (!m_useTrainingMode)
626  {
627  std::string mvaName;
628  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "MvaName", mvaName));
629 
630  std::string mvaFileName;
631  PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, XmlHelper::ReadValue(xmlHandle, "MvaFileName", mvaFileName));
632 
633  const std::string fullMvaFileName(LArFileHelper::FindFileInPath(mvaFileName, m_filePathEnvironmentVariable));
634  m_mva.Initialize(fullMvaFileName, mvaName);
635  }
636 
637  return STATUS_CODE_SUCCESS;
638 }
639 
642 
643 } // namespace lar_content
process_name vertex
Definition: cheaterreco.fcl:51
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...
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
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:72
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:282
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)
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.
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
process_name gaushit a
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.
float GetNeutrinoProbability(const T &t) const
Get the probability that this slice contains a neutrino interaction.
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:73
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...
std::vector< pandora::PfoList > SliceHypotheses
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.
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.
std::vector< SliceFeatures > SliceFeaturesVector
Header file for the lar three dimensional sliding fit result class.
tuple dir
Definition: dropbox.py:28
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...
required by fuzzyCluster table::sbnd_g4_services gaushitTruthMatch pandora
Definition: reco_sbnd.fcl:182
static void GetTrueNeutrinos(const pandora::MCParticleList *const pMCParticleList, pandora::MCParticleVector &trueNeutrinos)
Get neutrino MC particles from an input MC particle list.
std::vector< pandora::CartesianVector > EigenVectors
Definition: LArPcaHelper.h:25
then echo fcl name
temporary value
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:366
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
BEGIN_PROLOG could also be cout
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 &quot;reconstructable&quot; regions of the event...