LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
DumpPFParticles_module.cc
Go to the documentation of this file.
1 
8 // LArSoft includes
11 
12 // art libraries
18 
19 // support libraries
20 #include "fhiclcpp/ParameterSet.h"
21 #include "fhiclcpp/types/Atom.h"
23 
24 // C//C++ standard libraries
25 #include <string>
26 
27 // ... and more in the implementation part
28 
29 namespace recob {
30 
133  public:
134 
135  struct Config {
136  using Name = fhicl::Name;
138 
140  Name("PFModuleLabel"),
141  Comment("label of producer of the recob::PFParticle to be dumped")
142  };
143 
145  Name("OutputCategory"),
146  Comment("message facility category used for output (for filtering)"),
147  "DumpPFParticles"
148  };
149 
151  Name("PrintHexFloats"),
152  Comment("print all the floating point numbers in base 16"),
153  false
154  };
155 
157  Name("MaxDepth"),
158  Comment("at most this number of particle generations will be printed")
159  };
160 
162  Name("MakeParticleGraphs"),
163  Comment("creates a DOT file with particle information for each event"),
164  false
165  };
166 
167  }; // struct Config
168 
170 
172  explicit DumpPFParticles(Parameters const& config);
173 
175  virtual void analyze (const art::Event& evt) override;
176 
177  private:
178 
180  std::string fOutputCategory;
182  unsigned int fMaxDepth;
184 
185 
186  static std::string DotFileName
187  (art::EventID const& evtID, art::Provenance const& prodInfo);
188 
189  void MakePFParticleGraph(
190  art::Event const& event,
191  art::ValidHandle<std::vector<recob::PFParticle>> const& handle
192  ) const;
193 
194  }; // class DumpPFParticles
195 
196 } // namespace recob
197 
198 
199 //==============================================================================
200 //=== Implementation section
201 //==============================================================================
202 // LArSoft includes
210 
211 // art libraries
216 
217 // support libraries
219 
220 // C//C++ standard libraries
221 #include <vector>
222 #include <fstream>
223 #include <utility> // std::swap()
224 #include <algorithm> // std::count(), std::find()
225 #include <limits> // std::numeric_limits<>
226 
227 
228 namespace {
233  template <typename T>
234  class int_map: private std::vector<T> {
235  using map_type = std::vector<T>;
236  public:
237  using this_type = int_map<T>;
238 
239  using typename map_type::value_type;
240  using typename map_type::reference;
241  using typename map_type::const_reference;
242  using typename map_type::iterator;
243  using typename map_type::const_iterator;
244 
245  using typename map_type::size_type;
246 
247 
249  int_map() = default;
250 
252  int_map(value_type invalid_value): invalid(invalid_value) {}
253 
254 
256  void resize(size_t new_size)
257  { get_map().resize(new_size, invalid_value()); }
258 
260  reference operator[] (size_t pos)
261  { if (pos >= size()) resize(pos + 1); return get_map()[pos]; }
262 
264  const_reference operator[] (size_t pos) const
265  { return (pos >= size())? invalid: get_map()[pos]; }
266 
267  using map_type::size;
268  using map_type::empty;
269 
270  using map_type::begin;
271  using map_type::end;
272  using map_type::cbegin;
273  using map_type::cend;
274  using map_type::rbegin;
275  using map_type::rend;
276  using map_type::crbegin;
277  using map_type::crend;
278 
280  void swap(this_type& data)
281  { swap(data.get_map()); std::swap(data.invalid, invalid); }
282 
284  void swap(std::vector<T>& data) { map_type::swap(data); }
285 
286  // non-standard calls follow
288  size_type n_invalid() const
289  { return std::count(begin(), end(), invalid); }
290 
292  size_type n_valid() const { return size() - n_invalid(); }
293 
295  bool is_valid(size_type pos) const
296  { return (pos < size())? is_valid_value(get_map()[pos]): false; }
297 
299  bool is_valid_value(value_type const& v) const { return v != invalid; }
300 
302  value_type const& invalid_value() const { return invalid; }
303 
304 
305  private:
306  value_type const invalid;
307 
308  map_type& get_map() { return static_cast<map_type&>(*this); }
309  map_type const& get_map() const
310  { return static_cast<map_type const&>(*this); }
311 
312  }; // int_map<>
313 
314 
315  //----------------------------------------------------------------------------
316  int_map<size_t> CreateMap(std::vector<recob::PFParticle> const& particles) {
317 
318  int_map<size_t> pmap(std::numeric_limits<size_t>::max());
319  pmap.resize(particles.size());
320 
321  size_t const nParticles = particles.size();
322  for (size_t iPart = 0; iPart < nParticles; ++iPart)
323  pmap[particles[iPart].Self()] = iPart;
324 
325  return pmap;
326  } // CreateMap()
327 
328 
329  //----------------------------------------------------------------------------
330  bool hasDaughter(recob::PFParticle const& particle, size_t partID) {
331  auto const& daughters = particle.Daughters();
332  return daughters.end()
333  != std::find(daughters.begin(), daughters.end(), partID);
334  } // hasDaughter()
335 
336 
337  //----------------------------------------------------------------------------
338  class ParticleDumper {
339  public:
340 
342  struct PrintOptions_t {
343  bool hexFloats = false;
344  unsigned int maxDepth = std::numeric_limits<unsigned int>::max();
347  std::string streamName;
348  }; // PrintOptions_t
349 
350 
352  ParticleDumper(std::vector<recob::PFParticle> const& particle_list)
353  : ParticleDumper(particle_list, {})
354  {}
355 
357  ParticleDumper(
358  std::vector<recob::PFParticle> const& particle_list,
359  PrintOptions_t print_options
360  )
361  : particles(particle_list)
362  , options(print_options)
363  , visited(particles.size(), 0U)
364  , particle_map (CreateMap(particles))
365  {}
366 
367 
369  void SetVertices(art::FindOne<recob::Vertex> const* vtx_query)
370  { vertices = vtx_query; }
371 
373  void SetTracks(art::FindMany<recob::Track> const* trk_query)
374  { tracks = trk_query; }
375 
377  void SetClusters(art::FindMany<recob::Cluster> const* cls_query)
378  { clusters = cls_query; }
379 
381  void SetSeeds(art::FindMany<recob::Seed> const* seed_query)
382  { seeds = seed_query; }
383 
385  void SetSpacePoints(art::FindMany<recob::SpacePoint> const* sp_query)
386  { spacepoints = sp_query; }
387 
389  void SetPCAxes(art::FindMany<recob::PCAxis> const* pca_query)
390  { pcaxes = pca_query; }
391 
392 
395  template <typename Stream>
396  void DumpParticle(
397  Stream&& out, size_t iPart, std::string indentstr = "",
398  unsigned int gen = 0
399  ) const;
400 
401 
404  template <typename Stream>
405  void DumpParticleWithID(
406  Stream&& out, size_t pID, std::string indentstr = "",
407  unsigned int gen = 0
408  ) const;
409 
410 
412  void DumpAllPrimaries(std::string indentstr = "") const;
413 
414 
416  void DumpAllParticles(std::string indentstr = "") const;
417 
418 
419  template <typename Stream>
420  static void DumpPDGID(Stream&& out, int ID);
421 
422 
423  protected:
424  std::vector<recob::PFParticle> const& particles;
425 
426  PrintOptions_t options;
427 
429  art::FindOne<recob::Vertex> const* vertices = nullptr;
430 
432  art::FindMany<recob::Track> const* tracks = nullptr;
433 
435  art::FindMany<recob::Cluster> const* clusters = nullptr;
436 
438  art::FindMany<recob::Seed> const* seeds = nullptr;
439 
441  art::FindMany<recob::SpacePoint> const* spacepoints = nullptr;
442 
444  art::FindMany<recob::PCAxis> const* pcaxes = nullptr;
445 
447  mutable std::vector<unsigned int> visited;
448 
449  int_map<size_t> const particle_map;
450 
451 
452  template <typename Stream>
453  void DumpPFParticleInfo(
454  Stream&& out,
455  recob::PFParticle const& part,
456  unsigned int iPart,
457  std::string indentstr
458  ) const;
459 
460  template <typename Stream>
461  void DumpVertex(
462  Stream&& out,
463  cet::maybe_ref<recob::Vertex const> VertexRef,
464  std::string indentstr
465  ) const;
466 
467  template <typename Stream>
468  void DumpPCAxisDirection
469  (Stream&& out, recob::PCAxis const& axis) const;
470 
471  template <typename Stream>
472  void DumpPCAxes(
473  Stream&& out,
474  std::vector<recob::PCAxis const*> const& myAxes,
475  std::string indentstr
476  ) const;
477 
478  template <typename Stream>
479  void DumpTrack(Stream&& out, recob::Track const& track) const;
480 
481  template <typename Stream>
482  void DumpTracks(
483  Stream&& out,
484  std::vector<recob::Track const*> const& myTracks,
485  std::string indentstr
486  ) const;
487 
488  template <typename Stream>
489  void DumpSeed
490  (Stream&& out, recob::Seed const& seed, std::string indentstr) const;
491 
492  template <typename Stream>
493  void DumpSeeds(
494  Stream&& out,
495  std::vector<recob::Seed const*> const& mySeeds,
496  std::string indentstr
497  ) const;
498 
499  template <typename Stream>
500  void DumpSpacePoint(Stream&& out, recob::SpacePoint const& sp) const;
501 
502  template <typename Stream>
503  void DumpSpacePoints(
504  Stream&& out,
505  std::vector<recob::SpacePoint const*> const& mySpacePoints,
506  std::string indentstr
507  ) const;
508 
509  template <typename Stream>
510  void DumpClusterSummary(Stream&& out, recob::Cluster const& track) const;
511 
512  template <typename Stream>
513  void DumpClusters(
514  Stream&& out,
515  std::vector<recob::Cluster const*> const& myClusters,
516  std::string indentstr
517  ) const;
518 
519  }; // class ParticleDumper
520 
521 
522  //----------------------------------------------------------------------------
523  class PFParticleGraphMaker {
524  public:
525 
526  PFParticleGraphMaker() = default;
527 
528  template <typename Stream>
529  void MakeGraph
530  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
531 
532  template <typename Stream>
533  void WriteGraphHeader(Stream&& out) const;
534 
535  template <typename Stream>
536  void WriteParticleRelations
537  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
538 
539  template <typename Stream>
540  void WriteParticleNodes
541  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
542 
543  template <typename Stream>
544  void WriteParticleEdges
545  (Stream&& out, std::vector<recob::PFParticle> const& particles) const;
546 
547  template <typename Stream>
548  void WriteGraphFooter(Stream&& out) const;
549 
550  }; // class PFParticleGraphMaker
551 
552 
553  //----------------------------------------------------------------------------
554  //--- ParticleDumper implementation
555  //---
556  template <typename Stream>
557  void ParticleDumper::DumpParticle(
558  Stream&& out, size_t iPart, std::string indentstr /* = "" */,
559  unsigned int gen /* = 0 */
560  ) const
561  {
562  lar::OptionalHexFloat hexfloat(options.hexFloats);
563 
564  recob::PFParticle const& part = particles.at(iPart);
565  ++visited[iPart];
566 
567  if (visited[iPart] > 1) {
568  out << indentstr << "particle " << part.Self()
569  << " already printed!!!";
570  return;
571  }
572 
573  //
574  // intro
575  //
576  DumpPFParticleInfo(std::forward<Stream>(out), part, iPart, indentstr);
577 
578  //
579  // vertex information
580  //
581  if (vertices)
582  DumpVertex(std::forward<Stream>(out), vertices->at(iPart), indentstr);
583 
584  // daughters tag
585  if (part.NumDaughters() > 0)
586  out << " with " << part.NumDaughters() << " daughters";
587  else
588  out << " with no daughter";
589 
590  //
591  // axis
592  //
593  if (pcaxes)
594  DumpPCAxes(std::forward<Stream>(out), pcaxes->at(iPart), indentstr);
595 
596  //
597  // tracks
598  //
599  if (tracks)
600  DumpTracks(std::forward<Stream>(out), tracks->at(iPart), indentstr);
601 
602  //
603  // seeds
604  //
605  if (seeds)
606  DumpSeeds(std::forward<Stream>(out), seeds->at(iPart), indentstr);
607 
608  //
609  // space points
610  //
611  if (spacepoints) {
613  (std::forward<Stream>(out), spacepoints->at(iPart), indentstr);
614  }
615 
616  //
617  // clusters
618  //
619  if (clusters) {
621  (std::forward<Stream>(out), clusters->at(iPart), indentstr);
622  }
623 
624  //
625  // daughters
626  //
627  auto const PartID = part.Self();
628  if (part.NumDaughters() > 0) {
629  std::vector<size_t> const& daughters = part.Daughters();
630  out << "\n" << indentstr
631  << " " << part.NumDaughters() << " particle daughters";
632  if (gen > 0) {
633  out << ":";
634  for (size_t DaughterID: daughters) {
635  if (DaughterID == PartID) {
636  out << "\n" << indentstr
637  << " oh, just great: this particle is its own daughter!";
638  }
639  else {
640  out << '\n';
641  DumpParticleWithID(out, DaughterID, indentstr + " ", gen - 1);
642  }
643  }
644  } // if descending
645  else {
646  out << " (further descend suppressed)";
647  }
648  } // if has daughters
649 
650  //
651  // warnings
652  //
653  if (visited[iPart] == 2) {
654  out << "\n" << indentstr << " WARNING: particle ID=" << PartID
655  << " connected more than once!";
656  }
657 
658  //
659  // done
660  //
661 
662  } // ParticleDumper::DumpParticle()
663 
664 
665  //----------------------------------------------------------------------------
666  template <typename Stream>
667  void ParticleDumper::DumpParticleWithID(
668  Stream&& out, size_t pID, std::string indentstr /* = "" */,
669  unsigned int gen /* = 0 */
670  ) const {
671  size_t const pos = particle_map[pID];
672  if (particle_map.is_valid_value(pos)) {
673  DumpParticle(out, pos, indentstr, gen);
674  }
675  else {
676  out /* << "\n" */ << indentstr << "<ID=" << pID << " not found>";
677  }
678  } // ParticleDumper::DumpParticleWithID()
679 
680 
681  //----------------------------------------------------------------------------
682  void ParticleDumper::DumpAllPrimaries(std::string indentstr /* = "" */) const
683  {
684  indentstr += " ";
685  size_t const nParticles = particles.size();
686  unsigned int nPrimaries = 0;
687  for (size_t iPart = 0; iPart < nParticles; ++iPart) {
688  if (!particles[iPart].IsPrimary()) continue;
689  DumpParticle(
690  mf::LogVerbatim(options.streamName),
691  iPart, indentstr, options.maxDepth
692  );
693  } // for
694  if (nPrimaries == 0) {
695  mf::LogVerbatim(options.streamName)
696  << indentstr << "No primary particle found";
697  }
698  } // ParticleDumper::DumpAllPrimaries()
699 
700 
701  //----------------------------------------------------------------------------
702  void ParticleDumper::DumpAllParticles(std::string indentstr /* = "" */) const
703  {
704  // first print all the primary particles
705  DumpAllPrimaries(indentstr);
706  // then find out if there are any that are "disconnected"
707  unsigned int const nDisconnected
708  = std::count(visited.begin(), visited.end(), 0U);
709  if (nDisconnected) {
710  mf::LogVerbatim(options.streamName) << indentstr
711  << nDisconnected << " particles not coming from primary ones:";
712  size_t const nParticles = visited.size();
713  for (size_t iPart = 0; iPart < nParticles; ++iPart) {
714  if (visited[iPart] > 0) continue;
715  DumpParticle(
716  mf::LogVerbatim(options.streamName), iPart, indentstr + " ",
717  options.maxDepth
718  );
719  } // for unvisited particles
720  mf::LogVerbatim(options.streamName) << indentstr
721  << "(end of " << nDisconnected << " particles not from primaries)";
722  } // if there are disconnected particles
723  // TODO finally, note if there are multiply-connected particles
724 
725  } // ParticleDumper::DumpAllParticles()
726 
727 
728  //----------------------------------------------------------------------------
729  template <typename Stream>
730  void ParticleDumper::DumpPDGID(Stream&& out, int ID) {
731  switch (ID) {
732  case -11: out << "e+"; break;
733  case 11: out << "e-"; break;
734  case -13: out << "mu+"; break;
735  case 13: out << "mu-"; break;
736  default: out << "MCID=" << ID; break;
737  } // switch
738  } // DumpPDGID()
739 
740 
741  //----------------------------------------------------------------------------
742  template <typename Stream>
743  void ParticleDumper::DumpPFParticleInfo(
744  Stream&& out,
745  recob::PFParticle const& part,
746  unsigned int iPart,
747  std::string indentstr
748  ) const
749  {
750  auto const PartID = part.Self();
751  out << indentstr << "ID=" << PartID;
752  if (iPart != PartID) out << " [#" << iPart << "]";
753  out << " (type: ";
754  DumpPDGID(out, part.PdgCode());
755  out << ")";
756  if (part.IsPrimary()) out << " (primary)";
757  else out << " from ID=" << part.Parent();
758  } // ParticleDumper::DumpPFParticleInfo()
759 
760  //----------------------------------------------------------------------------
761  template <typename Stream>
762  void ParticleDumper::DumpVertex(
763  Stream&& out,
764  cet::maybe_ref<recob::Vertex const> VertexRef,
765  std::string indentstr
766  ) const
767  {
768  if (!VertexRef.isValid()) {
769  out << " [no vertex found!]";
770  return;
771  }
772  recob::Vertex const& vertex = VertexRef.ref();
773  std::array<double, 3> vtx_pos;
774  vertex.XYZ(vtx_pos.data());
775  lar::OptionalHexFloat hexfloat(options.hexFloats);
776  out << " [decay at ("
777  << hexfloat(vtx_pos[0]) << "," << hexfloat(vtx_pos[1])
778  << "," << hexfloat(vtx_pos[2])
779  << "), ID=" << vertex.ID() << "]";
780 
781  } // ParticleDumper::DumpVertex()
782 
783  //----------------------------------------------------------------------------
784  template <typename Stream>
785  void ParticleDumper::DumpPCAxisDirection
786  (Stream&& out, recob::PCAxis const& axis) const
787  {
788  if (!axis.getSvdOK()) {
789  out << "axis is invalid";
790  return;
791  }
792  lar::OptionalHexFloat hexfloat(options.hexFloats);
793  out << "axis ID=" << axis.getID() << ", principal: ("
794  << hexfloat(axis.getEigenVectors()[0][0])
795  << ", " << hexfloat(axis.getEigenVectors()[0][1])
796  << ", " << hexfloat(axis.getEigenVectors()[0][2]) << ")";
797  } // ParticleDumper::DumpPCAxisDirection()
798 
799  template <typename Stream>
800  void ParticleDumper::DumpPCAxes(
801  Stream&& out,
802  std::vector<recob::PCAxis const*> const& myAxes,
803  std::string indentstr
804  ) const
805  {
806  out << "\n" << indentstr;
807  if (myAxes.empty()) {
808  out << " [no axis found!]";
809  return;
810  }
811  if (myAxes.size() == 1) {
812  DumpPCAxisDirection(std::forward<Stream>(out), *(myAxes.front()));
813  }
814  else {
815  out << " " << myAxes.size() << " axes present:";
816  for (recob::PCAxis const* axis: myAxes) {
817  out << "\n" << indentstr << " ";
818  DumpPCAxisDirection(std::forward<Stream>(out), *axis);
819  } // for axes
820  } // else
821  } // ParticleDumper::DumpPCAxes()
822 
823  //----------------------------------------------------------------------------
824  template <typename Stream>
825  void ParticleDumper::DumpSeed
826  (Stream&& out, recob::Seed const& seed, std::string indentstr) const
827  {
828  if (!seed.IsValid()) {
829  out << " <invalid>";
830  return;
831  }
832  std::array<double, 3> start, dir;
833  seed.GetDirection(dir.data(), nullptr);
834  seed.GetPoint(start.data(), nullptr);
835  lar::OptionalHexFloat hexfloat(options.hexFloats);
836  out << "\n" << indentstr
837  << " (" << hexfloat(start[0]) << "," << hexfloat(start[1])
838  << "," << hexfloat(start[2])
839  << ")=>(" << hexfloat(dir[0]) << "," << hexfloat(dir[1])
840  << "," << hexfloat(dir[2])
841  << "), " << hexfloat(seed.GetLength()) << " cm"
842  ;
843  } // ParticleDumper::DumpSeed()
844 
845  template <typename Stream>
846  void ParticleDumper::DumpSeeds(
847  Stream&& out,
848  std::vector<recob::Seed const*> const& mySeeds,
849  std::string indentstr
850  ) const
851  {
852  if (mySeeds.empty()) return;
853  out << "\n" << indentstr << " "
854  << mySeeds.size() << " seeds:";
855  for (recob::Seed const* seed: mySeeds)
856  DumpSeed(std::forward<Stream>(out), *seed, indentstr);
857  } // ParticleDumper::DumpSeeds()
858 
859  //----------------------------------------------------------------------------
860  template <typename Stream>
862  (Stream&& out, recob::SpacePoint const& sp) const
863  {
864  const double* pos = sp.XYZ();
865  lar::OptionalHexFloat hexfloat(options.hexFloats);
866  out << " ID=" << sp.ID()
867  << " at (" << hexfloat(pos[0]) << "," << hexfloat(pos[1])
868  << "," << hexfloat(pos[2]) << ") cm"
869  ;
870  } // ParticleDumper::DumpSpacePoints()
871 
872  template <typename Stream>
873  void ParticleDumper::DumpSpacePoints(
874  Stream&& out,
875  std::vector<recob::SpacePoint const*> const& mySpacePoints,
876  std::string indentstr
877  ) const
878  {
879  out << "\n" << indentstr << " ";
880  if (mySpacePoints.empty()) {
881  out << "no space points";
882  return;
883  }
884  constexpr unsigned int PointsPerLine = 2;
885  out << mySpacePoints.size() << " space points:";
886  for (size_t iSP = 0; iSP < mySpacePoints.size(); ++iSP) {
887  if ((iSP % PointsPerLine) == 0)
888  out << "\n" << indentstr << " ";
889 
890  DumpSpacePoint(std::forward<Stream>(out), *(mySpacePoints[iSP]));
891  } // for points
892  } // ParticleDumper::DumpSpacePoints()
893 
894  //----------------------------------------------------------------------------
895  template <typename Stream>
896  void ParticleDumper::DumpTrack(Stream&& out, recob::Track const& track) const
897  {
898  lar::OptionalHexFloat hexfloat(options.hexFloats);
899  out << " length " << hexfloat(track.Length())
900  << "cm from (" << hexfloat(track.Vertex().X())
901  << ";" << hexfloat(track.Vertex().Y())
902  << ";" << hexfloat(track.Vertex().Z())
903  << ") to (" << hexfloat(track.End().X())
904  << ";" << hexfloat(track.End().Y())
905  << ";" << hexfloat(track.End().Z())
906  << ") (ID=" << track.ID() << ")";
907  } // ParticleDumper::DumpTrack()
908 
909  template <typename Stream>
910  void ParticleDumper::DumpTracks(
911  Stream&& out,
912  std::vector<recob::Track const*> const& myTracks,
913  std::string indentstr
914  ) const
915  {
916  if (myTracks.empty()) return;
917  out << "\n" << indentstr << " "
918  << myTracks.size() << " tracks:";
919  for (recob::Track const* track: myTracks) {
920  if (myTracks.size() > 1) out << "\n" << indentstr << " ";
921  DumpTrack(std::forward<Stream>(out), *track);
922  } // for tracks
923  } // ParticleDumper::DumpTracks()
924 
925  //----------------------------------------------------------------------------
926  template <typename Stream>
927  void ParticleDumper::DumpClusterSummary
928  (Stream&& out, recob::Cluster const& cluster) const
929  {
930  out << " " << cluster.NHits() << " hits on " << cluster.Plane()
931  << " (ID=" << cluster.ID() << ")";
932  } // ParticleDumper::DumpClusterSummary()
933 
934  template <typename Stream>
935  void ParticleDumper::DumpClusters(
936  Stream&& out,
937  std::vector<recob::Cluster const*> const& myClusters,
938  std::string indentstr
939  ) const
940  {
941  if (myClusters.empty()) return;
942  out << "\n" << indentstr << " "
943  << myClusters.size() << " clusters:";
944  for (recob::Cluster const* cluster: myClusters) {
945  if (myClusters.size() > 1) out << "\n" << indentstr << " ";
946  DumpClusterSummary(std::forward<Stream>(out), *cluster);
947  }
948  } // ParticleDumper::DumpClusters()
949 
950 
951  //----------------------------------------------------------------------------
952  //--- PFParticleGraphMaker
953  //---
954  template <typename Stream>
955  void PFParticleGraphMaker::MakeGraph
956  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
957  {
958 
959  WriteGraphHeader (std::forward<Stream>(out));
960  WriteParticleRelations(std::forward<Stream>(out), particles);
961  WriteGraphFooter (std::forward<Stream>(out));
962 
963  } // PFParticleGraphMaker::MakeGraph()
964 
965 
966  //----------------------------------------------------------------------------
967  template <typename Stream>
968  void PFParticleGraphMaker::WriteGraphHeader(Stream&& out) const {
969  out
970  << "\ndigraph {"
971  << "\n";
972  } // PFParticleGraphMaker::WriteGraphHeader()
973 
974 
975  template <typename Stream>
976  void PFParticleGraphMaker::WriteParticleNodes
977  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
978  {
979 
980  for (auto const& particle: particles) {
981 
982  std::ostringstream label;
983  label << "#" << particle.Self();
984  if (particle.PdgCode() != 0) {
985  label << ", ";
986  ParticleDumper::DumpPDGID(label, particle.PdgCode());
987  }
988 
989  out << "\n P" << particle.Self()
990  << " ["
991  << " label = \"" << label.str() << "\"";
992  if (particle.IsPrimary()) out << ", style = bold";
993  out << " ]";
994  } // for
995 
996  } // PFParticleGraphMaker::WriteParticleNodes()
997 
998 
999  template <typename Stream>
1000  void PFParticleGraphMaker::WriteParticleEdges
1001  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
1002  {
1003  auto particle_map = CreateMap(particles);
1004 
1005  out
1006  << "\n "
1007  << "\n // relations"
1008  << "\n // "
1009  << "\n // the arrow is close to the information provider,;"
1010  << "\n // and it points from parent to daughter"
1011  << "\n // "
1012  << "\n // "
1013  << "\n "
1014  ;
1015 
1016  for (auto const& particle: particles) {
1017  auto const partID = particle.Self();
1018 
1019  // draw parent line
1020  if (!particle.IsPrimary()) {
1021  auto const parentID = particle.Parent();
1022  size_t const iParent = particle_map[parentID];
1023  if (!particle_map.is_valid_value(iParent)) {
1024  // parent is ghost
1025  out
1026  << "\nP" << parentID
1027  << " [ style = dashed, color = red"
1028  << ", label = \"(#" << parentID << ")\" ] // ghost particle"
1029  << "\nP" << parentID << " -> P" << partID
1030  << " [ dir = both, arrowhead = empty, arrowtail = none ]";
1031  }
1032  else {
1033  // parent is a known particle
1034  recob::PFParticle const& parent = particles[iParent];
1035 
1036  // is the relation bidirectional?
1037  if (hasDaughter(parent, partID)) {
1038  out
1039  << "\nP" << parentID << " -> P" << partID
1040  << " [ dir = both, arrowtail = inv ]";
1041  } // if bidirectional
1042  else {
1043  out
1044  << "\nP" << parentID << " -> P" << partID
1045  << " [ dir = forward, color = red ]"
1046  << " // claimed parent";
1047  } // if parent disowns
1048 
1049  } // if ghost ... else
1050  } // if not primary
1051 
1052  // print daughter relationship only if daughters do not recognise us
1053  for (auto daughterID: particle.Daughters()) {
1054  size_t const iDaughter = particle_map[daughterID];
1055  if (!particle_map.is_valid_value(iDaughter)) {
1056  // daughter is ghost
1057  out
1058  << "\nP" << daughterID
1059  << " [ style = dashed, color = red"
1060  << ", label = \"(#" << daughterID << ")\" ] // ghost daughter"
1061  << "\nP" << partID << " -> P" << daughterID
1062  << " [ dir = both, arrowhead = none, arrowtail = invempty ]";
1063  }
1064  else {
1065  // daughter is a known particle
1066  recob::PFParticle const& daughter = particles[iDaughter];
1067 
1068  // is the relation bidirectional? (if so, the daughter will draw)
1069  if (daughter.Parent() != partID) {
1070  out
1071  << "\nP" << partID << " -> P" << daughterID
1072  << " [ dir = both, arrowhead = none, arrowtail = inv, color = orange ]"
1073  << " // claimed daughter";
1074  } // if parent disowns
1075 
1076  } // if ghost ... else
1077 
1078 
1079  } // for all daughters
1080 
1081  } // for all particles
1082 
1083  } // PFParticleGraphMaker::WriteParticleNodes()
1084 
1085 
1086  template <typename Stream>
1087  void PFParticleGraphMaker::WriteParticleRelations
1088  (Stream&& out, std::vector<recob::PFParticle> const& particles) const
1089  {
1090  out << "\n // " << particles.size() << " particles (nodes)";
1091  WriteParticleNodes(std::forward<Stream>(out), particles);
1092 
1093  out
1094  << "\n"
1095  << "\n // parent/children relations";
1096  WriteParticleEdges(std::forward<Stream>(out), particles);
1097 
1098  } // PFParticleGraphMaker::WriteParticleRelations()
1099 
1100 
1101  template <typename Stream>
1102  void PFParticleGraphMaker::WriteGraphFooter(Stream&& out) const {
1103  out
1104  << "\n"
1105  << "\n} // digraph"
1106  << std::endl;
1107  } // PFParticleGraphMaker::WriteGraphFooter()
1108 
1109 
1110 
1111  //----------------------------------------------------------------------------
1112 
1113 } // local namespace
1114 
1115 
1116 
1117 namespace recob {
1118 
1119  //----------------------------------------------------------------------------
1121  : EDAnalyzer(config)
1122  , fInputTag(config().PFModuleLabel())
1123  , fOutputCategory(config().OutputCategory())
1124  , fPrintHexFloats(config().PrintHexFloats())
1125  , fMaxDepth(std::numeric_limits<unsigned int>::max())
1126  , fMakeEventGraphs(config().MakeParticleGraphs())
1127  {
1128  // here we are handling the optional configuration key as it had just a
1129  // default value
1130  if (!config().MaxDepth(fMaxDepth))
1132  }
1133 
1134 
1135  //----------------------------------------------------------------------------
1136  std::string DumpPFParticles::DotFileName
1137  (art::EventID const& evtID, art::Provenance const& prodInfo)
1138  {
1139  return prodInfo.processName()
1140  + '_' + prodInfo.moduleLabel()
1141  + '_' + prodInfo.productInstanceName()
1142  + "_Run" + std::to_string(evtID.run())
1143  + "_Subrun" + std::to_string(evtID.subRun())
1144  + "_Event" + std::to_string(evtID.event())
1145  + "_particles.dot";
1146  } // DumpPFParticles::DotFileName()
1147 
1148 
1149  //----------------------------------------------------------------------------
1151  art::Event const& event,
1152  art::ValidHandle<std::vector<recob::PFParticle>> const& handle
1153  ) const {
1154  art::EventID const eventID = event.id();
1155  std::string fileName = DotFileName(eventID, *(handle.provenance()));
1156  std::ofstream outFile(fileName); // overwrite by default
1157 
1158  outFile
1159  << "// " << fileName
1160  << "\n// "
1161  << "\n// Created for run " << eventID.run()
1162  << " subrun " << eventID.subRun()
1163  << " event " << eventID.event()
1164  << "\n// "
1165  << "\n// dump of " << handle->size() << " particles"
1166  << "\n// "
1167  << std::endl;
1168 
1169  PFParticleGraphMaker graphMaker;
1170  graphMaker.MakeGraph(outFile, *handle);
1171 
1172  } // DumpPFParticles::MakePFParticleGraph()
1173 
1174 
1175  //----------------------------------------------------------------------------
1177 
1178  //
1179  // collect all the available information
1180  //
1181  // fetch the data to be dumped on screen
1183  = evt.getValidHandle<std::vector<recob::PFParticle>>(fInputTag);
1184 
1185  if (fMakeEventGraphs)
1186  MakePFParticleGraph(evt, PFParticles);
1187 
1188  art::FindOne<recob::Vertex> const ParticleVertices
1189  (PFParticles, evt, fInputTag);
1190  art::FindMany<recob::Track> const ParticleTracks
1191  (PFParticles, evt, fInputTag);
1192  art::FindMany<recob::Cluster> const ParticleClusters
1193  (PFParticles, evt, fInputTag);
1194  art::FindMany<recob::Seed> const ParticleSeeds
1195  (PFParticles, evt, fInputTag);
1196  art::FindMany<recob::SpacePoint> const ParticleSpacePoints
1197  (PFParticles, evt, fInputTag);
1198  art::FindMany<recob::PCAxis> const ParticlePCAxes
1199  (PFParticles, evt, fInputTag);
1200 
1201  size_t const nParticles = PFParticles->size();
1202  mf::LogVerbatim(fOutputCategory) << "Event " << evt.id()
1203  << " contains " << nParticles << " particles from '"
1204  << fInputTag.encode() << "'";
1205 
1206  // prepare the dumper
1207  ParticleDumper::PrintOptions_t options;
1208  options.hexFloats = fPrintHexFloats;
1209  options.maxDepth = fMaxDepth;
1210  options.streamName = fOutputCategory;
1211  ParticleDumper dumper(*PFParticles, options);
1212  if (ParticleVertices.isValid()) dumper.SetVertices(&ParticleVertices);
1213  else mf::LogPrint("DumpPFParticles") << "WARNING: vertex information not available";
1214  if (ParticleTracks.isValid()) dumper.SetTracks(&ParticleTracks);
1215  else mf::LogPrint("DumpPFParticles") << "WARNING: track information not available";
1216  if (ParticleClusters.isValid()) dumper.SetClusters(&ParticleClusters);
1217  else mf::LogPrint("DumpPFParticles") << "WARNING: cluster information not available";
1218  if (ParticleSeeds.isValid()) dumper.SetSeeds(&ParticleSeeds);
1219  else mf::LogPrint("DumpPFParticles") << "WARNING: seed information not avaialble";
1220  if (ParticleSpacePoints.isValid())
1221  dumper.SetSpacePoints(&ParticleSpacePoints);
1222  else {
1223  mf::LogPrint("DumpPFParticles")
1224  << "WARNING: space point information not available";
1225  }
1226  if (ParticlePCAxes.isValid())
1227  dumper.SetPCAxes(&ParticlePCAxes);
1228  else {
1229  mf::LogPrint("DumpPFParticles")
1230  << "WARNING: principal component axis not available";
1231  }
1232  dumper.DumpAllParticles(" ");
1233 
1234  mf::LogVerbatim(fOutputCategory) << "\n"; // two empty lines
1235 
1236  } // DumpPFParticles::analyze()
1237 
1239 
1240 } // namespace recob
void XYZ(double *xyz) const
Legacy method to access vertex position, preserved to avoid breaking code. Please try to use Vertex::...
Definition: Vertex.cxx:37
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
Reconstruction base classes.
size_t Self() const
Returns the index of this particle.
Definition: PFParticle.h:92
std::string const & productInstanceName() const
Definition: Provenance.h:73
intermediate_table::iterator iterator
bool IsValid() const
Definition: Seed.cxx:74
void GetPoint(double *Pt, double *Err) const
Definition: Seed.cxx:112
Prints the content of all the space points on screen.
std::string const & processName() const
Definition: Provenance.h:78
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
constexpr bool is_valid(IDNumber_t< L > const id)
Definition: IDNumber.h:112
geo::PlaneID Plane() const
Returns the plane ID this cluster lies on.
Definition: Cluster.h:744
Cluster finding and building.
Prints the content of all the seeds on screen.
fhicl::OptionalAtom< unsigned int > MaxDepth
Definition of vertex object for LArSoft.
Definition: Vertex.h:35
RunNumber_t run() const
Definition: EventID.h:99
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
Int_t max
Definition: plot.C:27
double Length(size_t p=0) const
Access to various track properties.
Definition: Track.h:170
std::string fOutputCategory
category for LogInfo output
intermediate_table::const_iterator const_iterator
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:42
Provides recob::Track data product.
unsigned int fMaxDepth
maximum generation to print (0: only primaries)
long seed
Definition: chem4.cc:68
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
std::string encode() const
Definition: InputTag.cc:36
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
TString part[npart]
Definition: Style.C:32
Point_t const & Vertex() const
Access to track position at different points.
Definition: Track.h:127
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:65
Prints the content of all the PCA axis object on screen.
virtual void analyze(const art::Event &evt) override
Does the printing.
EDAnalyzer(Table< Config > const &config)
Definition: EDAnalyzer.h:100
Declaration of cluster object.
Helper for formatting floats in base 16.
Definition: hexfloat.h:105
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:11
int ID() const
Definition: Track.h:201
Prints the content of all the tracks on screen.
ID_t ID() const
Definition: SpacePoint.h:64
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
std::string to_string(Flag_t< Storage > const flag)
Convert a flag into a stream (shows its index).
Definition: BitMask.h:187
TDirectory * dir
Definition: macro.C:5
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
std::string const & moduleLabel() const
Definition: Provenance.h:68
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
static std::string DotFileName(art::EventID const &evtID, art::Provenance const &prodInfo)
EventNumber_t event() const
Definition: EventID.h:117
Point_t const & End() const
Access to track position at different points.
Definition: Track.h:128
void MakePFParticleGraph(art::Event const &event, art::ValidHandle< std::vector< recob::PFParticle >> const &handle) const
art::InputTag fInputTag
input tag of the PFParticle product
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
unsigned int NHits() const
Number of hits in the cluster.
Definition: Cluster.h:275
TCEvent evt
Definition: DataStructs.cxx:5
ValidHandle< PROD > getValidHandle(InputTag const &tag) const
fhicl::Atom< art::InputTag > PFModuleLabel
Float_t track
Definition: plot.C:34
void GetDirection(double *Dir, double *Err) const
Definition: Seed.cxx:102
bool fPrintHexFloats
whether to print floats in base 16
SubRunNumber_t subRun() const
Definition: EventID.h:111
EventID id() const
Definition: Event.h:56
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:52
MaybeLogger_< ELseverityLevel::ELsev_warning, true > LogPrint
Event finding and building.
double GetLength() const
Definition: Seed.cxx:159
bool getSvdOK() const
Definition: PCAxis.h:63
vertex reconstruction