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
18 
19 // support libraries
20 #include "fhiclcpp/types/Atom.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  */
132  public:
133 
134  struct Config {
135  using Name = fhicl::Name;
137 
139  Name("PFModuleLabel"),
140  Comment("label of producer of the recob::PFParticle to be dumped")
141  };
142 
144  Name("OutputCategory"),
145  Comment("message facility category used for output (for filtering)"),
146  "DumpPFParticles"
147  };
148 
150  Name("PrintHexFloats"),
151  Comment("print all the floating point numbers in base 16"),
152  false
153  };
154 
156  Name("MaxDepth"),
157  Comment("at most this number of particle generations will be printed")
158  };
159 
161  Name("MakeParticleGraphs"),
162  Comment("creates a DOT file with particle information for each event"),
163  false
164  };
165 
166  }; // struct Config
167 
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
212 #include "canvas/Persistency/Common/FindOne.h"
213 #include "canvas/Persistency/Common/FindMany.h"
215 
216 // support libraries
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,
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) {
611  (std::forward<Stream>(out), spacepoints->at(iPart), indentstr);
612  }
613 
614  //
615  // clusters
616  //
617  if (clusters) {
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,
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))
1130  }
1131 
1132 
1133  //----------------------------------------------------------------------------
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  //----------------------------------------------------------------------------
1175 
1176  //
1177  // collect all the available information
1178  //
1179  // fetch the data to be dumped on screen
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 
1237 
1238 } // namespace recob
end
while True: pbar.update(maxval-len(onlies[E][S])) #print iS, "/", len(onlies[E][S]) found = False for...
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
intermediate_table::iterator iterator
DumpPFParticles(Parameters const &config)
Default constructor.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
const std::vector< size_t > & Daughters() const
Returns the collection of daughter particles.
Definition: PFParticle.h:114
const EigenVectors & getEigenVectors() const
Definition: PCAxis.h:66
int NumDaughters() const
Returns the number of daughter particles flowing from this one.
Definition: PFParticle.h:89
decltype(auto) constexpr cend(T &&obj)
ADL-aware version of std::cend.
Definition: StdUtils.h:87
Reconstruction base classes.
size_t Self() const
Returns the index of this particle.
Definition: PFParticle.h:92
bool IsValid() const
Definition: Seed.cxx:70
std::string string
Definition: nybbler.cc:12
unsigned int ID
std::string const & productInstanceName() const noexcept
Definition: Provenance.cc:60
void GetPoint(double *Pt, double *Err) const
Definition: Seed.cxx:108
Prints the content of all the space points on screen.
struct vector vector
ChannelGroupService::Name Name
STL namespace.
Prints the content of all the clusters on screen.
int PdgCode() const
Return the type of particle as a PDG ID.
Definition: PFParticle.h:83
Set of hits with a 2D structure.
Definition: Cluster.h:71
intermediate_table::const_iterator const_iterator
TFile * outFile
Definition: makeDST.cxx:36
geo::PlaneID Plane() const
Returns the plane ID this cluster lies on.
Definition: Cluster.h:744
string dir
Cluster finding and building.
Prints the content of all the seeds on screen.
EDAnalyzer(fhicl::ParameterSet const &pset)
Definition: EDAnalyzer.h:25
void resize(Vector< T > &vec1, Index n1, const V &val)
fhicl::OptionalAtom< unsigned int > MaxDepth
std::string encode() const
Definition: InputTag.cc:97
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
bool isValid() const
Definition: maybe_ref.h:60
RunNumber_t run() const
Definition: EventID.h:98
std::string const & moduleLabel() const noexcept
Definition: Provenance.cc:54
size_t getID() const
Definition: PCAxis.h:69
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
double Length(size_t p=0) const
Access to various track properties.
Definition: Track.h:167
std::string fOutputCategory
category for LogInfo output
constexpr bool is_valid(IDNumber_t< L > const id) noexcept
Definition: IDNumber.h:113
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
fileName
Definition: dumpTree.py:9
unsigned int fMaxDepth
maximum generation to print (0: only primaries)
void swap(Handle< T > &a, Handle< T > &b)
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.
size_t Parent() const
Definition: PFParticle.h:96
static Config * config
Definition: config.cpp:1054
Point_t const & Vertex() const
Definition: Track.h:124
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
Definition: DataViewImpl.h:441
gen
Definition: demo.py:24
bool IsPrimary() const
Returns whether the particle is the root of the flow.
Definition: PFParticle.h:86
Prints the content of all the PCA axis object on screen.
virtual void analyze(const art::Event &evt) override
Does the printing.
std::string const & processName() const noexcept
Definition: Provenance.cc:66
static int max(int a, int b)
Helper for formatting floats in base 16.
Definition: hexfloat.h:105
Definition: tracks.py:1
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:13
int ID() const
Definition: Track.h:198
Prints the content of all the tracks on screen.
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
#define Comment
Helper to support output of real numbers in base 16.
ID_t ID() const
Identifier of this cluster.
Definition: Cluster.h:738
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
Provides recob::Track data product.
decltype(auto) constexpr cbegin(T &&obj)
ADL-aware version of std::cbegin.
Definition: StdUtils.h:82
const Double32_t * XYZ() const
Definition: SpacePoint.h:76
static std::string DotFileName(art::EventID const &evtID, art::Provenance const &prodInfo)
EventNumber_t event() const
Definition: EventID.h:116
Point_t const & End() const
Definition: Track.h:125
void MakePFParticleGraph(art::Event const &event, art::ValidHandle< std::vector< recob::PFParticle >> const &handle) const
art::InputTag fInputTag
input tag of the PFParticle product
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:72
ID_t ID() const
Definition: SpacePoint.h:75
unsigned int NHits() const
Number of hits in the cluster.
Definition: Cluster.h:275
TCEvent evt
Definition: DataStructs.cxx:7
fhicl::Atom< art::InputTag > PFModuleLabel
void GetDirection(double *Dir, double *Err) const
Definition: Seed.cxx:98
std::string to_string(ModuleType const mt)
Definition: ModuleType.h:34
bool fPrintHexFloats
whether to print floats in base 16
SubRunNumber_t subRun() const
Definition: EventID.h:110
def parent(G, child, parent_type)
Definition: graph.py:67
EventID id() const
Definition: Event.cc:34
Track from a non-cascading particle.A recob::Track consists of a recob::TrackTrajectory, plus additional members relevant for a "fitted" track:
Definition: Track.h:49
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
MaybeLogger_< ELseverityLevel::ELsev_warning, true > LogPrint
QTextStream & endl(QTextStream &s)
Event finding and building.
double GetLength() const
Definition: Seed.cxx:155
bool getSvdOK() const
Definition: PCAxis.h:63
vertex reconstruction