All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
WindowTopologyAlg.cxx
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/Trigger/Algorithms/WindowTopologyAlg.cxx
3  * @brief Assembles the topology of trigger windows (implementation file).
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 25, 2021
6  * @see icaruscode/PMT/Trigger/Algorithms/WindowTopologyAlg.h
7  */
8 
9 
10 // library header
12 
13 // ICARUS libraries
14 #include "icarusalg/Utilities/sortBy.h" // also icarus::util::sortCollBy()
15 
16 // LArSoft libraries
21 #include "larcorealg/Geometry/geo_vectors_utils.h" // MiddlePointAccumulator
24 
25 // C/C++ standard libraries
26 #include <algorithm> // std::move(), std::sort()
27 #include <array> // std::move()
28 #include <utility> // std::move()
29 #include <cassert>
30 
31 
32 //------------------------------------------------------------------------------
33 namespace {
34 
35  template <typename T>
36  std::vector<T>& append(std::vector<T>& destColl, std::vector<T>&& srcColl) {
37 
38  if (destColl.empty()) destColl = std::move(srcColl);
39  else {
40  destColl.reserve(destColl.size() + srcColl.size());
41  std::move(srcColl.begin(), srcColl.end(), back_inserter(destColl));
42  srcColl.clear();
43  }
44 
45  return destColl;
46  } // append()
47 
48 } // local namespace
49 
50 
51 //------------------------------------------------------------------------------
52 //--- icarus::trigger::WindowTopologyAlg
53 //------------------------------------------------------------------------------
55  geo::GeometryCore const& geom,
56  std::string const& logCategory /* = "WindowTopologyAlg" */
57 )
58  : icarus::ns::util::mfLoggingClass(logCategory)
59  , fGeom(&geom)
60 {}
61 
62 
63 //------------------------------------------------------------------------------
66 {
67 
68  // store the window topology information here:
69  std::vector<WindowChannelMap::WindowInfo_t> windows;
70 
71  for (auto const& [ cryoGates, cryo ]
72  : util::zip(gates, fGeom->IterateCryostats()))
73  {
74 
75  append(
76  windows,
77  createWindowsFromCryostat
78  (extractGateChannels(cryoGates), cryo, *fGeom, windows.size())
79  );
80 
81  } // for cryostats
82 
83  return emplaceAndDumpMap(std::move(windows));
84 
85 } // icarus::trigger::WindowTopologyAlg::createFromGates()
86 
87 
88 //------------------------------------------------------------------------------
90  (TriggerGates_t const& gates) const -> WindowChannelMap
91 {
92 
93  // split gates by cryostat
94  TriggerGatesPerCryostat_t gatesByCryostat { fGeom->Ncryostats() };
95  for (InputTriggerGate_t const& gate: gates) {
96 
97  geo::CryostatID cid; // invalid
98  assert(!cid.isValid);
99 
100  for (raw::Channel_t const channel: gate.channels()) {
101  geo::OpDetGeo const& opDet = fGeom->OpDetGeoFromOpChannel(channel);
102  geo::OpDetID const oid = opDet.ID();
103  if (!cid) cid = oid;
104  else if (cid != oid) { // just in case
105  throw cet::exception("WindowTopologyAlg")
106  << "Input gate includes gates from different cryostats!!\n";
107  }
108  } // for channels in gate
109  if (!cid) continue; // gate with no channels does not contribute
110 
111  gatesByCryostat.at(cid.Cryostat).push_back(gate); // (copy)
112  } // for gates
113 
114  return createFromGates(gatesByCryostat);
115 
116 } // icarus::trigger::WindowTopologyAlg::createFromGates()
117 
118 
119 //------------------------------------------------------------------------------
121  (TriggerGates_t const& windowChannels, geo::CryostatGeo const& cryo) const
123 {
124  return emplaceAndDumpMap(
125  createWindowsFromCryostat(extractGateChannels(windowChannels), cryo, *fGeom)
126  );
127 } // icarus::trigger::WindowTopologyAlg::createFromCryostatGates()
128 
129 
130 //------------------------------------------------------------------------------
132  (TriggerGates_t const& windowChannels, geo::CryostatID cryoID) const
134 {
135  return createFromCryostatGates(windowChannels, fGeom->Cryostat(cryoID));
136 } // icarus::trigger::WindowTopologyAlg::createFromCryostatGates()
137 
138 
139 //------------------------------------------------------------------------------
140 template <typename... Args>
143 {
144  WindowChannelMap const map { std::forward<Args>(args)... };
145  mfLogTrace() << "Window map: << " << map;
146  return map;
147 } // icarus::trigger::WindowTopologyAlg::emplaceAndDumpMap()
148 
149 
150 //------------------------------------------------------------------------------
152  WindowChannelColl_t const& windowChannels,
153  geo::CryostatGeo const& cryo,
154  geo::GeometryCore const& geom,
155  std::size_t firstWindowIndex /* = 0U */
156  ) -> std::vector<WindowChannelMap::WindowInfo_t>
157 {
158  /*
159  * 1. fill the window information with local information
160  * 2. sort the windows in drift plane (first cryostat TPC as reference)
161  * 3. split the windows per plane
162  * 4. for each window plane:
163  * 4.1. sort windows on beam direction (TPC width direction)
164  * 4.2. fill the neighbour information on each window
165  */
166  std::vector<WindowChannelMap::WindowInfo_t> windows;
167  windows.reserve(windowChannels.size());
168 
169  // use the first TPC of the cryostat as reference for directions
170  assert(cryo.NTPC() > 0U);
171  geo::TPCGeo const& refTPC = cryo.TPC(0U);
172 
173  //
174  // 1. fill the window information with local information
175  //
176  using WindowInfoPtrs_t = std::vector<WindowChannelMap::WindowInfo_t*>;
177 
178  WindowInfoPtrs_t cryoWindowInfo;
179  cryoWindowInfo.reserve(windowChannels.size());
180 
181  std::size_t iWindow = firstWindowIndex;
182  for (auto const& channels: windowChannels) {
183 
185 
186  wInfo.index = iWindow++;
187  wInfo.channels = channels;
188  std::sort(wInfo.channels.begin(), wInfo.channels.end());
189  wInfo.cryoid = channels.empty()
190  ? geo::CryostatID{}
191  : geom.OpDetGeoFromOpChannel(channels.front()).ID().asCryostatID()
192  ;
193 
195  for (raw::Channel_t const channel: channels) {
196  // documentation of OpDetGeoFromOpChannel() does not say what on error...
197  geo::OpDetGeo const& opDet = geom.OpDetGeoFromOpChannel(channel);
198  middlePoint.add(opDet.GetCenter());
199  if (opDet.ID() != wInfo.cryoid) wInfo.cryoid = geo::CryostatID{};
200  } // for channel
201  wInfo.center = middlePoint.middlePoint();
202 
203  windows.push_back(std::move(wInfo));
204  cryoWindowInfo.push_back(&windows.back());
205 
206  } // for windows
207 
208  //
209  // 2. sort the windows in drift plane (first cryostat TPC as reference)
210  //
211  auto const normalProjection = [&refTPC](auto const* info)
212  { return refTPC.DistanceFromReferencePlane(info->center); };
213  WindowInfoPtrs_t const windowsByNormal
214  = util::sortCollBy(cryoWindowInfo, normalProjection);
215 
216  //
217  // 3. split the windows per plane
218  //
219  // split the list in two; there is a good deal of faith here
220  auto const beamCoordinate
221  = [&refPlane=refTPC.ReferencePlane()](auto const* info)
222  { return refPlane.PointWidthDepthProjection(info->center).X(); }
223  ;
224 
225  //
226  // 4. for each plane:
227  //
228  // 4.1. sort windows on beam direction (TPC width direction)
229  //
230  auto const iMiddleWindow
231  = std::next(windowsByNormal.cbegin(), windowsByNormal.size() / 2U);
232  std::array<WindowInfoPtrs_t, 2U> const windowsByPlane = {
233  util::sortBy(windowsByNormal.cbegin(), iMiddleWindow, beamCoordinate),
234  util::sortBy(iMiddleWindow, windowsByNormal.cend(), beamCoordinate)
235  };
236 
237  for (auto const& [ iPlane, planeWindows ]: util::enumerate(windowsByPlane)) {
238  //
239  // 4.2. fill the neighbour information on each window
240  //
241  auto const& otherPlaneWindows
242  = windowsByPlane.at(windowsByPlane.size() - 1U - iPlane);
243  std::size_t const iLastPlaneWindow = planeWindows.size() - 1U;
244  for (auto [ iPlaneWindow, windowInfo ]: util::enumerate(planeWindows)) {
245 
246  // assumes all topology information is InvalidWindowIndex by default
247 
248  if (WindowChannelMap::WindowInfo_t const* closestOppositeWindow
249  = findClosestWindow(otherPlaneWindows, windowInfo)
250  ) {
251  windowInfo->opposite = closestOppositeWindow->index;
252  }
253 
254  if (iPlaneWindow > 0U)
255  windowInfo->upstream = planeWindows[iPlaneWindow - 1U]->index;
256 
257  if (iPlaneWindow < iLastPlaneWindow)
258  windowInfo->downstream = planeWindows[iPlaneWindow + 1U]->index;
259 
260  } // for window in plane
261  } // for planes
262 
263  return windows;
264 
265 } // icarus::trigger::WindowTopologyAlg::createWindowsFromCryostat()
266 
267 
268 // -----------------------------------------------------------------------------
271 {
272 
273  WindowChannelColl_t windowChannels;
274  windowChannels.reserve(gates.size());
275 
276  for (InputTriggerGate_t const& gate: gates) {
277  auto const& gateChannels = gate.channels();
278  windowChannels.emplace_back(gateChannels.begin(), gateChannels.end());
279  } // for gates
280 
281  return windowChannels;
282 
283 } // icarus::trigger::WindowTopologyAlg::extractGateChannels()
284 
285 
286 //------------------------------------------------------------------------------
288  std::vector<WindowChannelMap::WindowInfo_t*> const& windowList,
289  WindowChannelMap::WindowInfo_t const* targetWindow
291 {
292 
293  if (!targetWindow || windowList.empty()) return nullptr;
294 
295  WindowChannelMap::WindowInfo_t const* closest = nullptr;
296  double minDistance2 = std::numeric_limits<double>::max();
297  for (auto const* window: windowList) {
298  if (!window) continue;
299 
300  double const d2 = (window->center - targetWindow->center).mag2();
301  if (minDistance2 <= d2) continue;
302  minDistance2 = d2;
303  closest = window;
304  } // for
305 
306  return closest;
307 } // icarus::trigger::WindowTopologyAlg::findClosestWindow()
308 
309 
310 // -----------------------------------------------------------------------------
311 //--- icarus::trigger::WindowTopologyVerification
312 //------------------------------------------------------------------------------
314  (TriggerGates_t const& gates) const
315 {
316  /*
317  * Verifies that the `gates` are in the expected order and have
318  * the expected channel content.
319  */
320 
321  if (!hasTopology()) {
322  throw cet::exception("WindowTopologyVerification")
323  << "verify() called without any window topology set to be verified.\n";
324  }
325 
326  std::size_t iWindow = 0U;
327  std::string errorMsg; // if this stays `empty()` there is no error
328  for (auto const& gate: gates) {
329 
330  // if error message is `empty()` there is no error
331  std::string const windowError = verifyGate(iWindow++, gate);
332 
333  if (!windowError.empty()) errorMsg += windowError + '\n';
334 
335  } // for gates
336 
337  return errorMsg;
338 
339 } // icarus::trigger::WindowTopologyVerification::verify()
340 
341 
342 //------------------------------------------------------------------------------
344  (TriggerGatesPerCryostat_t const& gates) const
345 {
346  /*
347  * Verifies that the `gates` are in the expected order and have
348  * the expected channel content.
349  */
350 
351  if (!hasTopology()) {
352  throw cet::exception("WindowTopologyVerification")
353  << "verify() called without any window topology set to be verified.\n";
354  }
355 
356  std::size_t iWindow = 0U;
357  std::string errorMsg; // if this stays `empty()` there is no error
358  for (auto const& cryoGates: gates) {
359  for (auto const& gate: cryoGates) {
360 
361  // if error message is `empty()` there is no error
362  std::string const windowError = verifyGate(iWindow++, gate);
363 
364  if (!windowError.empty()) errorMsg += windowError + '\n';
365 
366  } // for gates in cryostat
367  } // for cryostats
368 
369  return errorMsg;
370 
371 } // icarus::trigger::WindowTopologyVerification::verifyTopologicalMap()
372 
373 
374 //------------------------------------------------------------------------------
376  (std::size_t iWindow, InputTriggerGate_t const& gate) const
377 {
378  std::string errors; // if this stays `empty()` there is no error
379 
380  WindowChannelMap::WindowInfo_t const& windowInfo = fWindowMap->info(iWindow);
381 
382  auto const channelInWindow
383  = [begin=windowInfo.channels.cbegin(),end=windowInfo.channels.cend()]
384  (raw::Channel_t channel)
385  { return std::binary_search(begin, end, channel); }
386  ;
387 
388  for (raw::Channel_t const channel: gate.channels()) {
389  if (channelInWindow(channel)) continue;
390  if (errors.empty()) {
391  errors =
392  "channels not in window #" + std::to_string(windowInfo.index)
393  + ":";
394  } // if first error
395  errors += " " + std::to_string(channel);
396  } // for all channels in gate
397 
398  return errors;
399 } // icarus::trigger::WindowTopologyVerification::verifyGate()
400 
401 
402 //------------------------------------------------------------------------------
Definition of util::zip().
icarus::trigger::WindowTopologyAlg::TriggerGates_t TriggerGates_t
Type of sets of trigger gates.
Assembles the topology of trigger windows.
auto sortBy(BIter begin, EIter end, Key key, Sorter sorter={})
Returns a vectors to pointers to coll elements, sorted by key.
Definition: sortBy.h:104
Encapsulate the construction of a single cyostat.
std::vector< InputTriggerGate_t > TriggerGates_t
A list of trigger gates from input.
static WindowChannelColl_t extractGateChannels(TriggerGates_t const &gates)
Extracts the channel ID from a collection of gates.
Definition of util::enumerate().
A wrapper to trigger gate objects tracking the input of operations.
Helper class to compute the middle point in a point set.
bool isValid
Whether this ID points to a valid element.
Definition: geo_types.h:211
Geometry information for a single TPC.
Definition: TPCGeo.h:38
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
std::vector< TriggerGates_t > TriggerGatesPerCryostat_t
Type of lists of gates, one list per cryostat (outer index: cryostat no).
void GetCenter(double *xyz, double localz=0.0) const
Definition: OpDetGeo.cxx:40
Information about composition and topology of trigger sliding windows.
icarus::trigger::WindowTopologyAlg::TriggerGatesPerCryostat_t TriggerGatesPerCryostat_t
Type of sets of trigger gates, grouped by cryostat.
std::vector< WindowChannels_t > WindowChannelColl_t
All channels in many gates (one list per gate).
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
void add(Point const &p)
Accumulates a point.
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
Access the description of detector geometry.
WindowTopologyAlg(geo::GeometryCore const &geom, std::string const &logCategory="WindowTopologyAlg")
Constructor.
WindowChannelMap emplaceAndDumpMap(Args &&...args) const
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
auto sortCollBy(Coll &coll, Key key, Sorter sorter={})
Returns a vectors to pointers to coll elements, sorted by key.
Definition: sortBy.h:169
Utilities to extend the interface of geometry vectors.
unsigned int NTPC() const
Number of TPCs in this cryostat.
Definition: CryostatGeo.h:181
std::string verifyGate(std::size_t iWindow, InputTriggerGate_t const &gate) const
constexpr CryostatID const & asCryostatID() const
Conversion to CryostatID (for convenience of notation).
Definition: geo_types.h:275
WindowChannelMap createFromGates(TriggerGatesPerCryostat_t const &gates) const
Returns the topology of the windows described by the gates.
Description of geometry of one entire detector.
geo::CryostatID cryoid
Which cryostat the channels are in.
auto mag2(Vector const &v)
Return norm of the specified vector.
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
Encapsulate the geometry of an optical detector.
const TPCGeo & TPC(unsigned int itpc) const
Return the itpc&#39;th TPC in the cryostat.
Definition: CryostatGeo.cxx:93
static WindowChannelMap::WindowInfo_t const * findClosestWindow(std::vector< WindowChannelMap::WindowInfo_t * > const &windowList, WindowChannelMap::WindowInfo_t const *targetWindow)
Returns the window in windowList closest to the targetWindow.
std::string to_string(WindowPattern const &pattern)
std::string verify(TriggerGatesPerCryostat_t const &gates) const
Verifies that gates match the topology current set up.
std::vector< raw::Channel_t > channels
Optical detector channels covered by this window.
WindowIndex_t index
Index of the window this information is about.
decltype(auto) channels() const
Returns the list of channels of the enclosed gate.
geo::OpDetID const & ID() const
Returns the geometry ID of this optical detector.
Definition: OpDetGeo.h:72
static std::vector< WindowChannelMap::WindowInfo_t > createWindowsFromCryostat(WindowChannelColl_t const &windowChannels, geo::CryostatGeo const &cryo, geo::GeometryCore const &geom, std::size_t firstWindowIndex=0U)
Extracts topology information from a set of windows.
WindowChannelMap createFromCryostatGates(TriggerGates_t const &windowChannels, geo::CryostatGeo const &cryo) const
Returns the topology of the windows described by the gates.
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
The data type to uniquely identify a optical detector.
Definition: geo_types.h:297
geo::Point_t middlePoint(BeginIter begin, EndIter end)
Returns the middle of the specified points.
Provides sortBy() class of utilities.
Encapsulate the construction of a single detector plane.
The data type to uniquely identify a cryostat.
Definition: geo_types.h:190