ATLAS Offline Software
Loading...
Searching...
No Matches
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
9namespace InDetDD {
10
11namespace {
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
196namespace {
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
224PixelDiodeTree 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{};
246 PixelDiodeTree::Vector2D width(PixelDiodeTree::Vector2D::Zero());
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
461 PixelDiodeTree::Vector2D diode_width(PixelDiodeTree::Vector2D::Zero());
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
545 std::pair< std::unordered_map<unsigned int, DiodeInfo >::iterator, bool>
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);
550 assert( diode_idx < std::numeric_limits<PixelDiodeTree::IndexType>::max());
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
577 diode_tree.computeMatrixCorner(PixelDiodeTree::makeCellIndex(dim[0],dim[1]));
578 return diode_tree;
579}
580}
static Double_t a
const double width
Tree structure to find the position, index or pitch of a pixel on a semi-regular grid The grid is con...
bool empty() const
Return true if no sub-matrices are defined, indicates an invalid state.
AttributeType attribute(IndexType idx) const
Get the attribute associated to a sub-matrix.
void setAttribute(IndexType idx, AttributeType new_attribute)
Set the attribute associated to a sub-matrix.
unsigned int addDiode(const Vector2D &width, AttributeType attribute)
unsigned int cloneSingleSplitsToUnusedHalf()
Clone half with valid split indices to "unused" half with invalid split indices.
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.
void computeMatrixCorner(const std::array< PixelDiodeTree::CellIndexType, 2 > &matrix_dim)
Compute the effective maximum lower and upper corner positions of the matrix.
void setDiodeForSubMatrix(unsigned int sub_matrix_idx, unsigned int split_i, unsigned int diode_idx)
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)
Message Stream Member.
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
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.
@ u
Enums for curvilinear frames.
Definition ParamDefs.h:77