Loading [MathJax]/extensions/tex2jax.js
ATLAS Offline Software
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
DefectsEmulatorCondAlgImpl.icc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 // Silicon trackers includes
5 #include "DefectsEmulatorCondAlgImpl.h"
6 
7 #include "Identifier/Identifier.h"
8 #include "AthenaKernel/RNGWrapper.h"
9 
10 #include "StoreGate/WriteHandle.h"
11 #include <ranges>
12 #include <type_traits>
13 #include <array>
14 #include <span>
15 #include <algorithm>
16 
17 #include "CLHEP/Random/RandFlat.h"
18 
19 #include "ModuleIdentifierMatchUtil.h"
20 #include "ConnectedModulesUtil.h"
21 
22 namespace {
23 
24  template <typename T_ID>
25  concept IdentifierHelperWithOtherSideConecpt = requires(T_ID id_helper) { id_helper.get_other_side(); };
26 
27  template <class T_ModuleHelper>
28  unsigned int getSensorColumn(const T_ModuleHelper &helper, unsigned int cell_idx) {
29  return cell_idx % helper.nSensorColumns();
30  }
31  template <class T_ModuleHelper>
32  unsigned int getSensorRow(const T_ModuleHelper &helper, unsigned int cell_idx) {
33  return cell_idx / helper.nSensorColumns();
34  }
35 
36  unsigned int makeCheckerboard(unsigned int cell_idx, unsigned int n_rows, unsigned int n_columns,
37  bool odd_row_toggle,
38  bool odd_col_toggle
39  ) {
40  unsigned int row_i=cell_idx / n_columns;
41  unsigned int col_i = cell_idx % n_columns;
42  unsigned int row_odd = row_i < n_rows/2 || !odd_row_toggle ? 1 : 0;
43  unsigned int div_row = n_rows/7;
44  unsigned int div_cols = n_columns/7;
45  row_i = std::min((((row_i / div_row ) & (0xffff-1)) + row_odd)*div_row + (row_i%div_row), n_rows-1);
46  unsigned int col_odd = col_i < n_columns/2 || !odd_col_toggle ? 1 : 0;
47  col_i = std::min((((col_i / div_cols ) & (0xffff-1)) + col_odd)*div_cols + (col_i%div_cols),n_columns-1);
48  cell_idx= row_i * n_columns + (col_i % n_columns);
49  return cell_idx;
50  }
51 
52  /** helper to consistently lock and unlock when leaving the scope.
53  * similar to std::lock_guard, but locking when not needed for the scope
54  * can be disabled.
55  */
56  class MyLockGuard {
57  public:
58  MyLockGuard(std::mutex &a_mutex, bool disable)
59  : m_mutex( !disable ? &a_mutex : nullptr)
60  {
61  if (m_mutex) {m_mutex->lock(); }
62  }
63  ~MyLockGuard() {
64  if (m_mutex) {m_mutex->unlock(); }
65  }
66  private:
67  std::mutex *m_mutex;
68  };
69 
70 
71  template <class T_ModuleHelper>
72  void histogramDefects(const T_ModuleHelper &helper,
73  typename T_ModuleHelper::KEY_TYPE key,
74  TH2 *h2) {
75  std::array<unsigned int,4> ranges_row_col = helper.offlineRange(key);
76  for (unsigned int row_i=ranges_row_col[0]; row_i<ranges_row_col[1]; ++row_i) {
77  for (unsigned int col_i=ranges_row_col[2]; col_i<ranges_row_col[3]; ++col_i) {
78  h2->Fill(col_i, row_i);
79  }
80  }
81  }
82 
83  // create an array containing all possible combinations of M-tuples of numbers of 1 to N
84  // The M numbers are unique per M-tuple, and the order has no significance.
85  template <typename T, std::size_t N_MAX, std::size_t M>
86  constexpr std::array<T, N_MAX> packCombinations(unsigned int N) {
87  std::array<T, N_MAX> packed_permutations{};
88  unsigned int idx=0;
89  std::array<unsigned int,M> last_val;
90  for (unsigned int i=0; i<N; ++i) {
91  last_val[i]=i;
92  }
93  for (;;) {
94  for (unsigned int i=0; i<N; ++i) {
95  packed_permutations[idx]|= static_cast<T>(1u<<last_val[i]);
96  }
97  ++idx;
98  unsigned int i=N;
99  for(; i-->0; ) {
100  if (++last_val[i]<=M-(N-i)) {
101  for (;++i<N;) {
102  last_val[i]=last_val[i-1]+1;
103  }
104  i=0;
105  break;
106  }
107  }
108  if (i>=N) break;
109  }
110  return packed_permutations;
111  }
112 
113  // count the number of elements up the first which is zero if it is not the first element.
114  template <typename T, std::size_t N_MAX>
115  constexpr unsigned int findLastNonEmpty(const std::array<T, N_MAX> &arr) {
116  unsigned int i=0;
117  while (++i<arr.size() && arr[i]!=0);
118  return i;
119  }
120 
121  // create an arry which contains the number of combinations for the array of combinations.
122  template <typename T, typename T2, std::size_t N_MAX, std::size_t N>
123  constexpr std::array<unsigned char,N> makeCombinationCounts(const std::array<std::array<T2, N_MAX>, N> &arr) {
124  std::array<unsigned char,N> ret;
125  unsigned int idx=0;
126  for (const std::array<T2, N_MAX> &sub_arr : arr) {
127  ret[idx++]=static_cast<T>(findLastNonEmpty(sub_arr));
128  }
129  return ret;
130  }
131 
132  // create the possible combinations of defect corners for 1 to 4 defect corners
133  static constexpr std::array< std::array<unsigned char,6>, 4> corner_combinations {
134  packCombinations<unsigned char,6,4>(1),
135  packCombinations<unsigned char,6,4>(2),
136  packCombinations<unsigned char,6,4>(3),
137  packCombinations<unsigned char,6,4>(4),
138  };
139 
140  // count the number of possible combinations;
141  static constexpr std::array<unsigned char,4> n_corner_combinations (makeCombinationCounts<unsigned char>(corner_combinations) );
142 
143  template <typename T>
144  inline T sqr(T a) {
145  return a*a;
146  }
147 }
148 
149 namespace InDet{
150 
151 
152  template<class T_Derived>
153  StatusCode DefectsEmulatorCondAlgImpl<T_Derived>::initialize(){
154  ATH_CHECK(m_detEleCollKey.initialize());
155  ATH_CHECK(m_writeKey.initialize());
156  ATH_CHECK(detStore()->retrieve(m_idHelper,derived().IDName()));
157  if constexpr( T_ModuleHelper::ROW_BITS==0 || T_ModuleHelper::COL_BITS==0) {
158  if (!m_cornerDefectParamsPerPattern.empty() || !m_cornerDefectNCornerFractionsPerPattern.empty()) {
159  ATH_MSG_ERROR("Corner defects are not supported in this instance but are configured.");
160  return StatusCode::FAILURE;
161  }
162  }
163 
164  return initializeBase(T_ModuleHelper::nMasks(), m_idHelper->wafer_hash_max());
165  }
166 
167  template<class T_Derived>
168  StatusCode DefectsEmulatorCondAlgImpl<T_Derived>::execute(const EventContext& ctx) const {
169  SG::WriteCondHandle<T_EmulatedDefects> defectsOut(m_writeKey, ctx);
170  if (defectsOut.isValid()) {
171  return StatusCode::SUCCESS;
172  }
173  SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> detEleColl(m_detEleCollKey,ctx);
174  ATH_CHECK(detEleColl.isValid());
175  defectsOut.addDependency(detEleColl);
176 
177  std::unique_ptr<T_EmulatedDefects> defects = std::make_unique<T_EmulatedDefects>(*(detEleColl.cptr()));
178  // last counts (+1) are counts for entire modules being defect
179  std::vector<std::array<unsigned int,kNCounts> > counts(T_ModuleHelper::nMasks()+1, std::array<unsigned int,kNCounts>{});
180 
181  std::size_t n_error=0u;
182  unsigned int n_defects_total=0;
183  unsigned int module_without_matching_pattern=0u;
184  {
185  auto connected_modules = derived().getModuleConnectionMap(*(detEleColl.cptr()));
186 
187  ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, m_rngName);
188  rngWrapper->setSeed( m_rngName, ctx );
189  CLHEP::HepRandomEngine *rndmEngine = rngWrapper->getEngine(ctx);
190 
191  ModuleIdentifierMatchUtil::ModuleData_t module_data;
192  std::vector<unsigned int> module_pattern_idx;
193  module_pattern_idx.reserve( m_modulePattern.size() );
194  std::vector<double> cumulative_prob_dist;
195  cumulative_prob_dist.reserve( m_modulePattern.size() );
196  std::vector<unsigned int> n_mask_defects;
197  n_mask_defects.reserve( T_ModuleHelper::nMasks());
198 
199  std::size_t det_ele_sz = detEleColl->size();
200  for (unsigned int module_i=0u; module_i < det_ele_sz; ++module_i) {
201  const typename T_DetectorElementCollection::value_type det_ele = (*detEleColl.cptr())[module_i];
202  if (defects->isModuleDefect(module_i)) continue;
203  if (derived().isModuleDefect(ctx, module_i)) {
204  defects->setModuleDefect(module_i);
205  if (m_histogrammingEnabled && !m_moduleHist.empty()) {
206  // Always fill externally provided defects as if the module would
207  // match the first pattern, since there is no pattern associated to
208  // the external defects.
209  std::lock_guard<std::mutex> lock(m_histMutex);
210  histogramDefectModule(0, 0, module_i, det_ele->center());
211  }
212  continue;
213  }
214 
215  T_ModuleHelper helper(det_ele->design());
216 
217  if (!helper) {
218  ++n_error;
219  continue;
220  }
221  ++(counts.back()[ kNElements ]); // module defects
222 
223  // find pattern matching this module
224  const T_ModuleDesign &moduleDesign = dynamic_cast<const T_ModuleDesign &>(det_ele->design());
225  ModuleIdentifierMatchUtil::setModuleData(*m_idHelper,
226  det_ele->identify(),
227  moduleDesign,
228  module_data);
229  ModuleIdentifierMatchUtil::moduleMatches(m_modulePattern.value(), module_data, module_pattern_idx);
230  if (module_pattern_idx.empty()) {
231  ++module_without_matching_pattern;
232  continue;
233  }
234 
235  // it is possible that multiple patterns match a module
236  // For module defects a pattern is selected from all matching patterns randomly. This matters, because
237  // patterns control whether a defect is propagated to all modules connected to the same physical sensor
238  // or not.
239  makeCumulativeProbabilityDist(module_pattern_idx,kModuleDefectProb, cumulative_prob_dist);
240  float prob=(cumulative_prob_dist.empty() || cumulative_prob_dist.back()<=0.) ? 1. : CLHEP::RandFlat::shoot(rndmEngine,1.);
241 
242  unsigned int match_i=module_pattern_idx.size();
243  assert (match_i>0);
244  for (; match_i-->0; ) {
245  assert( match_i < cumulative_prob_dist.size());
246  if (prob > cumulative_prob_dist[match_i]) break;
247  }
248  ++match_i;
249  // module defects
250  if (match_i < module_pattern_idx.size()) {
251  unsigned int hist_pattern_i = (m_fillHistogramsPerPattern ? module_pattern_idx[match_i] : 0 );
252  // mark entire module as defect;
253  (counts.back()[ kNDefects ])
254  += 1-defects->isModuleDefect(module_i); // do not double count
255  defects->setModuleDefect(module_i);
256  // if configured accordingly also mark the other "modules" as defect which
257  // are part of the same physical module
258  ATH_MSG_VERBOSE( "Add module defect "
259  << " hash=" << m_idHelper->wafer_hash(det_ele->identify())
260  << " barel_ec=" << m_idHelper->barrel_ec(det_ele->identify())
261  << " layer_disk=" << m_idHelper->layer_disk(det_ele->identify())
262  << " phi_module=" << m_idHelper->phi_module(det_ele->identify())
263  << " eta_module=" << m_idHelper->eta_module(det_ele->identify())
264  << " side=" << ModuleIdentifierMatchUtil::detail::getZeroOrSide(*m_idHelper,det_ele->identify())
265  << " columns_strips=" << module_data[4]);
266 
267  // if histogramming is enabled lock also for the connected modules
268  // to avoid frequent lock/unlocks.
269  // Anyway the algorithm should run only once per job
270  MyLockGuard lock(m_histMutex, m_histogrammingEnabled);
271  if (m_histogrammingEnabled) {
272  histogramDefectModule(module_pattern_idx[match_i], hist_pattern_i, module_i, det_ele->center());
273  }
274 
275  if constexpr(IdentifierHelperWithOtherSideConecpt<decltype(*m_idHelper)>) {
276  // propagate module defects to modules connected to the same physical sensor
277  // if configured accordingly.
278  assert( module_pattern_idx[match_i] < m_modulePattern.size());
279  ConnectedModulesUtil::visitMatchingConnectedModules(
280  *m_idHelper,
281  m_modulePattern[module_pattern_idx[match_i]],
282  *(detEleColl.cptr()),
283  module_i,
284  connected_modules,
285  [defects_ptr=defects.get(),
286  &counts,
287  module_pattern_i=module_pattern_idx[match_i],
288  hist_pattern_i,
289  this](unsigned int child_id_hash,const InDetDD::SiDetectorElement &connected_det_ele) {
290 
291  (counts.back()[ kNDefects ])
292  += 1-defects_ptr->isModuleDefect(child_id_hash); // do not double count
293  defects_ptr->setModuleDefect(child_id_hash);
294 
295  if (m_histogrammingEnabled) {
296  histogramDefectModule(module_pattern_i, hist_pattern_i, child_id_hash, connected_det_ele.center());
297  }
298 
299  ATH_MSG_VERBOSE( "Propagate module defect to other split modules "
300  << " hash=" << m_idHelper->wafer_hash(connected_det_ele.identify())
301  << " barel_ec=" << m_idHelper->barrel_ec(connected_det_ele.identify())
302  << " layer_disk=" << m_idHelper->layer_disk(connected_det_ele.identify())
303  << " phi_module=" << m_idHelper->phi_module(connected_det_ele.identify())
304  << " eta_module=" << m_idHelper->eta_module(connected_det_ele.identify())
305  << " side=" << ModuleIdentifierMatchUtil::detail::getZeroOrSide(*m_idHelper, connected_det_ele.identify()) );
306  });
307  }
308  continue;
309  }
310  unsigned int hist_pattern_i = (m_fillHistogramsPerPattern ? module_pattern_idx.front() : 0 );
311 
312  // throw number of defects of all defect types excluding module defects which are already handled
313  // e.g. chip-defects, core-column defects, single pixel or strip defects
314  unsigned int cells = helper.nCells();
315  std::vector<typename T_ModuleHelper::KEY_TYPE> &module_defects=(*defects).at(module_i);
316  module_defects.reserve( throwNumberOfDefects(rndmEngine, module_pattern_idx, T_ModuleHelper::nMasks(),cells, n_mask_defects) );
317 
318  // if histogramming is enabled lock for the entire loop, since it would
319  // be rather inefficient to lock for each defect pixel or strip separately and
320  // this algorithm should run only a single time per job anyway.
321  MyLockGuard lock(m_histMutex, m_histogrammingEnabled);
322  auto [matrix_histogram_index, matrix_index]=(m_histogrammingEnabled
323  ? findHist(hist_pattern_i, helper.nSensorRows(), helper.nSensorColumns())
324  : std::make_pair(0u,0u));
325 
326  // create defects starting from the mask (or group) defect covering the largest area (assuming
327  // that masks are in ascending order) e.g. defect chips, core-column defects, individual pixel
328  // or strip defects
329  for (unsigned int mask_i = T_ModuleHelper::nMasks(); mask_i-->0; ) {
330  assert( mask_i < n_mask_defects.size());
331  counts[mask_i][kNElements] += helper.nElements(mask_i);
332  unsigned int n_defects = n_mask_defects[mask_i];
333  if (n_defects>0) {
334  // module with mask (or group) defects i.e. core-column defects, defect chips, ...
335  assert( mask_i < counts.size());
336  counts[mask_i][kMaxDefectsPerModule] = std::max(counts[mask_i][kMaxDefectsPerModule],n_defects);
337  unsigned int current_defects = module_defects.size();
338  assert( !m_histogrammingEnabled || ( hist_pattern_i < m_hist.size() && matrix_histogram_index < m_hist[hist_pattern_i].size()) );
339  TH2 *h2 = (m_histogrammingEnabled ? m_hist.at(hist_pattern_i).at(matrix_histogram_index) : nullptr);
340 
341  for (unsigned int defect_i=0; defect_i < n_defects; ++defect_i) {
342  // retry if a random position has been chosen already, but at most m_maxAttempts times
343  unsigned int attempt_i=0;
344  for (attempt_i=0; attempt_i<m_maxAttempts.value(); ++attempt_i) {
345  // chose random pixel or strip which defines the defect location
346  unsigned int cell_idx=CLHEP::RandFlat::shoot(rndmEngine,cells);
347 
348  if (m_checkerBoardToggle) {
349  // for debugging
350  // restrict defects on checker board
351  cell_idx=makeCheckerboard(cell_idx,
352  helper.nSensorRows(),
353  helper.nSensorColumns(),
354  m_oddRowToggle.value(),
355  m_oddColToggle.value() );
356  }
357 
358  // create a key which identifies the particular defect
359  typename T_ModuleHelper::KEY_TYPE
360  key = helper.maskedKey( mask_i,
361  helper.hardwareCoordinates(getSensorRow(helper, cell_idx),
362  getSensorColumn(helper, cell_idx)));
363 
364  // ... and insert the defect in the list of defects registered for this module
365  auto [insert_iter,end_iter] = T_EmulatedDefects::lower_bound( module_defects, key);
366  if (insert_iter == end_iter) {
367  module_defects.push_back(key);
368  if (h2) {
369  histogramDefects(helper,key,h2);
370  }
371  break;
372  }
373  else {
374  if (!helper.isMatchingDefect(*insert_iter, key)) {
375  module_defects.insert( insert_iter, key);
376  if (h2) {
377  histogramDefects(helper,key,h2);
378  }
379  break;
380  }
381  else if (helper.getMaskIdx(key) < helper.getMaskIdx(*insert_iter)) {
382  // if smaller defects e.g single pixel defects overlap with a larger defect e.g. a core-column
383  // defect do not retry, since the total number of defects is not corrected for
384  // already existing larger area defects.
385  break;
386  }
387  }
388  assert( mask_i+1 < counts.size());
389  ++counts[mask_i][ kNRetries ];
390  }
391  assert( mask_i+1 < counts.size());
392  counts[mask_i][kNMaxRtriesExceeded] += attempt_i >= m_maxAttempts.value();
393  }
394  unsigned int new_defects = module_defects.size() - current_defects;
395  assert( mask_i+1 < counts.size());
396  counts[mask_i][ kNDefects ] += new_defects;
397  if (new_defects>0) {
398  ++(counts[mask_i][kNModulesWithDefects]);
399  }
400  }
401  }
402 
403  // Create corner defects only for modules with columns and rows.
404  if constexpr( T_ModuleHelper::ROW_BITS>0 && T_ModuleHelper::COL_BITS>0) {
405  if (!m_cornerDefectParamsPerPattern.empty() && !m_cornerDefectParamsPerPattern[module_pattern_idx.front()].empty()) {
406  if (module_pattern_idx.size()!=1) {
407  ATH_MSG_WARNING("Multiple module pattern match module " << module_i
408  << ", but for corner defects only the parameters are used which are associated to the first pattern" );
409  }
410  // create defects at certain corners outside of a circle which crosses the sensor edges at certain positions
411  // chosen in a random range. The edge crossing at the particular corner defines a sagitta of a certain value
412  // chosen in a random range
413 
414  // throw the number of corners with corner defects.
415  float defect_prob=CLHEP::RandFlat::shoot(rndmEngine,1.);
416  unsigned int n_corners=m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ].size();
417  for (;
418  n_corners>0 && defect_prob<m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ][n_corners-1];
419  --n_corners);
420  unsigned int n_current_defects =module_defects.size();
421  if (n_corners<m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ].size()) {
422  assert( !m_histogrammingEnabled || ( hist_pattern_i < m_hist.size() && matrix_histogram_index < m_hist[hist_pattern_i].size()) );
423  TH2 *h2 = (m_histogrammingEnabled ? m_hist.at(hist_pattern_i).at(matrix_histogram_index) : nullptr);
424 
425  assert( n_corners < n_corner_combinations.size() );
426  // chose the corners which have defects randomly
427  unsigned int the_combination =CLHEP::RandFlat::shoot(rndmEngine,n_corner_combinations[n_corners] );
428  unsigned char corner_mask = corner_combinations[n_corners][the_combination];
429  for (unsigned int corner_i=0; corner_i<4; ++corner_i) {
430  if (corner_mask & (1u << corner_i)) {
431  // chose random positions at which the circle crosses the edges at the particular corner
432  // and chose a random sagitta defined by the corssings.
433  assert( m_cornerDefectParamsPerPattern[module_pattern_idx.front()].size()==kNCornerDefectParams);
434  float rx=m_cornerDefectParamsPerPattern[module_pattern_idx.front()][kCornerDefectWidthColumnDirectionOffset]
435  +CLHEP::RandFlat::shoot(rndmEngine,m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectWidthColumnDirection]);
436  float ry=m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkWidthRowDirectionOffset]
437  +CLHEP::RandFlat::shoot(rndmEngine,m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkWidthRowDirection]);
438 
439  float sagitta;
440  {
441  // make sure that the sagitta is small enough that the circle can actually cross the bounds.
442  // conditions: the circle radius Rc has to be larger than rx and ry. If the circle radius
443  // is equal to rx or ry, the circle will just touch the border.
444  // This leads to quadratic equations x^2 + 2*hp1/2 * x + q solved by x1/2,1/2 = -hp1/2 +- sqrt(hp1/2^2-q)
445  float r2=rx*rx+ry*ry;
446  float hp1=0.5*sqrt(r2)*rx/ry;
447  float hp2=0.5*sqrt(r2)*ry/rx;
448  float q=-0.25*r2;
449  float sagitta_max = std::min( (-hp1 + sqrt( hp1*hp1 -q )),(-hp2 + sqrt( hp2*hp2 -q )) );
450  float sagitta_range = m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta];
451  float sagitta_offset = m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset];
452  sagitta_offset=std::min(sagitta_offset,sagitta_max);
453  sagitta_range=std::min( sagitta_range, sagitta_max - sagitta_offset);
454  if (sagitta_range != m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta]
455  || sagitta_offset != m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]) {
456  ATH_MSG_VERBOSE("limited sagitta range from "
457  << m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]
458  << ".."
459  << (m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]
460  + m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta])
461  << " to " << sagitta_offset << ".." << (sagitta_offset+sagitta_range));
462  }
463  sagitta=sagitta_offset+CLHEP::RandFlat::shoot(rndmEngine,sagitta_range);
464  }
465 
466  // compute column range with defects
467  double scale_rx=(corner_i==1 || corner_i==2) ? -1.f : 1.f;
468  double scale_ry=(corner_i<=1) ? -1.f : 1.f;
469  std::array<unsigned int,2> row_range{0,helper.rows()};
470  unsigned int row_var_idx=(corner_i<=1) ? 0 : 1;
471  unsigned int col_start=(corner_i==1 || corner_i==2) ? helper.columns() : 0;
472 
473  Amg::Vector2D corner_pos{col_start*helper.columnPitch(),row_range[row_var_idx^1]*helper.rowPitch()};
474 
475  unsigned int col_end = std::min(static_cast<unsigned int>(std::max(0,static_cast<int>(col_start)
476  + static_cast<int>(rx*scale_rx / helper.columnPitch() +.5))),
477  helper.columns());
478  if (col_end < col_start) {
479  std::swap(col_start, col_end);
480  }
481 
482  // compute circle parameters
483  Amg::Vector2D c0 = corner_pos + Amg::Vector2D{0.f,ry*scale_ry};
484  Amg::Vector2D c1 = corner_pos + Amg::Vector2D{rx*scale_rx,0.f};
485 
486  double radius = (rx*rx+ry*ry)/(8*sagitta)+0.5*sagitta;
487  Amg::Vector2D Rc = c0+(c1-c0)*.5 - (scale_ry*scale_rx) * (radius-sagitta)/(sqrt(rx*rx+ry*ry))*Amg::Vector2D{-ry*scale_ry,-rx*scale_rx};
488 
489  double ymin=std::min(c0[1],c1[1]);
490  double ymax=std::max(c0[1],c1[1]);
491  for (unsigned int col_i=col_start; col_i< col_end; ++col_i) {
492  //compute row range with defects per column i.e. the row at which the circle crosses the column
493  // and the corresponding edge row (top or bottom)
494  double q = sqr(col_i*helper.columnPitch()-Rc[0]) + sqr(Rc[1]) - sqr(radius);
495  double ph = -Rc[1];
496  double Q=sqr(ph)-q;
497  if (Q>=0.) {
498  double sQ=sqrt(Q);
499  double cx = (-ph+sQ );
500  if (cx<ymin || cx>=ymax) {
501  double cx2=(-ph-sQ );
502  // pick the physical solution and make sure that
503  // that the solution remains in the allowed range despite
504  // limited precision
505  if (cx2<ymin || cx2 >=ymax) {
506  double d1=std::min(std::abs(cx-ymin),std::abs(cx-ymax));
507  double d2=std::min(std::abs(cx2-ymin),std::abs(cx2-ymax));
508  if (d2<d1) {
509  cx=cx2;
510  }
511  cx = std::clamp( cx, ymin, ymax - helper.rowPitch()*.25);
512  }
513  else {
514  cx=cx2;
515  }
516  }
517  if (cx>=ymin && cx<ymax) {
518  row_range[row_var_idx]=static_cast<unsigned int>( std::max(0.,cx / helper.rowPitch() + .5));
519  // create corner defects for one column
520  for (unsigned int row_i=row_range[0]; row_i<row_range[1]; ++row_i) {
521  // create a key which identifies the particular defect
522  // @TODO should store as range instead e.g. mark that a key is the beginning of a range
523  // up the the next key
524  typename T_ModuleHelper::KEY_TYPE
525  key = helper.maskedKey( 0u,
526  helper.swapOfflineRowsColumns()
527  ? helper.hardwareCoordinates(col_i,row_i)
528  : helper.hardwareCoordinates(row_i,col_i));
529 
530  // ... and insert the defect in the list of defects registered for this module
531  auto [insert_iter,end_iter] = T_EmulatedDefects::lower_bound( module_defects, key);
532  if (insert_iter == end_iter) {
533  module_defects.push_back(key);
534  if (h2) {
535  histogramDefects(helper,key,h2);
536  }
537  }
538  else {
539  if (!helper.isMatchingDefect(*insert_iter, key)) {
540  module_defects.insert( insert_iter, key);
541  if (h2) {
542  histogramDefects(helper,key,h2);
543  }
544  }
545  }
546  }
547  }
548  }
549  }
550  }
551  }
552  // add corner defects to random pixel defects for histogramming
553  n_mask_defects[0] += module_defects.size() - n_current_defects;
554  }
555  }
556  }
557  if (m_histogrammingEnabled) {
558  fillPerModuleHistograms(module_pattern_idx.front(),hist_pattern_i,matrix_histogram_index, matrix_index, module_i,
559  T_ModuleHelper::nMasks(), n_mask_defects, det_ele->center());
560  }
561  n_defects_total+=module_defects.size();
562  }
563  }
564  m_modulesWithoutDefectParameters += module_without_matching_pattern;
565  ATH_CHECK( defectsOut.record (std::move(defects)) );
566 
567  if (msgLvl(MSG::INFO)) {
568  printSummaryOfDefectGeneration(T_ModuleHelper::nMasks(), n_error, n_defects_total,counts);
569  }
570 
571  return StatusCode::SUCCESS;
572  }
573 }