BeamParticleIdTool.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArControlFlow/BeamParticleIdTool.cc
3  *
4  * @brief Implementation of the beam particle id tool class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
12 
15 
16 using namespace pandora;
17 
18 namespace lar_content
19 {
20 
21 BeamParticleIdTool::BeamParticleIdTool() :
22  m_selectAllBeamParticles(false),
23  m_selectOnlyFirstSliceBeamParticles(false),
24  m_tpcMinX(std::numeric_limits<float>::max()),
25  m_tpcMaxX(-std::numeric_limits<float>::max()),
26  m_tpcMinY(std::numeric_limits<float>::max()),
27  m_tpcMaxY(-std::numeric_limits<float>::max()),
28  m_tpcMinZ(std::numeric_limits<float>::max()),
29  m_tpcMaxZ(-std::numeric_limits<float>::max()),
30  m_beamTPCIntersection(0.f, 0.f, 0.f),
31  m_beamDirection(0.f, 0.f, 0.f),
32  m_projectionIntersectionCut(100.f),
33  m_closestDistanceCut(100.f),
34  m_angleToBeamCut(150.f * M_PI / 180.f),
35  m_selectedFraction(10.f),
36  m_nSelectedHits(100)
37 {
38 }
39 
40 //------------------------------------------------------------------------------------------------------------------------------------------
41 
42 void BeamParticleIdTool::SelectOutputPfos(const Algorithm *const pAlgorithm, const SliceHypotheses &beamSliceHypotheses,
43  const SliceHypotheses &crSliceHypotheses, PfoList &selectedPfos)
44 {
45  if (beamSliceHypotheses.size() != crSliceHypotheses.size())
46  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
47 
48  // First, simple approach
50  {
51  for (unsigned int sliceIndex = 0, nSlices = beamSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
52  {
53  const PfoList &sliceOutput((m_selectAllBeamParticles || (m_selectOnlyFirstSliceBeamParticles && (0 == sliceIndex)))
54  ? beamSliceHypotheses.at(sliceIndex)
55  : crSliceHypotheses.at(sliceIndex));
56 
57  const float score(m_selectAllBeamParticles || (m_selectOnlyFirstSliceBeamParticles && (0 == sliceIndex)) ? 1.f : -1.f);
58 
59  for (const ParticleFlowObject *const pPfo : sliceOutput)
60  {
61  object_creation::ParticleFlowObject::Metadata metadata;
62  metadata.m_propertiesToAdd["TestBeamScore"] = score;
63  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
64  }
65 
66  selectedPfos.insert(selectedPfos.end(), sliceOutput.begin(), sliceOutput.end());
67  }
68 
69  return;
70  }
71 
72  // Now start to examine topology of beam slice hypotheses
73  for (unsigned int sliceIndex = 0, nSlices = beamSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
74  {
75  bool usebeamHypothesis(false);
76 
77  try
78  {
79  PfoList allConnectedPfoList;
80  LArPfoHelper::GetAllConnectedPfos(beamSliceHypotheses.at(sliceIndex), allConnectedPfoList);
81 
82  CaloHitList caloHitList3D;
83  LArPfoHelper::GetCaloHits(allConnectedPfoList, TPC_3D, caloHitList3D);
84 
85  CaloHitList selectedCaloHitList;
86  float closestDistance(std::numeric_limits<float>::max());
87  this->GetSelectedCaloHits(caloHitList3D, selectedCaloHitList, closestDistance);
88 
89  if (!selectedCaloHitList.empty())
90  {
91  CartesianVector centroidSel(0.f, 0.f, 0.f);
92  LArPcaHelper::EigenVectors eigenVecsSel;
93  LArPcaHelper::EigenValues eigenValuesSel(0.f, 0.f, 0.f);
94  LArPcaHelper::RunPca(selectedCaloHitList, centroidSel, eigenValuesSel, eigenVecsSel);
95 
96  const CartesianVector &majorAxisSel(eigenVecsSel.front());
97  const float supplementaryAngleToBeam(majorAxisSel.GetOpeningAngle(m_beamDirection));
98 
99  CartesianVector interceptOne(0.f, 0.f, 0.f), interceptTwo(0.f, 0.f, 0.f);
100  this->GetTPCIntercepts(centroidSel, majorAxisSel, interceptOne, interceptTwo);
101 
102  const float separationOne((interceptOne - m_beamTPCIntersection).GetMagnitude());
103  const float separationTwo((interceptTwo - m_beamTPCIntersection).GetMagnitude());
104 
105  if ((std::min(separationOne, separationTwo) < m_projectionIntersectionCut) && (closestDistance < m_closestDistanceCut) &&
106  (supplementaryAngleToBeam > m_angleToBeamCut))
107  {
108  usebeamHypothesis = true;
109  }
110  }
111  }
112  catch (const StatusCodeException &)
113  {
114  usebeamHypothesis = false;
115  }
116 
117  const PfoList &sliceOutput(usebeamHypothesis ? beamSliceHypotheses.at(sliceIndex) : crSliceHypotheses.at(sliceIndex));
118  selectedPfos.insert(selectedPfos.end(), sliceOutput.begin(), sliceOutput.end());
119 
120  const float score(usebeamHypothesis ? 1.f : -1.f);
121 
122  for (const ParticleFlowObject *const pPfo : sliceOutput)
123  {
124  object_creation::ParticleFlowObject::Metadata metadata;
125  metadata.m_propertiesToAdd["TestBeamScore"] = score;
126  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
127  }
128  }
129 }
130 
131 //------------------------------------------------------------------------------------------------------------------------------------------
132 
134 {
135  // Get global TPC geometry information
136  const LArTPCMap &larTPCMap(this->GetPandora().GetGeometry()->GetLArTPCMap());
137  const LArTPC *const pFirstLArTPC(larTPCMap.begin()->second);
138  float parentMinX(pFirstLArTPC->GetCenterX() - 0.5f * pFirstLArTPC->GetWidthX());
139  float parentMaxX(pFirstLArTPC->GetCenterX() + 0.5f * pFirstLArTPC->GetWidthX());
140  float parentMinY(pFirstLArTPC->GetCenterY() - 0.5f * pFirstLArTPC->GetWidthY());
141  float parentMaxY(pFirstLArTPC->GetCenterY() + 0.5f * pFirstLArTPC->GetWidthY());
142  float parentMinZ(pFirstLArTPC->GetCenterZ() - 0.5f * pFirstLArTPC->GetWidthZ());
143  float parentMaxZ(pFirstLArTPC->GetCenterZ() + 0.5f * pFirstLArTPC->GetWidthZ());
144 
145  for (const LArTPCMap::value_type &mapEntry : larTPCMap)
146  {
147  const LArTPC *const pLArTPC(mapEntry.second);
148  parentMinX = std::min(parentMinX, pLArTPC->GetCenterX() - 0.5f * pLArTPC->GetWidthX());
149  parentMaxX = std::max(parentMaxX, pLArTPC->GetCenterX() + 0.5f * pLArTPC->GetWidthX());
150  parentMinY = std::min(parentMinY, pLArTPC->GetCenterY() - 0.5f * pLArTPC->GetWidthY());
151  parentMaxY = std::max(parentMaxY, pLArTPC->GetCenterY() + 0.5f * pLArTPC->GetWidthY());
152  parentMinZ = std::min(parentMinZ, pLArTPC->GetCenterZ() - 0.5f * pLArTPC->GetWidthZ());
153  parentMaxZ = std::max(parentMaxZ, pLArTPC->GetCenterZ() + 0.5f * pLArTPC->GetWidthZ());
154  }
155  m_tpcMinX = parentMinX;
156  m_tpcMaxX = parentMaxX;
157  m_tpcMinY = parentMinY;
158  m_tpcMaxY = parentMaxY;
159  m_tpcMinZ = parentMinZ;
160  m_tpcMaxZ = parentMaxZ;
161 
162  const CartesianVector normalTop(0.f, 0.f, 1.f), pointTop(0.f, 0.f, m_tpcMaxZ);
163  const CartesianVector normalBottom(0.f, 0.f, -1.f), pointBottom(0.f, 0.f, m_tpcMinZ);
164  const CartesianVector normalRight(1.f, 0.f, 0.f), pointRight(m_tpcMaxX, 0.f, 0.f);
165  const CartesianVector normalLeft(-1.f, 0.f, 0.f), pointLeft(m_tpcMinX, 0.f, 0.f);
166  const CartesianVector normalBack(0.f, 1.f, 0.f), pointBack(0.f, m_tpcMaxY, 0.f);
167  const CartesianVector normalFront(0.f, -1.f, 0.f), pointFront(0.f, m_tpcMinY, 0.f);
168  m_tpcPlanes.emplace_back(normalTop, pointTop);
169  m_tpcPlanes.emplace_back(normalBottom, pointBottom);
170  m_tpcPlanes.emplace_back(normalRight, pointRight);
171  m_tpcPlanes.emplace_back(normalLeft, pointLeft);
172  m_tpcPlanes.emplace_back(normalBack, pointBack);
173  m_tpcPlanes.emplace_back(normalFront, pointFront);
174 
175  return STATUS_CODE_SUCCESS;
176 }
177 
178 //------------------------------------------------------------------------------------------------------------------------------------------
179 
180 void BeamParticleIdTool::GetSelectedCaloHits(const CaloHitList &inputCaloHitList, CaloHitList &outputCaloHitList, float &closestHitToFaceDistance) const
181 {
182  if (inputCaloHitList.empty())
183  throw StatusCodeException(STATUS_CODE_NOT_INITIALIZED);
184 
185  typedef std::pair<const CaloHit *, float> HitDistancePair;
186  typedef std::vector<HitDistancePair> HitDistanceVector;
187  HitDistanceVector hitDistanceVector;
188 
189  for (const CaloHit *const pCaloHit : inputCaloHitList)
190  hitDistanceVector.emplace_back(pCaloHit, (pCaloHit->GetPositionVector() - m_beamTPCIntersection).GetMagnitudeSquared());
191 
192  std::sort(hitDistanceVector.begin(), hitDistanceVector.end(),
193  [](const HitDistancePair &lhs, const HitDistancePair &rhs) -> bool { return (lhs.second < rhs.second); });
194  closestHitToFaceDistance = std::sqrt(hitDistanceVector.front().second);
195 
196  const unsigned int nInputHits(inputCaloHitList.size());
197  const unsigned int nSelectedCaloHits(
198  nInputHits < m_nSelectedHits ? nInputHits
199  : static_cast<unsigned int>(std::round(static_cast<float>(nInputHits) * m_selectedFraction / 100.f + 0.5f)));
200 
201  for (const HitDistancePair &hitDistancePair : hitDistanceVector)
202  {
203  outputCaloHitList.push_back(hitDistancePair.first);
204 
205  if (outputCaloHitList.size() >= nSelectedCaloHits)
206  break;
207  }
208 }
209 
210 //------------------------------------------------------------------------------------------------------------------------------------------
211 
213  const CartesianVector &a0, const CartesianVector &lineDirection, CartesianVector &interceptOne, CartesianVector &interceptTwo) const
214 {
215  CartesianPointVector intercepts;
216  const CartesianVector lineUnitVector(lineDirection.GetUnitVector());
217 
218  for (const Plane &plane : m_tpcPlanes)
219  {
220  const CartesianVector intercept(plane.GetLineIntersection(a0, lineUnitVector));
221 
222  if (this->IsContained(intercept))
223  intercepts.push_back(intercept);
224  }
225 
226  if (intercepts.size() == 2)
227  {
228  interceptOne = intercepts.at(0);
229  interceptTwo = intercepts.at(1);
230  }
231  else
232  {
233  throw StatusCodeException(STATUS_CODE_NOT_ALLOWED);
234  }
235 }
236 
237 //------------------------------------------------------------------------------------------------------------------------------------------
238 
239 bool BeamParticleIdTool::IsContained(const CartesianVector &spacePoint) const
240 {
241  if ((m_tpcMinX - spacePoint.GetX() > std::numeric_limits<float>::epsilon()) ||
242  (spacePoint.GetX() - m_tpcMaxX > std::numeric_limits<float>::epsilon()) ||
243  (m_tpcMinY - spacePoint.GetY() > std::numeric_limits<float>::epsilon()) ||
244  (spacePoint.GetY() - m_tpcMaxY > std::numeric_limits<float>::epsilon()) ||
245  (m_tpcMinZ - spacePoint.GetZ() > std::numeric_limits<float>::epsilon()) ||
246  (spacePoint.GetZ() - m_tpcMaxZ > std::numeric_limits<float>::epsilon()))
247  {
248  return false;
249  }
250 
251  return true;
252 }
253 
254 //------------------------------------------------------------------------------------------------------------------------------------------
255 //------------------------------------------------------------------------------------------------------------------------------------------
256 
257 BeamParticleIdTool::Plane::Plane(const CartesianVector &normal, const CartesianVector &point) :
258  m_unitNormal(normal.GetUnitVector()),
259  m_point(point),
260  m_d(-1.f * (normal.GetDotProduct(point)))
261 {
262 }
263 
264 //------------------------------------------------------------------------------------------------------------------------------------------
265 
266 CartesianVector BeamParticleIdTool::Plane::GetLineIntersection(const CartesianVector &a0, const CartesianVector &a) const
267 {
268  if (std::fabs(a.GetDotProduct(m_unitNormal)) < std::numeric_limits<float>::min())
270 
271  const float denominator(a.GetDotProduct(m_unitNormal));
272 
273  if (std::fabs(denominator) < std::numeric_limits<float>::epsilon())
274  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
275 
276  const float t(-1.f * (a0.GetDotProduct(m_unitNormal) + m_d) / denominator);
277  return (a0 + (a * t));
278 }
279 
280 //------------------------------------------------------------------------------------------------------------------------------------------
281 //------------------------------------------------------------------------------------------------------------------------------------------
282 
283 StatusCode BeamParticleIdTool::ReadSettings(const TiXmlHandle xmlHandle)
284 {
285  PANDORA_RETURN_RESULT_IF_AND_IF(
286  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SelectAllBeamParticles", m_selectAllBeamParticles));
287 
288  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
289  XmlHelper::ReadValue(xmlHandle, "SelectOnlyFirstSliceBeamParticles", m_selectOnlyFirstSliceBeamParticles));
290 
292  {
293  std::cout << "BeamParticleIdTool::ReadSettings - cannot use both SelectAllBeamParticles and SelectOnlyFirstSliceBeamParticles simultaneously"
294  << std::endl;
295  return STATUS_CODE_INVALID_PARAMETER;
296  }
297 
298  FloatVector beamTPCIntersection;
299  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
300  XmlHelper::ReadVectorOfValues(xmlHandle, "BeamTPCIntersection", beamTPCIntersection));
301 
302  if (3 == beamTPCIntersection.size())
303  {
304  m_beamTPCIntersection.SetValues(beamTPCIntersection.at(0), beamTPCIntersection.at(1), beamTPCIntersection.at(2));
305  }
306  else if (!beamTPCIntersection.empty())
307  {
308  std::cout << "BeamParticleIdTool::ReadSettings - invalid BeamTPCIntersection specified " << std::endl;
309  return STATUS_CODE_INVALID_PARAMETER;
310  }
311  else
312  {
313  // Default for protoDUNE.
314  m_beamTPCIntersection.SetValues(-33.051, 461.06, 0);
315  }
316 
317  FloatVector beamDirection;
318  PANDORA_RETURN_RESULT_IF_AND_IF(
319  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadVectorOfValues(xmlHandle, "BeamDirection", beamDirection));
320 
321  if (3 == beamDirection.size())
322  {
323  m_beamDirection.SetValues(beamDirection.at(0), beamDirection.at(1), beamDirection.at(2));
324  }
325  else if (!beamDirection.empty())
326  {
327  std::cout << "BeamParticleIdTool::ReadSettings - invalid BeamDirection specified " << std::endl;
328  return STATUS_CODE_INVALID_PARAMETER;
329  }
330  else
331  {
332  // Default for protoDUNE.
333  const float thetaXZ0(-11.844f * M_PI / 180.f);
334  m_beamDirection.SetValues(std::sin(thetaXZ0), 0, std::cos(thetaXZ0));
335  }
336 
337  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
338  XmlHelper::ReadValue(xmlHandle, "ProjectionIntersectionCut", m_projectionIntersectionCut));
339 
340  PANDORA_RETURN_RESULT_IF_AND_IF(
341  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ClosestDistanceCut", m_closestDistanceCut));
342 
343  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "AngleToBeamCut", m_angleToBeamCut));
344 
345  PANDORA_RETURN_RESULT_IF_AND_IF(
346  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SelectedFraction", m_selectedFraction));
347 
348  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "NSelectedHits", m_nSelectedHits));
349 
350  return STATUS_CODE_SUCCESS;
351 }
352 
353 } // namespace lar_content
pandora::CartesianVector m_beamTPCIntersection
Intersection of beam and global TPC volume.
Header file for the pfo helper class.
bool IsContained(const pandora::CartesianVector &spacePoint) const
Check if a given 3D spacepoint is inside the global TPC volume.
bool m_selectAllBeamParticles
First approach: select all beam particles, as opposed to selecting all cosmics.
#define a0
pandora::CartesianVector EigenValues
Definition: LArPcaHelper.h:24
void GetSelectedCaloHits(const pandora::CaloHitList &inputCaloHitList, pandora::CaloHitList &outputCaloHitList, float &closestHitToFaceDistance) const
Select a given fraction of a slice&#39;s calo hits that are closest to the beam spot. ...
float m_closestDistanceCut
Closest distance (of hit to beam spot), used in beam event selection.
pandora::CartesianVector GetLineIntersection(const pandora::CartesianVector &a0, const pandora::CartesianVector &a) const
Return the intersection between the plane and a line.
STL namespace.
float m_tpcMaxX
Global TPC volume maximum x extent.
Header file for the principal curve analysis helper class.
float m_tpcMinZ
Global TPC volume minimum z extent.
float m_tpcMaxZ
Global TPC volume maximum z extent.
float m_d
Parameter defining a plane.
Header file for the beam particle id tool class.
const double a
float m_angleToBeamCut
Angle between major axis and beam direction, used in beam event selection.
std::vector< pandora::PfoList > SliceHypotheses
bool m_selectOnlyFirstSliceBeamParticles
First approach: select first slice beam particles, cosmics for all subsequent slices.
pandora::CartesianVector m_unitNormal
Unit normal to plane.
static int max(int a, int b)
void GetTPCIntercepts(const pandora::CartesianVector &a0, const pandora::CartesianVector &majorAxis, pandora::CartesianVector &interceptOne, pandora::CartesianVector &interceptTwo) const
Find the intercepts of a line with the protoDUNE detector.
#define M_PI
Definition: includeROOT.h:54
Plane(const pandora::CartesianVector &normal, const pandora::CartesianVector &point)
Constructor, using equation of plane: m_a*x + m_b*y + m_c*z + m_d = 0.
unsigned int m_nSelectedHits
Minimum number of hits to use in 3D cluster fits.
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...
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
std::vector< pandora::CartesianVector > EigenVectors
Definition: LArPcaHelper.h:25
float m_selectedFraction
Fraction of hits to use in 3D cluster fits.
float m_projectionIntersectionCut
Projection intersection distance cut, used in beam event selection.
float m_tpcMaxY
Global TPC volume maximum y extent.
Dft::FloatVector FloatVector
pandora::CartesianVector m_beamDirection
Beam direction.
void SelectOutputPfos(const pandora::Algorithm *const pAlgorithm, const SliceHypotheses &beamSliceHypotheses, const SliceHypotheses &crSliceHypotheses, pandora::PfoList &selectedPfos)
Select which reconstruction hypotheses to use; neutrino outcomes or cosmic-ray muon outcomes for each...
static void GetAllConnectedPfos(const pandora::PfoList &inputPfoList, pandora::PfoList &outputPfoList)
Get a flat list of all pfos, recursively including all daughters and parents associated with those pf...
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.
float m_tpcMinY
Global TPC volume minimum y extent.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
PlaneVector m_tpcPlanes
Vector of all planes making up global TPC volume.
float m_tpcMinX
Global TPC volume minimum x extent.
QTextStream & endl(QTextStream &s)
GeomAnalyzerI * GetGeometry(void)
Definition: gAtmoEvGen.cxx:433