All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ROPandTPCsetBuildingAlg.cxx
Go to the documentation of this file.
1 /**
2  * @file icarusalg/Geometry/details/ROPandTPCsetBuildingAlg.cxx
3  * @brief Algorithm discovering TPC sets and readout planes for ICARUS.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date October 10, 2019
6  * @see `icarusalg/Geometry/details/ROPandTPCsetBuildingAlg.h`
7  */
8 
9 // library header
11 
12 // LArSoft libraries
15 #include "larcorealg/CoreUtils/StdUtils.h" // util::size()
16 #include "larcorealg/CoreUtils/RealComparisons.h" // lar::util::makeVector3DComparison()
17 #include "larcoreobj/SimpleTypesAndConstants/readout_types.h" // readout::TPCsetID, ...
19 
20 // framework libraries
21 #include "messagefacility/MessageLogger/MessageLogger.h"
22 #include "cetlib_except/exception.h" // cet::exception
23 
24 // C/C++ standard library
25 #include <string>
26 #include <vector>
27 #include <tuple>
28 #include <algorithm> // std::max(), std::transform(), std::stable_sort()
29 #include <utility> // std::move(), std::pair, std::declval()
30 #include <type_traits> // std::decay_t
31 #include <cmath> // std::abs()
32 #include <cassert>
33 
34 
35 namespace {
36 
37  // --------------------------------------------------------------------------
38 
39  // Creates a STL vector with the result of the transformation of `coll`.
40  template <typename Coll, typename Op>
41  auto transformCollection(Coll const& coll, Op op) {
42 
43  using Result_t
44  = std::decay_t<decltype(op(std::declval<typename Coll::value_type>()))>;
45 
46  std::vector<Result_t> transformed;
47  transformed.reserve(util::size(coll));
49  (coll.begin(), coll.end(), std::back_inserter(transformed), op);
50  return transformed;
51  } // transformCollection()
52 
53 
54  // --------------------------------------------------------------------------
55 
56 } // local namespace
57 
58 
59 // -----------------------------------------------------------------------------
60 // --- icarus::details::StableMerger
61 // -----------------------------------------------------------------------------
62 namespace icarus::details {
63  template <typename SetColl> class StableMerger;
64  template <typename SetColl> SetColl stableMerge(SetColl const& sets);
65 }
66 
67 template <typename SetColl>
69 
70  public:
71  using SetColl_t = SetColl;
72 
73  static SetColl_t merge(SetColl_t const& sets);
74 
75  private:
76  using Coll_t = typename SetColl_t::value_type;
77  using Value_t = typename Coll_t::value_type;
78 
79  static bool addNewValue(Coll_t& coll, Value_t const& value);
80 
81  static Coll_t mergeColls(Coll_t const& a, Coll_t const& b);
82 
83  static bool overlap(Coll_t const& a, Coll_t const& b);
84 
85  static SetColl_t mergePass(SetColl_t const& sets);
86 
87 
88 }; // class icarus::details::StableMerger<>
89 
90 
91 template <typename SetColl>
92 SetColl icarus::details::stableMerge(SetColl const& sets)
94 
95 
96 // -----------------------------------------------------------------------------
97 template <typename SetColl>
99  (Coll_t& coll, Value_t const& value)
100 {
101  if (std::find(coll.begin(), coll.end(), value) != coll.end()) return false;
102  coll.push_back(value);
103  return true;
104 } // icarus::details::StableMerger<>::mergeColls()
105 
106 
107 // -----------------------------------------------------------------------------
108 template <typename SetColl>
110  (Coll_t const& a, Coll_t const& b) -> Coll_t
111 {
112  Coll_t merged;
113  for (Value_t const& value: a) addNewValue(merged, value);
114  for (Value_t const& value: b) addNewValue(merged, value);
115  return merged;
116 } // icarus::details::StableMerger<>::mergeColls()
117 
118 
119 // -----------------------------------------------------------------------------
120 template <typename SetColl>
122  (Coll_t const& a, Coll_t const& b)
123 {
124  // brute force since we can't assume ordering
125  for (auto const& aElem: a) {
126  if (std::find(b.begin(), b.end(), aElem) != b.end()) return true;
127  } // for
128  return false;
129 } // icarus::details::StableMerger<>::overlap()
130 
131 
132 // -----------------------------------------------------------------------------
133 template <typename SetColl>
135  -> SetColl_t
136 {
137 
138  SetColl_t mergedSets;
139  for (Coll_t const& set: sets) {
140 
141  //
142  // overlap check
143  //
144  Coll_t* mergeWith = nullptr;
145  for (Coll_t& mergedSet: mergedSets) {
146  if (!overlap(mergedSet, set)) continue;
147  mergeWith = &mergedSet;
148  break;
149  }
150 
151  if (mergeWith) {
152  *mergeWith = (mergeWith->size() < set.size())
153  ? mergeColls(set, *mergeWith)
154  : mergeColls(*mergeWith, set)
155  ;
156  }
157  else {
158  mergedSets.push_back(set);
159  }
160 
161  } // for input sets
162  return mergedSets;
163 } // icarus::details::StableMerger<>::mergePass()
164 
165 
166 // -----------------------------------------------------------------------------
167 template <typename SetColl>
169  -> SetColl_t
170 {
171  //
172  // do merging until no more overlap is detected (i.e. no merge is performed)
173  // (for example, if the sets are {0} {1} {0, 1}, the first pass will merge
174  // {0, 1} with {0} but the resulting sets {0, 1} {1} still overlap
175  //
176  SetColl_t mergedSets { sets };
177  std::size_t nSets;
178  do {
179 
180  nSets = mergedSets.size();
181 
182  mergedSets = mergePass(mergedSets);
183 
184  } while (mergedSets.size() != nSets);
185 
186  return mergedSets;
187 } // icarus::details::StableMerger<SetColl>::merge()
188 
189 
190 // -----------------------------------------------------------------------------
191 // --- icarus::details::StableMerger
192 // -----------------------------------------------------------------------------
193 namespace icarus::details { class ROPnumberDispatcher; }
194 
195 /**
196  * @brief Algorithm assigning IDs to readout planes.
197  *
198  * The usage workflow is the following:
199  *
200  * 1. the algorithm is initialized with a TPC set to be taken as model
201  * (constructor);
202  * 2. a new TPC set is declared (`setTPCset()`);
203  * 3. for each readout plane, an ID is requested (`assignID()`);
204  * 4. if more TPC sets need to follow the same reference, repeat steps 2 and 3.
205  *
206  * The assignment of the ROP number is in blocks according to the view.
207  * The distribution between different views is first come, first serve: the
208  * first ROP within a certain view conquers the lowest number still available
209  * for all the ROP's within that view.
210  *
211  * The algorithm assumes that all TPC sets have the same view composition as
212  * the model one used in the constructor. If this requirement is violated,
213  * the behavior is undefined (but it's likely that a ROP ID will be assigned
214  * multiple times).
215  */
217 
218  /// Type of collection of `T` by view.
219  template <typename T>
220  using DataByView_t = std::map<geo::View_t, T>;
221 
222  std::string const fLogCategory; ///< Log category name for messages.
223 
224  /// The first ROP number for each view.
226 
227  /// The first available ROP number for each view.
229 
230  /// The TPC set being served.
232 
233  /// Returns the number of planes with each view.
235  (std::vector<PlaneColl_t> const& ROPplanes) const;
236 
237  /// Returns the fist ROP number for each view in `ROP`.
239  (std::vector<PlaneColl_t> const& ROPs) const;
240 
241  public:
242  /// Constructor: uses `refROP` as reference to assign number ranges to views.
244  (std::vector<PlaneColl_t> const& refROP, std::string const& logCategory)
245  : fLogCategory(logCategory)
248  {}
249 
250  /// Rests the object to work with the specified TPC set next.
251  void setTPCset(readout::TPCsetID const& TPCset)
252  { fTPCset = TPCset; fAvailableROPno = fFirstROPno; }
253 
254  /// Returns the next available ID for the specified ROP.
256  { return { fTPCset, fAvailableROPno.at(ROPview(ROP))++ }; }
257 
258  /// Returns the view of the `planes` (`geo::kUnknown` if mixed or none).
259  static geo::View_t ROPview(PlaneColl_t const& planes);
260 
261 }; // class icarus::details::ROPnumberDispatcher
262 
263 
264 // -----------------------------------------------------------------------------
266  (std::vector<PlaneColl_t> const& ROPs) const
268 {
269  /*
270  * ROP numbers are assigned in blocks of views, on a first come, first serve
271  * basis.
272  * This, the first ROP on a view books as many ROP numbers as there are ROPs
273  * with that view.
274  */
275  DataByView_t<unsigned int> const viewROPcounts = ViewROPcounts(ROPs);
276 
278  readout::ROPID::ROPID_t nextAvailable { 0 };
279 
280  for (PlaneColl_t const& planes: ROPs) {
281 
282  geo::View_t const view = ROPview(planes);
283 
284  //
285  // using the facts that:
286  // * std::map::emplace() does not insert anything if the key already exists
287  // * std::map::emplace() returns as `second` whether it insert or not
288  //
289  if (!FirstROPno.emplace(view, nextAvailable).second) continue;
290 
291  mf::LogTrace(fLogCategory)
292  << "ROPs on view " << geo::PlaneGeo::ViewName(view)
293  << " preferring numbers from " << nextAvailable
294  << " (" << viewROPcounts.at(view) << " numbers reserved)";
295 
296  // so, insertion happened; let's reserve numbers
297  nextAvailable += viewROPcounts.at(view);
298 
299  } // for ROPs
300 
301  return FirstROPno;
302 
303 } // icarus::details::ROPnumberDispatcher::PreferredROPrangesByView()
304 
305 
306 // ----------------------------------------------------------------------------
308  (std::vector<PlaneColl_t> const& ROPplanes) const
310 {
311  DataByView_t<unsigned int> viewROPcounts;
312 
313  for (PlaneColl_t const& planes: ROPplanes) {
314  if (planes.empty()) continue;
315 
316  geo::View_t const view = ROPview(planes);
317  if (view == geo::kUnknown) {
318  mf::LogWarning log(fLogCategory);
319  log << "A ROP spans multiple views:";
320  for (geo::PlaneGeo const* plane: planes) {
321  log << " " << geo::PlaneGeo::ViewName(plane->View())
322  << " (" << plane->ID() << ")";
323  }
324  } // if unknown
325 
326  ++(viewROPcounts[view]);
327 
328  } // for ROPs
329 
330  return viewROPcounts;
331 } // icarus::details::ROPnumberDispatcher::ViewROPcounts()
332 
333 
334 // -----------------------------------------------------------------------------
337 {
338  auto iPlane = planes.begin();
339  auto const pend = planes.end();
340  if (iPlane == pend) return geo::kUnknown;
341  geo::View_t const view = (*iPlane)->View();
342  while (++iPlane != pend) {
343  if ((*iPlane)->View() !=view) return geo::kUnknown;
344  } // while
345  return view;
346 } // icarus::details::ROPnumberDispatcher::ROPview()
347 
348 
349 // -----------------------------------------------------------------------------
350 // --- icarus::details::ROPandTPCsetBuildingAlg
351 // -----------------------------------------------------------------------------
354 {
355  /*
356  * The goals:
357  * (G1) number of actual TPC sets per cryostat
358  * (G2) composition in TPC's of each TPC set
359  * (G3) number of actual ROP's in each TPC set
360  * (G4) composition in readout planes of each ROP
361  *
362  * The plan:
363  *
364  * 1. extract the composition of all the readout planes
365  * and the TPCs each of them spans
366  * 2. extract the final set of TPC sets by collecting sets of TPCs
367  * from all the ROP's
368  * 3. assign each of the readout planes to a TPC set
369  * 4. sort out the readout plane assignments into the final sets
370  */
371 
372  //
373  // input and setup
374  //
375  mf::LogDebug(fLogCategory)
376  << "Building TPC sets and readout planes from " << Cryostats.size()
377  << " cryostats";
378 
379  fCryostats = &Cryostats;
380 
381  //
382  // output
383  //
384  clear();
385 
386  //
387  // 1. extract the composition of all the readout planes
388  // and the TPCs they span
389  //
390  auto standaloneHorizontalWires = [](geo::PlaneGeo const& plane)
391  { return std::abs(plane.ThetaZ()) < 1e-3; };
392 
393  std::vector<std::vector<PlaneColl_t>> AllPlanesInROPs
394  = groupPlanesAndTPCs(standaloneHorizontalWires);
395 
396  std::vector<std::vector<std::vector<geo::TPCID>>> const AllTPCsInTPCsets
397  = extractTPCsetsFromROPs(AllPlanesInROPs);
398 
399  //
400  // 2. extract the final set of TPC sets by collecting sets of TPCs
401  // from all the ROP's
402  //
403  fillTPCsInSet(AllTPCsInTPCsets);
404 
405  //
406  // 3. assign each of the readout planes to a TPC set
407  //
409  = groupPlanesIntoROPs(AllTPCsInTPCsets, std::move(AllPlanesInROPs));
410 
411  //
412  // 4. sort out the readout plane assignments into the final sets
413  //
414  fillPlanesInROP(PlanesInProtoROPs);
415 
416  //
417  // 5. invert the maps
418  //
419  fillTPCtoTPCsetMap();
420  fillPlaneToROPmap();
421 
422  //
423  // output
424  //
425  assert(!fTPCsetCount.empty());
426  assert(!fTPCsetTPCs .empty());
427  assert(!fROPcount .empty());
428  assert(!fTPCtoTPCset.empty());
429  assert(!fPlaneToROP .empty());
430  return ResultsBase_t{
431  std::move(fTPCsetCount),
432  std::move(fTPCsetTPCs ),
433  std::move(fROPcount ),
434  std::move(fROPplanes ),
435  std::move(fTPCtoTPCset),
436  std::move(fPlaneToROP )
437  };
438 } // icarus::details::ROPandTPCsetBuildingAlg::run()
439 
440 
441 // -----------------------------------------------------------------------------
443  fTPCsetCount.clear();
444  fTPCsetTPCs.reset();
445  fROPcount.reset();
446  fROPplanes.reset();
448  fPlaneToROP.reset();
449  fMaxTPCsets = 0U;
450  fMaxROPs = 0U;
451 } // icarus::details::ROPandTPCsetBuildingAlg::clear()
452 
453 
454 // -----------------------------------------------------------------------------
455 template <typename Pred>
457  (Pred standalonePlane)
458  -> std::vector<std::vector<PlaneColl_t>>
459 {
460  /*
461  * extract the composition of all the readout planes
462  * and the TPCs each of them spans
463  */
464 
465  //
466  // input check
467  //
468  assert(fCryostats);
469  geo::GeometryData_t::CryostatList_t const& Cryostats = *fCryostats;
470 
471  //
472  // output
473  //
474 
475  // for each cryostat (first index),
476  // a list of readout plane candidates
477  // (second index: readout plane within the cryostat)
478  // each listing its wire planes (third index: runs through all wire planes):
479  std::vector<std::vector<PlaneColl_t>> AllPlanesInROPs;
480 
481  // debug
482  auto logPlanes = [this](PlaneColl_t const& planes)
483  {
484  mf::LogTrace log(fLogCategory);
485  log << "New group of " << planes.size() << " planes:";
486  for (geo::PlaneGeo const* plane: planes)
487  log << " <" << plane->ID() << ">";
488  };
489 
490  //
491  // the algorithm
492  //
493  // extract the information one cryostat at a time
494  for (geo::CryostatGeo const& cryo: Cryostats) {
495 
496  //
497  // connect all wire planes with the same drift direction to build
498  // readout plane sets
499  //
500  std::vector<geo::PlaneGeo const*> planes;
501  for (geo::TPCGeo const& tpc: cryo.IterateTPCs()) {
502  for (geo::PlaneGeo const& plane: tpc.IteratePlanes()) {
503  planes.push_back(&plane);
504  } // for planes
505  } // for TPCs
506 
507  std::vector<PlaneColl_t> protoGroups = groupPlanesByDriftCoord(planes);
508 
509  //
510  // Are all the planes in the groups expected to be grouped?
511  // if not, add them as their own group before the group.
512  // We could have filtered out the planes not to be grouped before grouping,
513  // which makes a lot of sense but complicates the sorting afterwards.
514  //
515  std::vector<PlaneColl_t> groupedPlanes;
516  for (PlaneColl_t const& protoGroup: protoGroups) {
517  if (protoGroup.empty()) continue;
519  for (geo::PlaneGeo const* plane: protoGroup) {
520  if (standalonePlane(*plane)) {
521  groupedPlanes.push_back({ plane });
522  logPlanes(groupedPlanes.back());
523  }
524  else group.push_back(plane);
525  }
526  if (!group.empty()) {
527  groupedPlanes.push_back(std::move(group));
528  logPlanes(groupedPlanes.back());
529  }
530  } // for protogroups
531 
532  //
533  // save the results of the cryostat to detector-level data collection
534  //
535  AllPlanesInROPs.push_back(std::move(groupedPlanes));
536 
537  } // for cryostats
538 
539  return AllPlanesInROPs;
540 
541 } // icarus::details::ROPandTPCsetBuildingAlg::groupPlanesAndTPCs()
542 
543 
544 // -----------------------------------------------------------------------------
546  -> std::vector<std::vector<PlaneColl_t>>
547 { return groupPlanesAndTPCs([](geo::PlaneGeo const&){ return false; }); }
548 
549 
550 // -----------------------------------------------------------------------------
551 std::vector<std::vector<std::vector<geo::TPCID>>>
553  (std::vector<std::vector<PlaneColl_t>> const& groupedPlanes)
554 {
555 
556  // output: for each cryostat (first index),
557  // a list of TPC sets (second index: TPC set within the cryostat)
558  // each listing its TPC (third index: runs through all TPCs):
559  std::vector<std::vector<std::vector<geo::TPCID>>> AllTPCsOnROPs;
560 
561  //
562  // output
563  //
564  unsigned int& MaxTPCsets = fMaxTPCsets;
565 
566  MaxTPCsets = 0U;
567 
568  // extract the information one cryostat at a time
569  for (std::vector<PlaneColl_t> const& planeGroupsInCryostat: groupedPlanes) {
570 
571  //
572  // for each readout plane, collect the set of TPCs that contain its planes;
573  // keep them as IDs since they are easier to procure.
574  // Should these be sorted?
575  // (yes) because the set is the same regardless the order of TPCs => set
576  // (no) because we want to preserve the original order => vector
577  //
578  std::vector<std::vector<geo::TPCID>> TPCsOnROPs
579  = transformCollection(planeGroupsInCryostat, extractTPCIDs);
580 
581  // reduce the duplicate TPC sets and merge overlapping sets
582 // auto const iUniqueTPCend = std::unique(TPCsOnROPs.begin(), TPCsOnROPs.end());
583 // TPCsOnROPs.erase(iUniqueTPCend, TPCsOnROPs.end());
584 
585  TPCsOnROPs = icarus::details::stableMerge(TPCsOnROPs);
586 
587  //
588  // save the results of the cryostat to detector-level data collection
589  //
590  MaxTPCsets
591  = std::max(MaxTPCsets, static_cast<unsigned int>(TPCsOnROPs.size()));
592  AllTPCsOnROPs.push_back(std::move(TPCsOnROPs));
593 
594  } // for cryostats
595  mf::LogTrace("ICARUSChannelMapAlg")
596  << "The maximum number of TPC sets in any cryostat is " << MaxTPCsets;
597 
598  return AllTPCsOnROPs;
599 
600 } // icarus::details::ROPandTPCsetBuildingAlg::extractTPCsetsFromROPs()
601 
602 
603 // -----------------------------------------------------------------------------
605  (std::vector<std::vector<std::vector<geo::TPCID>>> const& AllTPCsOnROPs)
606 {
607  /*
608  * extract the final set of TPC sets by collecting sets of TPCs
609  * from all the ROP's
610  * (deliver G1 and G2)
611  */
612 
613  //
614  // input check
615  //
616  assert(fCryostats);
617  geo::GeometryData_t::CryostatList_t const& Cryostats = *fCryostats;
618 
619  unsigned int const MaxTPCsets = fMaxTPCsets;
620 
621  //
622  // output
623  //
624  // actual size of each TPC set: goal (G1)
625  fTPCsetCount.clear();
626  fTPCsetCount.resize(AllTPCsOnROPs.size(), 0U);
627  std::vector<unsigned int>& TPCsetCount = fTPCsetCount;
628 
629  fTPCsetTPCs.resize(AllTPCsOnROPs.size(), MaxTPCsets); // goal (G2)
631  = fTPCsetTPCs;
632 
633  //
634  // algorithm
635  //
636  for (auto&& [ c, TPCsets ]: util::enumerate(AllTPCsOnROPs)) {
637  geo::CryostatGeo const& cryo = Cryostats[c];
638  TPCsetCount[c] = TPCsets.size();
639  mf::LogTrace("ICARUSChannelMapAlg")
640  << "Cryostat " << cryo.ID() << " has " << TPCsetCount[c] << " TPC sets";
641  for (auto&& [ s, TPCIDs ]: util::enumerate(TPCsets)) {
642 
643  // convert the IDs into pointers to the TPC objects
644  readout::TPCsetID const tpcsetid
645  { cryo.ID(), static_cast<readout::TPCsetID::TPCsetID_t>(s) };
646  TPCsetTPCs[tpcsetid] = transformCollection(
647  TPCIDs, [&cryo](geo::TPCID const& tpcid){ return &(cryo.TPC(tpcid)); }
648  );
649 
650  { // local block for debug output
651  auto const& TPCs = TPCsetTPCs[tpcsetid];
652  mf::LogTrace log("ICARUSChannelMapAlg");
653  log << tpcsetid << " has " << TPCs.size() << " TPCs:";
654  for (geo::TPCGeo const* tpc: TPCs) log << " " << tpc->ID() << ";";
655  } // local block for debug output
656 
657  } // for TPC sets
658  } // for cryostats
659 } // icarus::details::ROPandTPCsetBuildingAlg::fillTPCsInSet()
660 
661 
662 // -----------------------------------------------------------------------------
664  std::vector<std::vector<std::vector<geo::TPCID>>> const& AllTPCsOnROPs,
665  std::vector<std::vector<PlaneColl_t>>&& AllPlanesInROPs
666 )
668 {
669  /*
670  * assigns each of the readout planes to a TPC set
671  */
672 
673  //
674  // input check
675  //
676  assert(fCryostats);
677  geo::GeometryData_t::CryostatList_t const& Cryostats = *fCryostats;
678 
679  assert(!fTPCsetTPCs.empty());
680  readout::TPCsetDataContainer<TPCColl_t> const& TPCsetTPCs = fTPCsetTPCs;
681 
682  //
683  // output
684  //
686  (TPCsetTPCs.dimSize<0U>(), TPCsetTPCs.dimSize<1U>());
687  unsigned int& MaxROPs = fMaxROPs;
688 
689 
690  unsigned int nErrors = 0U; // count errors, then bail out at the end
691  // we still don't know the maximum number of ROPs in a TPC set
692  MaxROPs = 0U;
693  for (auto&& [ c, ROPs ]: util::enumerate(AllPlanesInROPs)) {
694  // find which TPC set number each ROP belongs to;
695  // brute force approach: check the content of each TPC set until we find
696  // one that matched; then we are happy.
697  // We assume that each ROP has one plane from *each* of the TPC's in a set.
698  geo::CryostatGeo const& cryo = Cryostats[c];
699  std::vector<std::vector<geo::TPCID>> const& TPCsets = AllTPCsOnROPs[c];
700  for (std::vector<geo::PlaneGeo const*>& ROPplanes: ROPs) {
701  std::vector<geo::TPCID> const ROPTPCIDs = extractTPCIDs(ROPplanes);
702 
703  // find the TPC set
704  readout::TPCsetID tpcsetid; // destination set, invalid by default
705  for (auto&& [ s, TPCset ]: util::enumerate(TPCsets)) {
706  // here is where we assume the ROP has one plane per TOC in the TPC set;
707  // we are also gambling that the order is the same
708  if (!isROPinTPCset(ROPTPCIDs, TPCset)) continue;
709  tpcsetid = { cryo.ID(), static_cast<readout::TPCsetID::TPCsetID_t>(s) };
710  break;
711  } // for all sets
712 
713  if (!tpcsetid) { // long error message to help debugging
714  mf::LogError log(fLogCategory);
715  log << "Candidate ROP did not match any TPC set.";
716  log << "\nROP planes:";
717  for (geo::PlaneGeo const* plane: ROPplanes)
718  log << " (" << plane->ID() << ")";
719  log << "\nAvailable TPC sets (" << TPCsets.size() << "):";
720  for (auto&& [ s, TPCset ]: util::enumerate(TPCsets)) {
721  readout::TPCsetID const tpcsetid
722  { cryo.ID(), static_cast<readout::TPCsetID::TPCsetID_t>(s) };
723  log << "\n - " << tpcsetid << ", " << TPCset.size() << " TPC's:";
724  for (geo::TPCID const& tpcid: TPCset) log << " (" << tpcid << ")";
725  } // for TPC sets
726  ++nErrors;
727  continue;
728  } // if no TPC set matched
729 
730  // we have found the (first) TPC set matching the set of TPCs in ROP;
731  // we store (move) the information into the proper cell
732  auto& TPCsetROPs = PlanesInProtoROPs[tpcsetid];
733  TPCsetROPs.push_back(std::move(ROPplanes));
734  MaxROPs = std::max(MaxROPs, static_cast<unsigned int>(TPCsetROPs.size()));
735 
736  } // for TPC sets
737  } // for cryostats
738 
739  AllPlanesInROPs.clear(); // we have already depleted it anyway
740 
741  if (nErrors > 0) {
742  throw cet::exception(fLogCategory)
743  << "Encountered " << nErrors
744  << " errors while assigning TPC sets to ROPs (see error messages above)\n"
745  ;
746  } // if errors
747 
748  mf::LogTrace(fLogCategory)
749  << "The maximum number of readout planes in any TPC set is " << MaxROPs;
750 
751  return PlanesInProtoROPs;
752 
753 } // icarus::details::ROPandTPCsetBuildingAlg::groupPlanesIntoROPs()
754 
755 
756 // -----------------------------------------------------------------------------
758  readout::TPCsetDataContainer<std::vector<PlaneColl_t>> const& PlanesInProtoROPs
759 ) {
760  //
761  // sort out the readout plane assignments into the final sets
762  // (deliver G3 and G4: see `buildReadoutPlanes()`)
763  //
764 
765  assert(!fTPCsetTPCs.empty());
766  assert(!fTPCsetCount.empty());
767  assert(fMaxROPs > 0U);
768 
769  //
770  // prepare the input
771  //
772  auto const& TPCsetTPCs = fTPCsetTPCs;
773  auto const& TPCsetCount = fTPCsetCount;
774 
775  //
776  // prepare the output
777  //
778  // this is the actual number of readout planes in each TPC set: goal (G3)
779  fROPcount.resize(TPCsetTPCs.dimSize<0U>(), TPCsetTPCs.dimSize<1U>(), 0U);
780  fROPplanes.resize
781  (TPCsetTPCs.dimSize<0U>(), TPCsetTPCs.dimSize<1U>(), fMaxROPs); // goal (G4)
782 
783  auto& ROPcount = fROPcount;
784  auto& ROPplanes = fROPplanes;
785 
786  // assign the preferred numbers according to the first TPC set;
787  // before that, sort the planes in that TPC set by local drift coordinate
788  // (possibly different from the one used for the clustering by the verse)
789  std::vector<PlaneColl_t> const sortedTPCset0
790  = sortByNormalCoordinate(PlanesInProtoROPs[{ 0, 0 }]);
791 
793  { sortedTPCset0, fLogCategory };
794 
795  // (double) loop through all TPC sets:
796  for (auto [ c, nTPCsets ]: util::enumerate(TPCsetCount)) {
797  readout::CryostatID const cryoid
798  { static_cast<readout::CryostatID::CryostatID_t>(c) };
799  for (readout::TPCsetID::TPCsetID_t s = 0; s < nTPCsets; ++s) {
800  readout::TPCsetID const tpcsetid { cryoid, s };
801 
802  // all the ROPs in this TPC set, with their wire plane content:
803  std::vector<PlaneColl_t> const& ROPs = PlanesInProtoROPs[tpcsetid];
804 
805  ROPcount[tpcsetid] = ROPs.size();
806 
807  ROPIDdispatcher.setTPCset(tpcsetid);
808 
809  for (PlaneColl_t const& planes: ROPs) {
810 
811  readout::ROPID ropid = ROPIDdispatcher.assignID(planes);
812  {
813  mf::LogTrace log(fLogCategory);
814  log << "Readout plane " << ropid << " assigned with " << planes.size()
815  << " planes:";
816  for (geo::PlaneGeo const* plane: planes)
817  log << " (" << plane->ID() << ")";
818  }
819  if (!ROPplanes[ropid].empty()) {
820  //
821  // If this happens, it may be that the geometry is not compatible
822  // with the algorithm, or just a bug.
823  // Enabling the debug stream will show which planes are assigned
824  // each time, including the two conflicting assignments.
825  //
826  throw cet::exception(fLogCategory)
827  << "Logic error: ROPID " << ropid
828  << " has already been assigned!\n";
829  }
830  ROPplanes[ropid] = std::move(planes);
831 
832  } // for all ROPs in the TPC set
833  } // for all TPC sets in cryostat
834  } // for all cryostats
835 
836 } // icarus::details::ROPandTPCsetBuildingAlg::fillPlanesInROP()
837 
838 
839 // ----------------------------------------------------------------------------
841 
842  //
843  // invert the TPC sets map content
844  //
845 
846  assert(!fCryostats->empty());
847  assert(!fTPCsetTPCs.empty());
848 
849  //
850  // prepare the input
851  //
852  auto const& Cryostats = *fCryostats;
853  auto const& TPCsetTPCs = fTPCsetTPCs;
854 
855  //
856  // output
857  //
858  auto const [ NCryostats, MaxTPCs ]
859  = geo::details::extractMaxGeometryElements<2U>(Cryostats);
860 
861  MF_LOG_TRACE(fLogCategory)
862  << "Detected " << NCryostats << " cryostats."
863  << "\nDetected at most " << MaxTPCs << " TPCs per cryostat."
864  ;
865  fTPCtoTPCset.resize(NCryostats, MaxTPCs, {});
866 
867  for (auto c
868  : util::counter<geo::CryostatID::CryostatID_t>(TPCsetTPCs.dimSize<0>())
869  )
870  {
871  geo::CryostatID const cid { c };
872  for (auto s: util::counter<readout::TPCsetID::TPCsetID_t>(TPCsetTPCs.dimSize<1>()))
873  {
874  readout::TPCsetID const sid { cid, s };
875 
876  for (geo::TPCGeo const* TPC: TPCsetTPCs[sid]) {
877  assert(TPC && TPC->ID());
878  fTPCtoTPCset[TPC->ID()] = sid;
879  MF_LOG_TRACE(fLogCategory) << TPC->ID() << " => " << sid;
880  } // for TPCs in TPC set
881 
882  } // for TPC sets in cryostat
883 
884  } // for cryostats
885 
886 } // icarus::details::ROPandTPCsetBuildingAlg::fillTPCtoTPCsetMap()
887 
888 
889 // ----------------------------------------------------------------------------
891 
892  //
893  // invert the TPC sets map content
894  //
895 
896  assert(!fCryostats->empty());
897  assert(!fROPplanes.empty());
898 
899  //
900  // prepare the input
901  //
902  auto const& Cryostats = *fCryostats;
903  auto const& ROPplanes = fROPplanes;
904 
905  //
906  // output
907  //
908  auto const [ NCryostats, MaxTPCs, MaxPlanes ]
909  = geo::details::extractMaxGeometryElements<3U>(Cryostats);
910 
911  MF_LOG_TRACE(fLogCategory)
912  << "Detected " << NCryostats << " cryostats."
913  << "\nDetected at most " << MaxTPCs << " TPCs per cryostat."
914  << "\nDetected at most " << MaxPlanes << " planes per TPC."
915  ;
916  fPlaneToROP.resize(NCryostats, MaxTPCs, MaxPlanes, {});
917 
918  for (auto c
919  : util::counter<geo::CryostatID::CryostatID_t>(ROPplanes.dimSize<0>())
920  )
921  {
922  geo::CryostatID const cid { c };
923  for (auto s: util::counter<readout::TPCsetID::TPCsetID_t>(ROPplanes.dimSize<1>()))
924  {
925  readout::TPCsetID const sid { cid, s };
926  for (auto r: util::counter<readout::ROPID::ROPID_t>(ROPplanes.dimSize<2>()))
927  {
928  readout::ROPID const rid { sid, r };
929 
930  for (geo::PlaneGeo const* plane: ROPplanes[rid]) {
931  assert(plane && plane->ID());
932  fPlaneToROP[plane->ID()] = rid;
933  MF_LOG_TRACE(fLogCategory) << plane->ID() << " => " << rid;
934  } // for planes in readout plane
935 
936  } // for readout planes in TPC set
937 
938  } // for TPC sets in cryostat
939 
940  } // for cryostats
941 
942 } // icarus::details::ROPandTPCsetBuildingAlg::fillPlaneToROPmap()
943 
944 
945 // -----------------------------------------------------------------------------
947  (std::vector<PlaneColl_t> const& ROPs) const -> std::vector<PlaneColl_t>
948 {
949  /*
950  * The `ROPs` are sorted in order of decreasing normal plane coordinate.
951  *
952  * 1. consistency of normal direction is first verified
953  * 2. the ROPs are sorted so that the center of the first sorted plane,
954  * projected on the normal direction of a reference plane, is the largest
955  * (plane closest to the cathode); sorting is stable.
956  *
957  */
958  assert(!ROPs.empty()); // don't waste our time!
959 
960  checkNormalDirection(ROPs); // throws on error
961 
962  geo::PlaneGeo const* const pRefPlane = ROPs.front().front();
963  auto sortedROPs { ROPs };
964  std::stable_sort(sortedROPs.begin(), sortedROPs.end(),
965  [pRefPlane](PlaneColl_t const& A, PlaneColl_t const& B) -> bool
966  {
967  return
968  pRefPlane->DistanceFromPlane(A.front()->GetCenter<geo::Point_t>())
969  > pRefPlane->DistanceFromPlane(B.front()->GetCenter<geo::Point_t>());
970  }
971  );
972 
973  return sortedROPs;
974 } // icarus::details::ROPandTPCsetBuildingAlg::sortByNormalCoordinate()
975 
976 
977 // -----------------------------------------------------------------------------
979  (std::vector<PlaneColl_t> const& ROPs) const
980 {
981  /*
982  * Checks the normal direction of each plane in each ROP.
983  * Throws an exception when it finds two inconsistent ones.
984  */
985  if (ROPs.empty()) return;
986 
987  auto iROP = ROPs.begin();
988  assert(!iROP->empty()); // just be there already
989 
990  auto const& comp = lar::util::makeVector3DComparison(1.0e-4);
991 
992  geo::PlaneGeo const* const pFirstPlane = iROP->front();
993  auto const normDir = pFirstPlane->GetNormalDirection<geo::Vector_t>();
994 
995  for (PlaneColl_t const& ROP: ROPs) {
996  if (ROP.empty()) { // we do not accept this!
997  throw cet::exception(fLogCategory)
998  << "icarus::details::ROPandTPCsetBuildingAlg::checkNormalDirection(): "
999  "ROP must contain planes.\n"
1000  ;
1001  }
1002  for (geo::PlaneGeo const* plane: ROP) {
1003  if (comp.equal(normDir, plane->GetNormalDirection<geo::Vector_t>()))
1004  continue;
1005 
1006  throw cet::exception(fLogCategory)
1007  << "icarus::details::ROPandTPCsetBuildingAlg::checkNormalDirection(): "
1008  " plane " << plane->ID()
1009  << " has normal " << plane->GetNormalDirection<geo::Vector_t>()
1010  << ", " << normDir << " (as for " << pFirstPlane->ID()
1011  << ") expected.\n"
1012  ;
1013 
1014  } // for all planes in ROP
1015  } // for all ROPs
1016 
1017 } // icarus::details::ROPandTPCsetBuildingAlg::checkNormalDirection()
1018 
1019 
1020 // ----------------------------------------------------------------------------
1021 template <typename PlaneColl>
1023  (PlaneColl const& planes, double tolerance /* = 0.1 */)
1024  -> std::vector<PlaneColl_t>
1025 {
1026  std::map<double, PlaneColl_t> groupedByDrift;
1027 
1028  geo::Vector_t const driftDir = geo::Xaxis();
1029 
1030  /*
1031  * we can't std::sort by drift coordinate because that would break the
1032  * stability of the sorting (we are guaranteeing that if plane _A_, drift
1033  * coordinate _x_, is before plane _B_, drift coordinate _x - epsilon_,
1034  * and _epsilon_ is smaller than `tolerance`, then in the group plane _A_
1035  * should still be of plane _B_.
1036  */
1037  for (geo::PlaneGeo const* plane: planes) {
1038 
1039  double const planeD = plane->GetCenter<geo::Point_t>().Dot(driftDir);
1040  // find the group before the plane
1041 
1042  auto iGroup = groupedByDrift.lower_bound(planeD);
1043 
1044  //
1045  // if the group we found is compatible with the plane, we add it in;
1046  // the key is the upper limit of the group coordinate range
1047  //
1048  if ((iGroup != groupedByDrift.end())
1049  && (iGroup->first - planeD <= tolerance))
1050  {
1051  iGroup->second.push_back(plane);
1052  continue;
1053  }
1054  //
1055  // the plane has a drift coordinate too small to join the group we found;
1056  // or we did not find any; so we create a new group centered on this plane;
1057  // the key is the right end of the allowed range, to simplify both search
1058  // and check
1059  //
1060  groupedByDrift.emplace_hint
1061  (iGroup, planeD + tolerance / 2.0, PlaneColl_t({ plane }));
1062 
1063  } // for
1064 
1065  //
1066  // moving the groups into the result structure; they'll be sorted by
1067  // increasing nominal drift coordinate
1068  //
1069  std::vector<PlaneColl_t> groups;
1070  groups.reserve(groupedByDrift.size());
1071  for (auto&& group: groupedByDrift)
1072  groups.push_back(std::move(std::get<1U>(group)));
1073  return groups;
1074 
1075 } // icarus::details::ROPandTPCsetBuildingAlg::groupPlanesByDriftCoord()
1076 
1077 
1078 // ----------------------------------------------------------------------------
1080  (std::vector<geo::PlaneGeo const*> const& planes)
1081 {
1082  return transformCollection(planes,
1083  [](geo::PlaneGeo const* plane){ return plane->ID().asTPCID(); }
1084  );
1085 } // icarus::details::ROPandTPCsetBuildingAlg::extractTPCIDs()
1086 
1087 
1088 // -----------------------------------------------------------------------------
1091  (PlaneColl_t const& planes)
1092 {
1094  for (geo::PlaneGeo const* plane: planes) {
1095  if (!plane) continue;
1096 
1097  auto const fromPlane
1098  = static_cast<readout::ROPID::ROPID_t>(plane->ID().Plane);
1099 
1100  if (r == readout::ROPID::getInvalidID())
1101  r = fromPlane;
1102  else if (r != fromPlane)
1104 
1105  } // for
1106  return r;
1107 } // icarus::details::ROPandTPCsetBuildingAlg::ROPnumberFromPlanes()
1108 
1109 
1110 // -----------------------------------------------------------------------------
1112  std::vector<geo::TPCID> const& ROPTPCIDs,
1113  std::vector<geo::TPCID> const& TPCsetTPCIDs
1114 ) {
1115 
1116  for (geo::TPCID const& ROPTPC: ROPTPCIDs) {
1117  bool found = false;
1118  for (geo::TPCID const& TPC: TPCsetTPCIDs) {
1119  if (TPC != ROPTPC) continue;
1120  found = true;
1121  break;
1122  } // for all TPCs in set
1123  if (found) continue;
1124  return false;
1125  } // for all TPCs in ROP
1126  return true;
1127 } // icarus::details::ROPandTPCsetBuildingAlg::isROPinTPCset()
1128 
1129 
1130 // ----------------------------------------------------------------------------
void reset()
Sets all the elements to a default-constructed value_type.
typename Coll_t::value_type Value_t
ROOT::Math::DisplacementVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Vector_t
Type for representation of momenta in 3D space.
Definition: geo_vectors.h:164
readout::TPCsetDataContainer< TPCColl_t > fTPCsetTPCs
Output: TPC&#39;s in each TPC set.
void fillTPCsInSet(std::vector< std::vector< std::vector< geo::TPCID >>> const &AllTPCsOnROPs)
Extracts the final set of TPC sets.
static std::string ViewName(geo::View_t view)
Returns the name of the specified view.
Definition: PlaneGeo.cxx:763
static bool addNewValue(Coll_t &coll, Value_t const &value)
Container with one element per readout TPC set.
static constexpr Sample_t transform(Sample_t sample)
Classes identifying readout-related concepts.
static constexpr ROPID_t getInvalidID()
Return the value of the invalid ROP ID as a r-value.
Definition of util::enumerate().
enum geo::_plane_proj View_t
Enumerate the possible plane projections.
Unknown view.
Definition: geo_types.h:136
unsigned int ROPID_t
Type for the ID number.
unsigned int fMaxTPCsets
Highest number of TPC sets in any cryostat.
readout::TPCsetDataContainer< unsigned int > fROPcount
Output: number of readout planes in each TPC set.
auto const tolerance
BEGIN_PROLOG opflashtpc1 TPCs
unsigned short TPCsetID_t
Type for the ID number.
Definition: readout_types.h:71
Geometry information for a single TPC.
Definition: TPCGeo.h:38
Class identifying a set of TPC sharing readout channels.
Definition: readout_types.h:70
DataByView_t< readout::ROPID::ROPID_t > fAvailableROPno
The first available ROP number for each view.
std::map< geo::View_t, T > DataByView_t
Type of collection of T by view.
static std::vector< std::vector< geo::PlaneGeo const * > > groupPlanesByDriftCoord(PlaneColl const &planes, double tolerance=0.1)
Returns the planes grouped by their drift coordinate.
std::vector< geo::CryostatGeo > CryostatList_t
Type of list of cryostats.
Definition: GeometryData.h:34
std::string const fLogCategory
Log category name for messages.
Algorithm discovering the number of elements in the geometry.
std::vector< std::vector< PlaneColl_t > > groupPlanesAndTPCs()
Extracts composition of all readout planes.
void fillTPCtoTPCsetMap()
Creates the map from each TPC to its TPC set.
SetColl stableMerge(SetColl const &sets)
void setTPCset(readout::TPCsetID const &TPCset)
Rests the object to work with the specified TPC set next.
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
Vector GetNormalDirection() const
Returns the direction normal to the plane.
Definition: PlaneGeo.h:442
static bool overlap(Coll_t const &a, Coll_t const &b)
DataByView_t< unsigned int > ViewROPcounts(std::vector< PlaneColl_t > const &ROPplanes) const
Returns the number of planes with each view.
Class for approximate comparisons.
BEGIN_PROLOG TPC
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
process_name gaushit a
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
std::vector< unsigned int > fTPCsetCount
Output: number of TPC sets in each cryostat.
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:92
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
T abs(T value)
DataByView_t< readout::ROPID::ROPID_t > const fFirstROPno
The first ROP number for each view.
static readout::ROPID::ROPID_t ROPnumberFromPlanes(PlaneColl_t const &planes)
Returns ROP number matching the plane number shared by all planes.
ROPnumberDispatcher(std::vector< PlaneColl_t > const &refROP, std::string const &logCategory)
Constructor: uses refROP as reference to assign number ranges to views.
auto makeVector3DComparison(RealType threshold)
Creates a Vector3DComparison from a RealComparisons object.
static std::vector< geo::TPCID > extractTPCIDs(std::vector< geo::PlaneGeo const * > const &planes)
Returns a collection with a TPC ID for each plane in the list of planes.
std::vector< PlaneColl_t > sortByNormalCoordinate(std::vector< PlaneColl_t > const &planes) const
Returns the planes sorted by decreasing normal coordinate.
void fillPlanesInROP(readout::TPCsetDataContainer< std::vector< PlaneColl_t >> const &PlanesInProtoROPs)
Builds final readout plane information.
readout::TPCsetID fTPCset
The TPC set being served.
Geometry information for a single wire plane.The plane is represented in the geometry by a solid whic...
Definition: PlaneGeo.h:82
std::vector< std::vector< std::vector< geo::TPCID > > > extractTPCsetsFromROPs(std::vector< std::vector< PlaneColl_t >> const &planes)
Extracts all the TPC sets covered by any of the plane groups.
readout::TPCsetDataContainer< std::vector< PlaneColl_t > > groupPlanesIntoROPs(std::vector< std::vector< std::vector< geo::TPCID >>> const &AllTPCsOnROPs, std::vector< std::vector< PlaneColl_t >> &&AllPlanesInROPs)
Assigns each of the readout planes to a TPC set.
constexpr Vector Xaxis()
Returns a x axis vector of the specified type.
Definition: geo_vectors.h:215
std::tuple< std::vector< unsigned int >, readout::TPCsetDataContainer< TPCColl_t >, readout::TPCsetDataContainer< unsigned int >, readout::ROPDataContainer< PlaneColl_t >, geo::TPCDataContainer< readout::TPCsetID >, geo::PlaneDataContainer< readout::ROPID > > ResultsBase_t
Full group of results of the algorithm.
Algorithm discovering TPC sets and readout planes for ICARUS.
static SetColl_t merge(SetColl_t const &sets)
static SetColl_t mergePass(SetColl_t const &sets)
std::vector< geo::PlaneGeo const * > PlaneColl_t
Type of collection of planes (pointers to geo::PlaneGeo).
Algorithm assigning IDs to readout planes.
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
Definition of data types for geometry description.
typename SetColl_t::value_type Coll_t
Class identifying a set of planes sharing readout channels.
static geo::View_t ROPview(PlaneColl_t const &planes)
Returns the view of the planes (geo::kUnknown if mixed or none).
unsigned int fMaxROPs
Highest number of ROPs in any TPC set.
constexpr TPCID const & asTPCID() const
Conversion to TPCID (for convenience of notation).
Definition: geo_types.h:446
BEGIN_PROLOG Z planes
geo::PlaneDataContainer< readout::ROPID > fPlaneToROP
Output: the ROP each wire plane belongs to.
readout::ROPDataContainer< PlaneColl_t > fROPplanes
Output: planes in each of the readout plane.
unsigned int CryostatID_t
Type for the ID number.
Definition: geo_types.h:191
geo::TPCDataContainer< readout::TPCsetID > fTPCtoTPCset
Output: the TPC set each TPC belongs to.
DataByView_t< readout::ROPID::ROPID_t > PreferredROPrangesByView(std::vector< PlaneColl_t > const &ROPs) const
Returns the fist ROP number for each view in ROP.
double DistanceFromPlane(geo::Point_t const &point) const
Returns the distance of the specified point from the wire plane.
Definition: PlaneGeo.h:621
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Definition: CryostatGeo.cxx:93
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
static Coll_t mergeColls(Coll_t const &a, Coll_t const &b)
geo::PlaneID const & ID() const
Returns the identifier of this plane.
Definition: PlaneGeo.h:203
Functions pulling in STL customization if available.
readout::ROPID assignID(PlaneColl_t const &ROP)
Returns the next available ID for the specified ROP.
void fillPlaneToROPmap()
Creates the map from each wire plane to its readout plane.
do i e
static bool isROPinTPCset(std::vector< geo::TPCID > const &ROPTPCIDs, std::vector< geo::TPCID > const &TPCsetTPCIDs)
Returns whether all the TPCs covered by a ROP are in a given TPC set.
void clear()
Destroys all the result data members.
temporary value
Results_t run(geo::GeometryData_t::CryostatList_t const &Cryostats)
Runs the algorithm as configured from start to end.
float A
Definition: dedx.py:137
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:184
void checkNormalDirection(std::vector< PlaneColl_t > const &planes) const
Throws an exception if planes do not share the same normal direction.
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
esac echo uname r
The data type to uniquely identify a cryostat.
Definition: geo_types.h:190
geo::CryostatID const & ID() const
Returns the identifier of this cryostat.
Definition: CryostatGeo.h:132