LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
DriftPartitions.cxx
Go to the documentation of this file.
1 
11 
12 
13 // LArSoft libraries
18 #include "larcorealg/CoreUtils/DumpUtils.h" // lar::dump::vector3D(), ...
20 
21 // utility libraries
22 #include "cetlib_except/exception.h"
24 
25 // common libraries
26 // #include "range/v3/view/transform.hpp" // TODO library not available in larcorealg yet
27 
28 // C/C++ standard libraries
29 #include <vector>
30 #include <iterator> // std::back_inserter(), std::distance(), ...
31 #include <algorithm> // std::transform(), std::sort(), ...
32 #include <utility> // std::pair<>, std::move()
33 #include <memory> // std::addressof(), std::unique_ptr<>, ...
34 #include <type_traits> // std::declval(), ...
35 #include <cassert>
36 #include <cmath> // std::abs()
37 #include <cstdlib> // std::size_t
38 
39 //------------------------------------------------------------------------------
40 namespace {
41 
42  //----------------------------------------------------------------------------
44  template <typename T, typename CONT>
45  void append(std::vector<T>& dest, CONT const& src) {
46  using std::cbegin;
47  using std::cend;
48  dest.insert(dest.end(), cbegin(src), cend(src));
49  } // append()
50 
52  template <typename T>
53  void append(std::vector<T>& dest, std::vector<T>&& src) {
54  if (dest.empty()) dest = std::move(src);
55  else append(dest, src);
56  } // append(vector<T>&&)
57 
58 
59  //----------------------------------------------------------------------------
61  template <typename Cont>
62  auto keys(Cont const& m) {
63  // TODO use range library instead (this temporary implementation copies keys)
64  std::vector<typename Cont::value_type::first_type> result;
65  result.reserve(m.size());
66  std::transform(
67  m.cbegin(), m.cend(),
68  std::back_inserter(result), [](auto const& p){ return p.first; }
69  );
70  return result;
71  } // keys()
72 
73 
74  //----------------------------------------------------------------------------
75 
76 } // local namespace
77 
78 
79 //------------------------------------------------------------------------------
80 //--- geo::DriftPartitions
81 //------------------------------------------------------------------------------
83  (double drift) const
84 {
85  auto iVol = volumeAfter(drift); // points to partition after the good one
86  if (iVol == volumes.cbegin()) return nullptr;
87  if (!(--iVol)->coversDrift(drift)) return nullptr; // maybe not that good?
88  return &*iVol;
89 } // geo::DriftPartitions::driftVolumeAt()
90 
91 
92 //------------------------------------------------------------------------------
94  auto comp = decomposer.DecomposePoint(pos);
95  auto volume = driftVolumeAt(comp.distance);
96  return (volume && volume->partition)
97  ? volume->partition->atPoint(comp.projection.X(), comp.projection.Y())
98  : nullptr;
99 } // geo::DriftPartitions::TPCat()
100 
101 
102 //------------------------------------------------------------------------------
103 void geo::DriftPartitions::addPartition(std::unique_ptr<TPCPartition_t>&& part)
104 {
105  auto const range = computeCoverage(*part);
106  auto iVol = volumeAfter(range.lower);
107  volumes.emplace(iVol, std::move(part), range);
108 } // geo::DriftPartitions::addPartition()
109 
110 
111 //------------------------------------------------------------------------------
113  (TPCPartition_t const& TPCpart) const -> Range_t
114 {
115  /*
116  * Computes the range of drift covered by the TPCs in the specified partition.
117  * The range is currently defined to include any drift distance covered by
118  * any single TPC.
119  * The drift distance is computed in "absolute" coordinates, meaning that the
120  * origin of the drift is at the origin of the global coordinate system.
121  * The drift direction is the one stored in this object.
122  */
123 
124  struct CoverageExtractor {
125  Range_t coverage;
126 
127  CoverageExtractor(DriftPartitions::Decomposer_t const& decomp)
128  : decomp(decomp) {}
129 
130  void operator() (TPCPartition_t const& TPCpart)
131  {
132  geo::TPCGeo const* TPC = TPCpart.data();
133  if (!TPC) return;
134  includePoint(TPC->GetCathodeCenter<Position_t>());
135  includePoint(TPC->LastPlane().GetCenter<Position_t>());
136  }
137 
138  private:
139  DriftPartitions::Decomposer_t const& decomp;
140 
141  double driftCoord(Position_t const& pos) const
142  { return decomp.PointNormalComponent(pos); }
143  void includePoint(Position_t const& pos)
144  { coverage.extendToInclude(driftCoord(pos)); }
145  }; // struct CoverageExtractor
146 
147  CoverageExtractor extractor(decomposer);
148  TPCpart.walk(extractor);
149  return extractor.coverage;
150 
151 } // DriftPartitions::computeCoverage()
152 
153 
154 //******************************************************************************
155 //*** Algorithms for building the drift volume partition.
156 //******************************************************************************
157 //------------------------------------------------------------------------------
158 // Keeps a TPC and its position as a single coordinate on a axis.
159 using TPCandPos_t = std::pair<double, geo::TPCGeo const*>;
160 
161 // Data structure collecting all TPCs in a drift volume.
162 struct TPCgroup_t {
163  double pos; // Common coordinate of the group.
164  std::vector<geo::TPCGeo const*> TPCs; // All TPCs in the group.
165 
166  // Explicit constructor.
167  TPCgroup_t(double pos, std::vector<geo::TPCGeo const*>&& TPCs)
168  : pos(pos), TPCs(std::move(TPCs)) {}
169 
170  // Returns the position of a TPC group.
171  static double Position(TPCgroup_t const& tpcg) { return tpcg.pos; }
172 
173  // Comparison object useful to sort and find TPC groups.
174  using Comparer_t
176 
177 }; // struct TPCgroup_t
178 
179 
180 //------------------------------------------------------------------------------
182  <std::pair<geo::DriftPartitions::DriftDir_t, std::vector<geo::TPCGeo const*>>>
184 {
185  /*
186  * TPCs are grouped by their drift direction, allowing for a small rounding
187  * error.
188  * The result is a collection with one element for each group of TPCs sharing
189  * the same drift direction. Each element is a pair with that drift direction
190  * first, and a collection of all TPCs with that drift direction.
191  * Elements are in no particular order.
192  */
193  std::vector<std::pair
194  <geo::DriftPartitions::DriftDir_t, std::vector<geo::TPCGeo const*>>
195  >
196  result;
197 
199  auto vectorIs = lar::util::makeVector3DComparison(coordIs);
200 
201  auto const nTPCs = cryo.NTPC();
202  for (unsigned int iTPC = 0; iTPC < nTPCs; ++iTPC) {
203  geo::TPCGeo const& TPC = cryo.TPC(iTPC);
204 
205  decltype(auto) driftDir = TPC.DriftDir();
206 
207  std::size_t iGroup = 0;
208  for (; iGroup < result.size(); ++iGroup) {
209  if (vectorIs.nonEqual(driftDir, result[iGroup].first)) continue;
210  result[iGroup].second.push_back(&TPC);
211  break;
212  } // for
213 
214  // if we did not find a group yet, make a new one
215  if (iGroup == result.size()) {
216  result.emplace_back(
217  geo::vect::rounded01(driftDir, coordIs.threshold),
218  std::vector<geo::TPCGeo const*>{ &TPC }
219  );
220  } // if
221 
222  } // for
223 
224  return result;
225 } // groupTPCsByDriftDir()
226 
227 
228 //------------------------------------------------------------------------------
229 std::vector<TPCandPos_t> sortTPCsByDriftCoord(
230  std::vector<geo::TPCGeo const*> const& TPCs,
232 ) {
233  /*
234  * TPCs in the argument are sorted by their drift coordinate.
235  * The drift coordinate is defined as the coordinate specified by the normal
236  * direction of the decomposer in argument.
237  * The result is a collection of data structures containing each a TPC and
238  * the drift coordinate that was used to sort it.
239  *
240  * Sorting happens by the drift coordinate of the first wire plane;
241  * the absolute value of the drift coordinate value is not relevant nor it is
242  * well defined.
243  * The result preserves that coordinate for further processing (grouping).
244  */
245  auto const driftCoord = [&decomp](geo::TPCGeo const& TPC)
246  { return decomp.PointNormalComponent(geo::vect::convertTo<geo::DriftPartitions::Position_t>(TPC.FirstPlane().GetCenter())); };
247 
248  std::vector<TPCandPos_t> result;
249  result.reserve(TPCs.size());
250  std::transform(TPCs.cbegin(), TPCs.cend(), std::back_inserter(result),
251  [&driftCoord](geo::TPCGeo const* pTPC)
252  { return TPCandPos_t(driftCoord(*pTPC), pTPC); }
253  );
254  // std::pair sorts by first key first, and second key on par
255  // (on par, which may happen often, we don't have means to decide here...)
256  std::sort(result.begin(), result.end());
257  return result;
258 } // sortTPCsByDriftCoord()
259 
260 
261 //------------------------------------------------------------------------------
262 std::vector<TPCgroup_t> groupByDriftCoordinate
263  (std::vector<TPCandPos_t> const& TPCs)
264 {
265  /*
266  * Produces a list of TPC groups. Within each group, the TPCs have similar
267  * drift coordinate.
268  * Similar is defined arbitrarily as ten times the plane pitch (of the first
269  * planes of the TPC).
270  */
271  if (TPCs.empty()) return {};
272 
273  geo::TPCGeo const& firstTPC = *(TPCs.front().second);
274  // arbitrary 5 cm if the first TPC has only one plane (pixel readout?);
275  // protect against the case where planes have the same position
276  // (e.g. dual phase)
277  double const groupThickness = 10.0
278  * std::min(((firstTPC.Nplanes() > 1)? firstTPC.Plane0Pitch(1): 0.5), 0.1);
279 
280  auto iFirstTPC = TPCs.cbegin(), tend = TPCs.cend();
281 
282  std::vector<TPCgroup_t> result;
283  while (iFirstTPC != tend) {
284  double const posEnd = iFirstTPC->first + groupThickness; // not beyond here
285  double sumPos = 0.0;
286  std::vector<geo::TPCGeo const*> TPCs;
287  auto iEndGroup = iFirstTPC;
288  do {
289  TPCs.push_back(iEndGroup->second);
290  sumPos += iEndGroup->first;
291  ++iEndGroup;
292  } while ((iEndGroup != tend) && (iEndGroup->first < posEnd));
293 
294  double const averagePos = sumPos / TPCs.size();
295  result.emplace_back(averagePos, std::move(TPCs));
296 
297  iFirstTPC = iEndGroup;
298  } // while (outer)
299 
300  return result;
301 } // groupByDriftCoordinate()
302 
303 
304 //------------------------------------------------------------------------------
305 unsigned int checkTPCcoords(std::vector<geo::TPCGeo const*> const& TPCs) {
306  /*
307  * Verify coordinate system consistency between TPCs:
308  * * need to have the same drift direction
309  * * need to have the same drift coordinate
310  *
311  * On error, it prints information on the error stream ("GeometryPartitions").
312  * It returns the number of errors found.
313  */
314 
315  auto iTPC = TPCs.cbegin(), tend = TPCs.cend();
316  if (iTPC == tend) {
317  mf::LogProblem("GeometryPartitions")
318  << "checkTPCcoords() got an empty partition.";
319  return 0;
320  }
321 
322  geo::TPCGeo const& refTPC = **iTPC;
323  decltype(auto) refDriftDir = refTPC.DriftDir();
324 
325  auto driftCoord = [&refDriftDir](geo::TPCGeo const& TPC)
326  { return geo::vect::dot(TPC.FirstPlane().GetCenter(), refDriftDir); };
327 
328  auto const refDriftPos = driftCoord(refTPC);
329 
331  auto vectorIs = lar::util::makeVector3DComparison(coordIs);
332 
333  unsigned int nErrors = 0U;
334  while (++iTPC != tend) {
335  geo::TPCGeo const& TPC = **iTPC;
336 
337  if (vectorIs.nonEqual(TPC.DriftDir(), refDriftDir)) {
338  mf::LogProblem("GeometryPartitions")
339  << "Incompatible drift directions between " << TPC.ID()
340  << " " << lar::dump::vector3D(TPC.DriftDir()) << " and " << refTPC.ID()
341  << " " << lar::dump::vector3D(refTPC.DriftDir());
342  ++nErrors;
343  }
344  auto const driftPos = driftCoord(TPC);
345  if (coordIs.nonEqual(driftPos, refDriftPos)) {
346  mf::LogProblem("GeometryPartitions")
347  << "Incompatible drift coordinate between " << TPC.ID()
348  << " (" << driftPos << "( and " << refTPC.ID() << " ("
349  << refDriftPos << ")";
350  ++nErrors;
351  }
352  } // while
353  return nErrors;
354 } // checkTPCcoords()
355 
356 
357 //------------------------------------------------------------------------------
358 template <typename Range>
360  /*
361  * Returns the first of the specified directions (possibly flipped);
362  * throws an exception if any of them is not parallel to that one.
363  */
364  using std::cbegin;
365  using std::cend;
366  auto iDir = cbegin(directions);
367  auto dend = cend(directions);
368  if (!(iDir != dend)) {
369  throw cet::exception("buildDriftVolumes")
370  << "detectGlobalDriftDir(): no TPCs provided!\n";
371  }
372 
374  auto compatibleDir = [comp](auto const& a, auto const& b)
375  { return comp.equal(std::abs(geo::vect::dot(a, b)), +1.0); };
376 
377  auto const dir = *(iDir++);
378  for (; iDir != dend; ++iDir) {
379  if (compatibleDir(dir, *iDir)) continue;
380  throw cet::exception("buildDriftVolumes")
381  << "Found drift directions not compatible: " << lar::dump::vector3D(dir)
382  << " and " << lar::dump::vector3D(*iDir) << "\n";
383  } // for
384 
385  // mildly prefer positive directions
386  return ((dir.X() <= 0.0) && (dir.Y() <= 0.0) && (dir.Z() <= 0.0))? -dir: dir;
387 } // detectGlobalDriftDir()
388 
389 
390 //------------------------------------------------------------------------------
391 // A TPC and the area it covers in the partition.
394 
395  geo::TPCGeo const* TPC = nullptr;
396 
397  TPCwithArea_t(Area_t area, geo::TPCGeo const* TPC)
398  : geo::part::AreaOwner(area), TPC(TPC) {}
399 
400 }; // TPCwithArea_t
401 
402 
403 //------------------------------------------------------------------------------
406 {
407  /*
408  * Returns the "area" of the TPC.
409  * The area is delimited by TPC bounding box
410  */
411 
413  { TPC.MinX(), TPC.MinY(), TPC.MinZ() };
415  { TPC.MaxX(), TPC.MaxY(), TPC.MaxZ() };
416 
417  auto const lowerProj = decomposer.ProjectPointOnPlane(lower);
418  auto const upperProj = decomposer.ProjectPointOnPlane(upper);
419 
420  // we ask to sort the ranges, since the reference base may be flipped
421  return {
422  { lowerProj.X(), upperProj.X(), true },
423  { lowerProj.Y(), upperProj.Y(), true }
424  };
425 
426 } // TPCarea()
427 
428 
429 std::vector<TPCwithArea_t> addAreaToTPCs(
430  std::vector<geo::TPCGeo const*> const& TPCs,
431  geo::DriftPartitions::Decomposer_t const& decomposer
432 ) {
433  /*
434  * Transforms a collection of TPCs into a collection of TPCs with area.
435  */
436  std::vector<TPCwithArea_t> result;
437  result.reserve(TPCs.size());
438 
439  for (auto const& TPC: TPCs)
440  result.emplace_back(TPCarea(*TPC, decomposer), TPC);
441 
442  return result;
443 } // addAreaToTPCs()
444 
445 
446 //------------------------------------------------------------------------------
447 template <typename BeginIter, typename EndIter>
449  (BeginIter TPCbegin, EndIter TPCend)
450 {
451  /*
452  * Computes an area covering the areas from all TPCs delimited by the
453  * iterators in argument.
454  * Each iterator points to a AreaOwner pointer.
455  */
456  auto iTPC = TPCbegin;
457  geo::part::AreaOwner::Area_t totalArea((*iTPC)->area());
458  while (++iTPC != TPCend) totalArea.extendToInclude((*iTPC)->area());
459  return totalArea;
460 } // computeTotalArea()
461 
462 
463 //------------------------------------------------------------------------------
464 // Class applying a comparison function to keys from the arguments.
465 template <typename Key, typename ExtractKey, typename Comparer = std::less<Key>>
466 struct SorterByKey {
467  // differently from geo::details::Comparer, this class is not focused on a
468  // specific type and its ExtractKey is not required to be a function operating
469  // on that object.
470  using Key_t = Key;
471 
472  static bool sortKey(Key_t a, Key_t b) { return Comparer()(a, b); }
473  static Key_t key(Key_t k) { return k; }
474  template <typename Data>
475  static Key_t key(Data const& obj) { return ExtractKey()(obj); }
476 
477  template <typename A, typename B>
478  bool operator() (A const& a, B const& b) const
479  { return sortKey(key(a), key(b)); }
480 
481 }; // struct SorterByKey
482 
483 
484 //------------------------------------------------------------------------------
485 // Class sorting any datum by a TPC area range lower boundary.
486 template<geo::part::AreaOwner::AreaRangeMember_t Range>
489 
494 
495 
496 //------------------------------------------------------------------------------
497 template<
499  typename BeginIter, typename EndIter
500  >
503  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
504 {
505  /*
506  * Groups each TPC with all the following ones overlapping in the selected
507  * sorting range.
508  * The range of TPCs must be already sorted by lower sorting range coordinate.
509  * The result is a list of iterators like the ones in input (in fact, the
510  * first is always beginTPCwithArea).
511  * The iterators are expected to be valid also after this function has
512  * returned (lest the result be unusable).
513  */
514 
516 
517  // tolerate 1mm overlaps; this is way forgiving, but apparently there are
518  // geometries around (DUNE 35t) with overlaps of that size.
520 
521  auto gbegin = beginTPCwithArea;
522  while (gbegin != endTPCwithArea) {
523 
524  groupStart.push_back(gbegin);
525 
526  //
527  // detect the end of this group
528  //
529  auto range = (*gbegin)->area().*sortingRange;
530  auto gend = gbegin;
531  while (++gend != endTPCwithArea) {
532  //
533  // check if the sorting range of this TPC (gend) overlaps the accumulated
534  // one; since TPCs are sorted by lower end of the range, gend has that one
535  // larger than the accumulated one, and overlap happens only if that lower
536  // bound is smaller than the upper bound of the accumulated range;
537  // we need to avoid rounding errors: close borders are decided as
538  // non-overlapping
539  //
540  auto const& TPCrange = (*gend)->area().*sortingRange;
541  if (coordIs.nonSmaller(TPCrange.lower, range.upper))
542  break;
543  range.extendToInclude(TPCrange);
544  } // while (inner)
545 
546  // prepare for the next group
547  gbegin = gend;
548  } // while (outer)
549 
550  return groupStart;
551 
552 } // groupTPCsByRangeCoord<>()
553 
554 
555 //------------------------------------------------------------------------------
556 // Returns the TPCs sorted by range coordinate, and the group limits.
557 template<
559  typename BeginIter, typename EndIter
560  >
561 std::pair<
562  std::vector<TPCwithArea_t const*>,
564  >
566  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
567 {
568  //
569  // sort by coordinate; work with pointers for convenience
570  //
571  std::vector<TPCwithArea_t const*> TPCs(beginTPCwithArea, endTPCwithArea);
572  if (TPCs.size() <= 1) return {}; // with only one TPC, refuse to operate
573  std::sort
574  (TPCs.begin(), TPCs.end(), SortTPCareaByAreaRangeLower<sortingRange>());
575 
576  //
577  // group
578  //
580  = groupTPCsByRangeCoord<sortingRange>(TPCs.cbegin(), TPCs.cend());
581  assert(!TPCgroups.empty());
582 
583  return { std::move(TPCs), std::move(TPCgroups) };
584 
585 } // sortAndGroupTPCsByRangeCoord()
586 
587 
588 //------------------------------------------------------------------------------
589 template <
590  typename BeginIter, typename EndIter, typename TPCendIter,
591  typename SubpartMaker
592  >
594  BeginIter itTPCbegin, EndIter itTPCend, TPCendIter TPCend,
595  SubpartMaker subpartMaker
596 ) {
597  /*
598  * Internal helper to create a sequence of partitions (geo::part::Partition)
599  * from groups of TPCs. Each TPC is specified as a pointer to TPCwithArea_t
600  * object.
601  *
602  * The groups are specified in a way that is more or less convenient after
603  * calling groupTPCsByRangeCoord(): the uber-iterators itTPCbegin and itTPCend
604  * delimit a collection of iterators pointing to the first TPC of a group
605  * (the pointed TPCs are on a different collection). This can define all
606  * groups, each group delimited by the TPC pointed by an iterator pointing at
607  * the beginning of that group and the TPC pointed by the next iterator,
608  * pointing at the beginning of the next group. The exception is the last
609  * group, for which there is no "next iterator pointing to the beginning of
610  * the next group". That information is provided separately by the third
611  * iterator argument, that is an iterator of a different type than the other
612  * two (which are "uber-iterators" whose values are iterators), and which
613  * points to the last available TPC, that is also the end iterator for the
614  * TPCs of the last group.
615  *
616  */
618 
619  // TPCgroups has an iterator to the starting TPC of each group.
620  // Iterators refer to the `TPCs` collection.
621  // The end iterator of the group is the starting iterator of the next one;
622  // the last group includes all the remaining TPCs and its end iterator is
623  // the end iterator of `TPCs`.
624  auto igbegin = itTPCbegin;
625  while (igbegin != itTPCend) {
626  auto const gbegin = *igbegin;
627  auto const gend = (++igbegin == itTPCend)? TPCend: *igbegin;
628 
629  //
630  // create a partition from the new group
631  //
632  if (std::distance(gbegin, gend) == 1) {
633  subparts.emplace_back(makeTPCPartitionElement(**gbegin));
634  }
635  else {
636  auto subpart = subpartMaker(gbegin, gend);
637  if (!subpart) return {}; // failure!!
638 
639  subparts.emplace_back(std::move(subpart));
640  }
641  } // while
642  return subparts;
643 } // createSubpartitions()
644 
645 
646 //------------------------------------------------------------------------------
648 {
649  return std::make_unique<geo::part::PartitionElement<geo::TPCGeo const>>
650  (TPCinfo.area(), TPCinfo.TPC);
651 }
652 
653 
654 //------------------------------------------------------------------------------
655 // prototypes for makePartition-type functions: they call each other.
656 template <typename BeginIter, typename EndIter>
657 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeWidthPartition
658  (BeginIter beginTPCwithArea, EndIter endTPCwithArea);
659 template <typename BeginIter, typename EndIter>
660 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeDepthPartition
661  (BeginIter beginTPCwithArea, EndIter endTPCwithArea);
662 template <typename BeginIter, typename EndIter>
663 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeGridPartition
664  (BeginIter beginTPCwithArea, EndIter endTPCwithArea);
665 template <typename BeginIter, typename EndIter>
666 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makePartition
667  (BeginIter beginTPCwithArea, EndIter endTPCwithArea);
668 
669 
670 //------------------------------------------------------------------------------
671 template <
672  typename TPCPartitionResultType,
674  typename BeginIter, typename EndIter,
675  typename SubpartMaker
676  >
677 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeSortedPartition(
678  BeginIter beginTPCwithArea, EndIter endTPCwithArea,
679  SubpartMaker subpartMaker
680 ) {
681  /*
682  * TPCs in input are arranged into a partition split on width direction.
683  * In case of failure, a null pointer is returned.
684  * Do not use this function for a single TPC.
685  * The algorithm is as follows:
686  *
687  * 1. sort the TPCs by width coordinate
688  * 2. for each one, group it with all the following ones overlapping in width
689  * 3. for each group with:
690  * .1 more than one element:
691  * .1 let the group be partitioned on depth
692  * .2 if that fails, we fail
693  * .2 just one element: create a partition element
694  * 4. the resulting partition is the list of one-TPC "groups" and depth-based
695  * subpartitions of many-TPC groups
696  * 5. if the result is a single partition, it must be a depth partition;
697  * then, there is no room for a partition along width, and we declare
698  * failure
699  */
700 
701  //
702  // sort by coordinate and group TPCs; work with pointers for convenience
703  //
704  auto const TPCgroupInfo
705  = sortAndGroupTPCsByRangeCoord<Range>(beginTPCwithArea, endTPCwithArea);
706  std::vector<TPCwithArea_t const*> const& TPCs = TPCgroupInfo.first;
708  TPCgroups = TPCgroupInfo.second;
709 
710  if (TPCs.empty()) return {}; // failure?
711 
712  //
713  // for each group, create a subpartition
714  //
715  auto subparts = createSubpartitions
716  (TPCgroups.cbegin(), TPCgroups.cend(), TPCs.cend(), subpartMaker);
717 
718  // if we have grouped everything in a single unit, we have not done any good
719  if (subparts.size() == 1) return {};
720 
721  //
722  // compute the total area (it might have been merged in a previous loop...)
723  //
724  auto totalArea = computeTotalArea(TPCs.cbegin(), TPCs.cend());
725 
726  //
727  // construct and return the final partition
728  //
729  return std::make_unique<TPCPartitionResultType>
730  (totalArea, std::move(subparts));
731 
732 } // makeSortedPartition()
733 
734 
735 //------------------------------------------------------------------------------
736 template <typename BeginIter, typename EndIter>
737 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeWidthPartition
738  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
739 {
740  return makeSortedPartition<
743  >
744  (beginTPCwithArea, endTPCwithArea, &makeDepthPartition<BeginIter, EndIter>);
745 } // makeWidthPartition()
746 
747 
748 template <typename BeginIter, typename EndIter>
749 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeDepthPartition
750  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
751 {
752  return makeSortedPartition<
755  >
756  (beginTPCwithArea, endTPCwithArea, &makeWidthPartition<BeginIter, EndIter>);
757 } // makeDepthPartition()
758 
759 
760 //------------------------------------------------------------------------------
761 template <typename BeginIter, typename EndIter>
762 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makeGridPartition
763  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
764 {
765  /*
766  * Requires at least 4 input TPCs (otherwise, do not use GridPartition).
767  *
768  * 1. attempt a partition on width
769  * 1. if failed, return failure
770  * 2. attempt to partition the first subpartition on depth
771  * 1. if failed, return failure
772  * 3. extend each depth partition to the other width partitions
773  * 1. if the extension of one partition line fails, discard it
774  * 2. if no partition line survives, return failure
775  * 4. run makePartition() on each of the cells
776  * 5. create and return a GridPartition object from the subpartitions so
777  * created
778  *
779  * This algorithm could use some factorization...
780  */
781  using Area_t = geo::part::AreaOwner::Area_t;
782 
783  //
784  // sort by width coordinate; work with pointers for convenience
785  //
786  auto const TPCgroupInfo = sortAndGroupTPCsByRangeCoord<&Area_t::width>
787  (beginTPCwithArea, endTPCwithArea);
788  std::vector<TPCwithArea_t const*> const& TPCs = TPCgroupInfo.first;
790  TPCgroups = TPCgroupInfo.second;
791 
792  if (TPCs.empty()) return {}; // failure?
793  // with only one TPC, then makeTPCPartitionElement() should be used instead!
794  if (TPCs.size() < 4) return {};
795 
796  unsigned int const nWidthParts = TPCgroups.size();
797  if (nWidthParts <= 1) return {}; // only one group ain't no good
798 
799  //
800  // sort TPCs in the first width partition by depth coordinate
801  //
802  auto const FirstColGroupInfo
803  = sortAndGroupTPCsByRangeCoord<&Area_t::depth>(TPCgroups[0], TPCgroups[1]);
804  std::vector<TPCwithArea_t const*> const& FirstColTPCs
805  = FirstColGroupInfo.first;
807  FirstColGroups = FirstColGroupInfo.second;
808 
809  if (FirstColTPCs.empty()) return {}; // failure?
810  if (FirstColGroups.size() <= 1 ) return {}; // only one row ain't good either
811 
812  //
813  // collect all candidate separation ranges
814  //
815  // First depth partition has no lower limit, last one has no upper limit
816  // (they include all TPCs with depth lower than the upper limit in the first
817  // case, all TPCs with depth higher of the lower limit in the last case).
818  // Checks need to be done in the gaps between depth partitions.
819  // So we start by skipping the first border.
820 
822  std::vector<Area_t::Range_t> depthGaps; // candidate gaps
823  auto icnext = FirstColGroups.cbegin(), icprev = icnext,
824  icend = FirstColGroups.cend();
825  while (++icnext != icend) {
826  //
827  // establish the range of the group in the candidate depth partition
828  // from the group in the first width partition
829  //
830  auto const cprev = *icprev;
831  auto const cnext = *icnext;
832 
833  depthGaps.emplace_back
834  ((*cprev)->area().depth.upper, (*cnext)->area().depth.lower);
835 
836  icprev = icnext;
837  } // while
838  assert(!depthGaps.empty());
839 
840  //
841  // see that for every other width partition separations hold
842  //
843  auto igbegin = TPCgroups.cbegin();
844  while (++igbegin != TPCgroups.cend()) {
845  //
846  // prepare the TPC groups within this width partition
847  //
848  auto igend = std::next(igbegin);
849  auto gbegin = *igbegin;
850  auto gend = (igend == TPCgroups.cend())? TPCs.cend(): *igend;
851 
852  auto const ColGroupInfo
853  = sortAndGroupTPCsByRangeCoord<&Area_t::depth>(gbegin, gend);
854  std::vector<TPCwithArea_t const*> const& ColTPCs = ColGroupInfo.first;
856  ColGroups = ColGroupInfo.second;
857 
858  // failure to partition a single column means total failure
859  if (ColTPCs.empty()) return {};
860  if (ColGroups.size() <= 1) return {}; // only one row ain't good either
861 
862  //
863  // compute the coverage of each of the depth groups
864  //
865  std::vector<TPCwithArea_t::Area_t::Range_t> groupDepths(ColGroups.size());
866  auto iGDepth = groupDepths.begin();
867  for (auto icgstart = ColGroups.cbegin(); icgstart != ColGroups.cend();
868  ++icgstart, ++iGDepth)
869  {
870  auto const icgend = std::next(icgstart);
871  auto ictpc = *icgstart;
872  auto const ictend = (icgend == ColGroups.cend())? ColTPCs.cend(): *icgend;
873  while (ictpc != ictend)
874  iGDepth->extendToInclude((*(ictpc++))->area().depth);
875  } // for
876 
877  //
878  // check each of the remaining candidate gaps
879  //
880  auto iGap = depthGaps.begin();
881  while (iGap != depthGaps.end()) {
882  Area_t::Range_t& gap = *iGap;
883 
884  //
885  // check that the gap holds
886  //
887  bool bGoodGap = false;
888  // first TPC starting after the gap (even immediately after):
889  auto iCGroup = std::lower_bound(
890  groupDepths.cbegin(), groupDepths.cend(), gap.upper,
892  );
893 
894  // any TPCs before/after this gap?
895  if ((iCGroup != groupDepths.begin()) && (iCGroup != groupDepths.end())) {
896  Area_t::Range_t const& before = *(std::prev(iCGroup));
897  Area_t::Range_t const& after = *iCGroup;
898  Area_t::Range_t const TPCgap{ before.upper, after.lower };
899 
900  // correct the gap
901  if (coordIs.strictlySmaller(iGap->lower, TPCgap.lower))
902  iGap->lower = TPCgap.lower;
903  if (coordIs.strictlyGreater(iGap->upper, TPCgap.upper))
904  iGap->upper = TPCgap.upper;
905 
906  // if nothing is left, gap is gone
907  bGoodGap = coordIs.nonSmaller(iGap->upper, iGap->lower);
908  } // if TPCs around the gap
909 
910  //
911  // if the gap has been flagged as bad, remove it
912  //
913  if (bGoodGap) ++iGap;
914  else iGap = depthGaps.erase(iGap);
915 
916  } // while (separation)
917 
918  if (depthGaps.empty()) return {}; // no surviving gaps means failure
919 
920  } // while (width partition)
921 
922  //
923  // turn the gaps into separators
924  //
925  std::vector<double> depthSep;
926  std::transform(
927  depthGaps.cbegin(), depthGaps.cend(), std::back_inserter(depthSep),
928  [](auto const& r){ return (r.lower + r.upper) / 2.0; }
929  );
930  unsigned int const nDepthParts = depthSep.size() + 1;
931 
932  //
933  // fill the groups with TPCs, and create subpartitions from each of them
934  //
936  (nWidthParts * nDepthParts);
937  Area_t totalArea;
938 
939  unsigned int iWidth = 0;
940  for (auto igbegin = TPCgroups.cbegin(); igbegin != TPCgroups.cend();
941  ++igbegin, ++iWidth
942  ) {
943 
944  // sort TPCs in this group (yes, again; this time we don't group just yet)
945  auto igend = std::next(igbegin);
946  auto gbegin = *igbegin;
947  auto gend = (igend == TPCgroups.cend())? TPCs.cend(): *igend;
948  std::vector<TPCwithArea_t const*> ColTPCs(gbegin, gend);
949  std::sort(ColTPCs.begin(), ColTPCs.end(),
951 
952  unsigned int iDepth = 0;
953  auto cgstart = ColTPCs.cbegin(), TPCend = ColTPCs.cend();
954  for (double sep: depthSep) {
955 
956  //
957  // collect all TPCs for this partition
958  //
959  // the first TPC that starts *after* the separator:
960  auto cgend
961  = std::upper_bound(cgstart, TPCend, sep, SortTPCwithAreaByDepth());
962  // if we cut out TPCs that were included because of some tolerance,
963  // recover them now
964  while (cgend != cgstart) {
965  auto cglast = std::prev(cgend);
966  if (coordIs.strictlySmaller((*cglast)->area().depth.lower, sep)) break;
967  cgend = cglast;
968  } // while
969  assert(cgstart != cgend); // separator selection should guarantee this
970 
971  //
972  // create and register the partition
973  //
974  auto part = makePartition(cgstart, cgend);
975  if (!part) return {}; // late failure!
976  totalArea.extendToInclude(part->area());
977  subparts[iDepth * nWidthParts + iWidth] = std::move(part);
978 
979  ++iDepth;
980  cgstart = cgend;
981  } // for all depth separators
982 
983  //
984  // collect all the TPCs after the last separator
985  //
986  auto part = makePartition(cgstart, TPCend);
987  if (!part) return {}; // super-late failure!
988  totalArea.extendToInclude(part->area());
989  subparts[iDepth * nWidthParts + iWidth] = std::move(part);
990 
991  } // for all width partitions
992 
993  return std::make_unique<geo::part::GridPartition<geo::TPCGeo const>>
994  (totalArea, std::move(subparts), nWidthParts, nDepthParts);
995 
996 } // makeGridPartition()
997 
998 
999 //------------------------------------------------------------------------------
1000 template <typename BeginIter, typename EndIter>
1001 std::unique_ptr<geo::part::Partition<geo::TPCGeo const>> makePartition
1002  (BeginIter beginTPCwithArea, EndIter endTPCwithArea)
1003 {
1004  /*
1005  * Organizes a list of TPCs in a hierarchical partition.
1006  * Three main elements are used:
1007  * - single element partition objects: that's the single TPC end point
1008  * - TPC groups organised along width
1009  * - TPC groups organised along depth
1010  *
1011  * The procedure is recursively analysing a set of TPCs:
1012  * - if the set is actually one TPC only, use a PartitionElement
1013  * - attempt partitioning o a grid; if fails:
1014  * - attempt partitioning along width:
1015  * * determine overlapping groups: a set of TPCs which share part of the
1016  * width range
1017  * * recurse on each overlapping group with more than one TPC,
1018  * attempting a depth partition
1019  * * if that fails, bail out since we don't have code to deal with a layout
1020  * with areas overlapping on both directions at the same time
1021  * * add the single elements and the overlapping groups to the width
1022  * partition
1023  * - attempt partitioning along height:
1024  * * same algorithm as for width
1025  * - pick the partitioning with less elements
1026  */
1027  using value_type = std::remove_reference_t<decltype(*beginTPCwithArea)>;
1028  static_assert(
1029  std::is_pointer<value_type>()
1030  && std::is_same
1031  <std::decay_t<std::remove_pointer_t<value_type>>, TPCwithArea_t>(),
1032  "Iterators must point to TPCwithArea_t pointers."
1033  );
1034 
1035 
1036  auto const size = std::distance(beginTPCwithArea, endTPCwithArea);
1037  if (size == 1) {
1038  return makeTPCPartitionElement(**beginTPCwithArea);
1039  }
1040 
1041  auto gPart = makeGridPartition(beginTPCwithArea, endTPCwithArea);
1042  if (gPart) return gPart;
1043 
1044  auto wPart = makeWidthPartition(beginTPCwithArea, endTPCwithArea);
1045  auto dPart = makeDepthPartition(beginTPCwithArea, endTPCwithArea);
1046 
1047  if (wPart) {
1048 
1049  if (dPart) { // wPart && dPart
1050  if (wPart->nParts() < dPart->nParts()) return wPart;
1051  else return dPart; // slight preference
1052  }
1053  else { // wPart && !dPart
1054  return wPart; // easy choice
1055  }
1056 
1057  }
1058  else {
1059 
1060  if (dPart) { // !wPart && dPart
1061  return dPart; // easy choice
1062  }
1063  else { // !wPart && !dPart
1064  return {}; // failure!!
1065  }
1066 
1067  }
1068 
1069 } // makePartition(Iter)
1070 
1071 
1072 //------------------------------------------------------------------------------
1073 template <typename BeginIter, typename EndIter>
1074 auto makeCPointerVector(BeginIter b, EndIter e) {
1075  using value_type = typename BeginIter::value_type;
1076  std::vector<value_type const*> result;
1077  result.reserve(std::distance(b, e));
1078  std::transform(b, e, std::back_inserter(result),
1079  [](auto& obj){ return std::addressof(obj); });
1080  return result;
1081 } // makeCPointerVector()
1082 
1083 template <typename T>
1084 auto makeCPointerVector(std::vector<T> const& v)
1085  { return makeCPointerVector(v.cbegin(), v.cend()); }
1086 
1087 
1088 //------------------------------------------------------------------------------
1089 std::unique_ptr<geo::DriftPartitions::TPCPartition_t> makePartition
1090  (std::vector<TPCwithArea_t> const& TPCs)
1091 {
1092  // TODO use range library instead:
1093 // auto TPCptrs = TPCs | ranges::view::transform(std::addressof);
1094  auto TPCptrs = makeCPointerVector(TPCs);
1095  using std::cbegin;
1096  using std::cend;
1097  return makePartition(cbegin(TPCptrs), cend(TPCptrs));
1098 } // makePartition(coll)
1099 
1100 
1101 //------------------------------------------------------------------------------
1103 
1104  //
1105  // group TPCs by drift direction
1106  //
1107  auto TPCsByDriftDir = groupTPCsByDriftDir(cryo);
1108 
1109  //
1110  // determine the cryostat-wide drift direction (arbitrary but consistent)
1111  // and the decomposition base (using the same for all drift partitions);
1112  //
1113 
1114  // In practice we use the coordinate system from the first TPC;
1115  // we still check that all drift directions are compatible,
1116  // but the result of detection is ignored.
1117  /* auto globalDriftDir = */ detectGlobalDriftDir(keys(TPCsByDriftDir));
1119  geo::TPCGeo const& firstTPC = cryo.TPC(0);
1121  ({ cryo.Center(), firstTPC.RefWidthDir<Direction_t>(), firstTPC.RefDepthDir<Direction_t>() });
1122 
1123  //
1124  // further group TPCs by plane position in drift direction
1125  //
1126  std::vector<TPCgroup_t> TPCgroups;
1127  for (auto const& TPCsOnDriftDir: TPCsByDriftDir) {
1128  auto TPCs = sortTPCsByDriftCoord(TPCsOnDriftDir.second, decomposer);
1129  append(TPCgroups, groupByDriftCoordinate(TPCs));
1130  } // for
1131 
1132  //
1133  // verify coordinate system consistency between TPCs
1134  //
1135  for (auto const& TPCgroup: TPCgroups) {
1136  unsigned int errors = checkTPCcoords(TPCgroup.TPCs);
1137  if (errors > 0) {
1138  throw cet::exception("buildDriftVolumes")
1139  << "TPCs in partition have different drift directions ("
1140  << errors << " errors found in " << TPCgroup.TPCs.size() << " TPCs).\n";
1141  } // if
1142  } // for
1143 
1144  //
1145  // partition each group
1146  //
1147  geo::DriftPartitions partitions(decomposer);
1148  for (auto const& TPCgroup: TPCgroups) {
1149  auto TPCs = addAreaToTPCs(TPCgroup.TPCs, decomposer);
1150  auto part = makePartition(TPCs);
1151  if (!part) {
1152  cet::exception e("buildDriftVolumes");
1153  e << "Failed to construct partition out of " << TPCs.size() << " TPCs:";
1154  for (auto const& TPCinfo: TPCs) {
1155  e << "\n at " << TPCinfo.area() << " TPC ";
1156  TPCinfo.TPC->PrintTPCInfo(e, " ", 5U);
1157  } // for
1158  throw e;
1159  } // if error
1160  partitions.addPartition(std::move(part));
1161  } // for
1162 
1163  return partitions;
1164 } // geo::buildDriftVolumes()
1165 
1166 
1167 //------------------------------------------------------------------------------
Range_t computeCoverage(TPCPartition_t const &TPCpart) const
Computes the coverage of the specified partition in the drift direction.
geo::TPCID const & ID() const
Returns the identifier of this TPC.
Definition: TPCGeo.h:278
double driftCoord(Position_t const &pos) const
Returns drift coordinate (in the drift-volume-specific frame) of pos.
Data structures and algorithms to partition a cryostat volume.
A basic interface for objects owning an area.
Definition: Partitions.h:39
Vector DriftDir() const
Returns the direction of the drift (vector pointing toward the planes).
Definition: TPCGeo.h:687
Decomposer_t decomposer
Decomposition on drift, width and depth axes.
std::unique_ptr< geo::part::Partition< geo::TPCGeo const > > makePartition(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
geo::TPCGeo const * TPC
auto vector3D(Vector3D const &v)
Returns a manipulator which will print the specified vector.
Definition: DumpUtils.h:301
constexpr auto dot(Vector const &a, Vector const &b)
Return cross product of two vectors.
auto PointNormalComponent(Point_t const &point) const
Returns the secondary component of a point.
Definition: Decomposer.h:486
static Key_t key(Data const &obj)
geo::part::Partition< geo::TPCGeo const >::Subpartitions_t createSubpartitions(BeginIter itTPCbegin, EndIter itTPCend, TPCendIter TPCend, SubpartMaker subpartMaker)
constexpr bool nonEqual(Value_t a, Value_t b) const
Returns whether a and b are farther than the threshold.
Partition of area along the width dimension.
Definition: Partitions.h:489
Encapsulate the construction of a single cyostat.
geo::part::AreaOwner::Area_t computeTotalArea(BeginIter TPCbegin, EndIter TPCend)
unsigned int Nplanes() const
Number of planes in this tpc.
Definition: TPCGeo.h:145
Provides simple real number checks.
TPCgroup_t(double pos, std::vector< geo::TPCGeo const * > &&TPCs)
Int_t B
Definition: plot.C:25
std::vector< DriftVolume_t >::iterator volumeAfter(double pos)
Returns an iterator to the drift volume starting after pos.
Point GetCathodeCenter() const
Definition: TPCGeo.h:253
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:90
void extendToInclude(Data_t)
Extends the range to include the specified point.
Definition: SimpleGeo.h:456
Geometry information for a single TPC.
Definition: TPCGeo.h:37
SortTPCareaByAreaRangeLower<&geo::part::AreaOwner::Area_t::depth > SortTPCwithAreaByDepth
STL namespace.
DriftPartitions buildDriftVolumes(geo::CryostatGeo const &cryo)
Creates a DriftPartitions object from the TPCs in a cryostat.
Vector RefWidthDir() const
Return the direction of reference plane width.
Definition: TPCGeo.h:316
double MaxX() const
Returns the world x coordinate of the end of the box.
Definition: BoxBoundedGeo.h:93
virtual Data_t * data() const
Returns the datum directly stored (nullptr if none).
Definition: Partitions.h:205
Geometry information for a single cryostat.
Definition: CryostatGeo.h:36
geo::part::AreaOwner::Area_t TPCarea(geo::TPCGeo const &TPC, geo::DriftPartitions::Decomposer_t const &decomposer)
geo::TPCGeo const * TPCat(Position_t const &pos) const
Returns which TPC contains the specified position (nullptr if none).
Class for approximate comparisons.
Projection_t ProjectPointOnPlane(Point_t const &point) const
Returns the projection of the specified point on the plane.
Definition: Decomposer.h:500
std::vector< TPCwithArea_t > addAreaToTPCs(std::vector< geo::TPCGeo const * > const &TPCs, geo::DriftPartitions::Decomposer_t const &decomposer)
constexpr bool strictlySmaller(Value_t a, Value_t b) const
Returns whether a is strictly smaller than b.
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
MaybeLogger_< ELseverityLevel::ELsev_error, true > LogProblem
constexpr bool strictlyGreater(Value_t a, Value_t b) const
Returns whether a is strictly greater than b.
std::vector< std::unique_ptr< Partition_t const >> Subpartitions_t
Type of list of subpartitions. It needs to preserve polymorphism.
Definition: Partitions.h:196
Base element of a partitioned structure.
Definition: Partitions.h:188
Partition of area along the depth dimension.
Definition: Partitions.h:464
DriftVolume_t const * driftVolumeAt(Position_t const &pos) const
if(nlines<=0)
auto makeCPointerVector(BeginIter b, EndIter e)
Utilities to dump objects into a stream.
intermediate_table::const_iterator const_iterator
Vector RefDepthDir() const
Return the direction of reference plane depth.
Definition: TPCGeo.h:329
auto makeVector3DComparison(RealType threshold)
Creates a Vector3DComparison from a RealComparisons object.
geo::Point_t Position_t
Type representing a position in 3D space.
std::unique_ptr< geo::part::Partition< geo::TPCGeo const > > makeDepthPartition(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
Classes to project and compose a vector on a plane.
geo::DriftPartitions::DriftDir_t detectGlobalDriftDir(Range &&directions)
static bool sortKey(Key_t a, Key_t b)
std::unique_ptr< geo::part::Partition< geo::TPCGeo const > > makeWidthPartition(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
lar::util::simple_geo::Rectangle< double > Area_t
Type of area covered by the partition.
Definition: Partitions.h:43
static double Position(TPCgroup_t const &tpcg)
Point GetCenter() const
Returns the centre of the wire plane in world coordinates [cm].
Definition: PlaneGeo.h:426
TString part[npart]
Definition: Style.C:32
Class managing comparisons between T objects via a Key key.
double MinZ() const
Returns the world z coordinate of the start of the box.
Area_t const & area() const
Returns the covered area.
Definition: Partitions.h:56
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:155
DecomposedVector_t DecomposePoint(Point_t const &point) const
Decomposes a 3D point in two components.
Definition: Decomposer.h:517
std::pair< double, geo::TPCGeo const * > TPCandPos_t
std::pair< std::vector< TPCwithArea_t const * >, std::vector< std::vector< TPCwithArea_t const * >::const_iterator > > sortAndGroupTPCsByRangeCoord(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
void walk(Pred &&pred) const
Applies pred to all partitions.
Definition: Partitions.h:246
Range_t width
Range along width direction.
Definition: SimpleGeo.h:393
auto makeTPCPartitionElement(TPCwithArea_t const &TPCinfo)
double Plane0Pitch(unsigned int p) const
Returns the center of the TPC volume in world coordinates [cm].
Definition: TPCGeo.cxx:354
Provides a base class aware of world box coordinates.
Vector rounded01(Vector const &v, Scalar tol)
Returns a vector with all components rounded if close to 0, -1 or +1.
double MaxY() const
Returns the world y coordinate of the end of the box.
void addPartition(std::unique_ptr< TPCPartition_t > &&part)
Adds the specified partition as a new drift volume.
Range_t depth
Range along depth direction.
Definition: SimpleGeo.h:394
std::vector< TPCgroup_t > groupByDriftCoordinate(std::vector< TPCandPos_t > const &TPCs)
std::vector< DriftVolume_t > volumes
All drift volumes, sorted by position.
TDirectory * dir
Definition: macro.C:5
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Int_t min
Definition: plot.C:26
std::unique_ptr< geo::part::Partition< geo::TPCGeo const > > makeSortedPartition(BeginIter beginTPCwithArea, EndIter endTPCwithArea, SubpartMaker subpartMaker)
double MaxZ() const
Returns the world z coordinate of the end of the box.
std::vector< std::pair< geo::DriftPartitions::DriftDir_t, std::vector< geo::TPCGeo const * > > > groupTPCsByDriftDir(geo::CryostatGeo const &cryo)
geo::PlaneGeo const & LastPlane() const
Returns the last wire plane (the farther from TPC center).
Definition: TPCGeo.h:228
std::vector< geo::TPCGeo const * > TPCs
TPCwithArea_t(Area_t area, geo::TPCGeo const *TPC)
void extendToInclude(Rectangle_t const &r)
Extends the range to include the specified point.
Definition: SimpleGeo.h:517
constexpr bool nonSmaller(Value_t a, Value_t b) const
Returns whether a is greater than (or equal to) b.
static Key_t key(Key_t k)
Set of drift volumes.
Direction_t DriftDir_t
Type representing the drift direction (assumed to have norm 1).
constexpr bool equal(Value_t a, Value_t b) const
Returns whether a and b are no farther than the threshold.
Float_t e
Definition: plot.C:34
Data associated to a single drift volume.
std::unique_ptr< geo::part::Partition< geo::TPCGeo const > > makeGridPartition(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
Namespace collecting geometry-related classes utilities.
std::vector< std::vector< TPCwithArea_t const * >::const_iterator > groupTPCsByRangeCoord(BeginIter beginTPCwithArea, EndIter endTPCwithArea)
double MinY() const
Returns the world y coordinate of the start of the box.
unsigned int checkTPCcoords(std::vector< geo::TPCGeo const * > const &TPCs)
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
Area_t::Range_t(Area_t::*) AreaRangeMember_t
Type of pointer to Area_t data member of type Range_t.
Definition: Partitions.h:46
Encapsulate the construction of a single detector plane.
geo::Vector_t Direction_t
Type representing a direction in 3D space (norm is not constrained).
geo::Point_t Center() const
Returns the center point of the box.
std::vector< TPCandPos_t > sortTPCsByDriftCoord(std::vector< geo::TPCGeo const * > const &TPCs, geo::DriftPartitions::Decomposer_t const &decomp)