ATLAS Offline Software
Loading...
Searching...
No Matches
DefectsEmulatorCondAlgImpl.icc
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "Identifier/Identifier.h"
6#include "AthenaKernel/RNGWrapper.h"
7
8#include "StoreGate/WriteHandle.h"
9
10
11#include "CLHEP/Random/RandFlat.h"
12
13#include "ModuleIdentifierMatchUtil.h"
14#include "ConnectedModulesUtil.h"
15#include "EmulatedDefectsBuilder.h"
16
17#include "nlohmann/json.hpp"
18
19#include "TVirtualRWMutex.h"
20#include "ROOT/RNTupleWriter.hxx"
21#include "ROOT/RNTupleReader.hxx"
22#include "ROOT/RNTupleModel.hxx"
23#include "TFile.h"
24
25#include <cstdlib>
26#include <ranges>
27#include <type_traits>
28#include <array>
29#include <span>
30#include <algorithm>
31
32
33namespace {
34
35 template <typename T_ID>
36 concept IdentifierHelperWithOtherSideConecpt = requires(T_ID id_helper) { id_helper.get_other_side(); };
37
38 template <class T_ModuleHelper>
39 unsigned int getSensorColumn(const T_ModuleHelper &helper, unsigned int cell_idx) {
40 return cell_idx % helper.nSensorColumns();
41 }
42 template <class T_ModuleHelper>
43 unsigned int getSensorRow(const T_ModuleHelper &helper, unsigned int cell_idx) {
44 return cell_idx / helper.nSensorColumns();
45 }
46
47 unsigned int makeCheckerboard(unsigned int cell_idx, unsigned int n_rows, unsigned int n_columns,
48 bool odd_row_toggle,
49 bool odd_col_toggle
50 ) {
51 unsigned int row_i=cell_idx / n_columns;
52 unsigned int col_i = cell_idx % n_columns;
53 unsigned int row_odd = row_i < n_rows/2 || !odd_row_toggle ? 1 : 0;
54 unsigned int div_row = n_rows/7;
55 unsigned int div_cols = n_columns/7;
56 row_i = std::min((((row_i / div_row ) & (0xffff-1)) + row_odd)*div_row + (row_i%div_row), n_rows-1);
57 unsigned int col_odd = col_i < n_columns/2 || !odd_col_toggle ? 1 : 0;
58 col_i = std::min((((col_i / div_cols ) & (0xffff-1)) + col_odd)*div_cols + (col_i%div_cols),n_columns-1);
59 cell_idx= row_i * n_columns + (col_i % n_columns);
60 return cell_idx;
61 }
62
63 /** helper to consistently lock and unlock when leaving the scope.
64 * similar to std::lock_guard, but locking when not needed for the scope
65 * can be disabled.
66 */
67 class MyLockGuard {
68 public:
69 MyLockGuard(std::mutex &a_mutex, bool disable)
70 : m_mutex( !disable ? &a_mutex : nullptr)
71 {
72 if (m_mutex) {m_mutex->lock(); }
73 }
74 ~MyLockGuard() {
75 if (m_mutex) {m_mutex->unlock(); }
76 }
77 private:
78 std::mutex *m_mutex;
79 };
80
81
82 template <class T_ModuleHelper>
83 void histogramDefects(const T_ModuleHelper &helper,
84 const typename std::pair<typename T_ModuleHelper::KEY_TYPE,typename T_ModuleHelper::KEY_TYPE> &key,
85 TH2 *h2) {
86 std::array<unsigned int,4> ranges_row_col = helper.offlineRange(key);
87 for (unsigned int row_i=ranges_row_col[0]; row_i<ranges_row_col[1]; ++row_i) {
88 for (unsigned int col_i=ranges_row_col[2]; col_i<ranges_row_col[3]; ++col_i) {
89 h2->Fill(col_i, row_i);
90 }
91 }
92 }
93
94 // create an array containing all possible combinations of M-tuples of numbers of 1 to N
95 // The M numbers are unique per M-tuple, and the order has no significance.
96 template <typename T, std::size_t N_MAX, std::size_t M>
97 constexpr std::array<T, N_MAX> packCombinations(unsigned int N) {
98 std::array<T, N_MAX> packed_permutations{};
99 unsigned int idx=0;
100 std::array<unsigned int,M> last_val;
101 for (unsigned int i=0; i<N; ++i) {
102 last_val[i]=i;
103 }
104 for (;;) {
105 for (unsigned int i=0; i<N; ++i) {
106 packed_permutations[idx]|= static_cast<T>(1u<<last_val[i]);
107 }
108 ++idx;
109 unsigned int i=N;
110 for(; i-->0; ) {
111 if (++last_val[i]<=M-(N-i)) {
112 for (;++i<N;) {
113 last_val[i]=last_val[i-1]+1;
114 }
115 i=0;
116 break;
117 }
118 }
119 if (i>=N) break;
120 }
121 return packed_permutations;
122 }
123
124 // count the number of elements up the first which is zero if it is not the first element.
125 template <typename T, std::size_t N_MAX>
126 constexpr unsigned int findLastNonEmpty(const std::array<T, N_MAX> &arr) {
127 unsigned int i=0;
128 while (++i<arr.size() && arr[i]!=0);
129 return i;
130 }
131
132 // create an arry which contains the number of combinations for the array of combinations.
133 template <typename T, typename T2, std::size_t N_MAX, std::size_t N>
134 constexpr std::array<unsigned char,N> makeCombinationCounts(const std::array<std::array<T2, N_MAX>, N> &arr) {
135 std::array<unsigned char,N> ret;
136 unsigned int idx=0;
137 for (const std::array<T2, N_MAX> &sub_arr : arr) {
138 ret[idx++]=static_cast<T>(findLastNonEmpty(sub_arr));
139 }
140 return ret;
141 }
142
143 // create the possible combinations of defect corners for 1 to 4 defect corners
144 static constexpr std::array< std::array<unsigned char,6>, 4> corner_combinations {
145 packCombinations<unsigned char,6,4>(1),
146 packCombinations<unsigned char,6,4>(2),
147 packCombinations<unsigned char,6,4>(3),
148 packCombinations<unsigned char,6,4>(4),
149 };
150
151 // count the number of possible combinations;
152 static constexpr std::array<unsigned char,4> n_corner_combinations (makeCombinationCounts<unsigned char>(corner_combinations) );
153
154 template <typename T>
155 inline T sqr(T a) {
156 return a*a;
157 }
158
159 bool hasExtensions(const std::string &name, const std::string_view &ext) {
160 return name.size()>=ext.size() && name.substr(name.size()-ext.size(),ext.size())==ext;
161 }
162
163 template <typename T_EmulatedDefects>
164 void toJson(const T_EmulatedDefects &defects, const std::string &name) {
165 nlohmann::json data;
166 for (unsigned int id_hash = 0; id_hash < defects.size(); ++id_hash) {
167 if (defects.isModuleDefect(id_hash) || !defects[id_hash].empty()) {
168 nlohmann::json module_data;
169 if (defects.isModuleDefect(id_hash)) {
170 module_data["isDefect"]=true;
171 }
172 else {
173 module_data["defects"]=defects[id_hash];
174 }
175
176 std::stringstream id_hash_name;
177 id_hash_name << id_hash;
178 data[id_hash_name.str()]=module_data;
179 }
180 }
181 std::ofstream out(name.c_str());
182 out << data;
183 }
184
185 template <typename T_EmulatedDefects>
186 void toRoot(const T_EmulatedDefects &defects, const std::string &name) {
187 ROOT::TWriteLockGuard lock (ROOT::gCoreMutex);
188
189 auto model=ROOT::RNTupleModel::Create();
190 auto nt_defects=model->MakeField< std::vector<typename T_EmulatedDefects::KEY_TYPE> >("defects");
191 auto nt_is_defect=model->MakeField<bool>("isDefect");
192 ROOT::RNTupleWriteOptions options;
193 options.SetCompression(ROOT::RCompressionSetting::EAlgorithm::kZSTD, ROOT::RCompressionSetting::ELevel::kDefaultZSTD);
194 auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "defects", name.c_str(), options);
195 for (unsigned int id_hash = 0; id_hash < defects.size(); ++id_hash) {
196 nt_defects->clear();
197 if (defects.isModuleDefect(id_hash)) {
198 *nt_is_defect =true;
199 }
200 else {
201 *nt_is_defect =false;
202 *nt_defects = defects[id_hash];
203 }
204 writer->Fill();
205 }
206 }
207
208 template <typename KEY_TYPE>
209 class JsonAdapter {
210 public:
211 bool open(const std::string &name) {
212 std::ifstream input_file(name);
213 if (!input_file) {
214 return false;
215 }
216 input_file >> m_jsDefects;
217 return true;
218 }
219 auto items() {
220 return m_jsDefects.items();
221 }
222 template <typename T>
223 static std::size_t moduleHash(const T &element) {
224 char *ptr;
225 return strtol(element.key().c_str(),&ptr, 10);
226 }
227 template <typename T>
228 static nlohmann::json value(const T &element) {
229 return element.value();
230 }
231 static bool isDefect(const nlohmann::json &value) {
232 return !value.contains("isDefect") ? false : value["isDefect"].get<bool>();
233 }
234 static std::vector<KEY_TYPE> moduleDefects(const nlohmann::json &value) {
235 return !value.contains("defects") ? std::vector<KEY_TYPE>() : value["defects"].get< std::vector<KEY_TYPE> >();
236 }
237 private:
238 nlohmann::json m_jsDefects;
239 };
240
241 template <typename KEY_TYPE>
242 class RootAdapter {
243 public:
244 bool open(const std::string &name) {
245 auto model=ROOT::RNTupleModel::Create();
246 m_defects=model->MakeField< std::vector<KEY_TYPE> >("defects");
247 m_isDefect=model->MakeField<bool>("isDefect");
248 m_reader = ROOT::RNTupleReader::Open(std::move(model), "defects", name.c_str());
249 return m_reader.get() != nullptr;
250 }
251 ROOT::RNTupleReader &items() {
252 return *m_reader;
253 }
254 static auto moduleHash( std::size_t entry_i) {
255 return entry_i;
256 }
257 auto value(std::size_t entry_i) {
258 m_reader->LoadEntry(entry_i);
259 return entry_i;
260 }
261 bool isDefect([[maybe_unused]] std::size_t entry_i) {
262 return *m_isDefect;
263 }
264 const std::vector<KEY_TYPE> &moduleDefects([[maybe_unused]] std::size_t entry_i) {
265 return *m_defects;
266 }
267 private:
268 std::unique_ptr< ROOT::RNTupleReader > m_reader;
269 std::shared_ptr< bool > m_isDefect;
270 std::shared_ptr<std::vector<KEY_TYPE> > m_defects;
271 };
272
273 template <typename T_ModuleHelper, typename T_ReaderAdapter, typename T_EmulatedDefects>
274 void fromFile(const InDetDD::SiDetectorElementCollection &det_ele_coll, T_EmulatedDefects &defects, const std::string &name) {
275 T_ReaderAdapter reader;
276 if (!reader.open(name)) {
277 std::stringstream amsg;
278 amsg << "Failed to read \"" << name << "\"";
279 throw std::runtime_error(amsg.str());
280 }
281
282 for (auto entry_i : reader.items()) {
283 auto module_i = reader.moduleHash(entry_i);
284 assert( module_i < det_ele_coll.size());
285 auto value = reader.value(entry_i);
286 if (reader.isDefect(value)) {
287 defects.setModuleDefect(module_i);
288 }
289 else if (!defects.isModuleDefect(module_i)) {
290 std::vector<typename T_EmulatedDefects::KEY_TYPE> &module_defects = defects.at(module_i);
291 if (module_defects.empty()) {
292 module_defects = reader.moduleDefects(value); // move ?
293 }
294 else {
295 InDetDD::SiDetectorElementCollection::const_value_type det_ele = det_ele_coll[module_i];
296 T_ModuleHelper helper(det_ele->design());
297 auto input_defects = reader.moduleDefects(value);
298 for (typename std::vector<typename T_EmulatedDefects::KEY_TYPE>::const_reverse_iterator iter = input_defects.rbegin();
299 iter != input_defects.rend();
300 ++iter) {
301 auto key = *iter;
302 if (T_ModuleHelper::isRangeKey(*iter)) {
303 ++iter;
304 if (iter == input_defects.rend()) {
305 break;
306 }
307 EmulatedDefectsBuilder::insertKeyRange(helper,
308 module_defects,
309 std::make_pair(key, *iter),
310 T_ModuleHelper::getDefectTypeComponent(key));
311 if (T_ModuleHelper::getDefectTypeComponent(key) != T_ModuleHelper::getDefectTypeComponent(*iter)) {
312 auto [end_key_iter, end_iter] = InDet::EmulatedDefects<T_ModuleHelper>::lower_bound(module_defects, *iter);
313 if (end_key_iter != end_iter && T_ModuleHelper::makeBaseKey(*iter) == T_ModuleHelper::makeBaseKey(*end_key_iter)) {
314 *end_key_iter |= T_ModuleHelper::getDefectTypeComponent(*iter);
315 }
316 }
317 }
318 else {
319 EmulatedDefectsBuilder::insertKey(helper,
320 module_defects,
321 key,
322 T_ModuleHelper::getDefectTypeComponent(key));
323 }
324 }
325 }
326 }
327 }
328 }
329
330
331 template <typename T_ModuleHelper, typename T_EmulatedDefects>
332 void fromJson(const InDetDD::SiDetectorElementCollection &det_ele_coll, T_EmulatedDefects &defects, const std::string &name) {
333 fromFile<T_ModuleHelper, JsonAdapter<typename T_ModuleHelper::KEY_TYPE> >(det_ele_coll, defects, name);
334 }
335
336 template <typename T_ModuleHelper, typename T_EmulatedDefects>
337 void fromRoot(const InDetDD::SiDetectorElementCollection &det_ele_coll, T_EmulatedDefects &defects, const std::string &name) {
338 ROOT::TReadLockGuard lock (ROOT::gCoreMutex);
339 fromFile<T_ModuleHelper, RootAdapter<typename T_ModuleHelper::KEY_TYPE> >(det_ele_coll, defects, name);
340 }
341
342}
343
344namespace InDet{
345
346
347 template<class T_Derived>
348 StatusCode DefectsEmulatorCondAlgImpl<T_Derived>::initialize(){
349 ATH_CHECK(m_detEleCollKey.initialize());
350 ATH_CHECK(m_writeKey.initialize());
351 ATH_CHECK(detStore()->retrieve(m_idHelper,derived().IDName()));
352 if constexpr( T_ModuleHelper::ROW_BITS==0 || T_ModuleHelper::COL_BITS==0) {
353 if (!m_cornerDefectParamsPerPattern.empty() || !m_cornerDefectNCornerFractionsPerPattern.empty()) {
354 ATH_MSG_ERROR("Corner defects are not supported in this instance but are configured.");
355 return StatusCode::FAILURE;
356 }
357 }
358
359 return initializeBase(T_ModuleHelper::nMasks(), m_idHelper->wafer_hash_max());
360 }
361
362 template<class T_Derived>
363 StatusCode DefectsEmulatorCondAlgImpl<T_Derived>::execute(const EventContext& ctx) const {
364 SG::WriteCondHandle<T_EmulatedDefects> defectsOut(m_writeKey, ctx);
365 if (defectsOut.isValid()) {
366 return StatusCode::SUCCESS;
367 }
368 SG::ReadCondHandle<InDetDD::SiDetectorElementCollection> detEleColl(m_detEleCollKey,ctx);
369 ATH_CHECK(detEleColl.isValid());
370 defectsOut.addDependency(detEleColl);
371
372 std::unique_ptr<T_EmulatedDefects> defects = std::make_unique<T_EmulatedDefects>(*(detEleColl.cptr()));
373
374 for (const std::string &input_file : m_inputFiles.value()) {
375 if (hasExtensions(input_file,".json")) {
376 fromJson<T_ModuleHelper>(*(detEleColl.cptr()),*defects, input_file);
377 }
378 else if (hasExtensions(input_file,".root")) {
379 fromRoot<T_ModuleHelper>(*(detEleColl.cptr()), *defects, input_file);
380 }
381 }
382
383 // last counts (+1) are counts for entire modules being defect
384 std::vector<std::array<unsigned int,kNCounts> > counts(T_ModuleHelper::nMasks()+1, std::array<unsigned int,kNCounts>{});
385
386 std::size_t n_error=0u;
387 unsigned int n_defects_total=0;
388 unsigned int module_without_matching_pattern=0u;
389 {
390 auto connected_modules = derived().getModuleConnectionMap(*(detEleColl.cptr()));
391
392 std::array<CLHEP::HepRandomEngine *, kMaskDefects + T_ModuleHelper::nMasks() > rndmEngine;
393 if (this->m_rngPerDefectType) {
394 assert (m_rngName.size() == rndmEngine.size());
395 unsigned int idx=0;
396 for (const std::string &a_rngName : m_rngName) {
397 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, a_rngName );
398 rngWrapper->setSeed( a_rngName, ctx );
399 assert( idx < rndmEngine.size());
400 rndmEngine[idx++]=rngWrapper->getEngine(ctx);
401 }
402 }
403 else{
404 assert( !m_rngName.empty());
405 ATHRNG::RNGWrapper* rngWrapper = m_rndmSvc->getEngine(this, m_rngName[0]);
406 rngWrapper->setSeed( m_rngName[0], ctx );
407 CLHEP::HepRandomEngine *a_rndmEngine = rngWrapper->getEngine(ctx);
408 for (CLHEP::HepRandomEngine *&elm : rndmEngine) {
409 elm = a_rndmEngine;
410 }
411 }
412
413 ModuleIdentifierMatchUtil::ModuleData_t module_data;
414 std::vector<unsigned int> module_pattern_idx;
415 module_pattern_idx.reserve( m_modulePattern.size() );
416 std::vector<double> cumulative_prob_dist;
417 cumulative_prob_dist.reserve( m_modulePattern.size() );
418 std::vector<unsigned int> n_mask_defects;
419 n_mask_defects.reserve( T_ModuleHelper::nMasks());
420
421 std::size_t det_ele_sz = detEleColl->size();
422 for (unsigned int module_i=0u; module_i < det_ele_sz; ++module_i) {
423 typename T_DetectorElementCollection::const_value_type det_ele = (*detEleColl.cptr())[module_i];
424 if (defects->isModuleDefect(module_i)) continue;
425 if (derived().isModuleDefect(ctx, module_i)) {
426 defects->setModuleDefect(module_i);
427 if (m_histogrammingEnabled && !m_moduleHist.empty()) {
428 // Always fill externally provided defects as if the module would
429 // match the first pattern, since there is no pattern associated to
430 // the external defects.
431 std::lock_guard<std::mutex> lock(m_histMutex);
432 histogramDefectModule(0, 0, module_i, det_ele->center());
433 }
434 continue;
435 }
436
437 T_ModuleHelper helper(det_ele->design());
438
439 if (!helper) {
440 ++n_error;
441 continue;
442 }
443 ++(counts.back()[ kNElements ]); // module defects
444
445 // find pattern matching this module
446 const T_ModuleDesign &moduleDesign = dynamic_cast<const T_ModuleDesign &>(det_ele->design());
447 ModuleIdentifierMatchUtil::setModuleData(*m_idHelper,
448 det_ele->identify(),
449 moduleDesign,
450 module_data);
451 ModuleIdentifierMatchUtil::moduleMatches(m_modulePattern.value(), module_data, module_pattern_idx);
452 if (module_pattern_idx.empty()) {
453 ++module_without_matching_pattern;
454 continue;
455 }
456
457 // it is possible that multiple patterns match a module
458 // For module defects a pattern is selected from all matching patterns randomly. This matters, because
459 // patterns control whether a defect is propagated to all modules connected to the same physical sensor
460 // or not.
461 makeCumulativeProbabilityDist(module_pattern_idx,kModuleDefectProb, cumulative_prob_dist);
462 float prob=(cumulative_prob_dist.empty() || cumulative_prob_dist.back()<=0.) ? 1. : CLHEP::RandFlat::shoot(rndmEngine[kModuleDefects],1.);
463
464 unsigned int match_i=module_pattern_idx.size();
465 assert (match_i>0);
466 for (; match_i-->0; ) {
467 assert( match_i < cumulative_prob_dist.size());
468 if (prob > cumulative_prob_dist[match_i]) break;
469 }
470 ++match_i;
471 // module defects
472 if (match_i < module_pattern_idx.size()) {
473 unsigned int hist_pattern_i = (m_fillHistogramsPerPattern ? module_pattern_idx[match_i] : 0 );
474 // mark entire module as defect;
475 (counts.back()[ kNDefects ])
476 += 1-defects->isModuleDefect(module_i); // do not double count
477 defects->setModuleDefect(module_i);
478 // if configured accordingly also mark the other "modules" as defect which
479 // are part of the same physical module
480 ATH_MSG_VERBOSE( "Add module defect "
481 << " hash=" << m_idHelper->wafer_hash(det_ele->identify())
482 << " barel_ec=" << m_idHelper->barrel_ec(det_ele->identify())
483 << " layer_disk=" << m_idHelper->layer_disk(det_ele->identify())
484 << " phi_module=" << m_idHelper->phi_module(det_ele->identify())
485 << " eta_module=" << m_idHelper->eta_module(det_ele->identify())
486 << " side=" << ModuleIdentifierMatchUtil::detail::getZeroOrSide(*m_idHelper,det_ele->identify())
487 << " columns_strips=" << module_data[4]);
488
489 // if histogramming is enabled lock also for the connected modules
490 // to avoid frequent lock/unlocks.
491 // Anyway the algorithm should run only once per job
492 MyLockGuard lock(m_histMutex, m_histogrammingEnabled);
493 if (m_histogrammingEnabled) {
494 histogramDefectModule(module_pattern_idx[match_i], hist_pattern_i, module_i, det_ele->center());
495 }
496
497 if constexpr(IdentifierHelperWithOtherSideConecpt<decltype(*m_idHelper)>) {
498 // propagate module defects to modules connected to the same physical sensor
499 // if configured accordingly.
500 assert( module_pattern_idx[match_i] < m_modulePattern.size());
501 ConnectedModulesUtil::visitMatchingConnectedModules(
502 *m_idHelper,
503 m_modulePattern[module_pattern_idx[match_i]],
504 *(detEleColl.cptr()),
505 module_i,
506 connected_modules,
507 [defects_ptr=defects.get(),
508 &counts,
509 module_pattern_i=module_pattern_idx[match_i],
510 hist_pattern_i,
511 this](unsigned int child_id_hash,const InDetDD::SiDetectorElement &connected_det_ele) {
512
513 (counts.back()[ kNDefects ])
514 += 1-defects_ptr->isModuleDefect(child_id_hash); // do not double count
515 defects_ptr->setModuleDefect(child_id_hash);
516
517 if (m_histogrammingEnabled) {
518 histogramDefectModule(module_pattern_i, hist_pattern_i, child_id_hash, connected_det_ele.center());
519 }
520
521 ATH_MSG_VERBOSE( "Propagate module defect to other split modules "
522 << " hash=" << m_idHelper->wafer_hash(connected_det_ele.identify())
523 << " barel_ec=" << m_idHelper->barrel_ec(connected_det_ele.identify())
524 << " layer_disk=" << m_idHelper->layer_disk(connected_det_ele.identify())
525 << " phi_module=" << m_idHelper->phi_module(connected_det_ele.identify())
526 << " eta_module=" << m_idHelper->eta_module(connected_det_ele.identify())
527 << " side=" << ModuleIdentifierMatchUtil::detail::getZeroOrSide(*m_idHelper, connected_det_ele.identify()) );
528 });
529 }
530 continue;
531 }
532 unsigned int hist_pattern_i = (m_fillHistogramsPerPattern ? module_pattern_idx.front() : 0 );
533
534 // throw number of defects of all defect types excluding module defects which are already handled
535 // e.g. chip-defects, core-column defects, single pixel or strip defects
536 unsigned int cells = helper.nCells();
537 std::vector<typename T_ModuleHelper::KEY_TYPE> &module_defects=(*defects).at(module_i);
538 module_defects.reserve( throwNumberOfDefects(std::span(&rndmEngine.data()[kMaskDefects], rndmEngine.size()-kMaskDefects),
539 module_pattern_idx,
540 T_ModuleHelper::nMasks(),
541 cells,
542 n_mask_defects) );
543
544 // if histogramming is enabled lock for the entire loop, since it would
545 // be rather inefficient to lock for each defect pixel or strip separately and
546 // this algorithm should run only a single time per job anyway.
547 MyLockGuard lock(m_histMutex, m_histogrammingEnabled);
548 auto [matrix_histogram_index, matrix_index]=(m_histogrammingEnabled
549 ? findHist(hist_pattern_i, helper.nSensorRows(), helper.nSensorColumns())
550 : std::make_pair(0u,0u));
551
552 // create defects starting from the mask (or group) defect covering the largest area (assuming
553 // that masks are in ascending order) e.g. defect chips, core-column defects, individual pixel
554 // or strip defects
555 auto masks = helper.masks();
556 for (unsigned int mask_i = masks.size(); mask_i-->0; ) {
557
558 assert( mask_i < n_mask_defects.size());
559 counts[mask_i][kNElements] += helper.nElements(mask_i);
560 unsigned int n_defects = n_mask_defects[mask_i];
561 if (n_defects>0) {
562 // module with mask (or group) defects i.e. core-column defects, defect chips, ...
563 assert( mask_i < counts.size());
564 counts[mask_i][kMaxDefectsPerModule] = std::max(counts[mask_i][kMaxDefectsPerModule],n_defects);
565 unsigned int current_defects = module_defects.size();
566 assert( !m_histogrammingEnabled || ( hist_pattern_i < m_hist.size() && matrix_histogram_index < m_hist[hist_pattern_i].size()) );
567 TH2 *h2 = (m_histogrammingEnabled ? m_hist.at(hist_pattern_i).at(matrix_histogram_index) : nullptr);
568
569 for (unsigned int defect_i=0; defect_i < n_defects; ++defect_i) {
570 // retry if a random position has been chosen already, but at most m_maxAttempts times
571 unsigned int attempt_i=0;
572 for (attempt_i=0; attempt_i<m_maxAttempts.value(); ++attempt_i) {
573 // chose random pixel or strip which defines the defect location
574 assert(kMaskDefects+mask_i < rndmEngine.size() );
575 unsigned int cell_idx=CLHEP::RandFlat::shoot(rndmEngine[kMaskDefects+mask_i],cells);
576
577 if (m_checkerBoardToggle) {
578 // for debugging
579 // restrict defects on checker board
580 cell_idx=makeCheckerboard(cell_idx,
581 helper.nSensorRows(),
582 helper.nSensorColumns(),
583 m_oddRowToggle.value(),
584 m_oddColToggle.value() );
585 }
586
587 std::pair<typename T_ModuleHelper::KEY_TYPE, typename T_ModuleHelper::KEY_TYPE> key_range;
588 if (mask_i==0 && m_perPatternAndMaskFractions[match_i].at(mask_i).size()>1) {
589 // for cell defects throw a group size,
590 // the fractions are actual fractions for group sizes of 1,2,... where the first element
591 // is the fraction for a group size of 1.
592 float group_size_prob=CLHEP::RandFlat::shoot(rndmEngine[kMaskDefects+mask_i],1.);
593 unsigned int group_size=m_perPatternAndMaskFractions.at(match_i).at(mask_i).size();
594 for (; group_size-->0 && group_size_prob <= m_perPatternAndMaskFractions[match_i][mask_i][group_size];);
595 ++group_size;
596 // squeeze the random row index into the number of rows reduced by the group size.
597 unsigned int sensor_row = std::min(static_cast<unsigned int>(getSensorRow(helper, cell_idx) %
598 (helper.nSensorRows() - group_size -1)),
599 static_cast<unsigned int>(helper.nSensorRows() - group_size - 1));
600 unsigned int sensor_col = getSensorColumn(helper, cell_idx);
601 key_range = std::make_pair( helper.hardwareCoordinates(sensor_row,
602 sensor_col),
603 helper.hardwareCoordinates(sensor_row+group_size,
604 sensor_col));
605 }
606 else {
607 key_range= T_ModuleHelper::makeRangeForMask(helper.hardwareCoordinates(getSensorRow(helper, cell_idx),
608 getSensorColumn(helper, cell_idx)),
609 masks[mask_i]);
610 }
611
612 if (EmulatedDefectsBuilder::insertKeyRange(helper, module_defects, key_range, T_ModuleHelper::makeDefectTypeKey(mask_i))) {
613 if (h2) {
614 histogramDefects(helper,key_range,h2);
615 }
616 break;
617 }
618
619 assert( mask_i+1 < counts.size());
620 ++counts[mask_i][ kNRetries ];
621 }
622 assert( mask_i+1 < counts.size());
623 counts[mask_i][kNMaxRtriesExceeded] += attempt_i >= m_maxAttempts.value();
624 }
625 unsigned int new_defects = module_defects.size() - current_defects;
626 assert( mask_i+1 < counts.size());
627 counts[mask_i][ kNDefects ] += new_defects;
628 if (new_defects>0) {
629 ++(counts[mask_i][kNModulesWithDefects]);
630 }
631 }
632 }
633
634 // Create corner defects only for modules with columns and rows.
635 if constexpr( T_ModuleHelper::ROW_BITS>0 && T_ModuleHelper::COL_BITS>0) {
636 if (!m_cornerDefectParamsPerPattern.empty() && !m_cornerDefectParamsPerPattern[module_pattern_idx.front()].empty()) {
637 if (module_pattern_idx.size()!=1) {
638 ATH_MSG_WARNING("Multiple module pattern match module " << module_i
639 << ", but for corner defects only the parameters are used which are associated to the first pattern" );
640 }
641 // create defects at certain corners outside of a circle which crosses the sensor edges at certain positions
642 // chosen in a random range. The edge crossing at the particular corner defines a sagitta of a certain value
643 // chosen in a random range
644
645 // throw the number of corners with corner defects.
646 float defect_prob=CLHEP::RandFlat::shoot(rndmEngine[kCornerDefects],1.);
647 unsigned int n_corners=m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ].size();
648 for (;
649 n_corners>0 && defect_prob<m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ][n_corners-1];
650 --n_corners);
651 unsigned int n_current_defects =module_defects.size();
652 if (n_corners<m_perPatternCornerDefectNCornerCummulativeProb[module_pattern_idx.front() ].size()) {
653 assert( !m_histogrammingEnabled || ( hist_pattern_i < m_hist.size() && matrix_histogram_index < m_hist[hist_pattern_i].size()) );
654 TH2 *h2 = (m_histogrammingEnabled ? m_hist.at(hist_pattern_i).at(matrix_histogram_index) : nullptr);
655
656 assert( n_corners < n_corner_combinations.size() );
657 // chose the corners which have defects randomly
658 unsigned int the_combination =CLHEP::RandFlat::shoot(rndmEngine[kCornerDefects],n_corner_combinations[n_corners] );
659 unsigned char corner_mask = corner_combinations[n_corners][the_combination];
660 // columns, rows and their pitch are already in direction of the hardware columns and rows
661 unsigned int hwa_columns = helper.columns();
662 unsigned int hwa_rows = helper.rows();
663 float hwa_columnPitch = helper.columnPitch();
664 float hwa_rowPitch = helper.rowPitch();
665
666 for (unsigned int corner_i=0; corner_i<4; ++corner_i) {
667 if (corner_mask & (1u << corner_i)) {
668 // chose random positions at which the circle crosses the edges at the particular corner
669 // and chose a random sagitta defined by the corssings.
670 assert( m_cornerDefectParamsPerPattern[module_pattern_idx.front()].size()==kNCornerDefectParams);
671 float rx=m_cornerDefectParamsPerPattern[module_pattern_idx.front()][kCornerDefectWidthColumnDirectionOffset]
672 +CLHEP::RandFlat::shoot(rndmEngine[kCornerDefects],m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectWidthColumnDirection]);
673 float ry=m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkWidthRowDirectionOffset]
674 +CLHEP::RandFlat::shoot(rndmEngine[kCornerDefects],m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkWidthRowDirection]);
675
676 float sagitta;
677 {
678 // make sure that the sagitta is small enough that the circle can actually cross the bounds.
679 // conditions: the circle radius Rc has to be larger than rx and ry. If the circle radius
680 // is equal to rx or ry, the circle will just touch the border.
681 // 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)
682 float r2=rx*rx+ry*ry;
683 float hp1=0.5*sqrt(r2)*rx/ry;
684 float hp2=0.5*sqrt(r2)*ry/rx;
685 float q=-0.25*r2;
686 float sagitta_max = std::min( (-hp1 + sqrt( hp1*hp1 -q )),(-hp2 + sqrt( hp2*hp2 -q )) );
687 float sagitta_range = m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta];
688 float sagitta_offset = m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset];
689 sagitta_offset=std::min(sagitta_offset,sagitta_max);
690 sagitta_range=std::min( sagitta_range, sagitta_max - sagitta_offset);
691 if (sagitta_range != m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta]
692 || sagitta_offset != m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]) {
693 ATH_MSG_VERBOSE("limited sagitta range from "
694 << m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]
695 << ".."
696 << (m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagittaOffset]
697 + m_cornerDefectParamsPerPattern[module_pattern_idx.front() ][kCornerDefectkSagitta])
698 << " to " << sagitta_offset << ".." << (sagitta_offset+sagitta_range));
699 }
700 sagitta=sagitta_offset+CLHEP::RandFlat::shoot(rndmEngine[kCornerDefects],sagitta_range);
701 }
702
703 // compute column range with defects
704 double scale_rx=(corner_i==1 || corner_i==2) ? -1.f : 1.f;
705 double scale_ry=(corner_i<=1) ? -1.f : 1.f;
706 std::array<unsigned int,2> row_range{0,hwa_rows};
707 unsigned int row_var_idx=(corner_i<=1) ? 0 : 1;
708 unsigned int col_start=(corner_i==1 || corner_i==2) ? hwa_columns : 0;
709
710 Amg::Vector2D corner_pos{col_start*hwa_columnPitch,row_range[row_var_idx^1]*hwa_rowPitch};
711
712 unsigned int col_end = std::min(static_cast<unsigned int>(std::max(0,static_cast<int>(col_start)
713 + static_cast<int>(rx*scale_rx / hwa_columnPitch +.5))),
714 hwa_columns);
715 if (col_end < col_start) {
716 std::swap(col_start, col_end);
717 }
718
719 // compute circle parameters
720 Amg::Vector2D c0 = corner_pos + Amg::Vector2D{0.f,ry*scale_ry};
721 Amg::Vector2D c1 = corner_pos + Amg::Vector2D{rx*scale_rx,0.f};
722 double rho2=sqr(rx) + sqr(ry);
723 double radius = rho2/(8*sagitta)+0.5*sagitta;
724 Amg::Vector2D Rc = c0+(c1-c0)*.5 - (scale_ry*scale_rx) * (radius-sagitta)
725 /sqrt(rho2)
726 *Amg::Vector2D{-ry*scale_ry,-rx*scale_rx};
727
728 double ymin=std::min(c0[1],c1[1]);
729 double ymax=std::max(c0[1],c1[1]);
730 for (unsigned int col_i=col_start; col_i< col_end; ++col_i) {
731 //compute row range with defects per column i.e. the row at which the circle crosses the column
732 // and the corresponding edge row (top or bottom)
733 double q = sqr(col_i*hwa_columnPitch-Rc[0]) + sqr(Rc[1]) - sqr(radius);
734 double ph = -Rc[1];
735 double Q=sqr(ph)-q;
736 if (Q>=0.) {
737 double sQ=sqrt(Q);
738 double cx = (-ph+sQ );
739 if (cx<ymin || cx>=ymax) {
740 double cx2=(-ph-sQ );
741 // pick the physical solution and make sure that
742 // that the solution remains in the allowed range despite
743 // limited precision
744 if (cx2<ymin || cx2 >=ymax) {
745 double d1=std::min(std::abs(cx-ymin),std::abs(cx-ymax));
746 double d2=std::min(std::abs(cx2-ymin),std::abs(cx2-ymax));
747 if (d2<d1) {
748 cx=cx2;
749 }
750 cx = std::clamp( cx, ymin, ymax - hwa_rowPitch*.25);
751 }
752 else {
753 cx=cx2;
754 }
755 }
756 if (cx>=ymin && cx<ymax) {
757 row_range[row_var_idx]=static_cast<unsigned int>( std::max(0.,cx / hwa_rowPitch + .5));
758
759 if (row_range[0] < row_range[1] && row_range[0] <hwa_rows) {
760 // create corner defects for one column
761 std::pair<typename T_ModuleHelper::KEY_TYPE, typename T_ModuleHelper::KEY_TYPE> key_range{
762 helper.swapOfflineRowsColumns() ? helper.hardwareCoordinates(col_i, std::min(row_range[0],helper.nSensorColumns()-1u))
763 : helper.hardwareCoordinates(std::min(row_range[0],helper.nSensorRows()-1u),col_i),
764 helper.swapOfflineRowsColumns() ? helper.hardwareCoordinates(col_i, std::min(row_range[1]-1, helper.nSensorColumns()-1u))
765 : helper.hardwareCoordinates(std::min(row_range[1]-1,helper.nSensorRows()-1u),col_i)
766 };
767
768 if (EmulatedDefectsBuilder::insertKeyRange(helper, module_defects, key_range, T_ModuleHelper::makeDefectTypeKey(masks.size()>1 ? 1 : 0))) {
769 if (h2) {
770 histogramDefects(helper,key_range,h2);
771 }
772 }
773 }
774 }
775 }
776 }
777 }
778 }
779 // add corner defects to random pixel defects for histogramming
780 n_mask_defects[0] += module_defects.size() - n_current_defects;
781 }
782 }
783 }
784 if (m_histogrammingEnabled) {
785 fillPerModuleHistograms(module_pattern_idx.front(),hist_pattern_i,matrix_histogram_index, matrix_index, module_i,
786 T_ModuleHelper::nMasks(), n_mask_defects, det_ele->center());
787 }
788 n_defects_total+=module_defects.size();
789 }
790 }
791 m_modulesWithoutDefectParameters += module_without_matching_pattern;
792 if (!m_outputFile.value().empty()) {
793 if (hasExtensions(m_outputFile.value(),".json")) {
794 toJson(*defects, m_outputFile.value());
795 }
796 else if (hasExtensions(m_outputFile.value(),".root")) {
797 toRoot(*defects, m_outputFile.value());
798 }
799 }
800 ATH_CHECK( defectsOut.record (std::move(defects)) );
801
802 if (msgLvl(MSG::INFO)) {
803 printSummaryOfDefectGeneration(T_ModuleHelper::nMasks(), n_error, n_defects_total,counts);
804 }
805
806 return StatusCode::SUCCESS;
807 }
808
809}