LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
BeamParticleIdTool.cc
Go to the documentation of this file.
1 
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, const SliceHypotheses &crSliceHypotheses, PfoList &selectedPfos)
43 {
44  if (beamSliceHypotheses.size() != crSliceHypotheses.size())
45  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
46 
47  // First, simple approach
49  {
50  for (unsigned int sliceIndex = 0, nSlices = beamSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
51  {
52  const PfoList &sliceOutput((m_selectAllBeamParticles || (m_selectOnlyFirstSliceBeamParticles && (0 == sliceIndex))) ?
53  beamSliceHypotheses.at(sliceIndex) : crSliceHypotheses.at(sliceIndex));
54 
55  const float score(m_selectAllBeamParticles || (m_selectOnlyFirstSliceBeamParticles && (0 == sliceIndex)) ? 1.f : -1.f);
56 
57  for (const ParticleFlowObject *const pPfo : sliceOutput)
58  {
59  object_creation::ParticleFlowObject::Metadata metadata;
60  metadata.m_propertiesToAdd["TestBeamScore"] = score;
61  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
62  }
63 
64  selectedPfos.insert(selectedPfos.end(), sliceOutput.begin(), sliceOutput.end());
65  }
66 
67  return;
68  }
69 
70  // Now start to examine topology of beam slice hypotheses
71  for (unsigned int sliceIndex = 0, nSlices = beamSliceHypotheses.size(); sliceIndex < nSlices; ++sliceIndex)
72  {
73  bool usebeamHypothesis(false);
74 
75  try
76  {
77  PfoList allConnectedPfoList;
78  LArPfoHelper::GetAllConnectedPfos(beamSliceHypotheses.at(sliceIndex), allConnectedPfoList);
79 
80  CaloHitList caloHitList3D;
81  LArPfoHelper::GetCaloHits(allConnectedPfoList, TPC_3D, caloHitList3D);
82 
83  CaloHitList selectedCaloHitList;
84  float closestDistance(std::numeric_limits<float>::max());
85  this->GetSelectedCaloHits(caloHitList3D, selectedCaloHitList, closestDistance);
86 
87  if (!selectedCaloHitList.empty())
88  {
89  CartesianVector centroidSel(0.f, 0.f, 0.f);
90  LArPcaHelper::EigenVectors eigenVecsSel;
91  LArPcaHelper::EigenValues eigenValuesSel(0.f, 0.f, 0.f);
92  LArPcaHelper::RunPca(selectedCaloHitList, centroidSel, eigenValuesSel, eigenVecsSel);
93 
94  const CartesianVector &majorAxisSel(eigenVecsSel.front());
95  const float supplementaryAngleToBeam(majorAxisSel.GetOpeningAngle(m_beamDirection));
96 
97  CartesianVector interceptOne(0.f, 0.f, 0.f), interceptTwo(0.f, 0.f, 0.f);
98  this->GetTPCIntercepts(centroidSel, majorAxisSel, interceptOne, interceptTwo);
99 
100  const float separationOne((interceptOne - m_beamTPCIntersection).GetMagnitude());
101  const float separationTwo((interceptTwo - m_beamTPCIntersection).GetMagnitude());
102 
103  if ((std::min(separationOne, separationTwo) < m_projectionIntersectionCut) &&
104  (closestDistance < m_closestDistanceCut) && (supplementaryAngleToBeam > m_angleToBeamCut))
105  {
106  usebeamHypothesis = true;
107  }
108  }
109  }
110  catch (const StatusCodeException &)
111  {
112  usebeamHypothesis = false;
113  }
114 
115  const PfoList &sliceOutput(usebeamHypothesis ? beamSliceHypotheses.at(sliceIndex) : crSliceHypotheses.at(sliceIndex));
116  selectedPfos.insert(selectedPfos.end(), sliceOutput.begin(), sliceOutput.end());
117 
118  const float score(usebeamHypothesis ? 1.f : -1.f);
119 
120  for (const ParticleFlowObject *const pPfo : sliceOutput)
121  {
122  object_creation::ParticleFlowObject::Metadata metadata;
123  metadata.m_propertiesToAdd["TestBeamScore"] = score;
124  PANDORA_THROW_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::ParticleFlowObject::AlterMetadata(*pAlgorithm, pPfo, metadata));
125  }
126  }
127 }
128 
129 //------------------------------------------------------------------------------------------------------------------------------------------
130 
132 {
133  // Get global TPC geometry information
134  const LArTPCMap &larTPCMap(this->GetPandora().GetGeometry()->GetLArTPCMap());
135  const LArTPC *const pFirstLArTPC(larTPCMap.begin()->second);
136  float parentMinX(pFirstLArTPC->GetCenterX() - 0.5f * pFirstLArTPC->GetWidthX());
137  float parentMaxX(pFirstLArTPC->GetCenterX() + 0.5f * pFirstLArTPC->GetWidthX());
138  float parentMinY(pFirstLArTPC->GetCenterY() - 0.5f * pFirstLArTPC->GetWidthY());
139  float parentMaxY(pFirstLArTPC->GetCenterY() + 0.5f * pFirstLArTPC->GetWidthY());
140  float parentMinZ(pFirstLArTPC->GetCenterZ() - 0.5f * pFirstLArTPC->GetWidthZ());
141  float parentMaxZ(pFirstLArTPC->GetCenterZ() + 0.5f * pFirstLArTPC->GetWidthZ());
142 
143  for (const LArTPCMap::value_type &mapEntry : larTPCMap)
144  {
145  const LArTPC *const pLArTPC(mapEntry.second);
146  parentMinX = std::min(parentMinX, pLArTPC->GetCenterX() - 0.5f * pLArTPC->GetWidthX());
147  parentMaxX = std::max(parentMaxX, pLArTPC->GetCenterX() + 0.5f * pLArTPC->GetWidthX());
148  parentMinY = std::min(parentMinY, pLArTPC->GetCenterY() - 0.5f * pLArTPC->GetWidthY());
149  parentMaxY = std::max(parentMaxY, pLArTPC->GetCenterY() + 0.5f * pLArTPC->GetWidthY());
150  parentMinZ = std::min(parentMinZ, pLArTPC->GetCenterZ() - 0.5f * pLArTPC->GetWidthZ());
151  parentMaxZ = std::max(parentMaxZ, pLArTPC->GetCenterZ() + 0.5f * pLArTPC->GetWidthZ());
152  }
153  m_tpcMinX = parentMinX;
154  m_tpcMaxX = parentMaxX;
155  m_tpcMinY = parentMinY;
156  m_tpcMaxY = parentMaxY;
157  m_tpcMinZ = parentMinZ;
158  m_tpcMaxZ = parentMaxZ;
159 
160  const CartesianVector normalTop(0.f, 0.f, 1.f), pointTop(0.f, 0.f, m_tpcMaxZ);
161  const CartesianVector normalBottom(0.f, 0.f, -1.f), pointBottom(0.f, 0.f, m_tpcMinZ);
162  const CartesianVector normalRight(1.f, 0.f, 0.f), pointRight(m_tpcMaxX, 0.f, 0.f);
163  const CartesianVector normalLeft(-1.f, 0.f, 0.f), pointLeft(m_tpcMinX, 0.f, 0.f);
164  const CartesianVector normalBack(0.f, 1.f, 0.f), pointBack(0.f, m_tpcMaxY, 0.f);
165  const CartesianVector normalFront(0.f, -1.f, 0.f), pointFront(0.f, m_tpcMinY, 0.f);
166  m_tpcPlanes.emplace_back(normalTop, pointTop);
167  m_tpcPlanes.emplace_back(normalBottom, pointBottom);
168  m_tpcPlanes.emplace_back(normalRight, pointRight);
169  m_tpcPlanes.emplace_back(normalLeft, pointLeft);
170  m_tpcPlanes.emplace_back(normalBack, pointBack);
171  m_tpcPlanes.emplace_back(normalFront, pointFront);
172 
173  return STATUS_CODE_SUCCESS;
174 }
175 
176 //------------------------------------------------------------------------------------------------------------------------------------------
177 
178 void BeamParticleIdTool::GetSelectedCaloHits(const CaloHitList &inputCaloHitList, CaloHitList &outputCaloHitList,
179  float &closestHitToFaceDistance) const
180 {
181  if (inputCaloHitList.empty())
182  throw StatusCodeException(STATUS_CODE_NOT_INITIALIZED);
183 
184  typedef std::pair<const CaloHit*, float> HitDistancePair;
185  typedef std::vector<HitDistancePair> HitDistanceVector;
186  HitDistanceVector hitDistanceVector;
187 
188  for (const CaloHit *const pCaloHit : inputCaloHitList)
189  hitDistanceVector.emplace_back(pCaloHit, (pCaloHit->GetPositionVector() - m_beamTPCIntersection).GetMagnitudeSquared());
190 
191  std::sort(hitDistanceVector.begin(), hitDistanceVector.end(), [](const HitDistancePair &lhs, const HitDistancePair &rhs) -> bool {return (lhs.second < rhs.second);});
192  closestHitToFaceDistance = std::sqrt(hitDistanceVector.front().second);
193 
194  const unsigned int nInputHits(inputCaloHitList.size());
195  const unsigned int nSelectedCaloHits(nInputHits < m_nSelectedHits ? nInputHits :
196  static_cast<unsigned int>(std::round(static_cast<float>(nInputHits) * m_selectedFraction / 100.f + 0.5f)));
197 
198  for (const HitDistancePair &hitDistancePair : hitDistanceVector)
199  {
200  outputCaloHitList.push_back(hitDistancePair.first);
201 
202  if (outputCaloHitList.size() >= nSelectedCaloHits)
203  break;
204  }
205 }
206 
207 //------------------------------------------------------------------------------------------------------------------------------------------
208 
209 void BeamParticleIdTool::GetTPCIntercepts(const CartesianVector &a0, const CartesianVector &lineDirection,
210  CartesianVector &interceptOne, CartesianVector &interceptTwo) const
211 {
212  CartesianPointVector intercepts;
213  const CartesianVector lineUnitVector(lineDirection.GetUnitVector());
214 
215  for (const Plane &plane : m_tpcPlanes)
216  {
217  const CartesianVector intercept(plane.GetLineIntersection(a0, lineUnitVector));
218 
219  if (this->IsContained(intercept))
220  intercepts.push_back(intercept);
221  }
222 
223  if (intercepts.size() == 2)
224  {
225  interceptOne = intercepts.at(0);
226  interceptTwo = intercepts.at(1);
227  }
228  else
229  {
230  throw StatusCodeException(STATUS_CODE_NOT_ALLOWED);
231  }
232 }
233 
234 //------------------------------------------------------------------------------------------------------------------------------------------
235 
236 bool BeamParticleIdTool::IsContained(const CartesianVector &spacePoint) const
237 {
238  if ((m_tpcMinX - spacePoint.GetX() > std::numeric_limits<float>::epsilon()) || (spacePoint.GetX() - m_tpcMaxX > std::numeric_limits<float>::epsilon()) ||
239  (m_tpcMinY - spacePoint.GetY() > std::numeric_limits<float>::epsilon()) || (spacePoint.GetY() - m_tpcMaxY > std::numeric_limits<float>::epsilon()) ||
240  (m_tpcMinZ - spacePoint.GetZ() > std::numeric_limits<float>::epsilon()) || (spacePoint.GetZ() - m_tpcMaxZ > std::numeric_limits<float>::epsilon()))
241  {
242  return false;
243  }
244 
245  return true;
246 }
247 
248 //------------------------------------------------------------------------------------------------------------------------------------------
249 //------------------------------------------------------------------------------------------------------------------------------------------
250 
251 BeamParticleIdTool::Plane::Plane(const CartesianVector &normal, const CartesianVector &point) :
252  m_unitNormal(normal.GetUnitVector()),
253  m_point(point),
254  m_d(-1.f * (normal.GetDotProduct(point)))
255 {
256 }
257 
258 //------------------------------------------------------------------------------------------------------------------------------------------
259 
260 CartesianVector BeamParticleIdTool::Plane::GetLineIntersection(const CartesianVector &a0, const CartesianVector &a) const
261 {
262  if (std::fabs(a.GetDotProduct(m_unitNormal)) < std::numeric_limits<float>::min())
264 
265  const float denominator(a.GetDotProduct(m_unitNormal));
266 
267  if (std::fabs(denominator) < std::numeric_limits<float>::epsilon())
268  throw StatusCodeException(STATUS_CODE_OUT_OF_RANGE);
269 
270  const float t(-1.f * (a0.GetDotProduct(m_unitNormal) + m_d) / denominator);
271  return (a0 + (a * t));
272 }
273 
274 //------------------------------------------------------------------------------------------------------------------------------------------
275 //------------------------------------------------------------------------------------------------------------------------------------------
276 
277 StatusCode BeamParticleIdTool::ReadSettings(const TiXmlHandle xmlHandle)
278 {
279  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
280  "SelectAllBeamParticles", m_selectAllBeamParticles));
281 
282  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
283  "SelectOnlyFirstSliceBeamParticles", m_selectOnlyFirstSliceBeamParticles));
284 
286  {
287  std::cout << "BeamParticleIdTool::ReadSettings - cannot use both SelectAllBeamParticles and SelectOnlyFirstSliceBeamParticles simultaneously" << std::endl;
288  return STATUS_CODE_INVALID_PARAMETER;
289  }
290 
291  FloatVector beamTPCIntersection;
292  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadVectorOfValues(xmlHandle,
293  "BeamTPCIntersection", beamTPCIntersection));
294 
295  if (3 == beamTPCIntersection.size())
296  {
297  m_beamTPCIntersection.SetValues(beamTPCIntersection.at(0), beamTPCIntersection.at(1), beamTPCIntersection.at(2));
298  }
299  else if (!beamTPCIntersection.empty())
300  {
301  std::cout << "BeamParticleIdTool::ReadSettings - invalid BeamTPCIntersection specified " << std::endl;
302  return STATUS_CODE_INVALID_PARAMETER;
303  }
304  else
305  {
306  // Default for protoDUNE.
307  m_beamTPCIntersection.SetValues(-33.051, 461.06, 0);
308  }
309 
310  FloatVector beamDirection;
311  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadVectorOfValues(xmlHandle,
312  "BeamDirection", beamDirection));
313 
314  if (3 == beamDirection.size())
315  {
316  m_beamDirection.SetValues(beamDirection.at(0), beamDirection.at(1), beamDirection.at(2));
317  }
318  else if (!beamDirection.empty())
319  {
320  std::cout << "BeamParticleIdTool::ReadSettings - invalid BeamDirection specified " << std::endl;
321  return STATUS_CODE_INVALID_PARAMETER;
322  }
323  else
324  {
325  // Default for protoDUNE.
326  const float thetaXZ0(-11.844f * M_PI / 180.f);
327  m_beamDirection.SetValues(std::sin(thetaXZ0), 0, std::cos(thetaXZ0));
328  }
329 
330  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
331  "ProjectionIntersectionCut", m_projectionIntersectionCut));
332 
333  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
334  "ClosestDistanceCut", m_closestDistanceCut));
335 
336  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
337  "AngleToBeamCut", m_angleToBeamCut));
338 
339  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
340  "SelectedFraction", m_selectedFraction));
341 
342  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
343  "NSelectedHits", m_nSelectedHits));
344 
345  return STATUS_CODE_SUCCESS;
346 }
347 
348 } // 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:21
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.
TFile f
Definition: plotHisto.C:6
Header file for the beam particle id tool class.
Int_t max
Definition: plot.C:27
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.
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.
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...
std::vector< pandora::CartesianVector > EigenVectors
Definition: LArPcaHelper.h:22
Int_t min
Definition: plot.C:26
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.
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.