MDetector/PMT.cc
Go to the documentation of this file.
1 #include <mdet/PMT.h>
2 
3 #include <mdet/Pixel.h>
4 #include <mdet/MHierarchyInfo.h>
5 //
6 #include <utl/ErrorLogger.h>
7 #include <utl/RandomEngine.h>
8 //
9 #include <fwk/RandomEngineRegistry.h>
10 //
11 #include <CLHEP/Random/RandFlat.h>
12 //
13 #include <cmath>
14 #include <algorithm>
15 #include <numeric>
16 #include <sstream>
17 //
18 #include <boost/algorithm/minmax_element.hpp>
19 #include <boost/lambda/lambda.hpp>
20 
21 
22 namespace { // unnamed namespace with local helper stuff.
23 
36  struct PixelEq {
37  PixelEq(const mdet::Pixel& p) : fPixel(p) { }
38 
43  bool
44  operator()(const mdet::Pixel* const o)
45  const
46  {
47  /*
48  * Same object (equal addresses).
49  * Here we're relying on the fact that there
50  * isn't a subverted overload for operator&
51  * among Pixels and then that we're calling the
52  * built-in one which gives the object-address.
53  *
54  * Boost's utility AddressOf could have been used
55  * but it seems that using them would have been too
56  * fancy more than a real concern: don't overload
57  * operator&, please (at least for a mdet::Pixel). See
58  * http://www.artima.com/cppsource/YourAddress.html
59  * http://www.artima.com/weblogs/viewpost.jsp?thread=102857
60  */
61  if (o == &fPixel)
62  return true;
63  // Id has to be equal
64  return o->GetId() == fPixel.GetId() &&
65  // and then also within the hierarchy.
66  o->GetIdsMap() == fPixel.GetIdsMap();
67  }
68  const mdet::Pixel& fPixel;
69  };
70 
71 
79  template<class PGroup, class Container>
80  struct PixelPusher {
81  PixelPusher(PGroup& g, Container& c) : fGroup(g), fContainer(c) { }
82 
83  void
84  operator()(int id)
85  {
86  const mdet::Pixel& p = fGroup.Get(id);
87  fContainer.push_back(&p);
88  }
89  PGroup& fGroup;
90  Container& fContainer;
91  };
92 
93 }
94 
95 
96 namespace mdet {
97 
99 
100  const char* const PMT::kComponentId = MHierarchyInfo::kComponentsIds[5];
101 
102  /*
103  * See C++ standard ISO/IEC 14882.
104  * 9.4.2(4) Static data members
105  *
106  * If a static data member is of const integral or const enumeration type, its declaration in the class
107  * definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that
108  * case, the member can appear in integral constant expressions. The member shall still be defined in a namespace
109  * scope if it is used in the program and the namespace scope definition shall not contain an initializer.
110  *
111  * So we have to initialize this constant here.
112  */
113  const double PMT::kCentralPixelSelfTalk = 100;
114 
115 
116  PMT::PixelGroup::SizeType
118  const
119  {
120  PixelGroup::SizeType tot = fPixels.GetNumberOfComponents();
121  // Work with square PMTs.
122  // To the able to work with rectangular one another datum is needed in the config,
123  // for instance the number of rows.
124  PixelGroup::SizeType root = static_cast<PixelGroup::SizeType>(std::sqrt(double(tot)));
125  if (tot != root*root) {
126  std::ostringstream ss;
127  ss << "The total number of pixels is not a perfect square number in ";
128  AddIdMessage(ss);
129  ss << ".";
130  ERROR(ss);
131  }
132  return root;
133  }
134 
135 
136  PMT::PixelGroup::SizeType
138  const
139  {
140  PixelGroup::SizeType tot = fPixels.GetNumberOfComponents();
141  return tot / GetRows();
142  }
143 
144 
145  const Module&
147  const
148  {
149  return fModule;
150  }
151 
152 
153  PMT::PMT(int pId, const det::VManager::IndexMap& parentMap, const Module& parent) :
154  MDetectorComponent<PMT>::Type(pId, parentMap),
155  fPixels(*this),
156  fModule(parent)
157  {
158  fPixels.Update(GetIdsMap());
159  InitPixels();
160  }
161 
162 
163  void
165  {
166  if (fPixels.GetNumberOfComponents()) {
167  // Take the minimun element to make everything relative to this id.
168  /*
169  * Something like this can't be done cause the iterators doesn't model
170  * asignable concept.
171  *
172  * std::min_element(PixelsBegin(), PixelsEnd(),
173  * boost::bind(&Pixel::GetId, _1) < boost::bind(&Pixel::GetId, _2))->GetId()
174  *./det/ComponentGroup.h:108: error: non-static reference member
175  * 'const det::ComponentGroup<mdet::PMT, mdet::Pixel, det::ParentCreator,
176  * mdet::MManagerProvider>&
177  * det::ComponentGroup<mdet::PMT, mdet::Pixel, det::ParentCreator, mdet::MManagerProvider>
178  * ::InternalConstFunctor::fContainer', can't use default assignment operator
179  */
180  { // scope this!
183  // It's know that there's one at least.
184  fFirstIdPMT = i->GetId(); // Note that we're initializing the member variable.
185  for (++i; i != e; ++i)
186  fFirstIdPMT = std::min(fFirstIdPMT, i->GetId());
187  }
188  // The code inside asumes at least one: it initializes neighbors;
189  // which don't exist if there's only one.
190  if (fPixels.GetNumberOfComponents() > 1) {
191  // At last fill the neighbor information.
192  const PixelGroup::SizeType cols = GetCols();
193  const PixelGroup::SizeType colsLessOne = GetCols() - 1;
194  const PixelGroup::SizeType rowsLessOne = GetRows() - 1;
195  // To accumulate the ids to check'em.
196  std::vector<int> check;
197  // Preallocation.
198  check.reserve(fPixels.GetNumberOfComponents());
199  // Now loop over the pixs and calculate the neighbors.
200  for (PixelIterator i = fPixels.Begin(); i != fPixels.End(); ++i) {
201  const int id = i->GetId();
202  check.push_back(id);
203  /*
204  * Relativize: note that it's positive since
205  * there's no id that's less that firstId.
206  */
207  const unsigned int idOffset = id - fFirstIdPMT;
208  /*
209  * Here we are assuming consecutiveness on the ids.
210  */
211  /*
212  * From the ISO/IEC 14882:2003(E) C++ Standard about / and %:
213  * 5.6(4) "If both operands are nonnegative then the remainder is
214  * nonnegative; if not, the sign of the remainder is implementation-defined"
215  */
216  const unsigned int quot = idOffset / cols;
217  const unsigned int mod = idOffset % cols;
218  const bool firstCol = !mod;
219  const bool firstRow = !quot;
220  const bool lastCol = (mod == colsLessOne);
221  const bool lastRow = (quot == rowsLessOne);
222  // Count the booleans. Note that the count is at most 2 (there's more
223  // than 1 pixel); and rely on the (guaranteed) true -> 1 conversion.
224  int count = firstCol + firstRow + lastCol + lastRow;
225  // Preallocate the neighbors container.
226  if (count == 2)
227  i->fNeighbors.reserve(kNumNeighborsCorner);
228  else if (count == 1)
229  i->fNeighbors.reserve(kNumNeighborsSide);
230  else
231  i->fNeighbors.reserve(kNumNeighborsInside);
232  // Build a helper functor to do the actual filling.
233  PixelPusher<PixelGroup, NeighborsContainer> pusher(fPixels, i->fNeighbors);
234  /*
235  * Push the neighbors in increasing id order.
236  * (cols is at least one, of course), and so
237  * the order in which the following checks are
238  * made isn't arbitrary.
239  *
240  * As indicated by the constant we work with 8
241  * neighbors.
242  *
243  * This is related to the way each Pixel stores
244  * it's crosstalk data.
245  */
246  if (!firstRow) {
247  if (!firstCol)
248  pusher(id - cols -1); // Prev col, prev row.
249  pusher(id - cols); // Same col, prev row.
250  if (!lastCol)
251  pusher(id - cols + 1); // Next col, prev row.
252  }
253  if (!firstCol)
254  pusher(id - 1); // Prev col, same row.
255  if (!lastCol)
256  pusher(id + 1); // Next col, same row.
257  if (!lastRow) {
258  if (!firstCol)
259  pusher(id + cols - 1); // Prev col, next row.
260  pusher(id + cols); // Same col, next row.
261  if (!lastCol)
262  pusher(id + cols + 1); // Next col, next row.
263  }
264  } // End loop over pixels.
265  //
266  // Check Consecutiveness.
267  //
268  // It's likely that it will be already sorted
269  // (DetectorComponent sorts while retreiving data)
270  // but ensure that it's needed.
271  //
272  // It's known that there's one element at least.
273  const bool sorted =
274  std::adjacent_find(check.begin(), check.end(), boost::lambda::_1 > boost::lambda::_2) == check.end();
275  if (!sorted)
276  std::sort(check.begin(), check.end());
277  // Take the difference between all elements & overwrite the vector.
278  std::adjacent_difference(check.begin(), check.end(), check.begin());
279  // Remember that the first element is kept the same, so ignore it: that's why the + 1.
280  // Now take the min & max: should be one if there's consecutiveness.
281  typedef std::vector<int>::const_iterator It;
282  std::pair<It, It> mm = boost::minmax_element(check.begin() + 1, check.end());
283  if (*mm.first != 1 || *mm.second != 1) {
284  std::ostringstream ss;
285  ss << "There is no consecutiveness in the pixels of PMT ";
286  AddIdMessage(ss);
287  ss << ".";
288  FATAL(ss);
289  }
290  }
291  }
292  }
293 
294 
297  const
298  {
299  return boost::make_indirect_iterator(p.fNeighbors.begin());
300  }
301 
302 
305  const
306  {
307  return boost::make_indirect_iterator(p.fNeighbors.end());
308  }
309 
310 
311  bool
312  PMT::IsNeighbor(const Pixel& src, const Pixel& dst)
313  const
314  {
315  PixelEq e(dst);
316  return IsNeighbor(src, dst, e) != src.fNeighbors.end();
317  }
318 
319 
320  template<class Predicate>
321  NeighborsContainer::const_iterator
322  PMT::IsNeighbor(const Pixel& src, const Pixel& /*dst*/, const Predicate& p)
323  const
324  {
325  // Look for the destination in the neighbors of src.
326  return std::find_if(src.fNeighbors.begin(), src.fNeighbors.end(), p);
327  }
328 
329 
330  const Pixel&
332  const
333  {
334  utl::RandomEngine& eng = fwk::RandomEngineRegistry::GetInstance().Get(fwk::RandomEngineRegistry::eDetector);
335  // We shoot an uniform random number between 0 and 1 (that's why we are working with xtalk proportion).
336  const double rnd = CLHEP::RandFlat::shoot(& eng.GetEngine());
337  /*
338  * We iterate over the neighbors, and in this variable we represent the right side limit
339  * of an internal: if the random number lays to the left: that's the pixel (note that
340  * laying to the left means that lays to the right of the previous right limit).
341  */
342  double rightSide = 0;
343  for (NeighborConstIterator nIt = NeighborsBegin(src), end = NeighborsEnd(src); nIt != end; ++nIt) {
344  // Increase the limit with the crosstalk proportion of the current neighbor:
345  rightSide += GetCrossTalkProportion(src, *nIt);
346  /*
347  * If rand lays between "previous rightSize" (it can't happen to be to the left of the
348  * "previous rightSize" because in that case we would have exited on the previous loop)
349  * and rightSide, that's the detination neighbor.
350  */
351  if (rnd < rightSide)
352  return *nIt;
353  }
354  // Stayed in the same pixel.
355  return src;
356  }
357 
358 
359  double
361  const
362  {
363  // Lazy evaluation...
365  const CrossTalkContainer* xt = nullptr;
366  switch (pix.fNeighbors.size()) {
367  case kNumNeighborsInside:
368  xt = &pix.GetNeighborsCrossTalkInside();
369  break;
370  case kNumNeighborsSide:
371  xt = &pix.GetNeighborsCrossTalkSide();
372  break;
373  case kNumNeighborsCorner:
374  xt = &pix.GetNeighborsCrossTalkCorner();
375  break;
376  }
377  // Assume xt != 0 (see mdet::PMT::GetCrossTalk).
378  pix.fCrossTalkNormalizationFactor = std::accumulate(xt->begin(), xt->end(), PMT::kCentralPixelSelfTalk);
379  }
380  return pix.fCrossTalkNormalizationFactor.Get();
381  }
382 
383 
384  double
385  PMT::GetCrossTalkProportion(const Pixel& src, const Pixel& dst)
386  const
387  {
388  return GetCrossTalk(src, dst) / GetCrossTalkNormalizationFactor(src);
389  }
390 
391 
392  double
393  PMT::GetCrossTalk(const Pixel& src, const Pixel& dst)
394  const
395  {
396  /*
397  * (Almost) Repeated code: see constructor.
398  */
399  const unsigned int idOffset = src.GetId() - fFirstIdPMT;
400  const unsigned int quot = idOffset / GetCols();
401  const unsigned int mod = idOffset % GetCols();
402  // no need const bool firstCol = mod == 0;
403  const bool firstRow = !quot;
404  const bool lastCol = (mod == GetCols() - 1);
405  const bool lastRow = (quot == GetCols() - 1);
406  //----
407  //----
408  PixelEq e(dst); // to be used twice.
409  NeighborsContainer::const_iterator i = IsNeighbor(src, dst, e);
410  if (i != src.fNeighbors.end()) {
411  // take id offset wrt the first neighbor.
412  NeighborsContainer::difference_type o = i - src.fNeighbors.begin();
413  // Select the kind of cross-talk.
414  // Use pointer instead of reference to avoid the need to initialize
415  // it here. Note that we are taking the address of the return value,
416  // which is returned by const reference (it's not a temporary).
417  const CrossTalkContainer* xt = nullptr;
418  // Switch over to chose the proper cross-talk.
419  switch (src.fNeighbors.size()) {
420  /*
421  * See the declaration of these constants for
422  * the specification of how the coefficients
423  * are supposed to be laid out in the CrossTalkContainer.
424  *
425  * Note that we use integral literals in place-of
426  * NeighborsContainer::difference_type. We may
427  * assume that there's an implicit conversion
428  * available.
429  */
430  case kNumNeighborsInside:
431  xt = & src.GetNeighborsCrossTalkInside();
432  // the index remains the same.
433  break;
434  case kNumNeighborsSide:
435  xt = & src.GetNeighborsCrossTalkSide();
436  // XXX What an ugly piece of code!!!
437  // Recall we're in a side.
438  if (lastCol) {
439  if (o == 0)
440  o = 1;
441  else if (o == 1)
442  o = 0;
443  // the rest stay the same.
444  } else if (lastRow) {
445  if (o != 4) {
446  if (o == 3)
447  o = 0;
448  else
449  ++o;
450  }
451  } else if (firstRow) {
452  if (o != 0) {
453  if (o == 1)
454  o = 4;
455  else
456  --o;
457  }
458  } else {
459  // firstCol
460  if (o == 3)
461  o = 4;
462  else if (o == 4)
463  o = 3;
464  // the rest stay the same.
465  }
466  // End of ugly code!
467  break;
468  case kNumNeighborsCorner:
469  xt = & src.GetNeighborsCrossTalkCorner();
470  // Recall we're in a corner.
471  if (lastCol) {
472  if (lastRow) {
473  o = (o + 1) % kNumNeighborsCorner;
474  }
475  // if firstRow the indexes remain the same.
476  } else {
477  // firstCol
478  if (firstRow ) {
479  if (o != 0) {
480  // if not 2, then is o == 1.
481  o = o == 2 ? 1 : 2;
482  }
483  } else {
484  // lastRow
485  if (o != 1) {
486  o = o == 0 ? 2 : 0;
487  }
488  }
489  }
490  break;
491  default:
492  std::ostringstream ss;
493  ss << "The neighbors list for the Pixel hasn't got the "
494  "expected size, it has "
495  << src.fNeighbors.size()
496  << " in ";
497  AddIdMessage(ss);
498  ss << '.';
499  FATAL(ss);
500  return 0; // Fatal error happened: return 0.
501  }
502  if (xt->size() != src.fNeighbors.size()) {
503  std::ostringstream ss;
504  ss << "The neighbors coefficients list for the Pixel hasn't got the "
505  "same length that the cross-talk one, it has "
506  << src.fNeighbors.size()
507  << " and the second has "
508  << xt->size()
509  << " in ";
510  AddIdMessage(ss);
511  ss << '.';
512  FATAL(ss);
513  return 0; // idem.
514  }
515  return (*xt)[o];
516  } else {
517  // It can be the pixel itself (use the convention constant)...
518  if (e(&src))
519  return kCentralPixelSelfTalk;
520  return 0; // ..or a non-first neighbor pixel: there's no cross-talk.
521  }
522  }
523 
524 
525  void
526  PMT::Update(bool invalidateData, bool invalidateComponents)
527  {
528  MDetectorComponent<PMT>::Type::Update(invalidateData, invalidateComponents);
529  fPixels.Update(GetIdsMap(), invalidateData, invalidateComponents);
530  // After update pixels, we may re-init them; if no component invalidation then
531  // we skip this part which initializes fFirstIdPMT (which won't change if no
532  // component invalidation is performed).
533  if (invalidateComponents)
534  InitPixels();
535  }
536 
537 }
std::vector< double > CrossTalkContainer
Typedef for mdet::Pixel crosstalk container.
NeighborConstIterator NeighborsBegin(const Pixel &p) const
Begin iterator over neighbors of the given pixel.
constexpr double mm
Definition: AugerUnits.h:113
double GetCrossTalkNormalizationFactor(const mdet::Pixel &pix) const
Helper method to manage the conversion factor for cross-talk.
PixelGroup::ConstIterator PixelConstIterator
Convenience typedef for const iterator over the contained mdet::Pixel instances.
RandomEngineType & GetEngine()
Definition: RandomEngine.h:32
NeighborConstIterator NeighborsEnd(const Pixel &p) const
End iterator over neighbors of the given pixel.
static const char *const kComponentsNames[13]
static const double kCentralPixelSelfTalk
Assumed value for central pixel self-talk.
Definition: MDetector/PMT.h:70
Defines within it the common (templated) type for muon detector hierarchy components.
double GetCrossTalk(const Pixel &src, const Pixel &dst) const
Returns the cross-talk coefficient for dst seen as a neighbor of src.
void Update(std::vector< double > &init, const std::vector< double > &res)
Definition: Util.h:100
void InitPixels()
Initilization and validations related to pixels.
static const char *const kComponentsIds[13]
const CrossTalkContainer & GetNeighborsCrossTalkCorner() const
Cross-talk coeffcient for neighbors of corner located pixel.
static const char *const kComponentName
#define FATAL(message)
Macro for logging fatal messages.
Definition: ErrorLogger.h:167
const VManager::IndexMap & GetIdsMap() const
The id identifying this component within its detector hierarhy.
PixelGroup::Iterator PixelIterator
Non-const private alias.
Definition: MDetector/PMT.h:60
const Module & GetModule() const
The module to which this PMT belongs.
const Pixel & ComputePulseDestination(const Pixel &src) const
Computes a destination pixel according to crosstalk effect.
static const unsigned int kNumNeighborsCorner
Number of neighbors for a pixel on the corner of the PMT&#39;s cathode. The assumed layout for the neighb...
Wraps the random number engine used to generate distributions.
Definition: RandomEngine.h:27
double GetCrossTalkProportion(const Pixel &src, const Pixel &dst) const
The cross-talk proportion coefficient for dst seen as a neighbor of \ src.
static const unsigned int kNumNeighborsSide
Number of neighbors for a pixel on the side of the PMT&#39;s cathode.
const Module & fModule
bool IsValid() const
Definition: Validated.h:64
Array of Scintillator.
constexpr double g
Definition: AugerUnits.h:200
PixelGroup::SizeType GetRows() const
Retrieves the number of rows.
Multiple-pixel photo-multiplier tube.
Definition: MDetector/PMT.h:49
PixelGroup::SizeType GetCols() const
Retrieves the number of columns.
PixelConstIterator PixelsBegin() const
Begin iterator over the contained pixels.
bool IsNeighbor(const Pixel &src, const Pixel &dst) const
Tells whether dst is one of the neighbors of src.
PixelGroup fPixels
PMT pixel.
std::map< std::string, std::string > IndexMap
Definition: VManager.h:133
void Update(bool invalidateData, bool invalidateComponents)
Perform update in this component and forward to subcomponents.
int GetId() const
The id of this component.
int fFirstIdPMT
Minimum id among PMT&#39;s ids.
double mod(const double d, const double periode)
utl::Validated< double > fCrossTalkNormalizationFactor
Conversion factor to percentual values. Handled within mdet::PMT.
Type
The type of file that we are acutally opening.
Definition: IoCodes.h:33
const CrossTalkContainer & GetNeighborsCrossTalkSide() const
Cross-talk coeffcient for neighbors of a side (not corner) located pixel.
NeighborsContainer fNeighbors
Neighbors.
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
PMT(int pId, const det::VManager::IndexMap &parentMap, const Module &parent)
Constructs the tube.
PixelConstIterator PixelsEnd() const
End iterator over the contained pixels.
static const unsigned int kNumNeighborsInside
Number of neighbors for a pixel inside (ie in the middle) the PMT&#39;s cathode.
Definition: MDetector/PMT.h:85
const CrossTalkContainer & GetNeighborsCrossTalkInside() const
Cross-talk coeffcient for neighbors of a pixel located inside the pixel matrix.
boost::indirect_iterator< NeighborsContainer::const_iterator > NeighborConstIterator
Convenience typedef for iteration over neighbors.
Definition: PMT_helper.h:37
static const char *const kComponentId
boost::indirect_iterator< NeighborsContainer::const_iterator > NeighborConstIterator
Convenience typedef for iteration over neighbors.

, generated on Tue Sep 26 2023.