Pandora
Pandora source code navigator
Loading...
Searching...
No Matches
ShowerTrajPointdEdx_tool.cc
Go to the documentation of this file.
1//############################################################################
2//### Name: ShowerTrajPointdEdx ###
3//### Author: Dominic Barker (dominic.barker@sheffield.ac.uk) ###
4//### Date: 13.05.19 ###
5//### Description: Tool for finding the dEdx of the start track of the ###
6//### shower using the standard calomitry module. This ###
7//### takes the sliding fit trajectory to make a 3D dEdx. ###
8//### This module is best used with the sliding linear fit ###
9//### and ShowerTrackTrajToSpacePoint ###
10//############################################################################
11
12//Framework Includes
13#include "art/Utilities/ToolMacros.h"
14
15//LArSoft Includes
16#include "lardata/DetectorInfoServices/DetectorClocksService.h"
17#include "lardata/DetectorInfoServices/DetectorPropertiesService.h"
18#include "lardataobj/AnalysisBase/T0.h"
19#include "lardataobj/RecoBase/PFParticle.h"
20#include "lardataobj/RecoBase/SpacePoint.h"
21#include "lardataobj/RecoBase/Track.h"
23#include "larreco/Calorimetry/CalorimetryAlg.h"
24
25//ROOT
26#include "Math/VectorUtil.h"
27
28using ROOT::Math::VectorUtil::Angle;
29
30namespace ShowerRecoTools {
31
33
34 public:
35 ShowerTrajPointdEdx(const fhicl::ParameterSet& pset);
36
37 //Physics Function. Calculate the dEdx.
38 int CalculateElement(const art::Ptr<recob::PFParticle>& pfparticle,
39 art::Event& Event,
40 reco::shower::ShowerElementHolder& ShowerEleHolder) override;
41
42 void FinddEdxLength(std::vector<double>& dEdx_vec, std::vector<double>& dEdx_val);
43
44 private:
45 //Servcies and Algorithms
46 art::ServiceHandle<geo::Geometry> fGeom;
47 calo::CalorimetryAlg fCalorimetryAlg;
48
49 //fcl parameters
50 float fMinAngleToWire; //Minimum angle between the wire direction and the shower
51 //direction for the spacepoint to be used. Default means
52 //the cut has no effect. In radians.
53 float fShapingTime; //Shaping time of the ASIC defualt so we don't cut on track
54 //going too much into the plane. In Microseconds
55 float fMinDistCutOff; //Distance in wires a hit has to be from the start position
56 //to be used
57 float fMaxDist, MaxDist; //Distance in wires a that a trajectory point can be from a
58 //spacepoint to match to it.
60 dEdxTrackLength; //Max Distance a spacepoint can be away from the start of the
61 //track. In cm
62 float fdEdxCut;
63 bool fUseMedian; //Use the median value as the dEdx rather than the mean.
64 bool fCutStartPosition; //Remove hits using MinDistCutOff from the vertex as well.
65
66 bool fT0Correct; // Whether to look for a T0 associated to the PFP
67 bool fSCECorrectPitch; // Whether to correct the "squeezing" of pitch, requires corrected input
68 bool
69 fSCECorrectEField; // Whether to use the local electric field, from SpaceChargeService, in recombination calc.
70 bool
71 fSCEInputCorrected; // Whether the input has already been corrected for spatial SCE distortions
72
73 bool fSumHitSnippets; // Whether to treat hits individually or only one hit per snippet
74
75 art::InputTag fPFParticleLabel;
77
85 };
86
87 ShowerTrajPointdEdx::ShowerTrajPointdEdx(const fhicl::ParameterSet& pset)
88 : IShowerTool(pset.get<fhicl::ParameterSet>("BaseTools"))
89 , fCalorimetryAlg(pset.get<fhicl::ParameterSet>("CalorimetryAlg"))
90 , fMinAngleToWire(pset.get<float>("MinAngleToWire"))
91 , fShapingTime(pset.get<float>("ShapingTime"))
92 , fMinDistCutOff(pset.get<float>("MinDistCutOff"))
93 , fMaxDist(pset.get<float>("MaxDist"))
94 , fdEdxTrackLength(pset.get<float>("dEdxTrackLength"))
95 , fdEdxCut(pset.get<float>("dEdxCut"))
96 , fUseMedian(pset.get<bool>("UseMedian"))
97 , fCutStartPosition(pset.get<bool>("CutStartPosition"))
98 , fT0Correct(pset.get<bool>("T0Correct"))
99 , fSCECorrectPitch(pset.get<bool>("SCECorrectPitch"))
100 , fSCECorrectEField(pset.get<bool>("SCECorrectEField"))
101 , fSCEInputCorrected(pset.get<bool>("SCEInputCorrected"))
102 , fSumHitSnippets(pset.get<bool>("SumHitSnippets"))
103 , fPFParticleLabel(pset.get<art::InputTag>("PFParticleLabel"))
104 , fVerbose(pset.get<int>("Verbose"))
105 , fShowerStartPositionInputLabel(pset.get<std::string>("ShowerStartPositionInputLabel"))
106 , fInitialTrackHitsInputLabel(pset.get<std::string>("InitialTrackHitsInputLabel"))
107 , fInitialTrackSpacePointsInputLabel(pset.get<std::string>("InitialTrackSpacePointsInputLabel"))
108 , fInitialTrackInputLabel(pset.get<std::string>("InitialTrackInputLabel"))
109 , fShowerdEdxOutputLabel(pset.get<std::string>("ShowerdEdxOutputLabel"))
110 , fShowerBestPlaneOutputLabel(pset.get<std::string>("ShowerBestPlaneOutputLabel"))
111 , fShowerdEdxVecOutputLabel(pset.get<std::string>("ShowerdEdxVecOutputLabel"))
112 {
114 throw cet::exception("ShowerTrajPointdEdx")
115 << "Can only correct for SCE if input is already corrected" << std::endl;
116 }
117 }
118
119 int ShowerTrajPointdEdx::CalculateElement(const art::Ptr<recob::PFParticle>& pfparticle,
120 art::Event& Event,
121 reco::shower::ShowerElementHolder& ShowerEleHolder)
122 {
123
126
127 // Shower dEdx calculation
128 if (!ShowerEleHolder.CheckElement(fShowerStartPositionInputLabel)) {
129 if (fVerbose)
130 mf::LogError("ShowerTrajPointdEdx") << "Start position not set, returning " << std::endl;
131 return 1;
132 }
133 if (!ShowerEleHolder.CheckElement(fInitialTrackSpacePointsInputLabel)) {
134 if (fVerbose)
135 mf::LogError("ShowerTrajPointdEdx")
136 << "Initial Track Spacepoints is not set returning" << std::endl;
137 return 1;
138 }
139 if (!ShowerEleHolder.CheckElement(fInitialTrackInputLabel)) {
140 if (fVerbose) mf::LogError("ShowerTrajPointdEdx") << "Initial Track is not set" << std::endl;
141 return 1;
142 }
143
144 //Get the initial track hits
145 std::vector<art::Ptr<recob::SpacePoint>> tracksps;
146 ShowerEleHolder.GetElement(fInitialTrackSpacePointsInputLabel, tracksps);
147
148 if (tracksps.empty()) {
149 if (fVerbose)
150 mf::LogWarning("ShowerTrajPointdEdx") << "no spacepointsin the initial track" << std::endl;
151 return 0;
152 }
153
154 // Get the spacepoints
155 auto const spHandle = Event.getValidHandle<std::vector<recob::SpacePoint>>(fPFParticleLabel);
156
157 // Get the hits associated with the space points
158 const art::FindManyP<recob::Hit>& fmsp =
159 ShowerEleHolder.GetFindManyP<recob::Hit>(spHandle, Event, fPFParticleLabel);
160
161 //Only consider hits in the same tpcs as the vertex.
162 geo::Point_t ShowerStartPosition = {-999, -999, -999};
163 ShowerEleHolder.GetElement(fShowerStartPositionInputLabel, ShowerStartPosition);
164 geo::TPCID vtxTPC = fGeom->FindTPCAtPosition(ShowerStartPosition);
165
166 //Get the initial track
167 recob::Track InitialTrack;
168 ShowerEleHolder.GetElement(fInitialTrackInputLabel, InitialTrack);
169
170 double pfpT0Time(0); // If no T0 found, assume the particle happened at trigger time (0)
171 if (fT0Correct) {
172 auto const pfpHandle = Event.getValidHandle<std::vector<recob::PFParticle>>(fPFParticleLabel);
173 const art::FindManyP<anab::T0>& fmpfpt0 =
174 ShowerEleHolder.GetFindManyP<anab::T0>(pfpHandle, Event, fPFParticleLabel);
175 std::vector<art::Ptr<anab::T0>> pfpT0Vec = fmpfpt0.at(pfparticle.key());
176 if (pfpT0Vec.size() == 1) { pfpT0Time = pfpT0Vec.front()->Time(); }
177 }
178
179 //Don't care that I could use a vector.
180 std::map<int, std::vector<double>> dEdx_vec;
181 std::map<int, std::vector<double>> dEdx_vecErr;
182 std::map<int, int> num_hits;
183
184 for (unsigned i = 0; i != fGeom->MaxPlanes(); ++i) {
185 dEdx_vec[i] = {};
186 dEdx_vecErr[i] = {};
187 num_hits[i] = 0;
188 }
189
190 auto const clockData =
191 art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(Event);
192 auto const detProp =
193 art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataFor(Event, clockData);
194
195 std::map<art::Ptr<recob::Hit>, std::vector<art::Ptr<recob::Hit>>> hitSnippets;
196 if (fSumHitSnippets) {
197 std::vector<art::Ptr<recob::Hit>> trackHits;
198 ShowerEleHolder.GetElement(fInitialTrackHitsInputLabel, trackHits);
199
200 hitSnippets = IShowerTool::GetLArPandoraShowerAlg().OrganizeHits(trackHits);
201 }
202
203 //Loop over the spacepoints
204 for (auto const& sp : tracksps) {
205
206 //Get the associated hit
207 std::vector<art::Ptr<recob::Hit>> hits = fmsp.at(sp.key());
208 if (hits.empty()) {
209 if (fVerbose)
210 mf::LogWarning("ShowerTrajPointdEdx")
211 << "no hit for the spacepoint. This suggest the find many is wrong." << std::endl;
212 continue;
213 }
214 const art::Ptr<recob::Hit> hit = hits[0];
215
216 if (fSumHitSnippets && !hitSnippets.count(hit)) continue;
217
218 double wirepitch = fGeom->WirePitch((geo::PlaneID)hit->WireID());
219
220 //Only consider hits in the same tpc
221 geo::PlaneID planeid = hit->WireID();
222 geo::TPCID TPC = planeid.asTPCID();
223 if (TPC != vtxTPC) { continue; }
224
225 //Ignore spacepoints within a few wires of the vertex.
226 auto const pos = sp->position();
227 double dist_from_start = (pos - ShowerStartPosition).R();
228
229 if (fCutStartPosition) {
230 if (dist_from_start < fMinDistCutOff * wirepitch) { continue; }
231
232 if (dist_from_start > dEdxTrackLength) { continue; }
233 }
234
235 //Find the closest trajectory point of the track. These should be in order if the user has used ShowerTrackTrajToSpacePoint_tool but the sake of gernicness I'll get the cloest sp.
236 unsigned int index = 999;
237 double MinDist = 999;
238 for (unsigned int traj = 0; traj < InitialTrack.NumberTrajectoryPoints(); ++traj) {
239
240 //ignore bogus info.
241 auto flags = InitialTrack.FlagsAtPoint(traj);
242 if (flags.isSet(recob::TrajectoryPointFlagTraits::NoPoint)) { continue; }
243
244 geo::Point_t const TrajPosition = InitialTrack.LocationAtPoint(traj);
245
246 auto const dist = (pos - TrajPosition).R();
247
248 if (dist < MinDist && dist < MaxDist * wirepitch) {
249 MinDist = dist;
250 index = traj;
251 }
252 }
253
254 //If there is no matching trajectory point then bail.
255 if (index == 999) { continue; }
256
257 geo::Point_t const TrajPosition = InitialTrack.LocationAtPoint(index);
258 geo::Point_t const TrajPositionStart = InitialTrack.LocationAtPoint(0);
259
260 //Ignore values with 0 mag from the start position
261 if ((TrajPosition - TrajPositionStart).R() == 0) { continue; }
262 if ((TrajPosition - ShowerStartPosition).R() == 0) { continue; }
263
264 if ((TrajPosition - TrajPositionStart).R() < fMinDistCutOff * wirepitch) { continue; }
265
266 //Get the direction of the trajectory point
267 geo::Vector_t const TrajDirection = InitialTrack.DirectionAtPoint(index);
268
269 //If the direction is in the same direction as the wires within some tolerance the hit finding struggles. Let remove these.
270 // Note that we project in the YZ plane to make sure we are not cutting on
271 // the angle into the wire planes, that should be done by the shaping time cut
272 geo::Vector_t const TrajDirectionYZ{0, TrajDirection.Y(), TrajDirection.Z()};
273 auto const PlaneDirection = fGeom->Plane(planeid).GetIncreasingWireDirection();
274
275 if (std::abs((TMath::Pi() / 2 - Angle(TrajDirectionYZ, PlaneDirection))) < fMinAngleToWire) {
276 if (fVerbose) mf::LogWarning("ShowerTrajPointdEdx") << "remove from angle cut" << std::endl;
277 continue;
278 }
279
280 //If the direction is too much into the wire plane then the shaping amplifer cuts the charge. Lets remove these events.
281 double velocity = detProp.DriftVelocity(detProp.Efield(), detProp.Temperature());
282 double distance_in_x = TrajDirection.X() * (wirepitch / TrajDirection.Dot(PlaneDirection));
283 double time_taken = std::abs(distance_in_x / velocity);
284
285 //Shaping time doesn't seem to exist in a global place so add it as a fcl.
286 if (fShapingTime < time_taken) {
287 if (fVerbose) mf::LogWarning("ShowerTrajPointdEdx") << "move for shaping time" << std::endl;
288 continue;
289 }
290
291 if ((TrajPosition - TrajPositionStart).R() > dEdxTrackLength) { continue; }
292
293 //Iterate the number of hits on the plane
294 ++num_hits[planeid.Plane];
295
296 //If we still exist then we can be used in the calculation. Calculate the 3D pitch
297 double trackpitch = (TrajDirection * (wirepitch / TrajDirection.Dot(PlaneDirection))).R();
298
299 if (fSCECorrectPitch) {
301 trackpitch, pos, TrajDirection.Unit(), hit->WireID().TPC);
302 }
303
304 //Calculate the dQdx
305 double dQdx = hit->Integral();
306 if (fSumHitSnippets) {
307 for (const art::Ptr<recob::Hit> secondaryHit : hitSnippets[hit])
308 dQdx += secondaryHit->Integral();
309 }
310 dQdx /= trackpitch;
311
312 //Calculate the dEdx
313 double localEField = detProp.Efield();
314 if (fSCECorrectEField) {
315 localEField = IShowerTool::GetLArPandoraShowerAlg().SCECorrectEField(localEField, pos);
316 }
317 double dEdx = fCalorimetryAlg.dEdx_AREA(
318 clockData, detProp, dQdx, hit->PeakTime(), planeid.Plane, pfpT0Time, localEField);
319
320 //Add the value to the dEdx
321 dEdx_vec[planeid.Plane].push_back(dEdx);
322 }
323
324 //Choose max hits based on hitnum
325 int max_hits = 0;
326 int best_plane = -std::numeric_limits<int>::max();
327 for (auto const& [plane, numHits] : num_hits) {
328 if (fVerbose > 2) std::cout << "Plane: " << plane << " with size: " << numHits << std::endl;
329 if (numHits > max_hits) {
330 best_plane = plane;
331 max_hits = numHits;
332 }
333 }
334
335 if (best_plane < 0) {
336 if (fVerbose)
337 mf::LogError("ShowerTrajPointdEdx") << "No hits in any plane, returning " << std::endl;
338 return 1;
339 }
340
341 //Search for blow ups and gradient changes.
342 //Electrons have a very flat dEdx as function of energy till ~10MeV.
343 //If there is a sudden jump particle has probably split
344 //If there is very large dEdx we have either calculated it wrong (probably) or the Electron is coming to end.
345 //Assumes hits are ordered!
346 std::map<int, std::vector<double>> dEdx_vec_cut;
347 for (geo::PlaneID plane_id : fGeom->Iterate<geo::PlaneID>()) {
348 dEdx_vec_cut[plane_id.Plane] = {};
349 }
350
351 for (auto& dEdx_plane : dEdx_vec) {
352 FinddEdxLength(dEdx_plane.second, dEdx_vec_cut[dEdx_plane.first]);
353 }
354
355 //Never have the stats to do a landau fit and get the most probable value. User decides if they want the median value or the mean.
356 std::vector<double> dEdx_val;
357 std::vector<double> dEdx_valErr;
358 for (auto const& dEdx_plane : dEdx_vec_cut) {
359
360 if ((dEdx_plane.second).empty()) {
361 dEdx_val.push_back(-999);
362 dEdx_valErr.push_back(-999);
363 continue;
364 }
365
366 if (fUseMedian) {
367 dEdx_val.push_back(TMath::Median((dEdx_plane.second).size(), &(dEdx_plane.second)[0]));
368 }
369 else {
370 //Else calculate the mean value.
371 double dEdx_mean = 0;
372 for (auto const& dEdx : dEdx_plane.second) {
373 if (dEdx > 10 || dEdx < 0) { continue; }
374 dEdx_mean += dEdx;
375 }
376 dEdx_val.push_back(dEdx_mean / (float)(dEdx_plane.second).size());
377 }
378 }
379
380 if (fVerbose > 1) {
381 std::cout << "#Best Plane: " << best_plane << std::endl;
382 for (unsigned int plane = 0; plane < dEdx_vec.size(); plane++) {
383 std::cout << "#Plane: " << plane << " #" << std::endl;
384 std::cout << "#Median: " << dEdx_val[plane] << " #" << std::endl;
385 if (fVerbose > 2) {
386 for (auto const& dEdx : dEdx_vec_cut[plane]) {
387 std::cout << "dEdx: " << dEdx << std::endl;
388 }
389 }
390 }
391 }
392
393 //Need to sort out errors sensibly.
394 ShowerEleHolder.SetElement(dEdx_val, dEdx_valErr, fShowerdEdxOutputLabel);
395 ShowerEleHolder.SetElement(best_plane, fShowerBestPlaneOutputLabel);
396 ShowerEleHolder.SetElement(dEdx_vec_cut, fShowerdEdxVecOutputLabel);
397 return 0;
398 }
399
400 void ShowerTrajPointdEdx::FinddEdxLength(std::vector<double>& dEdx_vec,
401 std::vector<double>& dEdx_val)
402 {
403
404 //As default do not apply this cut.
405 if (fdEdxCut > 10) {
406 dEdx_val = dEdx_vec;
407 return;
408 }
409
410 //Can only do this with 4 hits.
411 if (dEdx_vec.size() < 4) {
412 dEdx_val = dEdx_vec;
413 return;
414 }
415
416 bool upperbound = false;
417
418 //See if we are in the upper bound or upper bound defined by the cut.
419 int upperbound_int = 0;
420 if (dEdx_vec[0] > fdEdxCut) { ++upperbound_int; }
421 if (dEdx_vec[1] > fdEdxCut) { ++upperbound_int; }
422 if (dEdx_vec[2] > fdEdxCut) { ++upperbound_int; }
423 if (upperbound_int > 1) { upperbound = true; }
424
425 dEdx_val.push_back(dEdx_vec[0]);
426 dEdx_val.push_back(dEdx_vec[1]);
427 dEdx_val.push_back(dEdx_vec[2]);
428
429 for (unsigned int dEdx_iter = 2; dEdx_iter < dEdx_vec.size(); ++dEdx_iter) {
430
431 //The Function of dEdx as a function of E is flat above ~10 MeV.
432 //We are looking for a jump up (or down) above the ladau width in the dEx
433 //to account account for pair production.
434 //Dom Estimates that the somwhere above 0.28 MeV will be a good cut but 999 will prevent this stage.
435 double dEdx = dEdx_vec[dEdx_iter];
436
437 //We are really poo at physics and so attempt to find the pair production
438 if (upperbound) {
439 if (dEdx > fdEdxCut) {
440 dEdx_val.push_back(dEdx);
441 if (fVerbose > 1) std::cout << "Adding dEdx: " << dEdx << std::endl;
442 continue;
443 }
444 else {
445 //Maybe its a landau fluctation lets try again.
446 if (dEdx_iter < dEdx_vec.size() - 1) {
447 if (dEdx_vec[dEdx_iter + 1] > fdEdxCut) {
448 if (fVerbose > 1)
449 std::cout << "Next dEdx hit is good removing hit" << dEdx << std::endl;
450 continue;
451 }
452 }
453 //I'll let one more value
454 if (dEdx_iter < dEdx_vec.size() - 2) {
455 if (dEdx_vec[dEdx_iter + 2] > fdEdxCut) {
456 if (fVerbose > 1)
457 std::cout << "Next Next dEdx hit is good removing hit" << dEdx << std::endl;
458 continue;
459 }
460 }
461 //We are hopefully we have one of our electrons has died.
462 break;
463 }
464 }
465 else {
466 if (dEdx < fdEdxCut) {
467 dEdx_val.push_back(dEdx);
468 if (fVerbose > 1) std::cout << "Adding dEdx: " << dEdx << std::endl;
469 continue;
470 }
471 else {
472 //Maybe its a landau fluctation lets try again.
473 if (dEdx_iter < dEdx_vec.size() - 1) {
474 if (dEdx_vec[dEdx_iter + 1] > fdEdxCut) {
475 if (fVerbose > 1)
476 std::cout << "Next dEdx hit is good removing hit " << dEdx << std::endl;
477 continue;
478 }
479 }
480 //I'll let one more value
481 if (dEdx_iter < dEdx_vec.size() - 2) {
482 if (dEdx_vec[dEdx_iter + 2] > fdEdxCut) {
483 if (fVerbose > 1)
484 std::cout << "Next Next dEdx hit is good removing hit " << dEdx << std::endl;
485 continue;
486 }
487 }
488 //We are hopefully in the the pair production zone.
489 break;
490 }
491 }
492 }
493 return;
494 }
495
496}
497
498DEFINE_ART_CLASS_TOOL(ShowerRecoTools::ShowerTrajPointdEdx)
const shower::LArPandoraShowerAlg & GetLArPandoraShowerAlg() const
Definition IShowerTool.h:82
ShowerTrajPointdEdx(const fhicl::ParameterSet &pset)
int CalculateElement(const art::Ptr< recob::PFParticle > &pfparticle, art::Event &Event, reco::shower::ShowerElementHolder &ShowerEleHolder) override
art::ServiceHandle< geo::Geometry > fGeom
void FinddEdxLength(std::vector< double > &dEdx_vec, std::vector< double > &dEdx_val)
int GetElement(const std::string &Name, T &Element) const
const art::FindManyP< T1 > & GetFindManyP(const art::ValidHandle< std::vector< T2 > > &handle, const art::Event &evt, const art::InputTag &moduleTag)
void SetElement(T &dataproduct, const std::string &Name, bool checktag=false)
bool CheckElement(const std::string &Name) const
double SCECorrectPitch(double const &pitch, geo::Point_t const &pos, geo::Vector_t const &dir, unsigned int const &TPC) const
double SCECorrectEField(double const &EField, geo::Point_t const &pos) const
std::map< art::Ptr< recob::Hit >, std::vector< art::Ptr< recob::Hit > > > OrganizeHits(const std::vector< art::Ptr< recob::Hit > > &hits) const