LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ShowerStartFinderTool.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 #include "Pandora/AlgorithmTool.h"
11 
14 
18 
20 
21 using namespace pandora;
22 
23 namespace lar_content
24 {
25 
26 ShowerStartFinderTool::ShowerStartFinderTool() :
27  m_spineSlidingFitWindow(10),
28  m_longitudinalCoordinateBinSize(1.f),
29  m_nInitialEnergyBins(5),
30  m_minSigmaDeviation(1.f),
31  m_maxEdgeGap(3.f),
32  m_longitudinalShowerFraction(0.85f),
33  m_minShowerOpeningAngle(2.f),
34  m_molliereRadius(4.5f),
35  m_showerSlidingFitWindow(1000),
36  m_maxLayerSeparation(5)
37 {
38 }
39 
40 //------------------------------------------------------------------------------------------------------------------------------------------
41 
42 StatusCode ShowerStartFinderTool::Run(const ParticleFlowObject *const pShowerPfo, const CartesianVector &peakDirection,
43  const HitType hitType, const CaloHitList &showerSpineHitList, CartesianVector &showerStartPosition, CartesianVector &showerStartDirection)
44 {
45  try
46  {
47  // Fit the shower spine
48  CartesianPointVector hitPositions;
49  for (const CaloHit *const pCaloHit : showerSpineHitList)
50  hitPositions.push_back(pCaloHit->GetPositionVector());
51 
52  const TwoDSlidingFitResult spineTwoDSlidingFit(
53  &hitPositions, m_spineSlidingFitWindow, LArGeometryHelper::GetWirePitch(this->GetPandora(), hitType));
54 
55  // First obtain the longitudinal position of spine hits
56  LongitudinalPositionMap longitudinalPositionMap;
57  this->ObtainLongitudinalDecomposition(spineTwoDSlidingFit, showerSpineHitList, longitudinalPositionMap);
58 
59  // Obtain spine energy profile
60  EnergySpectrumMap energySpectrumMap;
61  this->GetEnergyDistribution(showerSpineHitList, longitudinalPositionMap, energySpectrumMap);
62 
63  // Now find the shower start position/direction
64  const bool isEndDownstream(peakDirection.GetZ() > 0.f);
65 
66  this->FindShowerStartAndDirection(pShowerPfo, hitType, spineTwoDSlidingFit, energySpectrumMap, showerSpineHitList, isEndDownstream,
67  showerStartPosition, showerStartDirection);
68  }
69  catch (...)
70  {
71  return STATUS_CODE_FAILURE;
72  }
73 
74  return STATUS_CODE_SUCCESS;
75 }
76 
77 //------------------------------------------------------------------------------------------------------------------------------------------
78 
80  const CaloHitList &showerSpineHitList, LongitudinalPositionMap &longitudinalPositionMap) const
81 {
82  // Find hits in each layer
83  LayerToHitMap layerToHitMap;
84 
85  for (const CaloHit *const pCaloHit : showerSpineHitList)
86  {
87  float hitL(0.f), hitT(0.f);
88  const CartesianVector hitPosition(pCaloHit->GetPositionVector());
89 
90  spineTwoDSlidingFit.GetLocalPosition(hitPosition, hitL, hitT);
91  layerToHitMap[spineTwoDSlidingFit.GetLayer(hitL)].push_back(pCaloHit);
92  }
93 
94  // Walk through layers and store the longitudinal distance of each hit
95  // IMPORTANT: layer positions correspond to the middle of the layer
96  // IMPORTANT: the longitudinal distance is measured relative to the first layer position (where l = 0)
97  // IMPORTANT: at any point, the running distance is that at the lowest neighbour central position
98  float runningDistance(0);
99  const LayerFitResultMap &layerFitResultMap(spineTwoDSlidingFit.GetLayerFitResultMap());
100 
101  for (auto iter = layerToHitMap.begin(); iter != layerToHitMap.end(); ++iter)
102  {
103  const int layer(iter->first);
104  const float layerL(layerFitResultMap.at(layer).GetL());
105 
106  // Get global positions of layer and its neighbours
107  const int higherLayer(std::next(iter) == layerToHitMap.end() ? layer : std::next(iter)->first);
108  const int middleLayer(layer);
109  const int lowerLayer(iter == layerToHitMap.begin() ? layer : std::prev(iter)->first);
110  CartesianVector lowerLayerPosition(0.f, 0.f, 0.f), middleLayerPosition(0.f, 0.f, 0.f), higherLayerPosition(0.f, 0.f, 0.f);
111 
112  spineTwoDSlidingFit.GetGlobalPosition(layerFitResultMap.at(lowerLayer).GetL(), layerFitResultMap.at(lowerLayer).GetFitT(), lowerLayerPosition);
113  spineTwoDSlidingFit.GetGlobalPosition(layerFitResultMap.at(middleLayer).GetL(), layerFitResultMap.at(middleLayer).GetFitT(), middleLayerPosition);
114  spineTwoDSlidingFit.GetGlobalPosition(layerFitResultMap.at(higherLayer).GetL(), layerFitResultMap.at(higherLayer).GetFitT(), higherLayerPosition);
115 
116  const float layerLength = std::next(iter) == layerToHitMap.end() ? 0.f
117  : iter == layerToHitMap.begin() ? 0.f
118  : (middleLayerPosition - lowerLayerPosition).GetMagnitude();
119 
120  for (const CaloHit *const pCaloHit : iter->second)
121  {
122  const CartesianVector &hitPosition(pCaloHit->GetPositionVector());
123 
124  float hitL(0.f), hitT(0.f);
125  spineTwoDSlidingFit.GetLocalPosition(hitPosition, hitL, hitT);
126 
127  // Is the hit below or above the middle of the layer?
128  // Therefore, determine which layer positions the hit is between
129  CartesianVector localLowerLayerPosition(0.f, 0.f, 0.f), localHigherLayerPosition(0.f, 0.f, 0.f);
130 
131  if (hitL < layerL)
132  {
133  localLowerLayerPosition = lowerLayerPosition;
134  localHigherLayerPosition = iter == layerToHitMap.begin() ? higherLayerPosition : middleLayerPosition;
135  }
136  else
137  {
138  localLowerLayerPosition = std::next(iter) == layerToHitMap.end() ? lowerLayerPosition : middleLayerPosition;
139  localHigherLayerPosition = higherLayerPosition;
140  }
141 
142  // Determine the axis to project onto
143  // Calculate the distance of axis between its two nearest neighbours
144  const CartesianVector displacement((higherLayerPosition - lowerLayerPosition).GetUnitVector());
145  float longitudinalDisplacement = (displacement.GetDotProduct(hitPosition - lowerLayerPosition) + runningDistance);
146 
147  // If we've passed the central position, we need to add on the layer length
148  longitudinalDisplacement += (hitL > layerL ? layerLength : 0.f);
149 
150  longitudinalPositionMap[pCaloHit] = longitudinalDisplacement;
151  }
152 
153  runningDistance += layerLength;
154  }
155 }
156 
157 //------------------------------------------------------------------------------------------------------------------------------------------
158 
159 void ShowerStartFinderTool::GetEnergyDistribution(const CaloHitList &showerSpineHitList,
160  const LongitudinalPositionMap &longitudinalPositionMap, EnergySpectrumMap &energySpectrumMap) const
161 {
162  float e0(0.f);
163 
164  for (const CaloHit *pCaloHit : showerSpineHitList)
165  e0 += pCaloHit->GetElectromagneticEnergy();
166 
167  for (const CaloHit *pCaloHit : showerSpineHitList)
168  {
169  const float fractionalEnergy(pCaloHit->GetElectromagneticEnergy() / e0);
170  const float projection(longitudinalPositionMap.at(pCaloHit));
171  const int longitudinalIndex = std::floor(projection / m_longitudinalCoordinateBinSize);
172 
173  if (energySpectrumMap.find(longitudinalIndex) == energySpectrumMap.end())
174  energySpectrumMap[longitudinalIndex] = fractionalEnergy;
175  else
176  energySpectrumMap[longitudinalIndex] += fractionalEnergy;
177  }
178 }
179 
180 //------------------------------------------------------------------------------------------------------------------------------------------
181 
182 void ShowerStartFinderTool::FindShowerStartAndDirection(const ParticleFlowObject *const pShowerPfo, const HitType hitType,
183  const TwoDSlidingFitResult &spineTwoDSlidingFit, const EnergySpectrumMap &energySpectrumMap, const CaloHitList &showerSpineHitList,
184  const bool isEndDownstream, CartesianVector &showerStartPosition, CartesianVector &showerStartDirection) const
185 {
186  // Search for shower start at significant energy deviations
187  int longitudinalStartBin(0);
188 
189  if (isEndDownstream)
190  {
191  longitudinalStartBin = this->FindShowerStartLongitudinalCoordinate(pShowerPfo, hitType, spineTwoDSlidingFit, energySpectrumMap,
192  showerSpineHitList, isEndDownstream, energySpectrumMap.begin(), std::prev(energySpectrumMap.end()));
193  }
194  else
195  {
196  longitudinalStartBin = this->FindShowerStartLongitudinalCoordinate(pShowerPfo, hitType, spineTwoDSlidingFit, energySpectrumMap,
197  showerSpineHitList, isEndDownstream, energySpectrumMap.rbegin(), std::prev(energySpectrumMap.rend()));
198  }
199 
200  const float longitudinalStartCoordinate(longitudinalStartBin * m_longitudinalCoordinateBinSize);
201 
202  this->ConvertLongitudinalProjectionToGlobal(spineTwoDSlidingFit, longitudinalStartCoordinate, showerStartPosition, showerStartDirection);
203 
204  showerStartDirection = isEndDownstream ? showerStartDirection : showerStartDirection * (-1.f);
205 }
206 
207 //------------------------------------------------------------------------------------------------------------------------------------------
208 
209 template <typename T>
210 int ShowerStartFinderTool::FindShowerStartLongitudinalCoordinate(const ParticleFlowObject *const pShowerPfo, const HitType hitType,
211  const TwoDSlidingFitResult &spineTwoDSlidingFit, const EnergySpectrumMap &energySpectrumMap, const CaloHitList &showerSpineHitList,
212  const bool isEndDownstream, const T startIter, const T endIter) const
213 {
214  // Characterise initial energy
215  float meanEnergy(0.f), energySigma(0.f);
216 
217  if (energySpectrumMap.size() > m_nInitialEnergyBins)
218  this->CharacteriseInitialEnergy(energySpectrumMap, isEndDownstream, meanEnergy, energySigma);
219 
220  // Now loop through energySpectrumMap
221  T iter(startIter);
222 
223  std::advance(iter, (energySpectrumMap.size() > m_nInitialEnergyBins) ? m_nInitialEnergyBins : (energySpectrumMap.size() - 1));
224 
225  for (; iter != endIter; iter++)
226  {
227  const float longitudinalCoordinate(iter->first * m_longitudinalCoordinateBinSize);
228  const float energyDeviation((iter->second - meanEnergy) / energySigma);
229 
230  // Use energy and local topology to assess whether we are at the shower start
231  if ((energyDeviation > m_minSigmaDeviation) &&
232  this->IsShowerTopology(pShowerPfo, hitType, spineTwoDSlidingFit, longitudinalCoordinate, showerSpineHitList, isEndDownstream))
233  {
234  break;
235  }
236  }
237 
238  return iter->first;
239 }
240 
241 //------------------------------------------------------------------------------------------------------------------------------------------
242 
244  const EnergySpectrumMap &energySpectrumMap, const bool isEndDownstream, float &meanEnergy, float &energySigma) const
245 {
246  auto energySpectrumIter(isEndDownstream ? energySpectrumMap.begin() : std::prev(energySpectrumMap.end()));
247 
248  for (unsigned int i = 0; i < m_nInitialEnergyBins; ++i)
249  {
250  meanEnergy += energySpectrumIter->second;
251  isEndDownstream ? ++energySpectrumIter : --energySpectrumIter;
252  }
253 
254  meanEnergy /= static_cast<float>(m_nInitialEnergyBins);
255 
256  energySpectrumIter = isEndDownstream ? energySpectrumMap.begin() : std::prev(energySpectrumMap.end());
257 
258  for (unsigned int i = 0; i < m_nInitialEnergyBins; ++i)
259  {
260  energySigma += std::pow(energySpectrumIter->second - meanEnergy, 2);
261  isEndDownstream ? ++energySpectrumIter : --energySpectrumIter;
262  }
263 
264  energySigma = std::sqrt(energySigma / m_nInitialEnergyBins);
265 }
266 
267 //------------------------------------------------------------------------------------------------------------------------------------------
268 
269 bool ShowerStartFinderTool::IsShowerTopology(const ParticleFlowObject *const pShowerPfo, const HitType hitType, const TwoDSlidingFitResult &spineTwoDSlidingFit,
270  const float longitudinalDistance, const CaloHitList &showerSpineHitList, const bool isEndDownstream) const
271 {
272  CartesianVector showerStartPosition(0.f, 0.f, 0.f), showerStartDirection(0.f, 0.f, 0.f);
273  this->ConvertLongitudinalProjectionToGlobal(spineTwoDSlidingFit, longitudinalDistance, showerStartPosition, showerStartDirection);
274 
275  // Build shower
276  CartesianPointVector showerRegionPositionVector;
277  if (this->BuildShowerRegion(pShowerPfo, hitType, showerSpineHitList, showerStartPosition, showerStartDirection, isEndDownstream,
278  showerRegionPositionVector) != STATUS_CODE_SUCCESS)
279  {
280  return false;
281  }
282 
283  // Characterise the shower
284  bool isBetween(false);
285  CartesianVector positiveEdgeStart(0.f, 0.f, 0.f), positiveEdgeEnd(0.f, 0.f, 0.f), positiveEdgeDirection(0.f, 0.f, 0.f);
286  CartesianVector negativeEdgeStart(0.f, 0.f, 0.f), negativeEdgeEnd(0.f, 0.f, 0.f), negativeEdgeDirection(0.f, 0.f, 0.f);
287 
288  if (this->CharacteriseShowerTopology(showerRegionPositionVector, showerStartPosition, hitType, isEndDownstream, showerStartDirection,
289  positiveEdgeStart, positiveEdgeEnd, negativeEdgeStart, negativeEdgeEnd, isBetween) != STATUS_CODE_SUCCESS)
290  {
291  return false;
292  }
293 
294  if (!isBetween)
295  return false;
296 
297  positiveEdgeStart = showerStartPosition;
298  negativeEdgeStart = showerStartPosition;
299  positiveEdgeDirection = positiveEdgeEnd - positiveEdgeStart;
300  negativeEdgeDirection = negativeEdgeEnd - negativeEdgeStart;
301 
302  const float showerOpeningAngle(positiveEdgeDirection.GetOpeningAngle(negativeEdgeDirection) * 180.f / 3.14);
303 
304  if (showerOpeningAngle < m_minShowerOpeningAngle)
305  return false;
306 
307  return true;
308 }
309 
310 //------------------------------------------------------------------------------------------------------------------------------------------
311 
313  const float longitudinalDistance, CartesianVector &globalPosition, CartesianVector &globalDirection) const
314 {
315  const LayerFitResultMap &layerFitResultMap(spineTwoDSlidingFit.GetLayerFitResultMap());
316 
317  float runningDistance(0.f);
318  int showerStartLayer(0);
319  CartesianVector previousLayerPosition(0.f, 0.f, 0.f);
320  spineTwoDSlidingFit.GetGlobalPosition(layerFitResultMap.begin()->second.GetL(), layerFitResultMap.begin()->second.GetFitT(), previousLayerPosition);
321 
322  for (auto iter = std::next(layerFitResultMap.begin()); iter != layerFitResultMap.end(); ++iter)
323  {
324  const int layer(iter->first);
325  showerStartLayer = layer;
326 
327  CartesianVector layerPosition(0.f, 0.f, 0.f);
328  spineTwoDSlidingFit.GetGlobalPosition(layerFitResultMap.at(layer).GetL(), layerFitResultMap.at(layer).GetFitT(), layerPosition);
329 
330  runningDistance += (layerPosition - previousLayerPosition).GetMagnitude();
331 
332  if (runningDistance > longitudinalDistance)
333  break;
334 
335  previousLayerPosition = layerPosition;
336  }
337 
338  const float lCoordinate(layerFitResultMap.at(showerStartLayer).GetL()), tCoordinate(layerFitResultMap.at(showerStartLayer).GetFitT());
339  const float localGradient(layerFitResultMap.at(showerStartLayer).GetGradient());
340 
341  spineTwoDSlidingFit.GetGlobalPosition(lCoordinate, tCoordinate, globalPosition);
342  spineTwoDSlidingFit.GetGlobalDirection(localGradient, globalDirection);
343 }
344 
345 //------------------------------------------------------------------------------------------------------------------------------------------
346 
347 StatusCode ShowerStartFinderTool::BuildShowerRegion(const ParticleFlowObject *const pShowerPfo, const HitType hitType,
348  const CaloHitList &showerSpineHitList, const CartesianVector &showerStartPosition, const CartesianVector &showerStartDirection,
349  const bool isEndDownstream, CartesianPointVector &showerRegionPositionVector) const
350 {
351  CaloHitList viewShowerHitList;
352  LArPfoHelper::GetCaloHits(pShowerPfo, hitType, viewShowerHitList);
353 
354  // Find shower region hits
355  CaloHitList showerRegionHitList;
356 
357  for (const CaloHit *const pCaloHit : viewShowerHitList)
358  {
359  if (std::find(showerSpineHitList.begin(), showerSpineHitList.end(), pCaloHit) != showerSpineHitList.end())
360  continue;
361 
362  const CartesianVector &hitPosition(pCaloHit->GetPositionVector());
363  float l(showerStartDirection.GetDotProduct(hitPosition - showerStartPosition));
364 
365  l *= isEndDownstream ? 1.f : -1.f;
366 
367  if (l < 0.f)
368  continue;
369 
370  if (showerStartDirection.GetCrossProduct(hitPosition - showerStartPosition).GetMagnitude() > m_molliereRadius)
371  continue;
372 
373  showerRegionPositionVector.push_back(pCaloHit->GetPositionVector());
374  showerRegionHitList.push_back(pCaloHit);
375  }
376 
377  // Searching for a continuous shower, so truncate at first gap
378  CartesianPointVector coordinateListP, coordinateListN;
379  try
380  {
381  // Perform shower sliding fit
382  const TwoDSlidingShowerFitResult twoDShowerSlidingFit(
383  &showerRegionPositionVector, m_showerSlidingFitWindow, LArGeometryHelper::GetWirePitch(this->GetPandora(), hitType));
384  const LayerFitResultMap &layerFitResultMapS(twoDShowerSlidingFit.GetShowerFitResult().GetLayerFitResultMap());
385  const LayerFitResultMap &layerFitResultMapP(twoDShowerSlidingFit.GetPositiveEdgeFitResult().GetLayerFitResultMap());
386  const LayerFitResultMap &layerFitResultMapN(twoDShowerSlidingFit.GetNegativeEdgeFitResult().GetLayerFitResultMap());
387 
388  float showerStartL(0.f), showerStartT(0.f);
389  twoDShowerSlidingFit.GetShowerFitResult().GetLocalPosition(showerStartPosition, showerStartL, showerStartT);
390 
391  const int startLayer(twoDShowerSlidingFit.GetShowerFitResult().GetLayer(showerStartL));
392 
393  // Check for gaps in the shower
394  for (LayerFitResultMap::const_iterator iterS = layerFitResultMapS.begin(); iterS != layerFitResultMapS.end(); ++iterS)
395  {
396  const int layer(iterS->first);
397 
398  if (isEndDownstream && (layer < startLayer))
399  continue;
400 
401  if (!isEndDownstream && (layer > startLayer))
402  continue;
403 
404  LayerFitResultMap::const_iterator iterP = layerFitResultMapP.find(layer);
405  LayerFitResultMap::const_iterator iterN = layerFitResultMapN.find(layer);
406 
407  if (layerFitResultMapP.end() != iterP)
408  {
409  CartesianVector positiveEdgePosition(0.f, 0.f, 0.f);
410  twoDShowerSlidingFit.GetShowerFitResult().GetGlobalPosition(iterP->second.GetL(), iterP->second.GetFitT(), positiveEdgePosition);
411  coordinateListP.push_back(positiveEdgePosition);
412  }
413 
414  if (layerFitResultMapN.end() != iterN)
415  {
416  CartesianVector negativeEdgePosition(0.f, 0.f, 0.f);
417  twoDShowerSlidingFit.GetShowerFitResult().GetGlobalPosition(iterN->second.GetL(), iterN->second.GetFitT(), negativeEdgePosition);
418  coordinateListN.push_back(negativeEdgePosition);
419  }
420  }
421 
422  if ((coordinateListP.size() == 0) || (coordinateListN.size() == 0))
423  return STATUS_CODE_FAILURE;
424 
425  std::sort(coordinateListP.begin(), coordinateListP.end(), LArConnectionPathwayHelper::SortByDistanceToPoint(showerStartPosition));
426  std::sort(coordinateListN.begin(), coordinateListN.end(), LArConnectionPathwayHelper::SortByDistanceToPoint(showerStartPosition));
427 
428  float pMaximumL(std::numeric_limits<float>::max());
429  CartesianVector previousCoordinateP(coordinateListP.front());
430 
431  for (auto iterP = std::next(coordinateListP.begin()); iterP != coordinateListP.end(); ++iterP)
432  {
433  const CartesianVector &coordinateP(*iterP);
434  const float separationSquared((coordinateP - previousCoordinateP).GetMagnitudeSquared());
435 
436  if (separationSquared > (m_maxEdgeGap * m_maxEdgeGap))
437  break;
438 
439  float thisT(0.f);
440  twoDShowerSlidingFit.GetShowerFitResult().GetLocalPosition(coordinateP, pMaximumL, thisT);
441 
442  previousCoordinateP = coordinateP;
443  }
444 
445  float nMaximumL(std::numeric_limits<float>::max());
446  CartesianVector previousCoordinateN(coordinateListN.front());
447 
448  for (auto iterN = std::next(coordinateListN.begin()); iterN != coordinateListN.end(); ++iterN)
449  {
450  const CartesianVector &coordinateN(*iterN);
451  const float separationSquared((coordinateN - previousCoordinateN).GetMagnitudeSquared());
452 
453  if (separationSquared > (m_maxEdgeGap * m_maxEdgeGap))
454  break;
455 
456  float thisT(0.f);
457  twoDShowerSlidingFit.GetShowerFitResult().GetLocalPosition(coordinateN, nMaximumL, thisT);
458 
459  previousCoordinateN = coordinateN;
460  }
461 
462  // Now truncate the halo hit position vector
463  showerRegionPositionVector.clear();
464 
465  for (const CaloHit *const pCaloHit : showerRegionHitList)
466  {
467  float thisL(0.f), thisT(0.f);
468  const CartesianVector &hitPosition(pCaloHit->GetPositionVector());
469 
470  twoDShowerSlidingFit.GetShowerFitResult().GetLocalPosition(hitPosition, thisL, thisT);
471 
472  // because the end of showers can get really narrow and thus fail the opening angle check
473  if (isEndDownstream && (thisL > (m_longitudinalShowerFraction * std::max(pMaximumL, nMaximumL))))
474  continue;
475 
476  if (!isEndDownstream && (thisL < (m_longitudinalShowerFraction * std::min(pMaximumL, nMaximumL))))
477  continue;
478 
479  showerRegionPositionVector.push_back(pCaloHit->GetPositionVector());
480  }
481  }
482  catch (const StatusCodeException &)
483  {
484  return STATUS_CODE_FAILURE;
485  }
486 
487  return STATUS_CODE_SUCCESS;
488 }
489 
490 //------------------------------------------------------------------------------------------------------------------------------------------
491 
492 StatusCode ShowerStartFinderTool::CharacteriseShowerTopology(const CartesianPointVector &showerRegionPositionVector, const CartesianVector &showerStartPosition,
493  const HitType hitType, const bool isEndDownstream, const CartesianVector &showerStartDirection, CartesianVector &positiveEdgeStart,
494  CartesianVector &positiveEdgeEnd, CartesianVector &negativeEdgeStart, CartesianVector &negativeEdgeEnd, bool &isBetween) const
495 {
496  try
497  {
498  // Perform shower sliding fit
499  const TwoDSlidingShowerFitResult twoDShowerSlidingFit(
500  &showerRegionPositionVector, m_showerSlidingFitWindow, LArGeometryHelper::GetWirePitch(this->GetPandora(), hitType));
501  const LayerFitResultMap &layerFitResultMapS(twoDShowerSlidingFit.GetShowerFitResult().GetLayerFitResultMap());
502  const LayerFitResultMap &layerFitResultMapP(twoDShowerSlidingFit.GetPositiveEdgeFitResult().GetLayerFitResultMap());
503  const LayerFitResultMap &layerFitResultMapN(twoDShowerSlidingFit.GetNegativeEdgeFitResult().GetLayerFitResultMap());
504 
505  float showerStartL(0.f), showerStartT(0.f);
506  twoDShowerSlidingFit.GetShowerFitResult().GetLocalPosition(showerStartPosition, showerStartL, showerStartT);
507 
508  const int startLayer(twoDShowerSlidingFit.GetShowerFitResult().GetLayer(showerStartL));
509  const int endLayer(twoDShowerSlidingFit.GetShowerFitResult().GetMaxLayer());
510 
511  // Does the shower look to originate from the shower start position
512  int layerCount(0);
513  bool isFirstBetween(false), isLastBetween(false);
514  CartesianPointVector coordinateListP, coordinateListN;
515 
516  for (LayerFitResultMap::const_iterator iterS = layerFitResultMapS.begin(); iterS != layerFitResultMapS.end(); ++iterS)
517  {
518  int layer(iterS->first);
519 
520  if (isEndDownstream && (layer < startLayer))
521  continue;
522 
523  if (!isEndDownstream && (layer > startLayer))
524  continue;
525 
526  LayerFitResultMap::const_iterator iterP = layerFitResultMapP.find(layer);
527  LayerFitResultMap::const_iterator iterN = layerFitResultMapN.find(layer);
528 
529  CartesianVector positiveEdgePosition(0.f, 0.f, 0.f), negativeEdgePosition(0.f, 0.f, 0.f);
530  if (layerFitResultMapP.end() != iterP)
531  {
532  twoDShowerSlidingFit.GetShowerFitResult().GetGlobalPosition(iterP->second.GetL(), iterP->second.GetFitT(), positiveEdgePosition);
533  coordinateListP.push_back(positiveEdgePosition);
534  }
535 
536  if (layerFitResultMapN.end() != iterN)
537  {
538  twoDShowerSlidingFit.GetShowerFitResult().GetGlobalPosition(iterN->second.GetL(), iterN->second.GetFitT(), negativeEdgePosition);
539  coordinateListN.push_back(negativeEdgePosition);
540  }
541 
542  if ((layerFitResultMapP.end() != iterP) && (layerFitResultMapN.end() != iterN))
543  {
544  const CartesianVector positiveDisplacement((positiveEdgePosition - showerStartPosition).GetUnitVector());
545  const bool isPositiveClockwise(this->IsClockwiseRotation(showerStartDirection, positiveDisplacement));
546 
547  const CartesianVector negativeDisplacement((negativeEdgePosition - showerStartPosition).GetUnitVector());
548  const bool isNegativeClockwise(this->IsClockwiseRotation(showerStartDirection, negativeDisplacement));
549 
550  ++layerCount;
551 
552  if (layerCount == 1)
553  isFirstBetween = (isPositiveClockwise != isNegativeClockwise);
554 
555  isLastBetween = (isPositiveClockwise != isNegativeClockwise);
556  }
557  }
558 
559  isBetween = (isFirstBetween || isLastBetween);
560 
561  // Find the extremal points of the shower
562  if (this->GetBoundaryExtremalPoints(twoDShowerSlidingFit, layerFitResultMapP, showerStartPosition, startLayer, endLayer,
563  positiveEdgeStart, positiveEdgeEnd) != STATUS_CODE_SUCCESS)
564  {
565  return STATUS_CODE_FAILURE;
566  }
567 
568  if (this->GetBoundaryExtremalPoints(twoDShowerSlidingFit, layerFitResultMapN, showerStartPosition, startLayer, endLayer,
569  negativeEdgeStart, negativeEdgeEnd) != STATUS_CODE_SUCCESS)
570  {
571  return STATUS_CODE_FAILURE;
572  }
573  }
574  catch (const StatusCodeException &)
575  {
576  return STATUS_CODE_FAILURE;
577  }
578 
579  return STATUS_CODE_SUCCESS;
580 }
581 
582 //------------------------------------------------------------------------------------------------------------------------------------------
583 
584 bool ShowerStartFinderTool::IsClockwiseRotation(const CartesianVector &showerStartDirection, const CartesianVector &displacementVector) const
585 {
586  // Clockwise with respect to +ve Z
587  const float openingAngleFromCore(showerStartDirection.GetOpeningAngle(displacementVector));
588 
589  const CartesianVector clockwiseRotation(
590  displacementVector.GetZ() * std::sin(openingAngleFromCore) + displacementVector.GetX() * std::cos(openingAngleFromCore), 0.f,
591  displacementVector.GetZ() * std::cos(openingAngleFromCore) - displacementVector.GetX() * std::sin(openingAngleFromCore));
592 
593  const CartesianVector anticlockwiseRotation(
594  displacementVector.GetZ() * std::sin(-1.f * openingAngleFromCore) + displacementVector.GetX() * std::cos(-1.f * openingAngleFromCore), 0.f,
595  displacementVector.GetZ() * std::cos(-1.f * openingAngleFromCore) - displacementVector.GetX() * std::sin(-1.f * openingAngleFromCore));
596 
597  const float clockwiseT((clockwiseRotation - showerStartDirection).GetMagnitude());
598  const float anticlockwiseT((anticlockwiseRotation - showerStartDirection).GetMagnitude());
599  const bool isClockwise(clockwiseT < anticlockwiseT);
600 
601  return isClockwise;
602 }
603 
604 //------------------------------------------------------------------------------------------------------------------------------------------
605 
607  const LayerFitResultMap &layerFitResultMap, const CartesianVector &showerStartPosition, const int showerStartLayer,
608  const int showerEndLayer, CartesianVector &boundaryEdgeStart, CartesianVector &boundaryEdgeEnd) const
609 {
610  int boundaryStartLayer(std::numeric_limits<int>::max());
611  int boundaryEndLayer(std::numeric_limits<int>::max());
612 
613  for (auto &entry : layerFitResultMap)
614  {
615  const int bestStartSeparation(std::abs(showerStartLayer - boundaryStartLayer));
616  const int thisStartSeparation(std::abs(showerStartLayer - entry.first));
617 
618  if (((bestStartSeparation - thisStartSeparation) > 0) && (thisStartSeparation < bestStartSeparation))
619  boundaryStartLayer = entry.first;
620 
621  const int bestEndSeparation(std::abs(showerEndLayer - boundaryEndLayer));
622  const int thisEndSeparation(std::abs(showerEndLayer - entry.first));
623 
624  if (((bestEndSeparation - thisEndSeparation) > 0) && (thisEndSeparation < bestEndSeparation))
625  boundaryEndLayer = entry.first;
626  }
627 
628  if (std::abs(showerStartLayer - boundaryStartLayer) > m_maxLayerSeparation)
629  return STATUS_CODE_FAILURE;
630 
631  const float showerStartBoundaryLocalL(layerFitResultMap.at(boundaryStartLayer).GetL()),
632  showerStartBoundaryLocalT(layerFitResultMap.at(boundaryStartLayer).GetFitT());
633  const float showerEndBoundaryLocalL(layerFitResultMap.at(boundaryEndLayer).GetL()),
634  showerEndBoundaryLocalT(layerFitResultMap.at(boundaryEndLayer).GetFitT());
635 
636  showerTwoDSlidingFit.GetShowerFitResult().GetGlobalPosition(showerStartBoundaryLocalL, showerStartBoundaryLocalT, boundaryEdgeStart);
637  showerTwoDSlidingFit.GetShowerFitResult().GetGlobalPosition(showerEndBoundaryLocalL, showerEndBoundaryLocalT, boundaryEdgeEnd);
638 
639  // Swap start and end if needs be
640  if ((showerStartPosition - boundaryEdgeEnd).GetMagnitudeSquared() < (showerStartPosition - boundaryEdgeStart).GetMagnitude())
641  {
642  const CartesianVector startTemp(boundaryEdgeStart), endTemp(boundaryEdgeEnd);
643 
644  boundaryEdgeStart = endTemp;
645  boundaryEdgeEnd = startTemp;
646  }
647 
648  return STATUS_CODE_SUCCESS;
649 }
650 
651 //------------------------------------------------------------------------------------------------------------------------------------------
652 
653 StatusCode ShowerStartFinderTool::ReadSettings(const TiXmlHandle xmlHandle)
654 {
655  PANDORA_RETURN_RESULT_IF_AND_IF(
656  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SpineSlidingFitWindow", m_spineSlidingFitWindow));
657 
658  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
659  XmlHelper::ReadValue(xmlHandle, "LongitudinalCoordinateBinSize", m_longitudinalCoordinateBinSize));
660 
661  PANDORA_RETURN_RESULT_IF_AND_IF(
662  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "nInitialEnergyBins", m_nInitialEnergyBins));
663 
664  PANDORA_RETURN_RESULT_IF_AND_IF(
665  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinSigmaDeviation", m_minSigmaDeviation));
666 
667  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxEdgeGap", m_maxEdgeGap));
668 
669  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
670  XmlHelper::ReadValue(xmlHandle, "LongitudinalShowerFraction", m_longitudinalShowerFraction));
671 
672  PANDORA_RETURN_RESULT_IF_AND_IF(
673  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinShowerOpeningAngle", m_minShowerOpeningAngle));
674 
675  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MolliereRadius", m_molliereRadius));
676 
677  PANDORA_RETURN_RESULT_IF_AND_IF(
678  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ShowerSlidingFitWindow", m_showerSlidingFitWindow));
679 
680  PANDORA_RETURN_RESULT_IF_AND_IF(
681  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxLayerSeparation", m_maxLayerSeparation));
682 
683  return STATUS_CODE_SUCCESS;
684 }
685 
686 } // namespace lar_content
std::map< int, pandora::CaloHitList > LayerToHitMap
float m_longitudinalShowerFraction
The shower region fraction considered.
Header file for the lar two dimensional sliding shower fit result class.
void CharacteriseInitialEnergy(const EnergySpectrumMap &energySpectrumMap, const bool isEndDownstream, float &meanEnergy, float &energySigma) const
Find the mean and standard deviation of the energy depositions in the initial region.
Header file for the pfo helper class.
Header file for the connection pathway helper class.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
float m_minShowerOpeningAngle
The min. opening angle of a sensible shower.
void ObtainLongitudinalDecomposition(const TwoDSlidingFitResult &spineTwoDSlidingFit, const pandora::CaloHitList &showerSpineHitList, LongitudinalPositionMap &longitudinalPositionMap) const
Create the [shower spine hit -> shower spine fit longitudinal projection] map.
float m_longitudinalCoordinateBinSize
The longitudinal coordinate bin size.
Header file for the shower start finder tool class.
int m_maxLayerSeparation
The max. allowed separation between the shower start and boundary start layers.
const TwoDSlidingFitResult & GetShowerFitResult() const
Get the sliding fit result for the full shower cluster.
constexpr auto abs(T v)
Returns the absolute value of the argument.
unsigned int m_showerSlidingFitWindow
The sliding window used to fit the shower region.
pandora::StatusCode BuildShowerRegion(const pandora::ParticleFlowObject *const pShowerPfo, const pandora::HitType hitType, const pandora::CaloHitList &showerSpineHitList, const pandora::CartesianVector &showerStartPosition, const pandora::CartesianVector &showerStartDirection, const bool isEndDownstream, pandora::CartesianPointVector &showerRegionPositionVector) const
Build the downstream &#39;shower region&#39; at a given longitudinal distance along the spine.
intermediate_table::const_iterator const_iterator
TFile f
Definition: plotHisto.C:6
unsigned int m_nInitialEnergyBins
The number of longitudinal bins that define the initial region.
Header file for the geometry helper class.
std::map< const pandora::CaloHit *, float > LongitudinalPositionMap
void GetEnergyDistribution(const pandora::CaloHitList &showerSpineHitList, const LongitudinalPositionMap &longitudinalPositionMap, EnergySpectrumMap &energySpectrumMap) const
Create the longituidnal energy distribution.
int GetMaxLayer() const
Get the maximum occupied layer in the sliding fit.
float m_maxEdgeGap
The max. allowed layer gap in a shower boundary.
Header file for the lar two dimensional sliding fit result class.
float m_molliereRadius
The max. distance from the shower core of a collected shower region hit.
std::map< int, LayerFitResult > LayerFitResultMap
pandora::StatusCode GetBoundaryExtremalPoints(const TwoDSlidingShowerFitResult &showerTwoDSlidingFit, const LayerFitResultMap &layerFitResultMap, const pandora::CartesianVector &showerStartPosition, const int showerStartLayer, const int showerEndLayer, pandora::CartesianVector &boundaryEdgeStart, pandora::CartesianVector &boundaryEdgeEnd) const
Determine the start and end positions of a shower boundary.
static float GetWirePitch(const pandora::Pandora &pandora, const pandora::HitType view, const float maxWirePitchDiscrepancy=0.01)
Return the wire pitch.
const TwoDSlidingFitResult & GetPositiveEdgeFitResult() const
Get the sliding fit result for the positive shower edge.
void GetGlobalDirection(const float dTdL, pandora::CartesianVector &direction) const
Get global direction coordinates for given sliding linear fit gradient.
const LayerFitResultMap & GetLayerFitResultMap() const
Get the layer fit result map.
bool IsShowerTopology(const pandora::ParticleFlowObject *const pShowerPfo, const pandora::HitType hitType, const TwoDSlidingFitResult &spineTwoDSlidingFit, const float longitudinalDistance, const pandora::CaloHitList &showerSpineHitList, const bool isEndDownstream) const
Whether a sensible shower cascade looks to originate at a given position.
pandora::StatusCode CharacteriseShowerTopology(const pandora::CartesianPointVector &showerRegionPositionVector, const pandora::CartesianVector &showerStartPosition, const pandora::HitType hitType, const bool isEndDownstream, const pandora::CartesianVector &showerStartDirection, pandora::CartesianVector &positiveEdgeStart, pandora::CartesianVector &positiveEdgeEnd, pandora::CartesianVector &negativeEdgeStart, pandora::CartesianVector &negativeEdgeEnd, bool &isBetween) const
Parameterise the topological structure of the shower region.
bool IsClockwiseRotation(const pandora::CartesianVector &showerStartDirection, const pandora::CartesianVector &displacementVector) const
Determine whether a point lies on the RHS or LHS (wrt +ve Z) of the shower core.
const TwoDSlidingFitResult & GetNegativeEdgeFitResult() const
Get the sliding fit result for the negative shower edge.
HitType
Definition: HitType.h:12
void GetGlobalPosition(const float rL, const float rT, pandora::CartesianVector &position) const
Get global coordinates for given sliding linear fit coordinates.
float m_minSigmaDeviation
The min. average energy deviation of a candidate shower start.
void ConvertLongitudinalProjectionToGlobal(const TwoDSlidingFitResult &spineTwoDSlidingFit, const float longitudinalDistance, pandora::CartesianVector &globalPosition, pandora::CartesianVector &globalDirection) const
Determine the (X,Y,Z) position and direction at a given longitudinal distance along the spine...
pandora::StatusCode Run(const pandora::ParticleFlowObject *const pShowerPfo, const pandora::CartesianVector &peakDirection, const pandora::HitType hitType, const pandora::CaloHitList &showerSpineHitList, pandora::CartesianVector &showerStartPosition, pandora::CartesianVector &showerStartDirection)
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.
void GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
int GetLayer(const float rL) const
Get layer number for given sliding linear fit longitudinal coordinate.
unsigned int m_spineSlidingFitWindow
The sliding window used to fit the shower spine.
int FindShowerStartLongitudinalCoordinate(const pandora::ParticleFlowObject *const pShowerPfo, const pandora::HitType hitType, const TwoDSlidingFitResult &spineTwoDSlidingFit, const EnergySpectrumMap &energySpectrumMap, const pandora::CaloHitList &showerSpineHitList, const bool isEndDownstream, const T startIter, const T endIter) const
Find the longitudinal bin which corresponds to the start position of the shower cascade.
void FindShowerStartAndDirection(const pandora::ParticleFlowObject *const pShowerPfo, const pandora::HitType hitType, const TwoDSlidingFitResult &spineTwoDSlidingFit, const EnergySpectrumMap &energySpectrumMap, const pandora::CaloHitList &showerSpineHitList, const bool isEndDownstream, pandora::CartesianVector &showerStartPosition, pandora::CartesianVector &showerStartDirection) const
Find the position at which the shower cascade looks to originate, and its initial direction...