All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ICARUSPhotonMappingTransformations.cxx
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/LibraryMappingTools/ICARUSPhotonMappingTransformations.cxx
3  * @brief A photon mapping identity transformation: implementation.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date April 3, 2019
6  * @see `icaruscode/PMT/LibraryMappingTools/ICARUSPhotonMappingTransformations.h`
7  * @see `icaruscode/PMT/LibraryMappingTools/ICARUSPhotonMappingTransformations.cc`
8  *
9  */
10 
11 
12 // ICARUS libraries
14 
15 // LArSoft libraries
16 #include "lardataalg/Utilities/StatCollector.h" // lar::util::MinMaxCollector<>
18 
19 // framework libraries
20 #include "messagefacility/MessageLogger/MessageLogger.h" // MF_LOG_DEBUG()
21 
22 // C/C++ standard libraries
23 #include <algorithm>
24 #include <string> // std::to_string()
25 #include <cassert>
26 #include <numeric> // std::iota()
27 
28 
29 //------------------------------------------------------------------------------
31  (Config const& config)
32  : fDumpMapping(config.DumpMapping())
33  , fGeom(lar::providerFrom<geo::Geometry>())
34  , fNOpDetChannels(fGeom? fGeom->NOpDets(): 0)
35 {
36  LibraryIndexToOpDetMap libraryIndices;
37  if (!config.CryostatChannelRemap(libraryIndices)) {
38  assert(fNOpDetChannels > 0U); // if no mapping is specified, we need to know
39  unsigned int nCryoChannels = fGeom->Cryostat(0U).NOpDet();
40  libraryIndices.resize(nCryoChannels);
41  std::iota(libraryIndices.begin(), libraryIndices.end(), 0U);
42  }
43  prepareMappings(libraryIndices);
44 
45 } // phot::ICARUSPhotonMappingTransformations::ICARUSPhotonMappingTransformations()
46 
47 
48 //------------------------------------------------------------------------------
50  (geo::Point_t const& location) const
51 {
52  geo::CryostatID onCryo = whichCryostat(location);
53  return onCryo? location + fTranslations[onCryo.Cryostat]: location;
54 } // phot::ICARUSPhotonMappingTransformations::detectorToLibrary()
55 
56 
57 //------------------------------------------------------------------------------
59  (geo::Point_t const& location) const -> OpDetToLibraryIndexMap const&
60 {
61  geo::CryostatID onCryo = whichCryostat(location);
62  return onCryo
63  ? fOpDetToLibraryIndexMaps[onCryo.Cryostat]: fInvalidOpDetToLibraryIndexMap;
64 } // phot::ICARUSPhotonMappingTransformations::opDetsToLibraryIndicesImpl()
65 
66 
67 //------------------------------------------------------------------------------
69  (geo::Point_t const& location) const -> LibraryIndexToOpDetMap const&
70 {
71  geo::CryostatID onCryo = whichCryostat(location);
72  return onCryo
73  ? fLibraryIndexToOpDetMaps[onCryo.Cryostat]: fInvalidLibraryIndexToOpDetMap;
74 } // phot::ICARUSPhotonMappingTransformations::libraryIndicesToOpDetsImpl()
75 
76 
77 //------------------------------------------------------------------------------
79 
80  /*
81  * geometry transformation:
82  * 1. determine the switch coordinate (on x axis) between cryostats
83  * 2. determine the amount of shift required
84  */
85 
86  //
87  // (1) geometry transformation
88  //
89  // (1.1) determine the switch coordinate (on x axis) between cryostats
90 
91  geo::Length_t const C0endX = fGeom->Cryostat(0U).MaxX();
92  geo::Length_t const C1startX = fGeom->Cryostat(1U).MinX();
93  if (C0endX > C1startX) {
94  throw std::runtime_error(
95  "phot::ICARUSPhotonMappingTransformations::prepareGeometryMapping(): "
96  "C:0 ends at x=" + std::to_string(C0endX)
97  + ", C:1 starts at x=" + std::to_string(C1startX)
98  + "... this algorithm does not understand this geometry."
99  );
100  }
101  fSwitchPoint = (C0endX + C1startX) / 2.0; // pick the middle point
102  MF_LOG_DEBUG("ICARUSPhotonMappingTransformations")
103  << "Switching from cryostat 0 to 1 when x > " << fSwitchPoint << " cm";
104 
105  // (1.2) determine the amount of shift required
106  geo::Point_t const refPoint = fGeom->Cryostat(0).BoundingBox().Min();
107  for (geo::CryostatGeo const& cryo: fGeom->IterateCryostats()) {
108  fTranslations.push_back(refPoint - cryo.BoundingBox().Min());
109  } // for all cryostats
110 
111 } // phot::ICARUSPhotonMappingTransformations::prepareGeometryMapping()
112 
113 
114 //------------------------------------------------------------------------------
116  (LibraryIndexToOpDetMap const& libraryIndices)
117 {
118 
119  /*
120  * 2. library transformation
121  * 1. determine the range of PMT channels in the two cryostats
122  * 2. determine the shift amount (hint: 180)
123  * 3. fill the maps with the shift
124  */
125 
126  //
127  // (2) library transformation
128  //
129  // (2.1) determine the range of PMT channels in the two cryostats
130  /*
131  * This algorithm is a bit cumbersome, but with the complications of optical
132  * detector geometry in LArSoft (which are usually irrelevant for ICARUS)
133  * it's a safer way.
134  * In short: we test for each possible channel in which cryostat its optical
135  * detector is, and extract ranges of channel numbers for each cryostat.
136  * In the end, the result is pretty much known:
137  * 0-179 on C:0 and 180-359 for C:1.
138  */
139  std::vector<lar::util::MinMaxCollector<OpDetID_t>> opDetChannelRangeByCryostat
140  (fGeom->Ncryostats());
141  lar::util::MinMaxCollector<OpDetID_t> opDetChannelRange;
142 
143  unsigned int const maxOpChannel = fGeom->MaxOpChannel();
144  for (unsigned int channel = 0; channel < maxOpChannel; ++channel) {
145 
146  geo::OpDetGeo const& opDet = fGeom->OpDetGeoFromOpChannel(channel);
147  auto const& opDetID = opDet.ID();
148 
149  if (opDetID.isValid) {
150  opDetChannelRange.add(OpDetID_t(channel));
151  opDetChannelRangeByCryostat[opDetID.Cryostat].add(OpDetID_t(channel));
152  }
153 
154  } // for all channels
155 
156  // (2.2) determine the shift amount (hint: 180)
157  fChannelShifts.clear();
158  for (geo::CryostatGeo const& cryo: fGeom->IterateCryostats()) {
159 
160  // sanity checks
161  geo::CryostatID const cid = cryo.ID();
162 
163  auto const& channelRange = opDetChannelRangeByCryostat[cid.Cryostat];
164  if (!channelRange.has_data()) {
165  throw std::runtime_error(
166  "phot::ICARUSPhotonMappingTransformations::prepareLibraryMappings(): "
167  + cid.toString() + " ends up with no optical channels??"
168  );
169  }
170 
171  auto const nChannels = channelRange.max() + 1 - channelRange.min();
172  if ((unsigned int) nChannels != cryo.NOpDet()) {
173  throw std::runtime_error(
174  "phot::ICARUSPhotonMappingTransformations::prepareLibraryMappings(): "
175  + cid.toString() + " expected to have "
176  + std::to_string(cryo.NOpDet()) + " optical channels, we end up with "
177  + std::to_string(nChannels) + " ("
178  + std::to_string(channelRange.min()) + " - "
179  + std::to_string(channelRange.max()) + ")"
180  );
181  } // if
182 
183  // relative shift with respect to the first cryostat:
184  fChannelShifts.push_back
185  (channelRange.min() - opDetChannelRangeByCryostat.front().min());
186 
187  } // for cryostats
188 
189 
190  // (2.3) fill the maps with the shift
191 
192  // (2.3.1) std::vector<LibraryIndexToOpDetMap> fLibraryIndexToOpDetMaps
193  // mapping library index => optical detector ID
194  // indexed by cryostat number of the source
195  //
196  // For ICARUS, this mapping is expected to be straightforward:
197  // * [ 0, 179 ] => [ 0, 179 ] if `location` is in the first cryostat (C:0)
198  // * [ 0, 179 ] => [ 180, 359 ] if `location` is in the other one (C:1)
199  //
200  fLibraryIndexToOpDetMaps.clear();
201  for (geo::CryostatGeo const& cryo: fGeom->IterateCryostats()) {
202 
203  // this is the library for sources in cryostat `cryo`;
204  // it is positioned at `cryo.ID().Cryostat` in `fLibOpDetIDmaps`;
205  // the library is expected to have only 180 entries,
206  // one per optical detector within `cryo`
207 
208  auto const nChannels = cryo.NOpDet();
209  if (libraryIndices.size() != nChannels) {
210  throw std::runtime_error(
211  "phot::ICARUSPhotonMappingTransformations::prepareLibraryMappings(): "
212  "the internal mapping should cover " + std::to_string(nChannels)
213  + " channels but it covers " + std::to_string(libraryIndices.size())
214  + " instead."
215  );
216  }
217 
218  auto const nFirst = fChannelShifts[cryo.ID().Cryostat];
219  LibraryIndexToOpDetMap libraryIndexToOpDetMap { libraryIndices }; // copy
220  for (auto& c: libraryIndexToOpDetMap) c += nFirst;
221 
222  fLibraryIndexToOpDetMaps.push_back(std::move(libraryIndexToOpDetMap));
223 
224  } // for channel range
225 
226  // if the position is invalid, no library data is actually present,
227  // and the mapping is moot; we choose the rank of this mootness to be the same
228  // as the first valid mapping
229  fInvalidLibraryIndexToOpDetMap.clear();
230  fInvalidLibraryIndexToOpDetMap.resize
231  (fLibraryIndexToOpDetMaps.front().size(), InvalidOpDetID);
232 
233  // (2.3.2) std::vector<OpDetToLibraryIndexMap> fOpDetToLibraryIndexMaps
234  // mapping detector optical ID => library index
235  //
236  // For ICARUS, this mapping is expected to be:
237  // * if `location` is in the first cryostat (C:0):
238  // * [ 0, 179 ] => [ 0, 179 ]
239  // * [ 180, 359 ] => invalid indices
240  // * if `location` is in the second cryostat (C:1):
241  // * [ 0, 179 ] => invalid indices
242  // * [ 180, 359 ] => [ 0, 179 ]
243  //
244  fOpDetToLibraryIndexMaps.clear();
245  for (auto const& libToOpDetMap: fLibraryIndexToOpDetMaps) {
246 
247  fOpDetToLibraryIndexMaps.push_back
248  (invertMapping(libToOpDetMap, maxOpChannel, InvalidLibraryIndex));
249 
250  } // for each library mapping
251 
252  fInvalidOpDetToLibraryIndexMap.clear();
253  fInvalidOpDetToLibraryIndexMap.resize(maxOpChannel, InvalidLibraryIndex);
254 
255 } // phot::ICARUSPhotonMappingTransformations::prepareMappings()
256 
257 
258 //------------------------------------------------------------------------------
260  (LibraryIndexToOpDetMap const& libraryIndices)
261 {
262 
263  /*
264  * 1. geometry transformation:
265  * 1. determine the switch coordinate (on x axis) between cryostats
266  * 2. determine the amount of shift required
267  * 2. library transformation
268  * 1. determine the range of PMT channels in the two cryostats
269  * 2. determine the shift amount (hint: 180)
270  * 3. fill the maps with the shift
271  */
272 
273  mf::LogInfo("ICARUSPhotonMappingTransformations")
274  << "Photon visibility mapping tool: 'ICARUSPhotonMappingTransformations'";
275 
276  //
277  // (1) geometry transformation
278  //
279  prepareGeometryMapping();
280 
281  //
282  // (2) library transformation
283  //
284  prepareLibraryMappings(libraryIndices);
285 
286  // debug:
287  if (fDumpMapping) dumpMapping();
288 
289 } // phot::ICARUSPhotonMappingTransformations::prepareMappings()
290 
291 
292 //------------------------------------------------------------------------------
293 namespace {
294 
295  std::string channelIndex
297  {
299  ? "<invalid>": std::to_string(channel)
300  ;
301  } // channelIndex()
302 
303  std::string libraryIndex
305  {
306  return
308  ? "<invalid>": std::to_string(libIndex)
309  ;
310  } // libraryIndex()
311 
312 } // local namespace
313 
314 
316  mf::LogInfo log("ICARUSPhotonMappingTransformations");
317 
318  log << "ICARUSPhotonMappingTransformations mapping";
319 
320  log << "\nMapping of geometry: '" << fGeom->DetectorName() << "':"
321  << "\n - " << fGeom->Ncryostats() << " cryostats"
322  << "\n - optical channels: " << fNOpDetChannels
323  << "\n - maximum optical detector channel number: " << fGeom->MaxOpChannel()
324  << "\n"
325  ;
326 
327  log << "\nThe switch point is at x=" << fSwitchPoint << " cm.";
328 
329  constexpr unsigned int PageBreak = 12;
330 
331  for (auto const& cryo: fGeom->IterateCryostats()) {
332 
333  auto const c = cryo.ID().Cryostat;
334 
335  log << "\n" << cryo.ID() << ":"
336  << "\n * optical detectors: " << cryo.NOpDet()
337  << "\n * channel -> library shift: " << fChannelShifts[c]
338  << "\n * translation: " << fTranslations[c];
339 
340  unsigned int pager;
341 
342  // print library to detector mapping
343  auto const& libOpDetIDmap = fLibraryIndexToOpDetMaps[c];
344  log
345  << "\n * mapping [library index @" << cryo.ID()
346  << "] => [optical detector] (" << libOpDetIDmap.size()
347  << " entries):";
348  ;
349  pager = PageBreak;
350  for (auto [ i, opDetID ]: util::enumerate(libOpDetIDmap)) {
351  if (++pager >= PageBreak) {
352  pager = 0;
353  log << "\n ";
354  }
355  log << " [" << i << "] => [" << channelIndex(opDetID) << "];";
356  } // for
357 
358  // print detector to library mapping
359  auto const& opDetLibMap = fOpDetToLibraryIndexMaps[c];
360  pager = PageBreak;
361  log
362  << "\n * mapping [optical detector] => [library index @"
363  << cryo.ID() << "] (" << opDetLibMap.size() << " entries):";
364  ;
365  pager = PageBreak;
366  for (std::size_t i = 0; i < opDetLibMap.size(); ++i) {
367  if (++pager >= PageBreak) {
368  pager = 0;
369  log << "\n ";
370  }
371  log << " [" << i << "] => [" << libraryIndex(opDetLibMap[i]) << "];";
372  }
373 
374  } // for cryostats
375 
376  {
377  log << "\n"
378  << "\nMapping [library index] => [no optical detector] ("
379  << fInvalidLibraryIndexToOpDetMap.size() << " entries):";
380  unsigned int pager = PageBreak;
381  for (std::size_t i = 0; i < fInvalidLibraryIndexToOpDetMap.size(); ++i) {
382  if (++pager >= PageBreak) {
383  pager = 0;
384  log << "\n ";
385  }
386  log << " [" << i << "] => ["
387  << libraryIndex(fInvalidLibraryIndexToOpDetMap[i]) << "];";
388  } // for
389  log << "\n";
390  } // anonymous scope block
391 
392  {
393  log << "\n"
394  << "\nMapping [optical detector] => [no library] ("
395  << fInvalidOpDetToLibraryIndexMap.size() << " entries):";
396  unsigned int pager = PageBreak;
397  for (std::size_t i = 0; i < fInvalidOpDetToLibraryIndexMap.size(); ++i) {
398  if (++pager >= PageBreak) {
399  pager = 0;
400  log << "\n ";
401  }
402  log << " [" << i << "] => ["
403  << channelIndex(fInvalidOpDetToLibraryIndexMap[i]) << "];";
404  } // for
405  log << "\n";
406  } // // anonymous scope block
407 
408 } // phot::ICARUSPhotonMappingTransformations::dumpMapping()
409 
410 
411 //------------------------------------------------------------------------------
412 
virtual geo::Point_t detectorToLibrary(geo::Point_t const &location) const override
Returns the representation within the library of a detector location.
double Length_t
Type used for coordinates and distances. They are measured in centimeters.
Definition: geo_vectors.h:137
Definition of util::enumerate().
std::vector< geo::Vector_t > fTranslations
Translation of the point.
double MinX() const
Returns the world x coordinate of the start of the box.
Definition: BoxBoundedGeo.h:88
OpDetID_t LibraryIndex_t
Type describing a library index. FIXME former LibraryOpDetID_t.
void dumpMapping() const
Writes the current mapping information into the console. Debug stuff.
CryostatID_t Cryostat
Index of cryostat.
Definition: geo_types.h:212
double MaxX() const
Returns the world x coordinate of the end of the box.
Definition: BoxBoundedGeo.h:91
Geometry information for a single cryostat.
Definition: CryostatGeo.h:43
geo::BoxBoundedGeo const & BoundingBox() const
Returns the bounding box of this cryostat.
Definition: CryostatGeo.h:128
Classes gathering simple statistics.
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
Keeps track of the minimum and maximum value we observed.
static constexpr LibraryIndex_t InvalidLibraryIndex
Value used for an invalid library index.
geo::Length_t fSwitchPoint
Switch coordinate on x axis [cm].
static constexpr OpDetID_t InvalidOpDetID
Value used to identify an invalid optical detector.
std::string toString() const
Human-readable representation of the cryostat ID.
Definition: geo_types.h:247
CryostatGeo const & Cryostat(geo::CryostatID const &cryoid) const
Returns the specified cryostat.
phot::IPhotonMappingTransformations::OpDetID_t OpDetID_t
Type of (global) optical detector ID.
geo::Point_t Min() const
Returns the corner point with the smallest coordinates.
std::vector< OpDetID_t > LibraryIndexToOpDetMap
Type describing the mapping of optical detectors into library indices.
void prepareLibraryMappings(LibraryIndexToOpDetMap const &libraryIndices)
geo::GeometryCore const * fGeom
Detector geometry service provider. Not really used.
IteratorBox< cryostat_iterator,&GeometryCore::begin_cryostat,&GeometryCore::end_cryostat > IterateCryostats() const
Enables ranged-for loops on all cryostats of the detector.
int OpDetID_t
Type describing a optical detector ID.
std::string to_string(WindowPattern const &pattern)
Photon library mapping for ICARUS geometry.
LibraryIndexToOpDetMap const & libraryIndicesToOpDetsImpl(geo::Point_t const &location) const
geo::OpDetID const & ID() const
Returns the geometry ID of this optical detector.
Definition: OpDetGeo.h:72
ICARUSPhotonMappingTransformations(Config const &config)
Constructor.
void prepareMappings(LibraryIndexToOpDetMap const &libraryIndices)
Extracts the necessary information for mapping from the geometry.
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< double >, ROOT::Math::GlobalCoordinateSystemTag > Point_t
Type for representation of position in physical 3D space.
Definition: geo_vectors.h:184
OpDetToLibraryIndexMap const & opDetsToLibraryIndicesImpl(geo::Point_t const &location) const
The data type to uniquely identify a cryostat.
Definition: geo_types.h:190