LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
EMShower_module.cc
Go to the documentation of this file.
1 // Class: EMShower
3 // Module Type: producer
4 // File: EMShower_module.cc
5 // Author: Mike Wallbank (m.wallbank@sheffield.ac.uk), September 2015
6 //
7 // Module to make EM showers.
8 // Takes the output from cluster finding and track finding and combines
9 // information to make complete 3D shower.
10 //
11 // See DUNE-DocDB 1369 (public) for a detailed description.
13 
14 // Framework includes:
24 #include "cetlib/pow.h"
25 #include "fhiclcpp/ParameterSet.h"
27 
28 // LArSoft includes
42 
43 // ROOT includes
44 #include "RtypesCore.h"
45 #include "TVector3.h"
46 
47 // C++ STL includes
48 #include <algorithm>
49 #include <array>
50 #include <float.h>
51 #include <iostream>
52 #include <map>
53 #include <math.h>
54 #include <memory>
55 #include <stddef.h>
56 #include <string>
57 #include <vector>
58 
59 #include "range/v3/view.hpp"
60 
61 namespace {
62  template <typename T, typename U>
63  struct AddMany {
64  AddMany(art::Ptr<T> const& ptr, art::Assns<T, U>& assns) : ptr_{ptr}, assns_{assns} {}
65 
66  void operator()(art::Ptr<U> const& b) const { assns_.addSingle(ptr_, b); }
67 
68  art::Ptr<T> const ptr_;
69  art::Assns<T, U>& assns_;
70  };
71 }
72 
73 using lar::to_element;
74 
75 namespace shower {
76  class EMShower;
77 }
78 
80 public:
81  EMShower(fhicl::ParameterSet const& pset);
82 
83 private:
84  void produce(art::Event& evt);
85 
92  int const fShower;
93  int const fPlane;
94  int const fDebug;
97  bool const fFindBadPlanes;
98  bool const fMakeSpacePoints;
99  bool const fUseCNNtoIDEMPFP;
100  bool const fUseCNNtoIDEMHit;
101  double const fMinTrackLikeScore;
102 
104 };
105 
107  : EDProducer{pset}
108  , fHitsModuleLabel{pset.get<art::InputTag>("HitsModuleLabel")}
109  , fClusterModuleLabel{pset.get<art::InputTag>("ClusterModuleLabel")}
110  , fTrackModuleLabel{pset.get<art::InputTag>("TrackModuleLabel")}
111  , fPFParticleModuleLabel{pset.get<art::InputTag>("PFParticleModuleLabel", "")}
112  , fVertexModuleLabel{pset.get<art::InputTag>("VertexModuleLabel", "")}
113  , fCNNEMModuleLabel{pset.get<art::InputTag>("CNNEMModuleLabel", "")}
114  , fShower{pset.get<int>("Shower", -1)}
115  , fPlane{pset.get<int>("Plane", -1)}
116  , fDebug{pset.get<int>("Debug", 0)}
117  , fEMShowerAlg{pset.get<fhicl::ParameterSet>("EMShowerAlg"), fDebug}
118  , fSaveNonCompleteShowers{pset.get<bool>("SaveNonCompleteShowers")}
119  , fFindBadPlanes{pset.get<bool>("FindBadPlanes")}
120  , fMakeSpacePoints{pset.get<bool>("MakeSpacePoints")}
121  , fUseCNNtoIDEMPFP{pset.get<bool>("UseCNNtoIDEMPFP")}
122  , fUseCNNtoIDEMHit{pset.get<bool>("UseCNNtoIDEMHit")}
123  , fMinTrackLikeScore{pset.get<double>("MinTrackLikeScore")}
124 {
125  produces<std::vector<recob::Shower>>();
126  produces<std::vector<recob::SpacePoint>>();
127  produces<art::Assns<recob::Shower, recob::Hit>>();
128  produces<art::Assns<recob::Shower, recob::Cluster>>();
129  produces<art::Assns<recob::Shower, recob::Track>>();
130  produces<art::Assns<recob::Shower, recob::SpacePoint>>();
131  produces<art::Assns<recob::SpacePoint, recob::Hit>>();
132 }
133 
135 {
136  // Output -- showers and associations with hits and clusters
137  auto showers = std::make_unique<std::vector<recob::Shower>>();
138  auto spacePoints = std::make_unique<std::vector<recob::SpacePoint>>();
139  auto clusterAssociations = std::make_unique<art::Assns<recob::Shower, recob::Cluster>>();
140  auto hitShowerAssociations = std::make_unique<art::Assns<recob::Shower, recob::Hit>>();
141  auto trackAssociations = std::make_unique<art::Assns<recob::Shower, recob::Track>>();
142  auto spShowerAssociations = std::make_unique<art::Assns<recob::Shower, recob::SpacePoint>>();
143  auto hitSpAssociations = std::make_unique<art::Assns<recob::SpacePoint, recob::Hit>>();
144 
145  // Event has hits, tracks and clusters found already
146 
147  // Hits
149  std::vector<art::Ptr<recob::Hit>> hits;
150  if (evt.getByLabel(fHitsModuleLabel, hitHandle)) art::fill_ptr_vector(hits, hitHandle);
151 
152  // Tracks
154  std::vector<art::Ptr<recob::Track>> tracks;
155  if (evt.getByLabel(fTrackModuleLabel, trackHandle)) art::fill_ptr_vector(tracks, trackHandle);
156 
157  // Clusters
159  std::vector<art::Ptr<recob::Cluster>> clusters;
160  if (evt.getByLabel(fClusterModuleLabel, clusterHandle))
161  art::fill_ptr_vector(clusters, clusterHandle);
162 
163  // PFParticles
165  std::vector<art::Ptr<recob::PFParticle>> pfps;
166  if (evt.getByLabel(fPFParticleModuleLabel, pfpHandle)) art::fill_ptr_vector(pfps, pfpHandle);
167 
168  // PFParticles
170  std::vector<art::Ptr<recob::Vertex>> vertices;
171  if (evt.getByLabel(fVertexModuleLabel, vtxHandle)) art::fill_ptr_vector(vertices, vtxHandle);
172 
173  // Associations
174  art::FindManyP<recob::Hit> fmh(clusterHandle, evt, fClusterModuleLabel);
175  art::FindManyP<recob::Track> fmt(hitHandle, evt, fTrackModuleLabel);
178 
179  // Make showers
180  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
181  auto const detProp =
183  std::vector<std::vector<int>> newShowers;
184  std::vector<unsigned int> pfParticles;
185 
186  std::map<int, std::vector<int>> clusterToTracks;
187  std::map<int, std::vector<int>> trackToClusters;
188 
189  if (!pfpHandle.isValid()) {
190 
191  // Map between tracks and clusters
192  fEMShowerAlg.AssociateClustersAndTracks(clusters, fmh, fmt, clusterToTracks, trackToClusters);
193 
194  // Make initial showers
195  std::vector<std::vector<int>> initialShowers = fEMShowerAlg.FindShowers(trackToClusters);
196 
197  // Deal with views in which 2D reconstruction failed
198  std::vector<int> clustersToIgnore;
199  if (fFindBadPlanes)
200  clustersToIgnore = fEMShowerAlg.CheckShowerPlanes(initialShowers, clusters, fmh);
201 
202  if (clustersToIgnore.size() > 0) {
203  clusterToTracks.clear();
204  trackToClusters.clear();
206  clusters, fmh, fmt, clustersToIgnore, clusterToTracks, trackToClusters);
207  newShowers = fEMShowerAlg.FindShowers(trackToClusters);
208  }
209  else
210  newShowers = initialShowers;
211  }
212 
213  else {
214 
215  // Use pfparticle information
217  for (size_t ipfp = 0; ipfp < pfps.size(); ++ipfp) {
218  art::Ptr<recob::PFParticle> pfp = pfps[ipfp];
219  if (fCNNEMModuleLabel != "" && fUseCNNtoIDEMPFP) { //use CNN to identify EM pfparticle
221  if (!hitResults) {
222  throw cet::exception("EMShower") << "Cannot get MVA results from " << fCNNEMModuleLabel;
223  }
224  int trkLikeIdx = hitResults->getIndex("track");
225  int emLikeIdx = hitResults->getIndex("em");
226  if ((trkLikeIdx < 0) || (emLikeIdx < 0)) {
227  throw cet::exception("EMShower") << "No em/track labeled columns in MVA data products.";
228  }
229  if (fmcp.isValid()) { //find clusters
230  std::vector<art::Ptr<recob::Hit>> pfphits;
231  std::vector<art::Ptr<recob::Cluster>> clus = fmcp.at(ipfp);
232  for (size_t iclu = 0; iclu < clus.size(); ++iclu) {
233  std::vector<art::Ptr<recob::Hit>> ClusterHits = fmh.at(clus[iclu].key());
234  pfphits.insert(pfphits.end(), ClusterHits.begin(), ClusterHits.end());
235  }
236  if (pfphits.size()) { //find hits
237  auto vout = hitResults->getOutput(pfphits);
238  double trk_like = -1, trk_or_em = vout[trkLikeIdx] + vout[emLikeIdx];
239  if (trk_or_em > 0) {
240  trk_like = vout[trkLikeIdx] / trk_or_em;
241  if (trk_like < fMinTrackLikeScore) { //EM like
242  std::vector<int> clusters;
243  for (size_t iclu = 0; iclu < clus.size(); ++iclu) {
244  clusters.push_back(clus[iclu].key());
245  }
246  if (clusters.size()) {
247  newShowers.push_back(clusters);
248  pfParticles.push_back(ipfp);
249  }
250  }
251  }
252  }
253  }
254  else {
255  throw cet::exception("EMShower") << "Cannot get associated cluster for PFParticle "
256  << fPFParticleModuleLabel.encode() << "[" << ipfp << "]";
257  }
258  }
259  else if (pfp->PdgCode() == 11) { //shower particle
260  if (fmcp.isValid()) {
261  std::vector<int> clusters;
262  std::vector<art::Ptr<recob::Cluster>> clus = fmcp.at(ipfp);
263  for (size_t iclu = 0; iclu < clus.size(); ++iclu) {
264  clusters.push_back(clus[iclu].key());
265  }
266  if (clusters.size()) {
267  newShowers.push_back(clusters);
268  pfParticles.push_back(ipfp);
269  }
270  }
271  }
272  }
273  }
274 
275  // Make output larsoft products
276  int showerNum = 0;
277  for (auto const& newShower : newShowers) {
278 
279  if (showerNum != fShower and fShower != -1) continue;
280 
281  // New shower
282  if (fDebug > 0) std::cout << "\n\nStart shower " << showerNum << '\n';
283 
284  // New associations
285  art::PtrVector<recob::Hit> showerHits;
286  art::PtrVector<recob::Cluster> showerClusters;
287  art::PtrVector<recob::Track> showerTracks;
288  art::PtrVector<recob::SpacePoint> showerSpacePoints_p;
289 
290  std::vector<int> associatedTracks;
291 
292  // Make showers and associations
293  for (int const showerCluster : newShower) {
294 
295  // Clusters
296  art::Ptr<recob::Cluster> const cluster = clusters.at(showerCluster);
297  showerClusters.push_back(cluster);
298 
299  // Hits
300  std::vector<art::Ptr<recob::Hit>> const& showerClusterHits = fmh.at(cluster.key());
301  if (fCNNEMModuleLabel != "" && fUseCNNtoIDEMHit) { // use CNN to identify EM hits
303  if (!hitResults) {
304  throw cet::exception("EMShower")
305  << "Cannot get MVA results from " << fCNNEMModuleLabel.encode();
306  }
307  int trkLikeIdx = hitResults->getIndex("track");
308  int emLikeIdx = hitResults->getIndex("em");
309  if (trkLikeIdx < 0 || emLikeIdx < 0) {
310  throw cet::exception("EMShower") << "No em/track labeled columns in MVA data products.";
311  }
312  for (auto const& showerHit : showerClusterHits) {
313  auto vout = hitResults->getOutput(showerHit);
314  double trk_like = -1, trk_or_em = vout[trkLikeIdx] + vout[emLikeIdx];
315  if (trk_or_em > 0) {
316  trk_like = vout[trkLikeIdx] / trk_or_em;
317  if (trk_like < fMinTrackLikeScore) { // EM like
318  showerHits.push_back(showerHit);
319  }
320  }
321  }
322  }
323  else {
324  for (auto const& showerClusterHit : showerClusterHits)
325  showerHits.push_back(showerClusterHit);
326  }
327  // Tracks
328  if (!pfpHandle.isValid()) { // Only do this for non-pfparticle mode
329  for (int const clusterTrack : clusterToTracks.at(showerCluster))
330  if (not cet::search_all(associatedTracks, clusterTrack))
331  associatedTracks.push_back(clusterTrack);
332  }
333  }
334 
335  if (!pfpHandle.isValid()) { // For non-pfparticles, get space points from tracks
336  // Tracks and space points
337  for (int const trackIndex : associatedTracks) {
338  showerTracks.push_back(tracks.at(trackIndex));
339  }
340  }
341  else { // For pfparticles, get space points from hits
343  if (fmspp.isValid()) {
344  for (size_t ihit = 0; ihit < showerHits.size(); ++ihit) {
345  for (auto const& spPtr : fmspp.at(ihit))
346  showerSpacePoints_p.push_back(spPtr);
347  }
348  }
349  }
350 
351  if (!pfpHandle.isValid()) {
352 
353  // First, order the hits into the correct shower order in each plane
354  if (fDebug > 1)
355  std::cout << " ------------------ Ordering shower hits --------------------\n";
356  std::map<int, std::vector<art::Ptr<recob::Hit>>> showerHitsMap =
357  fEMShowerAlg.OrderShowerHits(detProp, showerHits, fPlane);
358  if (fDebug > 1)
359  std::cout << " ------------------ End ordering shower hits "
360  "--------------------\n";
361 
362  // Find the track at the start of the shower
363  std::unique_ptr<recob::Track> initialTrack;
364  std::map<int, std::vector<art::Ptr<recob::Hit>>> initialTrackHits;
365  fEMShowerAlg.FindInitialTrack(detProp, showerHitsMap, initialTrack, initialTrackHits);
366 
367  // Make space points
368  std::vector<std::vector<art::Ptr<recob::Hit>>> hitAssns;
369  std::vector<recob::SpacePoint> showerSpacePoints;
370  if (fMakeSpacePoints)
371  showerSpacePoints = fEMShowerAlg.MakeSpacePoints(detProp, showerHitsMap, hitAssns);
372  else {
373  for (auto const& trkPtr : showerTracks) {
374  for (auto const& trackSpacePoint :
375  fmsp.at(trkPtr.key()) | ranges::views::transform(to_element)) {
376  showerSpacePoints.push_back(trackSpacePoint);
377  hitAssns.push_back(std::vector<art::Ptr<recob::Hit>>());
378  }
379  }
380  }
381 
382  // Save space points
383  art::PtrMaker<recob::SpacePoint> const make_space_point_ptr{evt};
384  size_t firstSpacePoint = spacePoints->size(), nSpacePoint = 0;
385  for (auto const& ssp : showerSpacePoints) {
386  spacePoints->emplace_back(ssp.XYZ(), ssp.ErrXYZ(), ssp.Chisq(), spacePoints->size());
387  auto const index = spacePoints->size() - 1;
388  auto const space_point_ptr = make_space_point_ptr(index);
389  cet::for_all(hitAssns.at(nSpacePoint), AddMany{space_point_ptr, *hitSpAssociations});
390  }
391  auto const lastSpacePoint = spacePoints->size();
392 
393  // Make shower object and associations
395  fEMShowerAlg.MakeShower(clockData, detProp, showerHits, initialTrack, initialTrackHits);
396  shower.set_id(showerNum);
398  (!fSaveNonCompleteShowers and shower.ShowerStart() != TVector3{})) {
399  showers->push_back(shower);
400 
401  auto const shower_ptr = art::PtrMaker<recob::Shower>{evt}(showers->size() - 1);
402  cet::for_all(showerHits, AddMany{shower_ptr, *hitShowerAssociations});
403  cet::for_all(showerClusters, AddMany{shower_ptr, *clusterAssociations});
404  cet::for_all(showerTracks, AddMany{shower_ptr, *trackAssociations});
405  for (size_t i = firstSpacePoint; i < lastSpacePoint; ++i) {
406  spShowerAssociations->addSingle(shower_ptr, make_space_point_ptr(i));
407  }
408  }
409  else
410  mf::LogInfo("EMShower") << "Discarding shower " << showerNum
411  << " due to incompleteness (SaveNonCompleteShowers == false)";
412  }
413 
414  else { // pfParticle
415 
416  if (vertices.size()) {
417  // found the most upstream vertex
419  Point_t nuvtx{0, 0, DBL_MAX};
420  for (auto const& vtx : vertices) {
421  auto const pos = vtx->position();
422  if (pos.Z() < nuvtx.Z()) { nuvtx = pos; }
423  }
424 
425  Point_t shwvtx{0, 0, 0};
426  double mindist2 = DBL_MAX;
427  for (auto const& sp : showerSpacePoints_p | ranges::views::transform(to_element)) {
428  double const dist2 = cet::sum_of_squares(
429  nuvtx.X() - sp.XYZ()[0], nuvtx.Y() - sp.XYZ()[1], nuvtx.Z() - sp.XYZ()[2]);
430  if (dist2 < mindist2) {
431  mindist2 = dist2;
432  shwvtx.SetXYZ(sp.XYZ()[0], sp.XYZ()[1], sp.XYZ()[2]);
433  }
434  }
435 
436  art::Ptr<recob::Vertex> bestvtx;
437  mindist2 = DBL_MAX;
438  for (auto const& vtx : vertices) {
439  auto const pos = vtx->position();
440  double const dist2 =
441  cet::sum_of_squares(pos.X() - shwvtx.X(), pos.Y() - shwvtx.Y(), pos.Z() - shwvtx.Z());
442  if (dist2 < mindist2) {
443  mindist2 = dist2;
444  bestvtx = vtx;
445  }
446  }
447 
448  int iok = 0;
450  fEMShowerAlg.MakeShower(clockData, detProp, showerHits, bestvtx, iok);
451  if (iok == 0) {
452  showers->push_back(shower);
453  auto const index = showers->size() - 1;
454  showers->back().set_id(index);
455 
456  auto const shower_ptr = art::PtrMaker<recob::Shower>{evt}(index);
457  cet::for_all(showerHits, AddMany{shower_ptr, *hitShowerAssociations});
458  cet::for_all(showerClusters, AddMany{shower_ptr, *clusterAssociations});
459  cet::for_all(showerTracks, AddMany{shower_ptr, *trackAssociations});
460  cet::for_all(showerSpacePoints_p, AddMany{shower_ptr, *spShowerAssociations});
461  }
462  }
463  }
464  }
465 
466  // Put in event
467  evt.put(std::move(showers));
468  evt.put(std::move(spacePoints));
469  evt.put(std::move(hitShowerAssociations));
470  evt.put(std::move(clusterAssociations));
471  evt.put(std::move(trackAssociations));
472  evt.put(std::move(spShowerAssociations));
473  evt.put(std::move(hitSpAssociations));
474 }
475 
const TVector3 & ShowerStart() const
Definition: Shower.h:197
std::vector< recob::SpacePoint > MakeSpacePoints(detinfo::DetectorPropertiesData const &detProp, std::map< int, std::vector< art::Ptr< recob::Hit >>> const &hits, std::vector< std::vector< art::Ptr< recob::Hit >>> &hitAssns) const
Makes space points from the shower hits in each plane.
bool const fUseCNNtoIDEMPFP
Utilities related to art service access.
constexpr to_element_t to_element
Definition: ToElement.h:25
art::InputTag const fVertexModuleLabel
art::InputTag const fClusterModuleLabel
recob::Shower MakeShower(detinfo::DetectorClocksData const &clockData, detinfo::DetectorPropertiesData const &detProp, art::PtrVector< recob::Hit > const &hits, std::unique_ptr< recob::Track > const &initialTrack, std::map< int, std::vector< art::Ptr< recob::Hit >>> const &initialTrackHits) const
Makes a recob::Shower object given the hits in the shower and the initial track-like part...
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Declaration of signal hit object.
bool const fMakeSpacePoints
EMShower(fhicl::ParameterSet const &pset)
bool const fUseCNNtoIDEMHit
void AssociateClustersAndTracks(std::vector< art::Ptr< recob::Cluster >> const &clusters, art::FindManyP< recob::Hit > const &fmh, art::FindManyP< recob::Track > const &fmt, std::map< int, std::vector< int >> &clusterToTracks, std::map< int, std::vector< int >> &trackToClusters) const
Map associated tracks and clusters together given their associated hits.
Definition: EMShowerAlg.cxx:59
int PdgCode() const
Return the type of particle as a PDG ID.
Definition: PFParticle.h:79
art::InputTag const fTrackModuleLabel
Cluster finding and building.
bool const fFindBadPlanes
std::string encode() const
Definition: InputTag.cc:97
std::vector< std::vector< int > > FindShowers(std::map< int, std::vector< int >> const &trackToClusters) const
Makes showers given a map between tracks and all clusters associated with them.
recob::tracking::Point_t Point_t
art::InputTag const fPFParticleModuleLabel
bool isValid() const noexcept
Definition: Handle.h:203
PutHandle< PROD > put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: Event.h:77
void set_id(const int id)
Definition: Shower.h:127
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:289
void hits()
Definition: readHits.C:15
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:65
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:435
void FindInitialTrack(detinfo::DetectorPropertiesData const &detProp, std::map< int, std::vector< art::Ptr< recob::Hit >>> const &hits, std::unique_ptr< recob::Track > &initialTrack, std::map< int, std::vector< art::Ptr< recob::Hit >>> &initialTrackHits) const
std::vector< int > CheckShowerPlanes(std::vector< std::vector< int >> const &initialShowers, std::vector< art::Ptr< recob::Cluster >> const &clusters, art::FindManyP< recob::Hit > const &fmh) const
Takes the initial showers found and tries to resolve issues where one bad view ruins the event...
key_type key() const noexcept
Definition: Ptr.h:166
std::map< int, std::vector< art::Ptr< recob::Hit > > > OrderShowerHits(detinfo::DetectorPropertiesData const &detProp, art::PtrVector< recob::Hit > const &shower, int plane) const
Takes the hits associated with a shower and orders them so they follow the direction of the shower...
art::InputTag const fHitsModuleLabel
Provides recob::Track data product.
double const fMinTrackLikeScore
Declaration of cluster object.
void produce(art::Event &evt)
size_type size() const
Definition: PtrVector.h:302
EMShowerAlg const fEMShowerAlg
bool getByLabel(std::string const &label, std::string const &instance, Handle< PROD > &result) const
art::InputTag const fCNNEMModuleLabel
TCEvent evt
Definition: DataStructs.cxx:8
bool const fSaveNonCompleteShowers
void fill_ptr_vector(std::vector< Ptr< T >> &ptrs, H const &h)
Definition: Ptr.h:306
art::ServiceHandle< geo::Geometry const > fGeom
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< Coord_t >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space. See recob::tracking::Coord_t for more detai...
Definition: TrackingTypes.h:27
Definition: fwd.h:26
static std::unique_ptr< MVAReader > create(const art::Event &evt, const art::InputTag &tag)
Definition: MVAReader.h:108
art framework interface to geometry description
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33