All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DumpPFParticles_module.cc
Go to the documentation of this file.
1 /**
2  * @file DumpPFParticles_module.cc
3  * @brief Dumps on screen the content of ParticleFlow particles
4  * @author Gianluca Petrillo (petrillo@fnal.gov)
5  * @date September 25th, 2015
6  */
7 
8 // LArSoft includes
11 
12 // art libraries
13 #include "canvas/Utilities/InputTag.h"
14 #include "canvas/Persistency/Provenance/EventID.h"
15 #include "art/Framework/Principal/Provenance.h"
16 #include "art/Framework/Core/EDAnalyzer.h"
17 #include "art/Framework/Principal/Event.h"
18 
19 // support libraries
20 #include "fhiclcpp/types/Atom.h"
21 #include "fhiclcpp/types/OptionalAtom.h"
22 
23 // C//C++ standard libraries
24 #include <string>
25 
26 // ... and more in the implementation part
27 
28 namespace recob {
29 
30  /**
31  * @brief Prints the content of all the ParticleFlow particles on screen
32  *
33  * This analyser prints the content of all the ParticleFlow particles into the
34  * LogInfo/LogVerbatim stream.
35  *
36  * Configuration parameters
37  * =========================
38  *
39  * - *PFModuleLabel* (art::InputTag, _required_): label of the
40  * producer used to create the recob::PFParticle collection to be dumped
41  * - *OutputCategory* (string, default: `"DumpPFParticles"`): the category
42  * used for the output (useful for filtering)
43  * - *PrintHexFloats* (boolean, default: `false`): print all the floating
44  * point numbers in base 16
45  * - *MaxDepth* (unsigned int, optional): if specified, at most this number of
46  * particle generations will be printed; 1 means printing only primaries and
47  * their daughters, 0 only primaries. If not specified, no limit will be
48  * applied. This is useful for buggy PFParticles with circular references.
49  * - *MakeParticleGraphs* (boolean, default: `false`): creates a DOT file for
50  * each event, with a graph of PFParticle relations; each file is named as:
51  * `ProcessName_ModuleLabel_InstanceName_Run#_Subrun#_Event#_particles.dot`,
52  * where the the input label elements refer to the data product being
53  * plotted.
54  *
55  *
56  * Particle connection graphs
57  * ---------------------------
58  *
59  * When _MakeParticleGraphs_ configuration option is activated, a file is
60  * created for each event, that contains the particle flow tree in GraphViz
61  * format. The GraphViz `dot` command can be used to render it into a PDF,
62  * SVG, EPS or one of the many supported bitmap formats.
63  * The typical command to use is:
64  *
65  * dot -Tpdf -oPMTrk.pdf PMTrk.dot
66  *
67  * A `bash` command to convert all files into a `OutputFormat` format:
68  *
69  * OutputFormat='pdf'
70  * for DotFile in *.dot ; do
71  * OutputFile="${DotFile%.dot}.${OutputFormat}"
72  * [[ "$OutputFile" -ot "$DotFile" ]] || continue # up to date already
73  * echo "${DotFile} => ${OutputFile} ..."
74  * dot -T"$OutputFormat" -o"$OutputFile" "$DotFile" || break
75  * done
76  *
77  * which will also skip files already converted.
78  *
79  * The output shows one cell ("node") per particle. The format of the node
80  * follows these prescriptions:
81  *
82  * * the label has the particle ID number prepended by a hash character (`#`)
83  * * if the particle has a PDG ID, that also appears in the label (either the
84  * name of the corresponding particle, or, if unknown, just the PDG ID
85  * number)
86  * * if the particle is primary, it is rendered in bold font
87  * * if the particle is referred by other particles, but it is not present
88  * ("ghost particle"), its border is red and dashed
89  *
90  * The relations between particles in the flow are represented by connecting
91  * lines ("edges"). Connection information is redundant: the parent particle
92  * should have the daughter in the daughter list, and the daughter should have
93  * the parent particle referenced as such. Since the connection is usually
94  * from two sources, there are usually two arrow heads, each one close to the
95  * particle that provides information on that connection; all arrow heads
96  * point from parent to daughter.
97  *
98  * * when the information of daughter and parent is consistent, a black line
99  * with two arrow heads both pointing to the daughter is shown
100  * * when the parent is ghost, the arrow head close to the daughter is hollow;
101  * ghost particles have no arrow heads close to them
102  * * when the daughter is ghost, the arrow head close to the parent is hollow;
103  * ghost particles have no arrow heads close to them
104  *
105  *
106  * If you are trying to interpret an existing diagram, the following list is
107  * more direct to the point.
108  * Nodes: represent particles (see above for the label content)
109  *
110  * * bold label: primary particle
111  * * red, dashed border: "ghost particle" (missing but referenced by others)
112  * * other: just a particle
113  *
114  * Connecting lines ("edges"):
115  * * all arrow heads point from parent to daughter
116  * * black with two full arrow heads: regular parent to daughter
117  * * black with a single inward empty arrow head: the particle close to the
118  * arrow claims the particle pointed by the arrow as a daughter, but there
119  * is no information on that daughter (ghost daughter)
120  * * black with a single outward empty arrow head: the particle at the tip of
121  * the arrow claims to be daughter of the other particle, but there is no
122  * information on that parent (ghost parent)
123  * * red, outward arrow: the daughter (at the tip of the only arrow) claims
124  * the other particle as parent, but that parent does not recognise it as
125  * daughter
126  * * orange, inward arrow: the parent (close to the only arrow head) claims
127  * the other particle as daughter, but that daighter does not recognise it
128  * as parent
129  *
130  */
131  class DumpPFParticles: public art::EDAnalyzer {
132  public:
133 
134  struct Config {
135  using Name = fhicl::Name;
136  using Comment = fhicl::Comment;
137 
138  fhicl::Atom<art::InputTag> PFModuleLabel {
139  Name("PFModuleLabel"),
140  Comment("label of producer of the recob::PFParticle to be dumped")
141  };
142 
143  fhicl::Atom<std::string> OutputCategory {
144  Name("OutputCategory"),
145  Comment("message facility category used for output (for filtering)"),
146  "DumpPFParticles"
147  };
148 
149  fhicl::Atom<bool> PrintHexFloats {
150  Name("PrintHexFloats"),
151  Comment("print all the floating point numbers in base 16"),
152  false
153  };
154 
155  fhicl::OptionalAtom<unsigned int> MaxDepth {
156  Name("MaxDepth"),
157  Comment("at most this number of particle generations will be printed")
158  };
159 
160  fhicl::Atom<bool> MakeParticleGraphs {
161  Name("MakeParticleGraphs"),
162  Comment("creates a DOT file with particle information for each event"),
163  false
164  };
165 
166  }; // struct Config
167 
168  using Parameters = art::EDAnalyzer::Table<Config>;
169 
170  /// Default constructor
171  explicit DumpPFParticles(Parameters const& config);
172 
173  /// Does the printing
174  virtual void analyze (const art::Event& evt) override;
175 
176  private:
177 
178  art::InputTag fInputTag; ///< input tag of the PFParticle product
179  std::string fOutputCategory; ///< category for LogInfo output
180  bool fPrintHexFloats; ///< whether to print floats in base 16
181  unsigned int fMaxDepth; ///< maximum generation to print (0: only primaries)
182  bool fMakeEventGraphs; ///< whether to create one DOT file per event
183 
184 
185  static std::string DotFileName
186  (art::EventID const& evtID, art::Provenance const& prodInfo);
187 
188  void MakePFParticleGraph(
189  art::Event const& event,
190  art::ValidHandle<std::vector<recob::PFParticle>> const& handle
191  ) const;
192 
193  }; // class DumpPFParticles
194 
195 } // namespace recob
196 
197 
198 //==============================================================================
199 //=== Implementation section
200 //==============================================================================
201 // LArSoft includes
209 
210 // art libraries
211 #include "art/Framework/Core/ModuleMacros.h"
212 #include "canvas/Persistency/Common/FindOne.h"
213 #include "canvas/Persistency/Common/FindMany.h"
214 #include "art/Framework/Principal/Handle.h"
215 
216 // support libraries
217 #include "messagefacility/MessageLogger/MessageLogger.h"
218 
219 // C//C++ standard libraries
220 #include <fstream>
221 #include <utility> // std::swap()
222 #include <algorithm> // std::count(), std::find()
223 #include <limits> // std::numeric_limits<>
224 
225 
226 namespace {
227  /**
228  * @brief A container keyed by integer key (size_t).
229  * @tparam T type of contained element
230  */
231  template <typename T>
232  class int_map: private std::vector<T> {
233  using map_type = std::vector<T>;
234  public:
235  using this_type = int_map<T>;
236 
237  using typename map_type::value_type;
238  using typename map_type::reference;
239  using typename map_type::const_reference;
240  using typename map_type::iterator;
241  using typename map_type::const_iterator;
242 
243  using typename map_type::size_type;
244 
245 
246  /// Default constructor: "invalid data" is default-constructed
247  int_map() = default;
248 
249  /// Constructor: sets the value of invalid data
250  int_map(value_type invalid_value): invalid(invalid_value) {}
251 
252 
253  /// Resizes the map to accomodate this many items
254  void resize(size_t new_size)
255  { get_map().resize(new_size, invalid_value()); }
256 
257  /// Creates the item at specified position with invalid value and returns it
258  reference operator[] (size_t pos)
259  { if (pos >= size()) resize(pos + 1); return get_map()[pos]; }
260 
261  /// Returns the item at specified position, or invalid value if not existing
262  const_reference operator[] (size_t pos) const
263  { return (pos >= size())? invalid: get_map()[pos]; }
264 
265  using map_type::size;
266  using map_type::empty;
267 
268  using map_type::begin;
269  using map_type::end;
270  using map_type::cbegin;
271  using map_type::cend;
272  using map_type::rbegin;
273  using map_type::rend;
274  using map_type::crbegin;
275  using map_type::crend;
276 
277  /// Swaps the content of data and of this map
278  void swap(this_type& data)
279  { swap(data.get_map()); std::swap(data.invalid, invalid); }
280 
281  /// Swaps the content of data and of this map
282  void swap(std::vector<T>& data) { map_type::swap(data); }
283 
284  // non-standard calls follow
285  /// Returns the number of invalid elements
286  size_type n_invalid() const
287  { return std::count(begin(), end(), invalid); }
288 
289  /// Returns the number of valid elements
290  size_type n_valid() const { return size() - n_invalid(); }
291 
292  /// Returns whether the element at specified position is valid
293  bool is_valid(size_type pos) const
294  { return (pos < size())? is_valid_value(get_map()[pos]): false; }
295 
296  /// Returns whether the specified value is valid
297  bool is_valid_value(value_type const& v) const { return v != invalid; }
298 
299  /// Returns the invalid value
300  value_type const& invalid_value() const { return invalid; }
301 
302 
303  private:
304  value_type const invalid; ///< value of invalid data
305 
306  map_type& get_map() { return static_cast<map_type&>(*this); }
307  map_type const& get_map() const
308  { return static_cast<map_type const&>(*this); }
309 
310  }; // int_map<>
311 
312 
313  //----------------------------------------------------------------------------
314  int_map<size_t> CreateMap(std::vector<recob::PFParticle> const& particles) {
315 
316  int_map<size_t> pmap(std::numeric_limits<size_t>::max());
317  pmap.resize(particles.size());
318 
319  size_t const nParticles = particles.size();
320  for (size_t iPart = 0; iPart < nParticles; ++iPart)
321  pmap[particles[iPart].Self()] = iPart;
322 
323  return pmap;
324  } // CreateMap()
325 
326 
327  //----------------------------------------------------------------------------
328  bool hasDaughter(recob::PFParticle const& particle, size_t partID) {
329  auto const& daughters = particle.Daughters();
330  return daughters.end()
331  != std::find(daughters.begin(), daughters.end(), partID);
332  } // hasDaughter()
333 
334 
335  //----------------------------------------------------------------------------
336  class ParticleDumper {
337  public:
338 
339  /// Collection of available printing style options
340  struct PrintOptions_t {
341  bool hexFloats = false; ///< print all floating point numbers in base 16
342  /// number of particle generations to descent into (0: only primaries)
343  unsigned int maxDepth = std::numeric_limits<unsigned int>::max();
344  /// name of the output stream
345  std::string streamName;
346  }; // PrintOptions_t
347 
348 
349  /// Constructor; will dump particles from the specified list
350  ParticleDumper(std::vector<recob::PFParticle> const& particle_list)
351  : ParticleDumper(particle_list, {})
352  {}
353 
354  /// Constructor; will dump particles from the specified list
355  ParticleDumper(
356  std::vector<recob::PFParticle> const& particle_list,
357  PrintOptions_t print_options
358  )
359  : particles(particle_list)
360  , options(print_options)
361  , visited(particles.size(), 0U)
362  , particle_map (CreateMap(particles))
363  {}
364 
365 
366  /// Sets the vertices associated to each particle
367  void SetVertices(art::FindOne<recob::Vertex> const* vtx_query)
368  { vertices = vtx_query; }
369 
370  /// Sets the vertices associated to each particle
371  void SetTracks(art::FindMany<recob::Track> const* trk_query)
372  { tracks = trk_query; }
373 
374  /// Sets the clusters associated to each particle
375  void SetClusters(art::FindMany<recob::Cluster> const* cls_query)
376  { clusters = cls_query; }
377 
378  /// Sets the seeds associated to each particle
379  void SetSeeds(art::FindMany<recob::Seed> const* seed_query)
380  { seeds = seed_query; }
381 
382  /// Sets the 3D points associated to each particle
383  void SetSpacePoints(art::FindMany<recob::SpacePoint> const* sp_query)
384  { spacepoints = sp_query; }
385 
386  /// Sets the 3D points associated to each particle
387  void SetPCAxes(art::FindMany<recob::PCAxis> const* pca_query)
388  { pcaxes = pca_query; }
389 
390 
391  /// Dump a particle specified by its index in the input particle list
392  /// @param gen max generations to print
393  template <typename Stream>
394  void DumpParticle(
395  Stream&& out, size_t iPart, std::string indentstr = "",
396  unsigned int gen = 0
397  ) const;
398 
399 
400  /// Dump a particle specified by its ID
401  /// @param gen max generations to print
402  template <typename Stream>
403  void DumpParticleWithID(
404  Stream&& out, size_t pID, std::string indentstr = "",
405  unsigned int gen = 0
406  ) const;
407 
408 
409  /// Dumps all primary particles
410  void DumpAllPrimaries(std::string indentstr = "") const;
411 
412 
413  /// Dumps all particles in the input list
414  void DumpAllParticles(std::string indentstr = "") const;
415 
416 
417  template <typename Stream>
418  static void DumpPDGID(Stream&& out, int ID);
419 
420 
421  protected:
422  std::vector<recob::PFParticle> const& particles; ///< input list
423 
424  PrintOptions_t options; ///< printing and formatting options
425 
426  /// Associated vertices (expected same order as for particles)
427  art::FindOne<recob::Vertex> const* vertices = nullptr;
428 
429  /// Associated tracks (expected same order as for particles)
430  art::FindMany<recob::Track> const* tracks = nullptr;
431 
432  /// Associated clusters (expected same order as for particles)
433  art::FindMany<recob::Cluster> const* clusters = nullptr;
434 
435  /// Associated seeds (expected same order as for particles)
436  art::FindMany<recob::Seed> const* seeds = nullptr;
437 
438  /// Associated space points (expected same order as for particles)
439  art::FindMany<recob::SpacePoint> const* spacepoints = nullptr;
440 
441  /// Associated principal component axes (expected same order as particles)
442  art::FindMany<recob::PCAxis> const* pcaxes = nullptr;
443 
444  /// Number of dumps on each particle
445  mutable std::vector<unsigned int> visited;
446 
447  int_map<size_t> const particle_map; ///< fast lookup index by particle ID
448 
449 
450  template <typename Stream>
451  void DumpPFParticleInfo(
452  Stream&& out,
453  recob::PFParticle const& part,
454  unsigned int iPart,
455  std::string indentstr
456  ) const;
457 
458  template <typename Stream>
459  void DumpVertex(
460  Stream&& out,
461  cet::maybe_ref<recob::Vertex const> VertexRef,
462  std::string indentstr
463  ) const;
464 
465  template <typename Stream>
466  void DumpPCAxisDirection
467  (Stream&& out, recob::PCAxis const& axis) const;
468 
469  template <typename Stream>
470  void DumpPCAxes(
471  Stream&& out,
472  std::vector<recob::PCAxis const*> const& myAxes,
473  std::string indentstr
474  ) const;
475 
476  template <typename Stream>
477  void DumpTrack(Stream&& out, recob::Track const& track) const;
478 
479  template <typename Stream>
480  void DumpTracks(
481  Stream&& out,
482  std::vector<recob::Track const*> const& myTracks,
483  std::string indentstr
484  ) const;
485 
486  template <typename Stream>
487  void DumpSeed
488  (Stream&& out, recob::Seed const& seed, std::string indentstr) const;
489 
490  template <typename Stream>
491  void DumpSeeds(
492  Stream&& out,
493  std::vector<recob::Seed const*> const& mySeeds,
494  std::string indentstr
495  ) const;
496 
497  template <typename Stream>
498  void DumpSpacePoint(Stream&& out, recob::SpacePoint const& sp) const;
499 
500  template <typename Stream>
501  void DumpSpacePoints(
502  Stream&& out,
503  std::vector<recob::SpacePoint const*> const& mySpacePoints,
504  std::string indentstr
505  ) const;
506 
507  template <typename Stream>
508  void DumpClusterSummary(Stream&& out, recob::Cluster const& track) const;
509 
510  template <typename Stream>
511  void DumpClusters(
512  Stream&& out,
513  std::vector<recob::Cluster const*> const& myClusters,
514  std::string indentstr
515  ) const;
516 
517  }; // class ParticleDumper
518 
519 
520  //----------------------------------------------------------------------------
521  class PFParticleGraphMaker {
522  public:
523 
524  PFParticleGraphMaker() = default;
525 
526  template <typename Stream>
527  void MakeGraph
528  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
529 
530  template <typename Stream>
531  void WriteGraphHeader(Stream&& out) const;
532 
533  template <typename Stream>
534  void WriteParticleRelations
535  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
536 
537  template <typename Stream>
538  void WriteParticleNodes
539  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
540 
541  template <typename Stream>
542  void WriteParticleEdges
543  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
544 
545  template <typename Stream>
546  void WriteGraphFooter(Stream&& out) const;
547 
548  }; // class PFParticleGraphMaker
549 
550 
551  //----------------------------------------------------------------------------
552  //--- ParticleDumper implementation
553  //---
554  template <typename Stream>
555  void ParticleDumper::DumpParticle(
556  Stream&& out, size_t iPart, std::string indentstr /* = "" */,
557  unsigned int gen /* = 0 */
558  ) const
559  {
560  lar::OptionalHexFloat hexfloat(options.hexFloats);
561 
562  recob::PFParticle const& part = particles.at(iPart);
563  ++visited[iPart];
564 
565  if (visited[iPart] > 1) {
566  out << indentstr << "particle " << part.Self()
567  << " already printed!!!";
568  return;
569  }
570 
571  //
572  // intro
573  //
574  DumpPFParticleInfo(std::forward<Stream>(out), part, iPart, indentstr);
575 
576  //
577  // vertex information
578  //
579  if (vertices)
580  DumpVertex(std::forward<Stream>(out), vertices->at(iPart), indentstr);
581 
582  // daughters tag
583  if (part.NumDaughters() > 0)
584  out << " with " << part.NumDaughters() << " daughters";
585  else
586  out << " with no daughter";
587 
588  //
589  // axis
590  //
591  if (pcaxes)
592  DumpPCAxes(std::forward<Stream>(out), pcaxes->at(iPart), indentstr);
593 
594  //
595  // tracks
596  //
597  if (tracks)
598  DumpTracks(std::forward<Stream>(out), tracks->at(iPart), indentstr);
599 
600  //
601  // seeds
602  //
603  if (seeds)
604  DumpSeeds(std::forward<Stream>(out), seeds->at(iPart), indentstr);
605 
606  //
607  // space points
608  //
609  if (spacepoints) {
610  DumpSpacePoints
611  (std::forward<Stream>(out), spacepoints->at(iPart), indentstr);
612  }
613 
614  //
615  // clusters
616  //
617  if (clusters) {
618  DumpClusters
619  (std::forward<Stream>(out), clusters->at(iPart), indentstr);
620  }
621 
622  //
623  // daughters
624  //
625  auto const PartID = part.Self();
626  if (part.NumDaughters() > 0) {
627  std::vector<size_t> const& daughters = part.Daughters();
628  out << "\n" << indentstr
629  << " " << part.NumDaughters() << " particle daughters";
630  if (gen > 0) {
631  out << ":";
632  for (size_t DaughterID: daughters) {
633  if (DaughterID == PartID) {
634  out << "\n" << indentstr
635  << " oh, just great: this particle is its own daughter!";
636  }
637  else {
638  out << '\n';
639  DumpParticleWithID(out, DaughterID, indentstr + " ", gen - 1);
640  }
641  }
642  } // if descending
643  else {
644  out << " (further descend suppressed)";
645  }
646  } // if has daughters
647 
648  //
649  // warnings
650  //
651  if (visited[iPart] == 2) {
652  out << "\n" << indentstr << " WARNING: particle ID=" << PartID
653  << " connected more than once!";
654  }
655 
656  //
657  // done
658  //
659 
660  } // ParticleDumper::DumpParticle()
661 
662 
663  //----------------------------------------------------------------------------
664  template <typename Stream>
665  void ParticleDumper::DumpParticleWithID(
666  Stream&& out, size_t pID, std::string indentstr /* = "" */,
667  unsigned int gen /* = 0 */
668  ) const {
669  size_t const pos = particle_map[pID];
670  if (particle_map.is_valid_value(pos)) {
671  DumpParticle(out, pos, indentstr, gen);
672  }
673  else {
674  out /* << "\n" */ << indentstr << "<ID=" << pID << " not found>";
675  }
676  } // ParticleDumper::DumpParticleWithID()
677 
678 
679  //----------------------------------------------------------------------------
680  void ParticleDumper::DumpAllPrimaries(std::string indentstr /* = "" */) const
681  {
682  indentstr += " ";
683  size_t const nParticles = particles.size();
684  unsigned int nPrimaries = 0;
685  for (size_t iPart = 0; iPart < nParticles; ++iPart) {
686  if (!particles[iPart].IsPrimary()) continue;
687  DumpParticle(
688  mf::LogVerbatim(options.streamName),
689  iPart, indentstr, options.maxDepth
690  );
691  } // for
692  if (nPrimaries == 0) {
693  mf::LogVerbatim(options.streamName)
694  << indentstr << "No primary particle found";
695  }
696  } // ParticleDumper::DumpAllPrimaries()
697 
698 
699  //----------------------------------------------------------------------------
700  void ParticleDumper::DumpAllParticles(std::string indentstr /* = "" */) const
701  {
702  // first print all the primary particles
703  DumpAllPrimaries(indentstr);
704  // then find out if there are any that are "disconnected"
705  unsigned int const nDisconnected
706  = std::count(visited.begin(), visited.end(), 0U);
707  if (nDisconnected) {
708  mf::LogVerbatim(options.streamName) << indentstr
709  << nDisconnected << " particles not coming from primary ones:";
710  size_t const nParticles = visited.size();
711  for (size_t iPart = 0; iPart < nParticles; ++iPart) {
712  if (visited[iPart] > 0) continue;
713  DumpParticle(
714  mf::LogVerbatim(options.streamName), iPart, indentstr + " ",
715  options.maxDepth
716  );
717  } // for unvisited particles
718  mf::LogVerbatim(options.streamName) << indentstr
719  << "(end of " << nDisconnected << " particles not from primaries)";
720  } // if there are disconnected particles
721  // TODO finally, note if there are multiply-connected particles
722 
723  } // ParticleDumper::DumpAllParticles()
724 
725 
726  //----------------------------------------------------------------------------
727  template <typename Stream>
728  void ParticleDumper::DumpPDGID(Stream&& out, int ID) {
729  switch (ID) {
730  case -11: out << "e+"; break;
731  case 11: out << "e-"; break;
732  case -13: out << "mu+"; break;
733  case 13: out << "mu-"; break;
734  default: out << "MCID=" << ID; break;
735  } // switch
736  } // DumpPDGID()
737 
738 
739  //----------------------------------------------------------------------------
740  template <typename Stream>
741  void ParticleDumper::DumpPFParticleInfo(
742  Stream&& out,
743  recob::PFParticle const& part,
744  unsigned int iPart,
745  std::string indentstr
746  ) const
747  {
748  auto const PartID = part.Self();
749  out << indentstr << "ID=" << PartID;
750  if (iPart != PartID) out << " [#" << iPart << "]";
751  out << " (type: ";
752  DumpPDGID(out, part.PdgCode());
753  out << ")";
754  if (part.IsPrimary()) out << " (primary)";
755  else out << " from ID=" << part.Parent();
756  } // ParticleDumper::DumpPFParticleInfo()
757 
758  //----------------------------------------------------------------------------
759  template <typename Stream>
760  void ParticleDumper::DumpVertex(
761  Stream&& out,
762  cet::maybe_ref<recob::Vertex const> VertexRef,
763  std::string indentstr
764  ) const
765  {
766  if (!VertexRef.isValid()) {
767  out << " [no vertex found!]";
768  return;
769  }
770  recob::Vertex const& vertex = VertexRef.ref();
771  std::array<double, 3> vtx_pos;
772  vertex.XYZ(vtx_pos.data());
773  lar::OptionalHexFloat hexfloat(options.hexFloats);
774  out << " [decay at ("
775  << hexfloat(vtx_pos[0]) << "," << hexfloat(vtx_pos[1])
776  << "," << hexfloat(vtx_pos[2])
777  << "), ID=" << vertex.ID() << "]";
778 
779  } // ParticleDumper::DumpVertex()
780 
781  //----------------------------------------------------------------------------
782  template <typename Stream>
783  void ParticleDumper::DumpPCAxisDirection
784  (Stream&& out, recob::PCAxis const& axis) const
785  {
786  if (!axis.getSvdOK()) {
787  out << "axis is invalid";
788  return;
789  }
790  lar::OptionalHexFloat hexfloat(options.hexFloats);
791  out << "axis ID=" << axis.getID() << ", principal: ("
792  << hexfloat(axis.getEigenVectors()[0][0])
793  << ", " << hexfloat(axis.getEigenVectors()[0][1])
794  << ", " << hexfloat(axis.getEigenVectors()[0][2]) << ")";
795  } // ParticleDumper::DumpPCAxisDirection()
796 
797  template <typename Stream>
798  void ParticleDumper::DumpPCAxes(
799  Stream&& out,
800  std::vector<recob::PCAxis const*> const& myAxes,
801  std::string indentstr
802  ) const
803  {
804  out << "\n" << indentstr;
805  if (myAxes.empty()) {
806  out << " [no axis found!]";
807  return;
808  }
809  if (myAxes.size() == 1) {
810  DumpPCAxisDirection(std::forward<Stream>(out), *(myAxes.front()));
811  }
812  else {
813  out << " " << myAxes.size() << " axes present:";
814  for (recob::PCAxis const* axis: myAxes) {
815  out << "\n" << indentstr << " ";
816  DumpPCAxisDirection(std::forward<Stream>(out), *axis);
817  } // for axes
818  } // else
819  } // ParticleDumper::DumpPCAxes()
820 
821  //----------------------------------------------------------------------------
822  template <typename Stream>
823  void ParticleDumper::DumpSeed
824  (Stream&& out, recob::Seed const& seed, std::string indentstr) const
825  {
826  if (!seed.IsValid()) {
827  out << " <invalid>";
828  return;
829  }
830  std::array<double, 3> start, dir;
831  seed.GetDirection(dir.data(), nullptr);
832  seed.GetPoint(start.data(), nullptr);
833  lar::OptionalHexFloat hexfloat(options.hexFloats);
834  out << "\n" << indentstr
835  << " (" << hexfloat(start[0]) << "," << hexfloat(start[1])
836  << "," << hexfloat(start[2])
837  << ")=>(" << hexfloat(dir[0]) << "," << hexfloat(dir[1])
838  << "," << hexfloat(dir[2])
839  << "), " << hexfloat(seed.GetLength()) << " cm"
840  ;
841  } // ParticleDumper::DumpSeed()
842 
843  template <typename Stream>
844  void ParticleDumper::DumpSeeds(
845  Stream&& out,
846  std::vector<recob::Seed const*> const& mySeeds,
847  std::string indentstr
848  ) const
849  {
850  if (mySeeds.empty()) return;
851  out << "\n" << indentstr << " "
852  << mySeeds.size() << " seeds:";
853  for (recob::Seed const* seed: mySeeds)
854  DumpSeed(std::forward<Stream>(out), *seed, indentstr);
855  } // ParticleDumper::DumpSeeds()
856 
857  //----------------------------------------------------------------------------
858  template <typename Stream>
860  (Stream&& out, recob::SpacePoint const& sp) const
861  {
862  const double* pos = sp.XYZ();
863  lar::OptionalHexFloat hexfloat(options.hexFloats);
864  out << " ID=" << sp.ID()
865  << " at (" << hexfloat(pos[0]) << "," << hexfloat(pos[1])
866  << "," << hexfloat(pos[2]) << ") cm"
867  ;
868  } // ParticleDumper::DumpSpacePoints()
869 
870  template <typename Stream>
871  void ParticleDumper::DumpSpacePoints(
872  Stream&& out,
873  std::vector<recob::SpacePoint const*> const& mySpacePoints,
874  std::string indentstr
875  ) const
876  {
877  out << "\n" << indentstr << " ";
878  if (mySpacePoints.empty()) {
879  out << "no space points";
880  return;
881  }
882  constexpr unsigned int PointsPerLine = 2;
883  out << mySpacePoints.size() << " space points:";
884  for (size_t iSP = 0; iSP < mySpacePoints.size(); ++iSP) {
885  if ((iSP % PointsPerLine) == 0)
886  out << "\n" << indentstr << " ";
887 
888  DumpSpacePoint(std::forward<Stream>(out), *(mySpacePoints[iSP]));
889  } // for points
890  } // ParticleDumper::DumpSpacePoints()
891 
892  //----------------------------------------------------------------------------
893  template <typename Stream>
894  void ParticleDumper::DumpTrack(Stream&& out, recob::Track const& track) const
895  {
896  lar::OptionalHexFloat hexfloat(options.hexFloats);
897  out << " length " << hexfloat(track.Length())
898  << "cm from (" << hexfloat(track.Vertex().X())
899  << ";" << hexfloat(track.Vertex().Y())
900  << ";" << hexfloat(track.Vertex().Z())
901  << ") to (" << hexfloat(track.End().X())
902  << ";" << hexfloat(track.End().Y())
903  << ";" << hexfloat(track.End().Z())
904  << ") (ID=" << track.ID() << ")";
905  } // ParticleDumper::DumpTrack()
906 
907  template <typename Stream>
908  void ParticleDumper::DumpTracks(
909  Stream&& out,
910  std::vector<recob::Track const*> const& myTracks,
911  std::string indentstr
912  ) const
913  {
914  if (myTracks.empty()) return;
915  out << "\n" << indentstr << " "
916  << myTracks.size() << " tracks:";
917  for (recob::Track const* track: myTracks) {
918  if (myTracks.size() > 1) out << "\n" << indentstr << " ";
919  DumpTrack(std::forward<Stream>(out), *track);
920  } // for tracks
921  } // ParticleDumper::DumpTracks()
922 
923  //----------------------------------------------------------------------------
924  template <typename Stream>
925  void ParticleDumper::DumpClusterSummary
926  (Stream&& out, recob::Cluster const& cluster) const
927  {
928  out << " " << cluster.NHits() << " hits on " << cluster.Plane()
929  << " (ID=" << cluster.ID() << ")";
930  } // ParticleDumper::DumpClusterSummary()
931 
932  template <typename Stream>
933  void ParticleDumper::DumpClusters(
934  Stream&& out,
935  std::vector<recob::Cluster const*> const& myClusters,
936  std::string indentstr
937  ) const
938  {
939  if (myClusters.empty()) return;
940  out << "\n" << indentstr << " "
941  << myClusters.size() << " clusters:";
942  for (recob::Cluster const* cluster: myClusters) {
943  if (myClusters.size() > 1) out << "\n" << indentstr << " ";
944  DumpClusterSummary(std::forward<Stream>(out), *cluster);
945  }
946  } // ParticleDumper::DumpClusters()
947 
948 
949  //----------------------------------------------------------------------------
950  //--- PFParticleGraphMaker
951  //---
952  template <typename Stream>
953  void PFParticleGraphMaker::MakeGraph
954  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
955  {
956 
957  WriteGraphHeader (std::forward<Stream>(out));
958  WriteParticleRelations(std::forward<Stream>(out), particles);
959  WriteGraphFooter (std::forward<Stream>(out));
960 
961  } // PFParticleGraphMaker::MakeGraph()
962 
963 
964  //----------------------------------------------------------------------------
965  template <typename Stream>
966  void PFParticleGraphMaker::WriteGraphHeader(Stream&& out) const {
967  out
968  << "\ndigraph {"
969  << "\n";
970  } // PFParticleGraphMaker::WriteGraphHeader()
971 
972 
973  template <typename Stream>
974  void PFParticleGraphMaker::WriteParticleNodes
975  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
976  {
977 
978  for (auto const& particle: particles) {
979 
980  std::ostringstream label;
981  label << "#" << particle.Self();
982  if (particle.PdgCode() != 0) {
983  label << ", ";
984  ParticleDumper::DumpPDGID(label, particle.PdgCode());
985  }
986 
987  out << "\n P" << particle.Self()
988  << " ["
989  << " label = \"" << label.str() << "\"";
990  if (particle.IsPrimary()) out << ", style = bold";
991  out << " ]";
992  } // for
993 
994  } // PFParticleGraphMaker::WriteParticleNodes()
995 
996 
997  template <typename Stream>
998  void PFParticleGraphMaker::WriteParticleEdges
999  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
1000  {
1001  auto particle_map = CreateMap(particles);
1002 
1003  out
1004  << "\n "
1005  << "\n // relations"
1006  << "\n // "
1007  << "\n // the arrow is close to the information provider,;"
1008  << "\n // and it points from parent to daughter"
1009  << "\n // "
1010  << "\n // "
1011  << "\n "
1012  ;
1013 
1014  for (auto const& particle: particles) {
1015  auto const partID = particle.Self();
1016 
1017  // draw parent line
1018  if (!particle.IsPrimary()) {
1019  auto const parentID = particle.Parent();
1020  size_t const iParent = particle_map[parentID];
1021  if (!particle_map.is_valid_value(iParent)) {
1022  // parent is ghost
1023  out
1024  << "\nP" << parentID
1025  << " [ style = dashed, color = red"
1026  << ", label = \"(#" << parentID << ")\" ] // ghost particle"
1027  << "\nP" << parentID << " -> P" << partID
1028  << " [ dir = both, arrowhead = empty, arrowtail = none ]";
1029  }
1030  else {
1031  // parent is a known particle
1032  recob::PFParticle const& parent = particles[iParent];
1033 
1034  // is the relation bidirectional?
1035  if (hasDaughter(parent, partID)) {
1036  out
1037  << "\nP" << parentID << " -> P" << partID
1038  << " [ dir = both, arrowtail = inv ]";
1039  } // if bidirectional
1040  else {
1041  out
1042  << "\nP" << parentID << " -> P" << partID
1043  << " [ dir = forward, color = red ]"
1044  << " // claimed parent";
1045  } // if parent disowns
1046 
1047  } // if ghost ... else
1048  } // if not primary
1049 
1050  // print daughter relationship only if daughters do not recognise us
1051  for (auto daughterID: particle.Daughters()) {
1052  size_t const iDaughter = particle_map[daughterID];
1053  if (!particle_map.is_valid_value(iDaughter)) {
1054  // daughter is ghost
1055  out
1056  << "\nP" << daughterID
1057  << " [ style = dashed, color = red"
1058  << ", label = \"(#" << daughterID << ")\" ] // ghost daughter"
1059  << "\nP" << partID << " -> P" << daughterID
1060  << " [ dir = both, arrowhead = none, arrowtail = invempty ]";
1061  }
1062  else {
1063  // daughter is a known particle
1064  recob::PFParticle const& daughter = particles[iDaughter];
1065 
1066  // is the relation bidirectional? (if so, the daughter will draw)
1067  if (daughter.Parent() != partID) {
1068  out
1069  << "\nP" << partID << " -> P" << daughterID
1070  << " [ dir = both, arrowhead = none, arrowtail = inv, color = orange ]"
1071  << " // claimed daughter";
1072  } // if parent disowns
1073 
1074  } // if ghost ... else
1075 
1076 
1077  } // for all daughters
1078 
1079  } // for all particles
1080 
1081  } // PFParticleGraphMaker::WriteParticleNodes()
1082 
1083 
1084  template <typename Stream>
1085  void PFParticleGraphMaker::WriteParticleRelations
1086  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
1087  {
1088  out << "\n // " << particles.size() << " particles (nodes)";
1089  WriteParticleNodes(std::forward<Stream>(out), particles);
1090 
1091  out
1092  << "\n"
1093  << "\n // parent/children relations";
1094  WriteParticleEdges(std::forward<Stream>(out), particles);
1095 
1096  } // PFParticleGraphMaker::WriteParticleRelations()
1097 
1098 
1099  template <typename Stream>
1100  void PFParticleGraphMaker::WriteGraphFooter(Stream&& out) const {
1101  out
1102  << "\n"
1103  << "\n} // digraph"
1104  << std::endl;
1105  } // PFParticleGraphMaker::WriteGraphFooter()
1106 
1107 
1108 
1109  //----------------------------------------------------------------------------
1110 
1111 } // local namespace
1112 
1113 
1114 
1115 namespace recob {
1116 
1117  //----------------------------------------------------------------------------
1119  : EDAnalyzer(config)
1120  , fInputTag(config().PFModuleLabel())
1121  , fOutputCategory(config().OutputCategory())
1122  , fPrintHexFloats(config().PrintHexFloats())
1123  , fMaxDepth(std::numeric_limits<unsigned int>::max())
1124  , fMakeEventGraphs(config().MakeParticleGraphs())
1125  {
1126  // here we are handling the optional configuration key as it had just a
1127  // default value
1128  if (!config().MaxDepth(fMaxDepth))
1129  fMaxDepth = std::numeric_limits<unsigned int>::max();
1130  }
1131 
1132 
1133  //----------------------------------------------------------------------------
1134  std::string DumpPFParticles::DotFileName
1135  (art::EventID const& evtID, art::Provenance const& prodInfo)
1136  {
1137  return prodInfo.processName()
1138  + '_' + prodInfo.moduleLabel()
1139  + '_' + prodInfo.productInstanceName()
1140  + "_Run" + std::to_string(evtID.run())
1141  + "_Subrun" + std::to_string(evtID.subRun())
1142  + "_Event" + std::to_string(evtID.event())
1143  + "_particles.dot";
1144  } // DumpPFParticles::DotFileName()
1145 
1146 
1147  //----------------------------------------------------------------------------
1149  art::Event const& event,
1150  art::ValidHandle<std::vector<recob::PFParticle>> const& handle
1151  ) const {
1152  art::EventID const eventID = event.id();
1153  std::string fileName = DotFileName(eventID, *(handle.provenance()));
1154  std::ofstream outFile(fileName); // overwrite by default
1155 
1156  outFile
1157  << "// " << fileName
1158  << "\n// "
1159  << "\n// Created for run " << eventID.run()
1160  << " subrun " << eventID.subRun()
1161  << " event " << eventID.event()
1162  << "\n// "
1163  << "\n// dump of " << handle->size() << " particles"
1164  << "\n// "
1165  << std::endl;
1166 
1167  PFParticleGraphMaker graphMaker;
1168  graphMaker.MakeGraph(outFile, *handle);
1169 
1170  } // DumpPFParticles::MakePFParticleGraph()
1171 
1172 
1173  //----------------------------------------------------------------------------
1174  void DumpPFParticles::analyze(const art::Event& evt) {
1175 
1176  //
1177  // collect all the available information
1178  //
1179  // fetch the data to be dumped on screen
1180  art::ValidHandle<std::vector<recob::PFParticle>> PFParticles
1181  = evt.getValidHandle<std::vector<recob::PFParticle>>(fInputTag);
1182 
1183  if (fMakeEventGraphs)
1184  MakePFParticleGraph(evt, PFParticles);
1185 
1186  art::FindOne<recob::Vertex> const ParticleVertices
1187  (PFParticles, evt, fInputTag);
1188  art::FindMany<recob::Track> const ParticleTracks
1189  (PFParticles, evt, fInputTag);
1190  art::FindMany<recob::Cluster> const ParticleClusters
1191  (PFParticles, evt, fInputTag);
1192  art::FindMany<recob::Seed> const ParticleSeeds
1193  (PFParticles, evt, fInputTag);
1194  art::FindMany<recob::SpacePoint> const ParticleSpacePoints
1195  (PFParticles, evt, fInputTag);
1196  art::FindMany<recob::PCAxis> const ParticlePCAxes
1197  (PFParticles, evt, fInputTag);
1198 
1199  size_t const nParticles = PFParticles->size();
1200  mf::LogVerbatim(fOutputCategory) << "Event " << evt.id()
1201  << " contains " << nParticles << " particles from '"
1202  << fInputTag.encode() << "'";
1203 
1204  // prepare the dumper
1205  ParticleDumper::PrintOptions_t options;
1206  options.hexFloats = fPrintHexFloats;
1207  options.maxDepth = fMaxDepth;
1208  options.streamName = fOutputCategory;
1209  ParticleDumper dumper(*PFParticles, options);
1210  if (ParticleVertices.isValid()) dumper.SetVertices(&ParticleVertices);
1211  else mf::LogPrint("DumpPFParticles") << "WARNING: vertex information not available";
1212  if (ParticleTracks.isValid()) dumper.SetTracks(&ParticleTracks);
1213  else mf::LogPrint("DumpPFParticles") << "WARNING: track information not available";
1214  if (ParticleClusters.isValid()) dumper.SetClusters(&ParticleClusters);
1215  else mf::LogPrint("DumpPFParticles") << "WARNING: cluster information not available";
1216  if (ParticleSeeds.isValid()) dumper.SetSeeds(&ParticleSeeds);
1217  else mf::LogPrint("DumpPFParticles") << "WARNING: seed information not avaialble";
1218  if (ParticleSpacePoints.isValid())
1219  dumper.SetSpacePoints(&ParticleSpacePoints);
1220  else {
1221  mf::LogPrint("DumpPFParticles")
1222  << "WARNING: space point information not available";
1223  }
1224  if (ParticlePCAxes.isValid())
1225  dumper.SetPCAxes(&ParticlePCAxes);
1226  else {
1227  mf::LogPrint("DumpPFParticles")
1228  << "WARNING: principal component axis not available";
1229  }
1230  dumper.DumpAllParticles(" ");
1231 
1232  mf::LogVerbatim(fOutputCategory) << "\n"; // two empty lines
1233 
1234  } // DumpPFParticles::analyze()
1235 
1236  DEFINE_ART_MODULE(DumpPFParticles)
1237 
1238 } // namespace recob
process_name vertex
Definition: cheaterreco.fcl:51
void XYZ(double *xyz) const
Legacy method to access vertex position, preserved to avoid breaking code. Please try to use Vertex::...
Definition: Vertex.cxx:36
DumpPFParticles(Parameters const &config)
Default constructor.
const std::vector< size_t > & Daughters() const
Returns the collection of daughter particles.
Definition: PFParticle.h:114
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
const EigenVectors & getEigenVectors() const
Definition: PCAxis.h:66
art::EDAnalyzer::Table< Config > Parameters
int NumDaughters() const
Returns the number of daughter particles flowing from this one.
Definition: PFParticle.h:89
ClusterModuleLabel join with tracks
size_t Self() const
Returns the index of this particle.
Definition: PFParticle.h:92
process_name cluster
Definition: cheaterreco.fcl:51
bool IsValid() const
Definition: Seed.cxx:70
void GetPoint(double *Pt, double *Err) const
Definition: Seed.cxx:108
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
int PdgCode() const
Return the type of particle as a PDG ID.
Definition: PFParticle.h:83
auto cbegin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:567
Set of hits with a 2D structure.
Definition: Cluster.h:71
process_name use argoneut_mc_hitfinder track
auto DumpSpacePoint(Stream &&out, recob::SpacePoint const &sp, SpacePointPrintOptions_t const &options={}) -> std::enable_if_t< std::is_same< NewLine< std::decay_t< Stream >>, std::decay_t< NewLineRef >>::value >
Dumps the content of the specified space point into a stream.
geo::PlaneID Plane() const
Returns the plane ID this cluster lies on.
Definition: Cluster.h:744
bool IsPrimary(const caf::SRTrueParticleProxy &p)
Whether this is a primary particle or generated by a secondary interaction.
fhicl::OptionalAtom< unsigned int > MaxDepth
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
TString outFile
auto cend(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:579
size_t getID() const
Definition: PCAxis.h:69
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
double Length(size_t p=0) const
Access to various track properties.
std::string fOutputCategory
category for LogInfo output
unsigned int fMaxDepth
maximum generation to print (0: only primaries)
size_t Parent() const
Definition: PFParticle.h:96
Point_t const & Vertex() const
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
unsigned int seed
BEGIN_PROLOG vertical distance to the surface Name
bool IsPrimary() const
Returns whether the particle is the root of the flow.
Definition: PFParticle.h:86
const Double32_t * XYZ() const
Definition: SpacePoint.h:76
virtual void analyze(const art::Event &evt) override
Does the printing.
Declaration of cluster object.
Helper for formatting floats in base 16.
Definition: hexfloat.h:105
Provides recob::Track data product.
tuple dir
Definition: dropbox.py:28
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:14
ID_t ID() const
Definition: SpacePoint.h:75
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
Helper to support output of real numbers in base 16.
ID_t ID() const
Identifier of this cluster.
Definition: Cluster.h:738
std::string to_string(WindowPattern const &pattern)
bool fMakeEventGraphs
whether to create one DOT file per event
Prints the content of all the ParticleFlow particles on screen.
int ID() const
Return vertex id.
Definition: Vertex.h:99
fhicl::Atom< std::string > OutputCategory
then echo echo For and will not be changed by echo further linking echo echo B echo The symbol is in the uninitialized data multiple common symbols may appear with the echo same name If the symbol is defined the common echo symbols are treated as undefined references For more echo details on common see the discussion of warn common echo in *Note Linker options
static std::string DotFileName(art::EventID const &evtID, art::Provenance const &prodInfo)
Point_t const & End() const
void MakePFParticleGraph(art::Event const &event, art::ValidHandle< std::vector< recob::PFParticle >> const &handle) const
art::InputTag fInputTag
input tag of the PFParticle product
unsigned int NHits() const
Number of hits in the cluster.
Definition: Cluster.h:275
TCEvent evt
Definition: DataStructs.cxx:8
std::size_t count(Cont const &cont)
fhicl::Atom< art::InputTag > PFModuleLabel
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
void GetDirection(double *Dir, double *Err) const
Definition: Seed.cxx:98
bool fPrintHexFloats
whether to print floats in base 16
Track from a non-cascading particle.A recob::Track consists of a recob::TrackTrajectory, plus additional members relevant for a &quot;fitted&quot; track:
bnb BNB Stream
double GetLength() const
Definition: Seed.cxx:155
bool getSvdOK() const
Definition: PCAxis.h:63