2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
4 // Silicon trackers includes
5 #include "DefectsEmulatorCondAlgImpl.h"
7 #include "Identifier/Identifier.h"
8 #include "AthenaKernel/RNGWrapper.h"
10 #include "StoreGate/WriteHandle.h"
12 #include <type_traits>
17 #include "CLHEP/Random/RandFlat.h"
19 #include "ModuleIdentifierMatchUtil.h"
20 #include "ConnectedModulesUtil.h"
24 template <typename T_ID>
25 concept IdentifierHelperWithOtherSideConecpt = requires(T_ID id_helper) { id_helper.get_other_side(); };
27 template <class T_ModuleHelper>
28 unsigned int getSensorColumn(const T_ModuleHelper &helper, unsigned int cell_idx) {
29 return cell_idx % helper.nSensorColumns();
31 template <class T_ModuleHelper>
32 unsigned int getSensorRow(const T_ModuleHelper &helper, unsigned int cell_idx) {
33 return cell_idx / helper.nSensorColumns();
36 unsigned int makeCheckerboard(unsigned int cell_idx, unsigned int n_rows, unsigned int n_columns,
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);
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
58 MyLockGuard(std::mutex &a_mutex, bool disable)
59 : m_mutex( !disable ? &a_mutex : nullptr)
61 if (m_mutex) {m_mutex->lock(); }
64 if (m_mutex) {m_mutex->unlock(); }
71 template <class T_ModuleHelper>
72 void histogramDefects(const T_ModuleHelper &helper,
73 typename T_ModuleHelper::KEY_TYPE key,
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);
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{};
89 std::array<unsigned int,M> last_val;
90 for (unsigned int i=0; i<N; ++i) {
94 for (unsigned int i=0; i<N; ++i) {
95 packed_permutations[idx]|= static_cast<T>(1u<<last_val[i]);
100 if (++last_val[i]<=M-(N-i)) {
102 last_val[i]=last_val[i-1]+1;
110 return packed_permutations;
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) {
117 while (++i<arr.size() && arr[i]!=0);
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;
126 for (const std::array<T2, N_MAX> &sub_arr : arr) {
127 ret[idx++]=static_cast<T>(findLastNonEmpty(sub_arr));
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),
140 // count the number of possible combinations;
141 static constexpr std::array<unsigned char,4> n_corner_combinations (makeCombinationCounts<unsigned char>(corner_combinations) );
143 template <typename T>
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;
164 return initializeBase(T_ModuleHelper::nMasks(), m_idHelper->wafer_hash_max());
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;
173 SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> detEleColl(m_detEleCollKey,ctx);
174 ATH_CHECK(detEleColl.isValid());
175 defectsOut.addDependency(detEleColl);
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>{});
181 std::size_t n_error=0u;
182 unsigned int n_defects_total=0;
183 unsigned int module_without_matching_pattern=0u;
185 auto connected_modules = derived().getModuleConnectionMap(*(detEleColl.cptr()));
187 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, m_rngName);
188 rngWrapper->setSeed( m_rngName, ctx );
189 CLHEP::HepRandomEngine *rndmEngine = rngWrapper->getEngine(ctx);
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());
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());
215 T_ModuleHelper helper(det_ele->design());
221 ++(counts.back()[ kNElements ]); // module defects
223 // find pattern matching this module
224 const T_ModuleDesign &moduleDesign = dynamic_cast<const T_ModuleDesign &>(det_ele->design());
225 ModuleIdentifierMatchUtil::setModuleData(*m_idHelper,
229 ModuleIdentifierMatchUtil::moduleMatches(m_modulePattern.value(), module_data, module_pattern_idx);
230 if (module_pattern_idx.empty()) {
231 ++module_without_matching_pattern;
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
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.);
242 unsigned int match_i=module_pattern_idx.size();
244 for (; match_i-->0; ) {
245 assert( match_i < cumulative_prob_dist.size());
246 if (prob > cumulative_prob_dist[match_i]) break;
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]);
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());
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(
281 m_modulePattern[module_pattern_idx[match_i]],
282 *(detEleColl.cptr()),
285 [defects_ptr=defects.get(),
287 module_pattern_i=module_pattern_idx[match_i],
289 this](unsigned int child_id_hash,const InDetDD::SiDetectorElement &connected_det_ele) {
291 (counts.back()[ kNDefects ])
292 += 1-defects_ptr->isModuleDefect(child_id_hash); // do not double count
293 defects_ptr->setModuleDefect(child_id_hash);
295 if (m_histogrammingEnabled) {
296 histogramDefectModule(module_pattern_i, hist_pattern_i, child_id_hash, connected_det_ele.center());
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()) );
310 unsigned int hist_pattern_i = (m_fillHistogramsPerPattern ? module_pattern_idx.front() : 0 );
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) );
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));
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
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];
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);
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);
348 if (m_checkerBoardToggle) {
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() );
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)));
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);
369 histogramDefects(helper,key,h2);
374 if (!helper.isMatchingDefect(*insert_iter, key)) {
375 module_defects.insert( insert_iter, key);
377 histogramDefects(helper,key,h2);
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.
388 assert( mask_i+1 < counts.size());
389 ++counts[mask_i][ kNRetries ];
391 assert( mask_i+1 < counts.size());
392 counts[mask_i][kNMaxRtriesExceeded] += attempt_i >= m_maxAttempts.value();
394 unsigned int new_defects = module_defects.size() - current_defects;
395 assert( mask_i+1 < counts.size());
396 counts[mask_i][ kNDefects ] += new_defects;
398 ++(counts[mask_i][kNModulesWithDefects]);
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" );
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
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();
418 n_corners>0 && defect_prob<m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ][n_corners-1];
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);
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]);
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;
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]
459 << (m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]
460 + m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta])
461 << " to " << sagitta_offset << ".." << (sagitta_offset+sagitta_range));
463 sagitta=sagitta_offset+CLHEP::RandFlat::shoot(rndmEngine,sagitta_range);
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;
473 Amg::Vector2D corner_pos{col_start*helper.columnPitch(),row_range[row_var_idx^1]*helper.rowPitch()};
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))),
478 if (col_end < col_start) {
479 std::swap(col_start, col_end);
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};
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};
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);
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
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));
511 cx = std::clamp( cx, ymin, ymax - helper.rowPitch()*.25);
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));
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);
535 histogramDefects(helper,key,h2);
539 if (!helper.isMatchingDefect(*insert_iter, key)) {
540 module_defects.insert( insert_iter, key);
542 histogramDefects(helper,key,h2);
552 // add corner defects to random pixel defects for histogramming
553 n_mask_defects[0] += module_defects.size() - n_current_defects;
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());
561 n_defects_total+=module_defects.size();
564 m_modulesWithoutDefectParameters += module_without_matching_pattern;
565 ATH_CHECK( defectsOut.record (std::move(defects)) );
567 if (msgLvl(MSG::INFO)) {
568 printSummaryOfDefectGeneration(T_ModuleHelper::nMasks(), n_error, n_defects_total,counts);
571 return StatusCode::SUCCESS;