Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SurfaceArrayCreator.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file SurfaceArrayCreator.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2020 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
18 #include "Acts/Utilities/IAxis.hpp"
21 
22 #include <algorithm>
23 #include <cmath>
24 #include <stdexcept>
25 
28 
29 std::unique_ptr<Acts::SurfaceArray>
31  const GeometryContext& gctx,
32  std::vector<std::shared_ptr<const Surface>> surfaces, size_t binsPhi,
33  size_t binsZ, std::optional<ProtoLayer> protoLayerOpt,
34  const Transform3& transform) const {
35  std::vector<const Surface*> surfacesRaw = unpack_shared_vector(surfaces);
36  // Check if we have proto layer, else build it
37  ProtoLayer protoLayer =
38  protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
39 
40  ACTS_VERBOSE("Creating a SurfaceArray on a cylinder");
41  ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.")
42  ACTS_VERBOSE(" -- with phi x z = " << binsPhi << " x " << binsZ << " = "
43  << binsPhi * binsZ << " bins.");
44 
45  Transform3 ftransform = transform;
46  ProtoAxis pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, binPhi,
47  protoLayer, ftransform, binsPhi);
48  ProtoAxis pAxisZ = createEquidistantAxis(gctx, surfacesRaw, binZ, protoLayer,
49  ftransform, binsZ);
50 
51  double R = protoLayer.medium(binR, true);
52 
53  Transform3 itransform = ftransform.inverse();
54  // Transform lambda captures the transform matrix
55  auto globalToLocal = [ftransform](const Vector3& pos) {
56  Vector3 loc = ftransform * pos;
57  return Vector2(phi(loc), loc.z());
58  };
59  auto localToGlobal = [itransform, R](const Vector2& loc) {
60  return itransform *
61  Vector3(R * std::cos(loc[0]), R * std::sin(loc[0]), loc[1]);
62  };
63 
64  std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
65  makeSurfaceGridLookup2D<detail::AxisBoundaryType::Closed,
66  detail::AxisBoundaryType::Bound>(
67  globalToLocal, localToGlobal, pAxisPhi, pAxisZ);
68 
69  sl->fill(gctx, surfacesRaw);
70  completeBinning(gctx, *sl, surfacesRaw);
71 
72  return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
73  ftransform);
74 }
75 
76 std::unique_ptr<Acts::SurfaceArray>
78  const GeometryContext& gctx,
79  std::vector<std::shared_ptr<const Surface>> surfaces, BinningType bTypePhi,
80  BinningType bTypeZ, std::optional<ProtoLayer> protoLayerOpt,
81  const Transform3& transform) const {
82  std::vector<const Surface*> surfacesRaw = unpack_shared_vector(surfaces);
83  // check if we have proto layer, else build it
84  ProtoLayer protoLayer =
85  protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
86 
87  double R = protoLayer.medium(binR, true);
88 
89  ProtoAxis pAxisPhi;
90  ProtoAxis pAxisZ;
91 
92  Transform3 ftransform = transform;
93 
94  if (bTypePhi == equidistant) {
95  pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, binPhi, protoLayer,
96  ftransform, 0);
97  } else {
98  pAxisPhi =
99  createVariableAxis(gctx, surfacesRaw, binPhi, protoLayer, ftransform);
100  }
101 
102  if (bTypeZ == equidistant) {
103  pAxisZ =
104  createEquidistantAxis(gctx, surfacesRaw, binZ, protoLayer, ftransform);
105  } else {
106  pAxisZ =
107  createVariableAxis(gctx, surfacesRaw, binZ, protoLayer, ftransform);
108  }
109 
110  Transform3 itransform = ftransform.inverse();
111  auto globalToLocal = [ftransform](const Vector3& pos) {
112  Vector3 loc = ftransform * pos;
113  return Vector2(phi(loc), loc.z());
114  };
115  auto localToGlobal = [itransform, R](const Vector2& loc) {
116  return itransform *
117  Vector3(R * std::cos(loc[0]), R * std::sin(loc[0]), loc[1]);
118  };
119 
120  std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
121  makeSurfaceGridLookup2D<detail::AxisBoundaryType::Closed,
122  detail::AxisBoundaryType::Bound>(
123  globalToLocal, localToGlobal, pAxisPhi, pAxisZ);
124 
125  sl->fill(gctx, surfacesRaw);
126  completeBinning(gctx, *sl, surfacesRaw);
127 
128  // get the number of bins
129  auto axes = sl->getAxes();
130  size_t bins0 = axes.at(0)->getNBins();
131  size_t bins1 = axes.at(1)->getNBins();
132 
133  ACTS_VERBOSE("Creating a SurfaceArray on a cylinder");
134  ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.")
135  ACTS_VERBOSE(" -- with phi x z = " << bins0 << " x " << bins1 << " = "
136  << bins0 * bins1 << " bins.");
137 
138  return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
139  ftransform);
140 }
141 
142 std::unique_ptr<Acts::SurfaceArray>
144  const GeometryContext& gctx,
145  std::vector<std::shared_ptr<const Surface>> surfaces, size_t binsR,
146  size_t binsPhi, std::optional<ProtoLayer> protoLayerOpt,
147  const Transform3& transform) const {
148  std::vector<const Surface*> surfacesRaw = unpack_shared_vector(surfaces);
149  // check if we have proto layer, else build it
150  ProtoLayer protoLayer =
151  protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
152 
153  ACTS_VERBOSE("Creating a SurfaceArray on a disc");
154 
155  Transform3 ftransform = transform;
156  ProtoAxis pAxisR = createEquidistantAxis(gctx, surfacesRaw, binR, protoLayer,
157  ftransform, binsR);
158  ProtoAxis pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, binPhi,
159  protoLayer, ftransform, binsPhi);
160 
161  double Z = protoLayer.medium(binZ, true);
162  ACTS_VERBOSE("- z-position of disk estimated as " << Z);
163 
164  Transform3 itransform = transform.inverse();
165  // transform lambda captures the transform matrix
166  auto globalToLocal = [ftransform](const Vector3& pos) {
167  Vector3 loc = ftransform * pos;
168  return Vector2(perp(loc), phi(loc));
169  };
170  auto localToGlobal = [itransform, Z](const Vector2& loc) {
171  return itransform *
172  Vector3(loc[0] * std::cos(loc[1]), loc[0] * std::sin(loc[1]), Z);
173  };
174 
175  std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
176  makeSurfaceGridLookup2D<detail::AxisBoundaryType::Bound,
177  detail::AxisBoundaryType::Closed>(
178  globalToLocal, localToGlobal, pAxisR, pAxisPhi);
179 
180  // get the number of bins
181  auto axes = sl->getAxes();
182  size_t bins0 = axes.at(0)->getNBins();
183  size_t bins1 = axes.at(1)->getNBins();
184 
185  ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.")
186  ACTS_VERBOSE(" -- with r x phi = " << bins0 << " x " << bins1 << " = "
187  << bins0 * bins1 << " bins.");
188  sl->fill(gctx, surfacesRaw);
189  completeBinning(gctx, *sl, surfacesRaw);
190 
191  return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
192  ftransform);
193 }
194 
195 std::unique_ptr<Acts::SurfaceArray>
197  const GeometryContext& gctx,
198  std::vector<std::shared_ptr<const Surface>> surfaces, BinningType bTypeR,
199  BinningType bTypePhi, std::optional<ProtoLayer> protoLayerOpt,
200  const Transform3& transform) const {
201  std::vector<const Surface*> surfacesRaw = unpack_shared_vector(surfaces);
202  // check if we have proto layer, else build it
203  ProtoLayer protoLayer =
204  protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
205 
206  ACTS_VERBOSE("Creating a SurfaceArray on a disc");
207 
208  ProtoAxis pAxisPhi;
209  ProtoAxis pAxisR;
210 
211  Transform3 ftransform = transform;
212 
213  if (bTypeR == equidistant) {
214  pAxisR =
215  createEquidistantAxis(gctx, surfacesRaw, binR, protoLayer, ftransform);
216  } else {
217  pAxisR =
218  createVariableAxis(gctx, surfacesRaw, binR, protoLayer, ftransform);
219  }
220 
221  // if we have more than one R ring, we need to figure out
222  // the number of phi bins.
223  if (pAxisR.nBins > 1) {
224  // more than one R-Ring, we need to adjust
225  // this FORCES equidistant binning
226  std::vector<std::vector<const Surface*>> phiModules(pAxisR.nBins);
227  for (const auto& srf : surfacesRaw) {
228  Vector3 bpos = srf->binningPosition(gctx, binR);
229  size_t bin = pAxisR.getBin(perp(bpos));
230  phiModules.at(bin).push_back(srf);
231  }
232 
233  std::vector<size_t> nPhiModules;
234  auto matcher = m_cfg.surfaceMatcher;
235  auto equal = [&gctx, &matcher](const Surface* a, const Surface* b) {
236  return matcher(gctx, binPhi, a, b);
237  };
238 
240  phiModules.begin(), phiModules.end(), std::back_inserter(nPhiModules),
241  [&equal, this](const std::vector<const Surface*>& surfaces_) -> size_t {
242  return this->findKeySurfaces(surfaces_, equal).size();
243  });
244 
245  // @FIXME: Problem: phi binning runs rotation to optimize
246  // for bin edges. This FAILS after this modification, since
247  // the bin count is the one from the lowest module-count bin,
248  // but the rotation is done considering all bins.
249  // This might be resolved through bin completion, but not sure.
250  // @TODO: check in extrapolation
251  size_t nBinsPhi =
252  (*std::min_element(nPhiModules.begin(), nPhiModules.end()));
253  pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, binPhi, protoLayer,
254  ftransform, nBinsPhi);
255 
256  } else {
257  // use regular determination
258  if (bTypePhi == equidistant) {
259  pAxisPhi = createEquidistantAxis(gctx, surfacesRaw, binPhi, protoLayer,
260  ftransform, 0);
261  } else {
262  pAxisPhi =
263  createVariableAxis(gctx, surfacesRaw, binPhi, protoLayer, ftransform);
264  }
265  }
266 
267  double Z = protoLayer.medium(binZ, true);
268  ACTS_VERBOSE("- z-position of disk estimated as " << Z);
269 
270  Transform3 itransform = ftransform.inverse();
271  // transform lambda captures the transform matrix
272  auto globalToLocal = [ftransform](const Vector3& pos) {
273  Vector3 loc = ftransform * pos;
274  return Vector2(perp(loc), phi(loc));
275  };
276  auto localToGlobal = [itransform, Z](const Vector2& loc) {
277  return itransform *
278  Vector3(loc[0] * std::cos(loc[1]), loc[0] * std::sin(loc[1]), Z);
279  };
280 
281  std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl =
282  makeSurfaceGridLookup2D<detail::AxisBoundaryType::Bound,
283  detail::AxisBoundaryType::Closed>(
284  globalToLocal, localToGlobal, pAxisR, pAxisPhi);
285 
286  // get the number of bins
287  auto axes = sl->getAxes();
288  size_t bins0 = axes.at(0)->getNBins();
289  size_t bins1 = axes.at(1)->getNBins();
290 
291  ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.")
292  ACTS_VERBOSE(" -- with r x phi = " << bins0 << " x " << bins1 << " = "
293  << bins0 * bins1 << " bins.");
294 
295  sl->fill(gctx, surfacesRaw);
296  completeBinning(gctx, *sl, surfacesRaw);
297 
298  return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
299  ftransform);
300 }
301 
303 std::unique_ptr<Acts::SurfaceArray>
305  const GeometryContext& gctx,
306  std::vector<std::shared_ptr<const Surface>> surfaces, size_t bins1,
307  size_t bins2, BinningValue bValue, std::optional<ProtoLayer> protoLayerOpt,
308  const Transform3& transform) const {
309  std::vector<const Surface*> surfacesRaw = unpack_shared_vector(surfaces);
310  // check if we have proto layer, else build it
311  ProtoLayer protoLayer =
312  protoLayerOpt ? *protoLayerOpt : ProtoLayer(gctx, surfacesRaw);
313 
314  ACTS_VERBOSE("Creating a SurfaceArray on a plance");
315  ACTS_VERBOSE(" -- with " << surfaces.size() << " surfaces.")
316  ACTS_VERBOSE(" -- with " << bins1 << " x " << bins2 << " = " << bins1 * bins2
317  << " bins.");
318  Transform3 ftransform = transform;
319  Transform3 itransform = transform.inverse();
320  // transform lambda captures the transform matrix
321  auto globalToLocal = [ftransform](const Vector3& pos) {
322  Vector3 loc = ftransform * pos;
323  return Vector2(loc.x(), loc.y());
324  };
325  auto localToGlobal = [itransform](const Vector2& loc) {
326  return itransform * Vector3(loc.x(), loc.y(), 0.);
327  };
328  // Build the grid
329  std::unique_ptr<SurfaceArray::ISurfaceGridLookup> sl;
330 
331  // Axis along the binning
332  switch (bValue) {
333  case BinningValue::binX: {
334  ProtoAxis pAxis1 = createEquidistantAxis(gctx, surfacesRaw, binY,
335  protoLayer, ftransform, bins1);
336  ProtoAxis pAxis2 = createEquidistantAxis(gctx, surfacesRaw, binZ,
337  protoLayer, ftransform, bins2);
338  sl = makeSurfaceGridLookup2D<detail::AxisBoundaryType::Bound,
339  detail::AxisBoundaryType::Bound>(
340  globalToLocal, localToGlobal, pAxis1, pAxis2);
341  break;
342  }
343  case BinningValue::binY: {
344  ProtoAxis pAxis1 = createEquidistantAxis(gctx, surfacesRaw, binX,
345  protoLayer, ftransform, bins1);
346  ProtoAxis pAxis2 = createEquidistantAxis(gctx, surfacesRaw, binZ,
347  protoLayer, ftransform, bins2);
348  sl = makeSurfaceGridLookup2D<detail::AxisBoundaryType::Bound,
349  detail::AxisBoundaryType::Bound>(
350  globalToLocal, localToGlobal, pAxis1, pAxis2);
351  break;
352  }
353  case BinningValue::binZ: {
354  ProtoAxis pAxis1 = createEquidistantAxis(gctx, surfacesRaw, binX,
355  protoLayer, ftransform, bins1);
356  ProtoAxis pAxis2 = createEquidistantAxis(gctx, surfacesRaw, binY,
357  protoLayer, ftransform, bins2);
358  sl = makeSurfaceGridLookup2D<detail::AxisBoundaryType::Bound,
359  detail::AxisBoundaryType::Bound>(
360  globalToLocal, localToGlobal, pAxis1, pAxis2);
361  break;
362  }
363  default: {
364  throw std::invalid_argument(
365  "Acts::SurfaceArrayCreator::"
366  "surfaceArrayOnPlane: Invalid binning "
367  "direction");
368  }
369  }
370 
371  sl->fill(gctx, surfacesRaw);
372  completeBinning(gctx, *sl, surfacesRaw);
373 
374  return std::make_unique<SurfaceArray>(std::move(sl), std::move(surfaces),
375  ftransform);
377 }
378 
379 std::vector<const Acts::Surface*> Acts::SurfaceArrayCreator::findKeySurfaces(
380  const std::vector<const Surface*>& surfaces,
381  const std::function<bool(const Surface*, const Surface*)>& equal) const {
382  std::vector<const Surface*> keys;
383  for (const auto& srfA : surfaces) {
384  bool exists = false;
385  for (const auto& srfB : keys) {
386  if (equal(srfA, srfB)) {
387  exists = true;
388  break;
389  }
390  }
391  if (!exists) {
392  keys.push_back(srfA);
393  }
394  }
395 
396  return keys;
397 }
398 
400  const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
401  BinningValue bValue) const {
402  auto matcher = m_cfg.surfaceMatcher;
403  auto equal = [&gctx, &bValue, &matcher](const Surface* a, const Surface* b) {
404  return matcher(gctx, bValue, a, b);
405  };
406  std::vector<const Surface*> keys = findKeySurfaces(surfaces, equal);
407 
408  return keys.size();
409 }
410 
413  const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
414  BinningValue bValue, const ProtoLayer& protoLayer,
415  Transform3& transform) const {
416  if (surfaces.empty()) {
417  throw std::logic_error(
418  "No surfaces handed over for creating arbitrary bin utility!");
419  }
420  // BinningOption is open for z and r, in case of phi binning reset later
421  // the vector with the binning Values (boundaries for each bin)
422 
423  // bind matcher with binning type
424  auto matcher = m_cfg.surfaceMatcher;
425  // find the key surfaces
426  auto equal = [&gctx, &bValue, &matcher](const Surface* a, const Surface* b) {
427  return matcher(gctx, bValue, a, b);
428  };
429  std::vector<const Acts::Surface*> keys = findKeySurfaces(surfaces, equal);
430 
431  std::vector<AxisScalar> bValues;
432  if (bValue == Acts::binPhi) {
433  std::stable_sort(keys.begin(), keys.end(),
434  [&gctx](const Acts::Surface* a, const Acts::Surface* b) {
435  return (phi(a->binningPosition(gctx, binPhi)) <
436  phi(b->binningPosition(gctx, binPhi)));
437  });
438 
439  AxisScalar maxPhi = 0.5 * (phi(keys.at(0)->binningPosition(gctx, binPhi)) +
440  phi(keys.at(1)->binningPosition(gctx, binPhi)));
441 
442  // create rotation, so that maxPhi is +pi
443  AxisScalar angle = -(M_PI + maxPhi);
444  transform = (transform)*AngleAxis3(angle, Vector3::UnitZ());
445 
446  // iterate over all key surfaces, and use their mean position as bValues,
447  // but
448  // rotate using transform from before
449  AxisScalar previous = phi(keys.at(0)->binningPosition(gctx, binPhi));
450  // go through key surfaces
451  for (size_t i = 1; i < keys.size(); i++) {
452  const Surface* surface = keys.at(i);
453  // create central binning values which is the mean of the center
454  // positions in the binning direction of the current and previous
455  // surface
456  AxisScalar edge =
457  0.5 * (previous + phi(surface->binningPosition(gctx, binPhi))) +
458  angle;
459  bValues.push_back(edge);
460  previous = phi(surface->binningPosition(gctx, binPhi));
461  }
462 
463  // segments
464  unsigned int segments = 72;
465 
466  // get the bounds of the last surfaces
467  const Acts::Surface* backSurface = keys.back();
468  const Acts::PlanarBounds* backBounds =
469  dynamic_cast<const Acts::PlanarBounds*>(&(backSurface->bounds()));
470  if (backBounds == nullptr) {
471  ACTS_ERROR(
472  "Given SurfaceBounds are not planar - not implemented for "
473  "other bounds yet! ");
474  }
475  // get the global vertices
476  std::vector<Acts::Vector3> backVertices =
477  makeGlobalVertices(gctx, *backSurface, backBounds->vertices(segments));
478  AxisScalar maxBValue = phi(
479  *std::max_element(backVertices.begin(), backVertices.end(),
480  [](const Acts::Vector3& a, const Acts::Vector3& b) {
481  return phi(a) < phi(b);
482  }));
483 
484  bValues.push_back(maxBValue);
485 
486  bValues.push_back(M_PI);
487 
488  } else if (bValue == Acts::binZ) {
489  std::stable_sort(keys.begin(), keys.end(),
490  [&gctx](const Acts::Surface* a, const Acts::Surface* b) {
491  return (a->binningPosition(gctx, binZ).z() <
492  b->binningPosition(gctx, binZ).z());
493  });
494 
495  bValues.push_back(protoLayer.min(binZ));
496  bValues.push_back(protoLayer.max(binZ));
497 
498  // the z-center position of the previous surface
499  AxisScalar previous = keys.front()->binningPosition(gctx, binZ).z();
500  // go through key surfaces
501  for (auto surface = keys.begin() + 1; surface != keys.end(); surface++) {
502  // create central binning values which is the mean of the center
503  // positions in the binning direction of the current and previous
504  // surface
505  bValues.push_back(
506  0.5 * (previous + (*surface)->binningPosition(gctx, binZ).z()));
507  previous = (*surface)->binningPosition(gctx, binZ).z();
508  }
509  } else { // binR
510  std::stable_sort(keys.begin(), keys.end(),
511  [&gctx](const Acts::Surface* a, const Acts::Surface* b) {
512  return (perp(a->binningPosition(gctx, binR)) <
513  perp(b->binningPosition(gctx, binR)));
514  });
515 
516  bValues.push_back(protoLayer.min(binR));
517  bValues.push_back(protoLayer.max(binR));
518 
519  // the r-center position of the previous surface
520  AxisScalar previous = perp(keys.front()->binningPosition(gctx, binR));
521 
522  // go through key surfaces
523  for (auto surface = keys.begin() + 1; surface != keys.end(); surface++) {
524  // create central binning values which is the mean of the center
525  // positions in the binning direction of the current and previous
526  // surface
527  bValues.push_back(
528  0.5 * (previous + perp((*surface)->binningPosition(gctx, binR))));
529  previous = perp((*surface)->binningPosition(gctx, binR));
530  }
531  }
532  std::sort(bValues.begin(), bValues.end());
533  ACTS_VERBOSE("Create variable binning Axis for binned SurfaceArray");
534  ACTS_VERBOSE(" BinningValue: " << bValue);
535  ACTS_VERBOSE(
536  " (binX = 0, binY = 1, binZ = 2, binR = 3, binPhi = 4, "
537  "binRPhi = 5, binH = 6, binEta = 7)");
538  ACTS_VERBOSE(" Number of bins: " << (bValues.size() - 1));
539  ACTS_VERBOSE(" (Min/Max) = (" << bValues.front() << "/"
540  << bValues.back() << ")");
541 
542  ProtoAxis pAxis;
543  pAxis.bType = arbitrary;
544  pAxis.bValue = bValue;
545  pAxis.binEdges = bValues;
546  pAxis.nBins = bValues.size() - 1;
547 
548  return pAxis;
549 }
550 
553  const GeometryContext& gctx, const std::vector<const Surface*>& surfaces,
554  BinningValue bValue, const ProtoLayer& protoLayer, Transform3& transform,
555  size_t nBins) const {
556  if (surfaces.empty()) {
557  throw std::logic_error(
558  "No surfaces handed over for creating equidistant axis!");
559  }
560  // check the binning type first
561 
562  double minimum = 0.;
563  double maximum = 0.;
564 
565  // binning option is open for z and r, in case of phi binning reset later
566  // Acts::BinningOption bOption = Acts::open;
567 
568  // the key surfaces - placed in different bins in the given binning
569  // direction
570  std::vector<const Acts::Surface*> keys;
571 
572  size_t binNumber = 0;
573  if (nBins == 0) {
574  // determine bin count
575  binNumber = determineBinCount(gctx, surfaces, bValue);
576  } else {
577  // use bin count
578  binNumber = nBins;
579  }
580 
581  // bind matcher & context with binning type
582  auto matcher = m_cfg.surfaceMatcher;
583 
584  // now check the binning value
585  if (bValue == binPhi) {
586  if (m_cfg.doPhiBinningOptimization) {
587  // Phi binning
588  // set the binning option for phi
589  // sort first in phi
590  const Acts::Surface* maxElem = *std::max_element(
591  surfaces.begin(), surfaces.end(),
592  [&gctx](const Acts::Surface* a, const Acts::Surface* b) {
593  return phi(a->binningPosition(gctx, binR)) <
594  phi(b->binningPosition(gctx, binR));
595  });
596 
597  // get the key surfaces at the different phi positions
598  auto equal = [&gctx, &bValue, &matcher](const Surface* a,
599  const Surface* b) {
600  return matcher(gctx, bValue, a, b);
601  };
602  keys = findKeySurfaces(surfaces, equal);
603 
604  // multiple surfaces, we bin from -pi to pi closed
605  if (keys.size() > 1) {
606  // bOption = Acts::closed;
607 
608  minimum = -M_PI;
609  maximum = M_PI;
610 
611  // double step = 2 * M_PI / keys.size();
612  double step = 2 * M_PI / binNumber;
613  // rotate to max phi module plus one half step
614  // this should make sure that phi wrapping at +- pi
615  // never falls on a module center
616  double max = phi(maxElem->binningPosition(gctx, binR));
617  double angle = M_PI - (max + 0.5 * step);
618 
619  // replace given transform ref
620  transform = (transform)*AngleAxis3(angle, Vector3::UnitZ());
621 
622  } else {
623  minimum = protoLayer.min(binPhi, true);
624  maximum = protoLayer.max(binPhi, true);
625  // we do not need a transform in this case
626  }
627  } else {
628  minimum = -M_PI;
629  maximum = M_PI;
630  }
631  } else {
632  maximum = protoLayer.max(bValue, false);
633  minimum = protoLayer.min(bValue, false);
634  }
635 
636  // assign the bin size
637  ACTS_VERBOSE("Create equidistant binning Axis for binned SurfaceArray");
638  ACTS_VERBOSE(" BinningValue: " << bValue);
639  ACTS_VERBOSE(
640  " (binX = 0, binY = 1, binZ = 2, binR = 3, binPhi = 4, "
641  "binRPhi = 5, binH = 6, binEta = 7)");
642  ACTS_VERBOSE(" Number of bins: " << binNumber);
643  ACTS_VERBOSE(" (Min/Max) = (" << minimum << "/" << maximum << ")");
644 
645  ProtoAxis pAxis;
646  pAxis.max = maximum;
647  pAxis.min = minimum;
648  pAxis.bType = equidistant;
649  pAxis.bValue = bValue;
650  pAxis.nBins = binNumber;
651 
652  return pAxis;
653 }
654 
656  const GeometryContext& gctx, const Acts::Surface& surface,
657  const std::vector<Acts::Vector2>& locVertices) const {
658  std::vector<Acts::Vector3> globVertices;
659  for (auto& vertex : locVertices) {
660  Acts::Vector3 globVertex =
661  surface.localToGlobal(gctx, vertex, Acts::Vector3());
662  globVertices.push_back(globVertex);
663  }
664  return globVertices;
665 }