ATLAS Offline Software
PixelDiodeTreeBuilder.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3  */
5 #include <stdexcept>
6 #include <iomanip>
7 #include <unordered_map>
8 
9 namespace InDetDD {
10 
11 namespace {
12  enum ELocation {
13  kOuter=0,
14  kInner=1
15  };
16 
17  struct SubMatrixData {
18  enum EEdgeLocation {kXmin, kXmax, kYmin, kYmax, kNEdgeLocations};
19  enum EEdgeType {kOuter,kInner,kInternal, kDeadZoneOuter, kDeadZoneInner};
20  static double invOrZero(double a) { return a>0 ? 1./a : 0.; }
21  static constexpr PixelDiodeTree::AttributeType s_defaultMatrixAttribute=0u;
22 
23  SubMatrixData(const PixelDiodeTree::Vector2D &pos,
25  const std::array<unsigned int,2> &idx,
26  const std::array<unsigned int,2> &dim,
27  const std::array<unsigned int,2> &chip_dim,
28  const std::array<SubMatrixData::EEdgeType, SubMatrixData::kNEdgeLocations> &edge_type,
30  unsigned int submatrix_idx,
31  unsigned int split_idx) :
32  m_pos(pos),
33  m_width(width),
34  m_idx(idx),
35  m_dim(dim),
36  m_chipDim(chip_dim),
37  m_edgeType(edge_type),
38  m_attribute(attribute),
39  m_subMatrixIdx(submatrix_idx),
40  m_splitIdx(split_idx)
41  { assert(m_splitIdx<4); }
42  SubMatrixData(const PixelDiodeTree::Vector2D &width,
43  const std::array<unsigned int,2> &dim,
44  const std::array<unsigned int,2> &chip_dim)
45  : SubMatrixData(-width*.5,width,
46  std::array<unsigned int,2>{0u,0u},dim,chip_dim,
47  std::array<SubMatrixData::EEdgeType, SubMatrixData::kNEdgeLocations>{kOuter,kOuter,kOuter,kOuter},
48  s_defaultMatrixAttribute,
49  std::numeric_limits<unsigned int>::max() /* no parent matrix*/,
50  0u /*split area index */ )
51  {}
52 
55  std::array<unsigned int,2> m_idx;
56  std::array<unsigned int,2> m_dim;
57  std::array<unsigned int,2> m_chipDim;
58  std::array<EEdgeType,kNEdgeLocations> m_edgeType;
60  unsigned int m_subMatrixIdx;
61  unsigned int m_splitIdx;
62  };
63 
64  // helper function to split a matrix in 4 sub-matrices where a sub-matrix may be empty.
65  void splitMatrix(const SubMatrixData &matrix_data, // matrix data of the matrix to be split
66  std::array<unsigned int,2> split_idx, // the absolute pixel index (row, column) where the matrix is to be split
67  const std::array<PixelDiodeTree::Vector2D,2> &width, // physical width of the 4 sub-matrices
68  const std::array<std::array<unsigned int, 2>,2> &split_chip_dim, // number of circuits per sub-matrix
69  const std::array<bool,2> &dead_zone_split, // flag per axis whether the split splits off a dead zone from an inner or outer edge
70  const std::array<std::array<PixelDiodeTree::AttributeType, 2>,2> &attribute,
71  // a special attribute to be associated to the sub-matrix (currently unused)
72  std::vector<SubMatrixData> &sub_matrix_list, // the new sub-matrices will be added to this list
73  PixelDiodeTree &diode_tree, // the diode tree which to be developed further
74  std::ostream *debug_out, // pointer to an output stream or nullptr
75  const std::array<std::string,SubMatrixData::kDeadZoneInner+1> *edgeName) // pointer to edge names used in the debug output
76  {
77  std::array<std::array<unsigned int,2>,2> new_dim;
78  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
79  if (split_idx[axis_i] < matrix_data.m_idx[axis_i]+matrix_data.m_dim[axis_i]) {
80  // if the split is not on the outer edge, compute the dimensions in the corresponding direction
81  // for the new areas
82  new_dim[0][axis_i]=split_idx[axis_i]-matrix_data.m_idx[axis_i];
83  new_dim[1][axis_i]=matrix_data.m_idx[axis_i] + matrix_data.m_dim[axis_i] - split_idx[axis_i];
84  }
85  else {
86  // if the split is on the outer edge. the first area inherits the entire width and the second
87  // is empty.
88  new_dim[0][axis_i]=matrix_data.m_dim[axis_i];
89  new_dim[1][axis_i]=0;
90  }
91  }
92 
93  if (debug_out) {
94  (*debug_out) << "Split (sub) matrix "
95  << matrix_data.m_idx[0] << " , " << matrix_data.m_idx[1] << " "
96  << matrix_data.m_dim[0] << "x" << matrix_data.m_dim[1] << " : "
97  << split_idx[0] << " < " << matrix_data.m_dim[0]+matrix_data.m_idx[0]
98  << " , " << split_idx[1] << " < " << matrix_data.m_dim[1]+matrix_data.m_idx[1]
99  << " -> " << (*edgeName)[matrix_data.m_edgeType[ 0 + 2*0]] << " | "
100  << new_dim[0][0] << " | " << new_dim[1][0] << " | " << (*edgeName)[matrix_data.m_edgeType[ 1 + 2*0]]
101  << " , " << (*edgeName)[matrix_data.m_edgeType[ 0 + 2*1]] << " | " << new_dim[0][1]
102  << " | " << new_dim[1][1] << " | " << (*edgeName)[matrix_data.m_edgeType[ 1 + 2*1]]
103  << std::endl;
104  }
105 
106  unsigned int sub_matrix_idx = std::numeric_limits<unsigned int>::max();
107  std::array<unsigned int,2> axis_split_i;
108  // the matrix is split into sub-matrices which may have zero size
109  // each axis is split into two regions side 0 is the "lower" and 1 the "upper side"
110  for (axis_split_i[0]=0; axis_split_i[0]<2; ++axis_split_i[0]) {
111  if (new_dim[axis_split_i[0]][0]==0) { continue; } // index order: [side][axis]
112  for (axis_split_i[1]=0; axis_split_i[1]<2; ++axis_split_i[1]) {
113  if (new_dim[axis_split_i[1]][1]==0) continue; // index order [side][axis]
114  PixelDiodeTree::Vector2D pos(matrix_data.m_pos); // width[0] is the width of the first sub-matrix
115  std::array<unsigned int,2> idx(matrix_data.m_idx);
116  std::array<SubMatrixData::EEdgeType, SubMatrixData::kNEdgeLocations> edge_type;
117  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
118  if (axis_split_i[axis_i]>0) {
119  pos[axis_i] += width[0][axis_i];
120  idx[axis_i] = split_idx[axis_i];
121  }
122  edge_type[axis_split_i[axis_i] + 2*axis_i] = matrix_data.m_edgeType[ axis_split_i[axis_i] + 2*axis_i];
123  unsigned int other_side_i=axis_split_i[axis_i]^1;
124  // the types of the outer edges of the sub-matrix are inherited from the matrix
125  // the inner edges become "inner" for circuit splits, or "internal" for inner or outer edge splits.
126  edge_type[other_side_i + 2*axis_i] = (new_dim[other_side_i][axis_i]>0
127  ? (split_chip_dim[axis_split_i[axis_i]][axis_i]>0
128  ? SubMatrixData::kInner
129  : SubMatrixData::kInternal )
130  : matrix_data.m_edgeType[ other_side_i + 2*axis_i] ); // if the other side has zero width inherit edge type
131 
132  // special handling if a "dead zone" is split off an inner or outer edge.
133  if (dead_zone_split[axis_i]) {
134  for (unsigned int side_i=0; side_i<2; ++side_i) {
135  if (edge_type[side_i+2*axis_i]==SubMatrixData::kInternal) {
136  unsigned int other_side_i=(side_i^1);
137  edge_type[side_i+2*axis_i]=( ( matrix_data.m_edgeType[side_i+2*axis_i] == SubMatrixData::kInner
138  || matrix_data.m_edgeType[other_side_i+2*axis_i] == SubMatrixData::kInner)
139  ? SubMatrixData::kDeadZoneInner
140  : (( matrix_data.m_edgeType[side_i+2*axis_i] == SubMatrixData::kOuter
141  || matrix_data.m_edgeType[other_side_i+2*axis_i] == SubMatrixData::kOuter)
142  ? SubMatrixData::kDeadZoneOuter
143  : SubMatrixData::kInternal));
144  if (edge_type[side_i+2*axis_i]==SubMatrixData::kInternal) {
145  throw std::logic_error("Deadzone not next to an inner or outer edge." );
146  }
147  }
148  }
149  }
150  }
151 
152  if (sub_matrix_idx == std::numeric_limits<unsigned int >::max()) {
153  // register the split in the diode tree
154  // the sub_matrix_idx is needed to set sub-matrix or diode indices for the 4 new sub-matrices
155  sub_matrix_idx = diode_tree.split( std::array<PixelDiodeTree::CellIndexType,2>{ static_cast< PixelDiodeTree::CellIndexType>(split_idx[0]),
156  static_cast< PixelDiodeTree::CellIndexType>(split_idx[1]) },
157  matrix_data.m_pos+width[0],
158  matrix_data.m_attribute,
159  matrix_data.m_subMatrixIdx,
160  matrix_data.m_splitIdx);
161  }
162  unsigned int split_i= axis_split_i[0]+axis_split_i[1]*2;
163  if (debug_out) {
164  (*debug_out) << "new sub matrix : "
165  << sub_matrix_idx << " . " << split_i << " axis sides "
166  << axis_split_i[0] << " , " << axis_split_i[1] << " : "
167  << std::setw(4) << idx[0] << ", " << std::setw(4) << idx[1]
168  << " dim " << std::setw(4) << new_dim[axis_split_i[0]][0] << " , " << std::setw(4) << new_dim[axis_split_i[1]][1]
169  << " " << std::setw(6) << pos[0] << ", " << std::setw(6) << pos[1]
170  << " w " << std::setw(6) << width[axis_split_i[0]][0] << " , " << std::setw(6) << width[axis_split_i[1]][1]
171  << " chips " << split_chip_dim[axis_split_i[0]][0] << " , " << split_chip_dim[axis_split_i[1]][1]
172  << " edges " << (*edgeName)[edge_type[0]] << "|" << (*edgeName)[edge_type[1]] << " , "
173  << (*edgeName)[edge_type[2]] << "|" << (*edgeName)[edge_type[3]]
174  << std::endl;
175  }
176 
177  // @TODO turn into assert?
178  if (new_dim[axis_split_i[0]][0] > matrix_data.m_dim[0] && new_dim[axis_split_i[1]][1] > matrix_data.m_dim[1]) {
179  throw std::logic_error("splitter increased size.");
180  }
181  // store data for all non empty sub-matrices for further splitting or to create or assign diodes
182  sub_matrix_list.emplace_back(pos, PixelDiodeTree::Vector2D{width[axis_split_i[0]][0],
183  width[axis_split_i[1]][1]},
184  idx, std::array<unsigned int,2>{new_dim[axis_split_i[0]][0],new_dim[axis_split_i[1]][1]},
185  std::array<unsigned int, 2>{split_chip_dim[axis_split_i[0]][0],split_chip_dim[axis_split_i[1]][1]},
186  edge_type,
187  attribute[axis_split_i[0]][axis_split_i[1]],
188  sub_matrix_idx,
189  split_i);
190  }
191  }
192  }
193 
194 }
195 
196 namespace {
197  // helper class to store diode index and for debug build also the attribute
198  // the attribute is used to verify that computed diode attributes agree
199  // for all "diodes" which are mapped to the same diode parameter set.
200  // The functional which is used to compute the diode attributes has to
201  // ensure this.
202  struct DiodeInfo {
203  DiodeInfo(unsigned int idx, [[maybe_unused]] const PixelDiodeTree::AttributeType &attribute)
204  : m_idx(idx)
205 #ifndef NDEBUG
206  ,m_attribute(attribute)
207 #endif
208  {}
209  void setIndex(unsigned int idx) { m_idx=idx; }
210  unsigned int diodeIndex() const { return m_idx; }
211 #ifndef NDEBUG
212  bool attributeAgrees(const PixelDiodeTree::AttributeType &attribute) {
213  return attribute == m_attribute;
214  }
215 #endif
216  private:
217  unsigned int m_idx;
218 #ifndef NDEBUG
220 #endif
221  };
222 }
223 
224 PixelDiodeTree createPixelDiodeTree(const std::array<unsigned int,2> &chip_dim,
225  const std::array<unsigned int,2> &chip_matrix_dim,
226  const PixelDiodeTree::Vector2D &pitch,
227  const std::array<std::array<unsigned int,2>,2> &edge_dim,
228  const std::array<PixelDiodeTree::Vector2D,2> &edge_pitch,
229  const std::array<std::array<unsigned int,2>,2> &dead_zone,
230  const AttributeRefiner &func_compute_attribute,
231  std::ostream *debug_out) {
232  // strategy :
233  // split the entire pixel matrix first by circuit, then by special pixel areas
234  // add new diodes for sub matrices only composed of a single diode type, where the
235  // diode type is defined by the area i.e. it is located on one of the outer edges of the full matrix,
236  // one of the inner edges i.e. areas "between" circuits, in the "dead" zone between circuits
237  // or on the regular matrix.
238  //
239  // 1) first split circuit arrays in roughly by half until all sub-matrices span only a single circuit
240  // 2) split sub-matrices until the sub matrix only contains pixel of an outer edge, inner edge
241  // or the "normal" pixels. Inner-edge sub-matrices are split further if there is a dead zone.
242  // @TODO split by circuit and edge type independently per axis, to reduce number of unused splits
243  // original pixel matrix (Run 1-Run 3)
244 
245  std::array<unsigned int,2> dim{};
247  // compute total width of diode matrix
248  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
249  dim[axis_i] = chip_dim[axis_i] * chip_matrix_dim[axis_i];
250  unsigned int n_inner_edges = chip_dim[axis_i]*2-2 ;
251  constexpr unsigned int n_outer_edges = 2;
252  width[axis_i] = ( ( chip_dim[axis_i]*chip_matrix_dim[axis_i]
253  - edge_dim[kInner][axis_i]*n_inner_edges
254  - edge_dim[kOuter][axis_i]*n_outer_edges ) * pitch[axis_i]
255  + edge_dim[kInner][axis_i]*edge_pitch[kInner][axis_i]*n_inner_edges
256  + edge_dim[kOuter][axis_i]*edge_pitch[kOuter][axis_i]*n_outer_edges);
257  }
258  PixelDiodeTree diode_tree(width);
259  std::unordered_map<unsigned int, DiodeInfo > diode_idx;
260 
261  std::array<std::string,SubMatrixData::kDeadZoneInner+1> edgeName {
262  std::string("Outer"),
263  std::string("Inner"),
264  std::string("Internal"),
265  std::string("DeadZoneOuter"),
266  std::string("DeadZoneInner")
267  };
268 
269 
270  std::vector< SubMatrixData > stack;
271  stack.emplace_back(width,dim, chip_dim);
272  while (!stack.empty()) {
273  SubMatrixData current_submatrix = std::move(stack.back());
274  const std::array<unsigned int,2 > &split_chip_dim = current_submatrix.m_chipDim;
275  stack.pop_back();
276 
277  std::array<PixelDiodeTree::Vector2D,2> split_width{current_submatrix.m_width,
278  PixelDiodeTree::Vector2D::Zero()}; // default for no splitting
279  std::array<unsigned int,2> split_idx{current_submatrix.m_idx[0]+current_submatrix.m_dim[0], //set split point to outside outermost cell
280  current_submatrix.m_idx[1]+current_submatrix.m_dim[1]};
281  std::array<std::array<unsigned int,2>,2 > new_chip_dim{ split_chip_dim, split_chip_dim};
282  unsigned int n_sub_matrices=0;
283 
284  if (debug_out) {
285  *debug_out << "Split : " << std::setw(4) << current_submatrix.m_idx[0] << ", " << std::setw(4) << current_submatrix.m_idx[1]
286  << " " << std::setw(4) << current_submatrix.m_dim[0] << " x " << std::setw(4) << current_submatrix.m_dim[1]
287  << " pos " << std::setw(6) << current_submatrix.m_pos[0] << " , " << std::setw(6) << current_submatrix.m_pos[1]
288  << " w " << std::setw(6) << current_submatrix.m_width[0] << " , " << std::setw(6) << current_submatrix.m_width[1]
289  << " chips " << current_submatrix.m_chipDim[0] << " x " << current_submatrix.m_chipDim[1]
290  << " edges: " << edgeName[current_submatrix.m_edgeType[0] ] << " | " << edgeName[current_submatrix.m_edgeType[1] ]
291  << " , " << edgeName[current_submatrix.m_edgeType[2] ] << " | " << edgeName[current_submatrix.m_edgeType[3] ]
292  << std::endl;
293  }
294 
295  // 1) split circuits until sub-matrix composed of a single circuit
296  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
297  if (current_submatrix.m_chipDim[axis_i]>1) {
298  n_sub_matrices+=2;
299  unsigned int odd = current_submatrix.m_chipDim[axis_i] & 1;
300  unsigned int axis_idx= current_submatrix.m_idx[axis_i];
301 
302  for (unsigned int part_i=0; part_i<2; ++part_i) {
303  unsigned n_chips = (current_submatrix.m_chipDim[axis_i] + odd)/2;
304  new_chip_dim[part_i][axis_i]=n_chips;
305  unsigned int n_outer_edges=(current_submatrix.m_edgeType[axis_i*2+part_i]==SubMatrixData::kOuter);
306  unsigned int n_inner_edges=n_chips*2-n_outer_edges;
307  split_idx[axis_i]=axis_idx;
308  split_width[part_i][axis_i] = ( n_chips*chip_matrix_dim[axis_i]
309  - edge_dim[kInner][axis_i]*n_inner_edges
310  - edge_dim[kOuter][axis_i]*n_outer_edges ) * pitch[axis_i]
311  + n_inner_edges*edge_dim[kInner][axis_i]*edge_pitch[kInner][axis_i]
312  + n_outer_edges*edge_dim[kOuter][axis_i]*edge_pitch[kOuter][axis_i];
313  axis_idx += n_chips * chip_matrix_dim[axis_i];
314  odd=0u;
315  }
316  }
317  }
318  std::array<bool,2> dead_zone_split{};
319  // composed of a single circuit split off special pixel edges, or split off dead zone
320  if (n_sub_matrices<=1) {
321  if (debug_out){
322  (*debug_out) << "Split circuit arrays into " << n_sub_matrices << " sub arrays." << std::endl;
323  }
324  n_sub_matrices=0;
325  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
326  // compute the dimension of the edges on both sides
327  std::array<unsigned int, 2> axis_edge_dim{0,0};
328  for (unsigned int side_i=0; side_i<2; ++side_i) {
329  new_chip_dim[side_i][axis_i]=0u;
330  if (current_submatrix.m_edgeType[axis_i*2+side_i]<SubMatrixData::kInternal) {
331  axis_edge_dim[side_i]=edge_dim[ kOuter + (current_submatrix.m_edgeType[axis_i*2+side_i]==SubMatrixData::kInner) ][axis_i];
332  }
333  }
334 
335  if (debug_out) {
336  (*debug_out) << "Split submatrix " << (axis_i == 0 ? "x: " : "y: ")
337  << edgeName[current_submatrix.m_edgeType[axis_i*2]]
338  << " | "
339  << edgeName[current_submatrix.m_edgeType[axis_i*2+1]]
340  << " -> " << axis_edge_dim[0] << " , " << axis_edge_dim[1]
341  << std::endl;
342  }
343  unsigned int axis_dim = std::min(current_submatrix.m_dim[axis_i],chip_matrix_dim[axis_i]);
344  split_idx[axis_i]=current_submatrix.m_idx[axis_i]+axis_dim;
345 
346  // loop over the two sides until one side has an edge or dead zone that can be split off
347  if (axis_edge_dim[0]+axis_edge_dim[1]>0 ) { // if either side has a margin tht needs to be split off
348  for (unsigned int side_i=0; side_i<2; ++side_i) {
349  split_width[side_i][axis_i]=0.;
350  if (axis_edge_dim[side_i] > 0 ) {
351  unsigned int inner_outer = kOuter + (current_submatrix.m_edgeType[axis_i*2+side_i]==SubMatrixData::kInner);
352  ++n_sub_matrices;
353  split_idx[axis_i]=current_submatrix.m_idx[axis_i];
354  if (side_i==0) {
355  split_idx[axis_i] += std::min(axis_edge_dim[side_i], current_submatrix.m_dim[axis_i]);
356  // if there is no split check whether the edge has a dead zone
357  if (split_idx[axis_i] == current_submatrix.m_idx[axis_i] + axis_dim
358  && axis_dim == edge_dim[inner_outer][axis_i]
359  && dead_zone[inner_outer][axis_i]>0u ) {
360  split_idx[axis_i] = current_submatrix.m_idx[axis_i] + dead_zone[inner_outer][axis_i];
361  split_width[side_i][axis_i]=dead_zone[inner_outer][axis_i]*edge_pitch[inner_outer][axis_i];
362  split_width[side_i^1][axis_i]=(edge_dim[inner_outer][axis_i] - dead_zone[inner_outer][axis_i])*edge_pitch[inner_outer][axis_i];
363  dead_zone_split[axis_i]=true;
364  break;
365  }
366  }
367  else {
368  split_idx[axis_i] += axis_dim - std::min(axis_edge_dim[side_i],current_submatrix.m_dim[axis_i]) ;
369  // if there is no split check whether the edge has a dead zone
370  if (split_idx[axis_i] == current_submatrix.m_idx[axis_i]
371  && axis_dim == axis_edge_dim[side_i]
372  && dead_zone[inner_outer][axis_i]>0u ) {
373  split_idx[axis_i] = current_submatrix.m_idx[axis_i] + current_submatrix.m_dim[axis_i] - dead_zone[inner_outer][axis_i];
374  split_width[side_i][axis_i]=dead_zone[inner_outer][axis_i]*edge_pitch[inner_outer][axis_i];
375  split_width[side_i^1][axis_i]=(edge_dim[inner_outer][axis_i] - dead_zone[inner_outer][axis_i])*edge_pitch[inner_outer][axis_i];
376  dead_zone_split[axis_i]=true;
377  break;
378  }
379  // lower side only
380  }
381  if (split_idx[axis_i]<current_submatrix.m_idx[axis_i]
382  || split_idx[axis_i]>current_submatrix.m_idx[axis_i]+current_submatrix.m_dim[axis_i]
383  || (current_submatrix.m_dim[axis_i] < edge_dim[kOuter][axis_i] && current_submatrix.m_dim[axis_i] < edge_dim[kInner][axis_i])
384  ) {
385  throw std::logic_error("invalid split index.");
386  }
387  split_width[side_i][axis_i]=axis_edge_dim[side_i] * edge_pitch[inner_outer][axis_i];
388  // only edges on side 0 here
389  unsigned int n_inner_edges = current_submatrix.m_edgeType[axis_i*2+(side_i^1)]==SubMatrixData::kInner;
390  unsigned int n_outer_edges = current_submatrix.m_edgeType[axis_i*2+(side_i^1)]==SubMatrixData::kOuter;
391  split_width[side_i^1][axis_i] = ( ( axis_dim
392  // edges on both sides here
393  - edge_dim[kInner][axis_i]*(n_inner_edges+current_submatrix.m_edgeType[axis_i*2+side_i]==SubMatrixData::kInner)
394  - edge_dim[kOuter][axis_i]*(n_outer_edges+current_submatrix.m_edgeType[axis_i*2+side_i]==SubMatrixData::kOuter))
395  * pitch[axis_i]
396  + edge_dim[kInner][axis_i]*edge_pitch[kInner][axis_i]*n_inner_edges
397  + edge_dim[kOuter][axis_i]*edge_pitch[kOuter][axis_i]*n_outer_edges);
398  break;
399  }
400  }
401  }
402  }
403  if (n_sub_matrices>0) {
404  if( (split_idx[0]>=current_submatrix.m_idx[0]+current_submatrix.m_dim[0] || split_idx[0]==current_submatrix.m_idx[0])
405  && (split_idx[1]>=current_submatrix.m_idx[1]+current_submatrix.m_dim[1] || split_idx[1]==current_submatrix.m_idx[1])) {
406  // nothing to split
407  n_sub_matrices=0;
408  }
409  }
410  }
411 
412  if (n_sub_matrices>0)
413  {
414  // split current matrix further into sub-matrices
415  if (debug_out) {
416  (*debug_out) << "Split : " << current_submatrix.m_idx[0] << " , " << current_submatrix.m_idx[1] << " " << current_submatrix.m_dim[0] << "x" << current_submatrix.m_dim[1]
417  << " split at " << split_idx[0] << ", " << split_idx[1]
418  << " " << (split_idx[0] - current_submatrix.m_idx[0]) << "|" << (current_submatrix.m_idx[0]+current_submatrix.m_dim[0]-split_idx[0])
419  << ", " << (split_idx[1] - current_submatrix.m_idx[1]) << "|" << (current_submatrix.m_idx[1]+current_submatrix.m_dim[1]-split_idx[1])
420  << " width " << split_width[0][0] << ", " << split_width[0][1] << "; " << split_width[1][0] << ", " << split_width[1][1]
421  << std::endl;
422  }
423  std::size_t stack_size=stack.size();
424  splitMatrix(current_submatrix,
425  split_idx,
426  split_width,
427  new_chip_dim,
428  dead_zone_split,
429  std::array<std::array<PixelDiodeTree::AttributeType, 2>,2> {std::array<PixelDiodeTree::AttributeType, 2>{SubMatrixData::s_defaultMatrixAttribute,
430  SubMatrixData::s_defaultMatrixAttribute},
431  std::array<PixelDiodeTree::AttributeType, 2>{SubMatrixData::s_defaultMatrixAttribute,
432  SubMatrixData::s_defaultMatrixAttribute}},
433  stack,
434  diode_tree,
435  debug_out,
436  &edgeName);
437  if (stack_size == stack.size()) {
438  // split surprisingly resulted in zero new sub-matrices. @TODO is this possible ? Should this be a logic error ?
439  n_sub_matrices=0;
440  }
441  }
442 
443  // if the current matrix was not split into further sub-matrices
444  // register and assign a diode for it.
445  if (n_sub_matrices==0) {
446 
447  // if the diode tree is still empty
448  // create a dummy split of which only the first area is used.
449  if (diode_tree.empty()) {
450  assert( stack.empty() ); // this can only happen if no matrix has been split yet, and then the stack must also be empty
451  assert( chip_dim[0]==1 && chip_dim[1]==1); // should only happen for single chip modules
452  assert( current_submatrix.m_subMatrixIdx==std::numeric_limits<unsigned int>::max());
453  current_submatrix.m_subMatrixIdx
454  = diode_tree.split( std::array<PixelDiodeTree::CellIndexType,2>{ static_cast< PixelDiodeTree::CellIndexType>(current_submatrix.m_dim[0]),
455  static_cast< PixelDiodeTree::CellIndexType>(current_submatrix.m_dim[1]) },
456  current_submatrix.m_pos+current_submatrix.m_width,
457  current_submatrix.m_attribute);
458  }
459 
460  // determine diode type by evaluating whether it is on an inner, or outer edge, a dead zone or just a normal pixel diode
462  PixelDiodeTree::AttributeType full_diode_type{};
463  for (unsigned int axis_i=0; axis_i<2; ++axis_i) {
464  // count inner and outer edges of the current sub matrix
465  unsigned int axis_inner_edges = ( (current_submatrix.m_edgeType[axis_i*2] == SubMatrixData::kInner)
466  + (current_submatrix.m_edgeType[axis_i*2+1] == SubMatrixData::kInner));
467  unsigned int axis_outer_edges = ( (current_submatrix.m_edgeType[axis_i*2] == SubMatrixData::kOuter)
468  + (current_submatrix.m_edgeType[axis_i*2+1] == SubMatrixData::kOuter));
469 
470  // set corresponding bit in diode type if sub-matrix has a non empty inner or outer edge
471  // in principle only one of the two should be true per axis
472  unsigned int diode_type = ((axis_inner_edges * edge_dim[kInner][axis_i]) > 0) << (kInner+1);
473  diode_type |= ((axis_outer_edges * edge_dim[kOuter][axis_i]) > 0) << (kOuter+1);
474  bool ganged=false;
475  unsigned int n = 0;
476  // special handling if this is an edge with a dead zone
477  // @TODO should distinguish inner or outer edges adjacent to dead zone from actual dead zones.
478  if ( current_submatrix.m_edgeType[axis_i*2]>SubMatrixData::kInternal
479  || current_submatrix.m_edgeType[axis_i*2+1]>SubMatrixData::kInternal) {
480  diode_type = ((current_submatrix.m_edgeType[axis_i*2]==SubMatrixData::kDeadZoneInner) << (kInner+1))
481  | ((current_submatrix.m_edgeType[axis_i*2+1]==SubMatrixData::kDeadZoneInner) << (kInner+1))
482  | ((current_submatrix.m_edgeType[axis_i*2]==SubMatrixData::kDeadZoneOuter) << (kOuter+1))
483  | ((current_submatrix.m_edgeType[axis_i*2+1]==SubMatrixData::kDeadZoneOuter) << (kOuter+1));
484  ganged=true;
485  }
486  else {
487  n = current_submatrix.m_dim[axis_i] - edge_dim[kOuter][axis_i] *axis_outer_edges - edge_dim[kInner][axis_i] * axis_inner_edges;
488  }
489  diode_type |= (n>0) << 0u;
490  if (debug_out) {
491  *debug_out << (axis_i==0 ? "x: " : "y:" )
492  << " outer " << axis_outer_edges << " * " << edge_dim[kOuter][axis_i] << " * " << edge_pitch[kOuter][axis_i]
493  << " inner " << axis_inner_edges << " * " << edge_dim[kInner][axis_i] << " * " << edge_pitch[kInner][axis_i]
494  << " normal " << n
495  << " -> " << diode_type
496  << std::endl;
497  }
498  // determine diode width depending on diode type
499  switch (diode_type) {
500  case (1u<<0):
501  diode_width[axis_i]=pitch[axis_i];
502  break;
503  case (1u<<(kOuter+1)):
504  diode_width[axis_i]=edge_pitch[kOuter][axis_i];
505  break;
506  case (1u<<(kInner+1)):
507  diode_width[axis_i]=edge_pitch[kInner][axis_i];
508  break;
509  default:
510  throw std::logic_error("Invalid diode type. Matrix not fully split.");
511  }
512 
513  // one byte per axis
514  full_diode_type<<=8;
515  diode_type|=ganged<<3u;
516  full_diode_type|=(diode_type&0xff);
517  }
518  // first two elements whether the diode is in a ganged area in one of the two directions
519  // second two elements whether
520  // if the diode is adjacent to an inner or outer edge it is in the dead zone if the diode is marked ganged.
521  std::array<bool,4> ganged_flags{ (static_cast<std::size_t>(full_diode_type) & (1u<<3u)) != 0u,
522  (static_cast<std::size_t>(full_diode_type) & (1u<<(3u+8u))) != 0u,
523  current_submatrix.m_edgeType[0]==SubMatrixData::kInner
524  || current_submatrix.m_edgeType[0+1]==SubMatrixData::kInner
525  || current_submatrix.m_edgeType[0]==SubMatrixData::kOuter
526  || current_submatrix.m_edgeType[0+1]==SubMatrixData::kOuter,
527  current_submatrix.m_edgeType[2]==SubMatrixData::kInner
528  || current_submatrix.m_edgeType[2+1]==SubMatrixData::kInner
529  || current_submatrix.m_edgeType[2]==SubMatrixData::kOuter
530  || current_submatrix.m_edgeType[2+1]==SubMatrixData::kOuter};
531  // cannot be in a dead-zone if the pixel is not marked ganged.
532  ganged_flags[2]=ganged_flags[2]&ganged_flags[0];
533  ganged_flags[3]=ganged_flags[3]&ganged_flags[1];
534 
535  PixelDiodeTree::AttributeType current_sub_matrix_attribute=diode_tree.attribute(current_submatrix.m_subMatrixIdx);
536  auto [new_sub_matrix_attribute, new_diode_attribute]
537  = func_compute_attribute(std::array<PixelDiodeTree::CellIndexType,2>{ static_cast<PixelDiodeTree::CellIndexType>(current_submatrix.m_idx[0]),
538  static_cast<PixelDiodeTree::CellIndexType>(current_submatrix.m_idx[1])},
539  diode_width,
540  ganged_flags,
541  current_submatrix.m_splitIdx,
542  current_sub_matrix_attribute,
543  full_diode_type);
544 
546  ret = diode_idx.insert( std::make_pair(full_diode_type, DiodeInfo(std::numeric_limits<unsigned int>::max(),new_diode_attribute)));
547  if (ret.second) {
548  unsigned int diode_idx = diode_tree.addDiode(diode_width,
549  new_diode_attribute);
551  ret.first->second.setIndex( diode_idx );
552  }
553  assert( ret.second || ret.first->second.attributeAgrees(new_diode_attribute) );
554  diode_tree.setAttribute(current_submatrix.m_subMatrixIdx, new_sub_matrix_attribute);
555  assert( current_sub_matrix_attribute==SubMatrixData::s_defaultMatrixAttribute
556  || current_sub_matrix_attribute==new_sub_matrix_attribute);
557 
558  diode_tree.setDiodeForSubMatrix(current_submatrix.m_subMatrixIdx, current_submatrix.m_splitIdx,ret.first->second.diodeIndex());
559  if (debug_out) {
560  (*debug_out) << "Created Diode "
561  << current_submatrix.m_idx[0] << ", " << current_submatrix.m_idx[1] << " "
562  << current_submatrix.m_dim[0] << " x " << current_submatrix.m_dim[1]
563  << " attr: " << new_sub_matrix_attribute
564  << " : diode_pitch " << diode_width[0] << ", " << diode_width[1]
565  << " sub-matrix index " << current_submatrix.m_subMatrixIdx
566  << " split index " << current_submatrix.m_splitIdx
567  << " -> " << -static_cast<PixelDiodeTree::IndexType>(ret.first->second.diodeIndex())
568  << " attribute " << full_diode_type << " -> " << new_diode_attribute
569  << std::endl;
570  }
571  }
572  }
573  if (diode_tree.cloneSingleSplitsToUnusedHalf()>0) {
574  throw std::logic_error("Some splits have invalid indices. That should not happen.");
575  }
576  // set the positions of the upper and lower corner of the matrix
578  return diode_tree;
579 }
580 }
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
InDetDD::PixelDiodeTree
Tree structure to find the position, index or pitch of a pixel on a semi-regular grid The grid is con...
Definition: PixelDiodeTree.h:33
m_dim
std::array< unsigned int, 2 > m_dim
Definition: PixelDiodeTreeBuilder.cxx:56
yodamerge_tmp.dim
dim
Definition: yodamerge_tmp.py:239
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
m_splitIdx
unsigned int m_splitIdx
Definition: PixelDiodeTreeBuilder.cxx:61
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
m_subMatrixIdx
unsigned int m_subMatrixIdx
Definition: PixelDiodeTreeBuilder.cxx:60
m_idx
std::array< unsigned int, 2 > m_idx
Definition: PixelDiodeTreeBuilder.cxx:55
InDetDD::PixelDiodeTree::CellIndexType
int CellIndexType
Definition: PixelDiodeTree.h:37
InDetDD::PixelDiodeTree::empty
bool empty() const
Return true if no sub-matrices are defined, indicates an invalid state.
Definition: PixelDiodeTree.h:294
InDetDD::PixelDiodeTree::split
unsigned int split(const std::array< CellIndexType, 2 > &idx_split, const Vector2D &pos_split, AttributeType an_attribute, unsigned int parent_idx=std::numeric_limits< unsigned int >::max(), unsigned int split_i=0)
Definition: PixelDiodeTree.h:92
xAOD::unsigned
unsigned
Definition: RingSetConf_v1.cxx:662
Trk::u
@ u
Enums for curvilinear frames.
Definition: ParamDefs.h:77
m_width
PixelDiodeTree::Vector2D m_width
Definition: PixelDiodeTreeBuilder.cxx:54
InDetDD::PixelDiodeTree::setAttribute
void setAttribute(IndexType idx, AttributeType new_attribute)
Set the attribute associated to a sub-matrix.
Definition: PixelDiodeTree.h:334
InDetDD::PixelDiodeTree::cloneSingleSplitsToUnusedHalf
unsigned int cloneSingleSplitsToUnusedHalf()
Clone half with valid split indices to "unused" half with invalid split indices.
Definition: PixelDiodeTree.cxx:11
beamspotman.n
n
Definition: beamspotman.py:729
InDetDD::PixelDiodeTree::AttributeType
unsigned int AttributeType
Definition: PixelDiodeTree.h:39
InDetDD::PixelDiodeTree::computeMatrixCorner
void computeMatrixCorner(const std::array< PixelDiodeTree::CellIndexType, 2 > &matrix_dim)
Compute the effective maximum lower and upper corner positions of the matrix.
Definition: PixelDiodeTree.cxx:61
m_chipDim
std::array< unsigned int, 2 > m_chipDim
Definition: PixelDiodeTreeBuilder.cxx:57
lumiFormat.array
array
Definition: lumiFormat.py:91
m_edgeType
std::array< EEdgeType, kNEdgeLocations > m_edgeType
Definition: PixelDiodeTreeBuilder.cxx:58
m_attribute
PixelDiodeTree::AttributeType m_attribute
Definition: PixelDiodeTreeBuilder.cxx:59
InDetDD::PixelDiodeTree::attribute
AttributeType attribute(IndexType idx) const
Get the attribute associated to a sub-matrix.
Definition: PixelDiodeTree.h:328
InDetDD::PixelDiodeTree::setDiodeForSubMatrix
void setDiodeForSubMatrix(unsigned int sub_matrix_idx, unsigned int split_i, unsigned int diode_idx)
Definition: PixelDiodeTree.h:79
InDetDD::PixelDiodeTree::Vector2D
Amg::Vector2D Vector2D
Definition: PixelDiodeTree.h:35
python.LumiBlobConversion.pos
pos
Definition: LumiBlobConversion.py:16
a
TList * a
Definition: liststreamerinfos.cxx:10
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
Base_Fragment.width
width
Definition: Sherpa_i/share/common/Base_Fragment.py:59
InDetDD
Message Stream Member.
Definition: FakeTrackBuilder.h:8
LArNewCalib_DelayDump_OFC_Cali.idx
idx
Definition: LArNewCalib_DelayDump_OFC_Cali.py:69
m_pos
PixelDiodeTree::Vector2D m_pos
Definition: PixelDiodeTreeBuilder.cxx:53
PixelDiodeTreeBuilder.h
InDetDD::PixelDiodeTree::makeCellIndex
static constexpr std::array< PixelDiodeTree::CellIndexType, 2 > makeCellIndex(T local_x_idx, T local_y_idx)
Create a 2D cell index from the indices in local-x (phi, row) and local-y (eta, column) direction.
Definition: PixelDiodeTree.h:320
InDetDD::createPixelDiodeTree
PixelDiodeTree createPixelDiodeTree(const std::array< unsigned int, 2 > &chip_dim, const std::array< unsigned int, 2 > &chip_matrix_dim, const PixelDiodeTree::Vector2D &pitch, const std::array< std::array< unsigned int, 2 >, 2 > &edge_dim, const std::array< PixelDiodeTree::Vector2D, 2 > &edge_pitch, const std::array< std::array< unsigned int, 2 >, 2 > &dead_zone, const AttributeRefiner &func_compute_attribute, std::ostream *debug_out=nullptr)
Create a pixel diode tree.
Definition: PixelDiodeTreeBuilder.cxx:224
InDetDD::PixelDiodeTree::IndexType
int IndexType
Definition: PixelDiodeTree.h:38
InDetDD::AttributeRefiner
std::function< std::tuple< PixelDiodeTree::AttributeType, PixelDiodeTree::AttributeType >(const std::array< PixelDiodeTree::IndexType, 2 > &, const PixelDiodeTree::Vector2D &, const std::array< bool, 4 > &, unsigned int, PixelDiodeTree::AttributeType, PixelDiodeTree::AttributeType)> AttributeRefiner
Definition: PixelDiodeTreeBuilder.h:46
InDetDD::PixelDiodeTree::addDiode
unsigned int addDiode(const Vector2D &width, AttributeType attribute)
Definition: PixelDiodeTree.h:66
generate::Zero
void Zero(TH1D *hin)
Definition: generate.cxx:32