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