File indexing completed on 2026-09-17 08:21:11
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Utilities/AxisDefinitions.hpp"
0013 #include "Acts/Utilities/BinningType.hpp"
0014 #include "Acts/Utilities/Helpers.hpp"
0015 #include "Acts/Utilities/ProtoAxis.hpp"
0016 #include "Acts/Utilities/ThrowAssert.hpp"
0017 #include "Acts/Utilities/VectorHelpers.hpp"
0018
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <memory>
0022 #include <sstream>
0023 #include <string>
0024 #include <utility>
0025 #include <vector>
0026
0027 namespace Acts {
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 class BinningData {
0045 public:
0046 BinningType type{};
0047 BinningOption option{};
0048 AxisDirection binvalue{};
0049 float min{};
0050 float max{};
0051 float step{};
0052 bool zdim{};
0053
0054
0055 std::unique_ptr<const BinningData> subBinningData;
0056
0057 bool subBinningAdditive{};
0058
0059
0060
0061
0062
0063
0064 BinningData(AxisDirection bValue, float bMin, float bMax)
0065 : type(equidistant),
0066 option(open),
0067 binvalue(bValue),
0068 min(bMin),
0069 max(bMax),
0070 step((bMax - bMin)),
0071 zdim(true),
0072 subBinningData(nullptr),
0073 m_bins(1),
0074 m_boundaries({{min, max}}),
0075 m_totalBins(1),
0076 m_totalBoundaries(std::vector<float>()),
0077 m_functionPtr(&searchEquidistantWithBoundary) {}
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090 BinningData(BinningOption bOption, AxisDirection bValue, std::size_t bBins,
0091 float bMin, float bMax,
0092 std::unique_ptr<const BinningData> sBinData = nullptr,
0093 bool sBinAdditive = false)
0094 : type(equidistant),
0095 option(bOption),
0096 binvalue(bValue),
0097 min(bMin),
0098 max(bMax),
0099 step((bMax - bMin) / static_cast<float>(bBins)),
0100 zdim(bBins == 1 ? true : false),
0101 subBinningData(std::move(sBinData)),
0102 subBinningAdditive(sBinAdditive),
0103 m_bins(bBins),
0104 m_boundaries(std::vector<float>()),
0105 m_totalBins(bBins),
0106 m_totalBoundaries(std::vector<float>()) {
0107
0108 m_functionPtr = &searchEquidistantWithBoundary;
0109
0110 m_boundaries.reserve(m_bins + 1);
0111 for (std::size_t ib = 0; ib < m_bins + 1; ++ib) {
0112 m_boundaries.push_back(min + static_cast<float>(ib) * step);
0113 }
0114
0115 checkSubStructure();
0116 }
0117
0118
0119
0120
0121
0122
0123
0124 BinningData(BinningOption bOption, AxisDirection bValue,
0125 const std::vector<float>& bBoundaries,
0126 std::unique_ptr<const BinningData> sBinData = nullptr)
0127 : type(arbitrary),
0128 option(bOption),
0129 binvalue(bValue),
0130 zdim(bBoundaries.size() == 2 ? true : false),
0131 subBinningData(std::move(sBinData)),
0132 subBinningAdditive(true),
0133 m_bins(bBoundaries.size() - 1),
0134 m_boundaries(bBoundaries),
0135 m_totalBins(bBoundaries.size() - 1),
0136 m_totalBoundaries(bBoundaries) {
0137
0138 throw_assert(m_boundaries.size() > 1, "Must have more than one boundary");
0139 min = m_boundaries[0];
0140 max = m_boundaries[m_boundaries.size() - 1];
0141
0142 m_functionPtr = &searchInVectorWithBoundary;
0143
0144 checkSubStructure();
0145 }
0146
0147
0148
0149
0150 BinningData(const BinningData& bdata)
0151 : type(bdata.type),
0152 option(bdata.option),
0153 binvalue(bdata.binvalue),
0154 min(bdata.min),
0155 max(bdata.max),
0156 step(bdata.step),
0157 zdim(bdata.zdim),
0158 subBinningData(nullptr),
0159 subBinningAdditive(bdata.subBinningAdditive),
0160 m_bins(bdata.m_bins),
0161 m_boundaries(bdata.m_boundaries),
0162 m_totalBins(bdata.m_totalBins),
0163 m_totalBoundaries(bdata.m_totalBoundaries) {
0164
0165 subBinningData =
0166 bdata.subBinningData
0167 ? std::make_unique<const BinningData>(*bdata.subBinningData)
0168 : nullptr;
0169
0170
0171 if (type == equidistant) {
0172 m_functionPtr = &searchEquidistantWithBoundary;
0173 } else {
0174 m_functionPtr = &searchInVectorWithBoundary;
0175 }
0176 }
0177
0178
0179
0180
0181
0182
0183 explicit BinningData(const IAxis& axis)
0184 : BinningData(directionOf(axis), axis) {}
0185
0186
0187
0188
0189
0190
0191 [[deprecated(
0192 "Use BinningData(const IAxis&) with a directed axis "
0193 "instead")]] explicit BinningData(const DirectedProtoAxis& dpAxis)
0194 : BinningData(dpAxis.getAxisDirection(), dpAxis.getAxis()) {}
0195
0196
0197
0198
0199
0200
0201 BinningData(AxisDirection axisDir, const IAxis& axis)
0202 : binvalue(axisDir), subBinningData(nullptr) {
0203 type = axis.getType() == AxisType::Equidistant ? equidistant : arbitrary;
0204 option = axis.getBoundaryType() == AxisBoundaryType::Closed ? closed : open;
0205 min = static_cast<float>(axis.getMin());
0206 max = static_cast<float>(axis.getMax());
0207 m_bins = axis.getNBins();
0208 step = (max - min) / static_cast<float>(m_bins);
0209 zdim = (m_bins == 1);
0210 m_boundaries.reserve(axis.getBinEdges().size());
0211 for (const auto& edge : axis.getBinEdges()) {
0212 m_boundaries.push_back(static_cast<float>(edge));
0213 }
0214 m_totalBins = m_bins;
0215 m_totalBoundaries = m_boundaries;
0216
0217 m_functionPtr = (type == equidistant) ? &searchEquidistantWithBoundary
0218 : &searchInVectorWithBoundary;
0219 }
0220
0221
0222
0223
0224
0225 BinningData& operator=(const BinningData& bdata) {
0226 if (this != &bdata) {
0227 type = bdata.type;
0228 option = bdata.option;
0229 binvalue = bdata.binvalue;
0230 min = bdata.min;
0231 max = bdata.max;
0232 step = bdata.step;
0233 zdim = bdata.zdim;
0234 subBinningAdditive = bdata.subBinningAdditive;
0235 subBinningData =
0236 bdata.subBinningData
0237 ? std::make_unique<const BinningData>(*bdata.subBinningData)
0238 : nullptr;
0239 m_bins = bdata.m_bins;
0240 m_boundaries = bdata.m_boundaries;
0241 m_totalBins = bdata.m_totalBins;
0242 m_totalBoundaries = bdata.m_totalBoundaries;
0243
0244 if (type == equidistant) {
0245 m_functionPtr = &searchEquidistantWithBoundary;
0246 } else {
0247 m_functionPtr = &searchInVectorWithBoundary;
0248 }
0249 }
0250 return (*this);
0251 }
0252
0253 BinningData() = default;
0254 ~BinningData() = default;
0255
0256
0257
0258
0259
0260
0261 bool operator==(const BinningData& bData) const {
0262 return (type == bData.type && option == bData.option &&
0263 binvalue == bData.binvalue && min == bData.min &&
0264 max == bData.max && step == bData.step && zdim == bData.zdim &&
0265 ((subBinningData == nullptr && bData.subBinningData == nullptr) ||
0266 (subBinningData != nullptr && bData.subBinningData != nullptr &&
0267 (*subBinningData == *bData.subBinningData))) &&
0268 subBinningAdditive == bData.subBinningAdditive);
0269 }
0270
0271
0272
0273 std::size_t bins() const { return m_totalBins; }
0274
0275
0276
0277 const std::vector<float>& boundaries() const {
0278 if (subBinningData) {
0279 return m_totalBoundaries;
0280 }
0281 return m_boundaries;
0282 }
0283
0284
0285
0286
0287
0288
0289 float value(const Vector2& lposition) const {
0290
0291 if (binvalue == AxisDirection::AxisR ||
0292 binvalue == AxisDirection::AxisRPhi ||
0293 binvalue == AxisDirection::AxisX ||
0294 binvalue == AxisDirection::AxisTheta) {
0295 return static_cast<float>(lposition[0]);
0296 }
0297
0298 return static_cast<float>(lposition[1]);
0299 }
0300
0301
0302
0303
0304
0305
0306 float value(const Vector3& position) const {
0307 using VectorHelpers::eta;
0308 using VectorHelpers::perp;
0309 using VectorHelpers::phi;
0310
0311 if (binvalue == AxisDirection::AxisR ||
0312 binvalue == AxisDirection::AxisTheta) {
0313 return static_cast<float>(perp(position));
0314 }
0315 if (binvalue == AxisDirection::AxisRPhi) {
0316 return static_cast<float>(perp(position) * phi(position));
0317 }
0318 if (binvalue == AxisDirection::AxisEta) {
0319 return static_cast<float>(eta(position));
0320 }
0321 if (toUnderlying(binvalue) < 3) {
0322 return static_cast<float>(position[toUnderlying(binvalue)]);
0323 }
0324
0325 return static_cast<float>(phi(position));
0326 }
0327
0328
0329
0330
0331
0332
0333 float center(std::size_t bin) const {
0334 const std::vector<float>& bvals = boundaries();
0335
0336 float value =
0337 bin < (bvals.size() - 1) ? 0.5f * (bvals[bin] + bvals[bin + 1]) : 0.f;
0338 return value;
0339 }
0340
0341
0342
0343
0344
0345
0346 float width(std::size_t bin) const {
0347 const std::vector<float>& bvals = boundaries();
0348
0349 float value = bin < (bvals.size() - 1) ? bvals[bin + 1] - bvals[bin] : 0.f;
0350 return value;
0351 }
0352
0353
0354
0355
0356
0357
0358 bool inside(const Vector3& position) const {
0359
0360 if (option == closed) {
0361 return true;
0362 }
0363
0364
0365 float val = value(position);
0366 return (val > min - 0.001 && val < max + 0.001);
0367 }
0368
0369
0370
0371
0372
0373
0374 bool inside(const Vector2& lposition) const {
0375
0376 if (option == closed) {
0377 return true;
0378 }
0379
0380
0381 float val = value(lposition);
0382 return (val > min - 0.001 && val < max + 0.001);
0383 }
0384
0385
0386
0387
0388
0389
0390 std::size_t searchLocal(const Vector2& lposition) const {
0391 if (zdim) {
0392 return 0;
0393 }
0394 return search(value(lposition));
0395 }
0396
0397
0398
0399
0400
0401
0402 std::size_t searchGlobal(const Vector3& position) const {
0403 if (zdim) {
0404 return 0;
0405 }
0406 return search(value(position));
0407 }
0408
0409
0410
0411
0412
0413
0414 std::size_t search(float value) const {
0415 if (zdim) {
0416 return 0;
0417 }
0418 assert(m_functionPtr != nullptr);
0419 return (!subBinningData) ? (*m_functionPtr)(value, *this)
0420 : searchWithSubStructure(value);
0421 }
0422
0423
0424
0425
0426
0427
0428
0429 std::size_t searchWithSubStructure(float value) const {
0430
0431 std::size_t masterbin = (*m_functionPtr)(value, *this);
0432
0433 if (subBinningAdditive) {
0434
0435 return masterbin + subBinningData->search(value);
0436 }
0437
0438 float gvalue = value - static_cast<float>(masterbin) *
0439 (subBinningData->max - subBinningData->min);
0440
0441 std::size_t subbin = subBinningData->search(gvalue);
0442
0443 return masterbin * subBinningData->bins() + subbin;
0444 }
0445
0446
0447
0448
0449
0450
0451
0452
0453 int nextDirection(const Vector3& position, const Vector3& dir) const {
0454 if (zdim) {
0455 return 0;
0456 }
0457 float val = value(position);
0458 Vector3 probe = position + dir.normalized();
0459 float nextval = value(probe);
0460 return (nextval > val) ? 1 : -1;
0461 }
0462
0463
0464
0465
0466
0467
0468
0469
0470 float centerValue(std::size_t bin) const {
0471 if (zdim) {
0472 return 0.5f * (min + max);
0473 }
0474 float bmin = m_boundaries[bin];
0475 float bmax = bin < m_boundaries.size() ? m_boundaries[bin + 1] : max;
0476 return 0.5f * (bmin + bmax);
0477 }
0478
0479
0480
0481
0482 BinningData scale(float factor) const {
0483 BinningData scaled = *this;
0484 scaled.min *= factor;
0485 scaled.max *= factor;
0486 scaled.step *= factor;
0487 for (auto& boundary : scaled.m_boundaries) {
0488 boundary *= factor;
0489 }
0490 for (auto& boundary : scaled.m_totalBoundaries) {
0491 boundary *= factor;
0492 }
0493 if (scaled.subBinningData) {
0494 scaled.subBinningData = std::make_unique<const BinningData>(
0495 scaled.subBinningData->scale(factor));
0496 }
0497 return scaled;
0498 }
0499
0500 private:
0501
0502
0503
0504
0505
0506
0507
0508 static AxisDirection directionOf(const IAxis& axis) {
0509 if (!axis.getDirection().has_value()) {
0510 throw std::invalid_argument(
0511 "BinningData: axis has no direction assigned");
0512 }
0513 return axis.getDirection().value();
0514 }
0515
0516 std::size_t m_bins{};
0517 std::vector<float> m_boundaries;
0518 std::size_t m_totalBins{};
0519 std::vector<float> m_totalBoundaries;
0520
0521 std::size_t (*m_functionPtr)(float,
0522 const BinningData&){};
0523
0524
0525 void checkSubStructure() {
0526
0527 if (subBinningData) {
0528 m_totalBoundaries.clear();
0529
0530 if (subBinningAdditive) {
0531
0532 m_totalBins = m_bins + subBinningData->bins() - 1;
0533
0534 m_totalBoundaries.reserve(m_totalBins + 1);
0535
0536 const std::vector<float>& subBinBoundaries =
0537 subBinningData->boundaries();
0538 float sBinMin = subBinBoundaries[0];
0539
0540 std::vector<float>::const_iterator mbvalue = m_boundaries.begin();
0541 for (; mbvalue != m_boundaries.end(); ++mbvalue) {
0542
0543 if (std::abs((*mbvalue) - sBinMin) < 10e-10) {
0544
0545 m_totalBoundaries.insert(m_totalBoundaries.begin(),
0546 subBinBoundaries.begin(),
0547 subBinBoundaries.end());
0548 ++mbvalue;
0549 } else {
0550 m_totalBoundaries.push_back(*mbvalue);
0551 }
0552 }
0553 } else {
0554
0555 m_totalBins = m_bins * subBinningData->bins();
0556 m_totalBoundaries.reserve(m_totalBins + 1);
0557
0558 const std::vector<float>& subBinBoundaries =
0559 subBinningData->boundaries();
0560
0561 m_totalBoundaries.push_back(min);
0562 for (std::size_t ib = 0; ib < m_bins; ++ib) {
0563 float offset = static_cast<float>(ib) * step;
0564 for (std::size_t isb = 1; isb < subBinBoundaries.size(); ++isb) {
0565 m_totalBoundaries.push_back(offset + subBinBoundaries[isb]);
0566 }
0567 }
0568 }
0569
0570 std::ranges::sort(m_totalBoundaries);
0571 }
0572 }
0573
0574
0575
0576 static std::size_t searchEquidistantWithBoundary(float value,
0577 const BinningData& bData) {
0578
0579
0580 int bin = static_cast<int>((value - bData.min) / bData.step);
0581
0582 if (bData.option == closed) {
0583 if (value < bData.min) {
0584 return (bData.m_bins - 1);
0585 }
0586 if (value > bData.max) {
0587 return 0;
0588 }
0589 }
0590
0591 bin =
0592 bin < 0
0593 ? ((bData.option == open) ? 0 : static_cast<int>(bData.m_bins - 1))
0594 : bin;
0595 return static_cast<std::size_t>(
0596 (bin <= static_cast<int>(bData.m_bins - 1))
0597 ? static_cast<std::size_t>(bin)
0598 : ((bData.option == open) ? (bData.m_bins - 1) : 0));
0599 }
0600
0601
0602 static std::size_t searchInVectorWithBoundary(float value,
0603 const BinningData& bData) {
0604
0605 if (value <= bData.m_boundaries[0]) {
0606 return (bData.option == closed) ? (bData.m_bins - 1) : 0;
0607 }
0608
0609 if (value >= bData.max) {
0610 return (bData.option == closed) ? 0 : (bData.m_bins - 1);
0611 }
0612
0613 auto lb = std::ranges::lower_bound(bData.m_boundaries, value);
0614 return static_cast<std::size_t>(
0615 std::ranges::distance(bData.m_boundaries.begin(), lb) - 1);
0616 }
0617
0618 public:
0619
0620
0621
0622 std::string toString(const std::string& indent = "") const {
0623 std::stringstream sl;
0624 sl << indent << "BinningData object:" << '\n';
0625 sl << indent << " - type : " << static_cast<std::size_t>(type)
0626 << '\n';
0627 sl << indent << " - option : " << static_cast<std::size_t>(option)
0628 << '\n';
0629 sl << indent << " - value : " << static_cast<std::size_t>(binvalue)
0630 << '\n';
0631 sl << indent << " - bins : " << bins() << '\n';
0632 sl << indent << " - min/max : " << min << " / " << max << '\n';
0633 if (type == equidistant) {
0634 sl << indent << " - step : " << step << '\n';
0635 }
0636 sl << indent << " - boundaries : | ";
0637 for (const auto& b : boundaries()) {
0638 sl << b << " | ";
0639 }
0640 sl << '\n';
0641 return sl.str();
0642 }
0643 };
0644
0645 }