File indexing completed on 2026-06-28 07:34:53
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Utilities/AxisDefinitions.hpp"
0012 #include "Acts/Utilities/IAxis.hpp"
0013 #include "Acts/Utilities/NeighborHoodIndices.hpp"
0014
0015 #include <algorithm>
0016 #include <cmath>
0017 #include <iostream>
0018 #include <stdexcept>
0019 #include <vector>
0020
0021 namespace Acts {
0022
0023
0024
0025
0026
0027 template <AxisBoundaryType bdt>
0028 class Axis<AxisType::Equidistant, bdt> : public IAxis {
0029 public:
0030
0031 static constexpr AxisType type = AxisType::Equidistant;
0032
0033
0034
0035
0036
0037
0038
0039
0040 Axis(double xmin, double xmax, std::size_t nBins,
0041 std::optional<AxisDirection> direction = std::nullopt)
0042 : IAxis(direction),
0043 m_min(xmin),
0044 m_max(xmax),
0045 m_width((xmax - xmin) / nBins),
0046 m_bins(nBins) {
0047 if (m_min >= m_max) {
0048 std::string msg = "Axis: Invalid axis range'";
0049 msg += "', min edge (" + std::to_string(m_min) + ") ";
0050 msg += " needs to be smaller than max edge (";
0051 msg += std::to_string(m_max) + ").";
0052 throw std::invalid_argument(msg);
0053 }
0054 if (m_bins < 1u) {
0055 throw std::invalid_argument(
0056 "Axis: Invalid binning, at least one bin is needed.");
0057 }
0058 }
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068 Axis(AxisBoundaryTypeTag<bdt> typeTag, double xmin, double xmax,
0069 std::size_t nBins, std::optional<AxisDirection> direction = std::nullopt)
0070 : Axis(xmin, xmax, nBins, direction) {
0071 static_cast<void>(typeTag);
0072 }
0073
0074
0075
0076 bool isEquidistant() const final { return true; }
0077
0078
0079
0080 bool isVariable() const final { return false; }
0081
0082
0083
0084 AxisType getType() const final { return type; }
0085
0086
0087
0088 AxisBoundaryType getBoundaryType() const final { return bdt; }
0089
0090
0091
0092
0093
0094
0095 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0096 std::size_t size = 1) const {
0097 return neighborHoodIndices(idx,
0098 std::make_pair(-static_cast<int>(size), size));
0099 }
0100
0101
0102
0103
0104
0105
0106 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0107 std::pair<int, int> sizes = {-1,
0108 1}) const
0109 requires(bdt == AxisBoundaryType::Open)
0110 {
0111 constexpr int min = 0;
0112 const int max = getNBins() + 1;
0113 const int itmin = std::clamp(static_cast<int>(idx + sizes.first), min, max);
0114 const int itmax =
0115 std::clamp(static_cast<int>(idx + sizes.second), min, max);
0116 return NeighborHoodIndices(itmin, itmax + 1);
0117 }
0118
0119
0120
0121
0122
0123
0124
0125 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0126 std::pair<int, int> sizes = {-1,
0127 1}) const
0128 requires(bdt == AxisBoundaryType::Bound)
0129 {
0130 if (idx <= 0 || idx >= (getNBins() + 1)) {
0131 return NeighborHoodIndices();
0132 }
0133 constexpr int min = 1;
0134 const int max = getNBins();
0135 const int itmin = std::clamp(static_cast<int>(idx) + sizes.first, min, max);
0136 const int itmax =
0137 std::clamp(static_cast<int>(idx) + sizes.second, min, max);
0138 return NeighborHoodIndices(itmin, itmax + 1);
0139 }
0140
0141
0142
0143
0144
0145
0146
0147
0148 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0149 std::pair<int, int> sizes = {-1,
0150 1}) const
0151 requires(bdt == AxisBoundaryType::Closed)
0152 {
0153
0154 if (idx <= 0 || idx >= (getNBins() + 1)) {
0155 return NeighborHoodIndices();
0156 }
0157
0158
0159
0160
0161 const int max = getNBins();
0162 sizes.first = std::clamp(sizes.first, -max, max);
0163 sizes.second = std::clamp(sizes.second, -max, max);
0164 if (std::abs(sizes.first - sizes.second) >= max) {
0165 sizes.first = 1 - idx;
0166 sizes.second = max - idx;
0167 }
0168
0169
0170
0171
0172
0173
0174
0175
0176 const int itmin = idx + sizes.first;
0177 const int itmax = idx + sizes.second;
0178 const std::size_t itfirst = wrapBin(itmin);
0179 const std::size_t itlast = wrapBin(itmax);
0180 if (itfirst <= itlast) {
0181 return NeighborHoodIndices(itfirst, itlast + 1);
0182 } else {
0183 return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0184 }
0185 }
0186
0187
0188
0189
0190
0191 std::size_t wrapBin(int bin) const
0192 requires(bdt == AxisBoundaryType::Open)
0193 {
0194 return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0195 }
0196
0197
0198
0199
0200
0201 std::size_t wrapBin(int bin) const
0202 requires(bdt == AxisBoundaryType::Bound)
0203 {
0204 return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0205 }
0206
0207
0208
0209
0210
0211 std::size_t wrapBin(int bin) const
0212 requires(bdt == AxisBoundaryType::Closed)
0213 {
0214 const int w = getNBins();
0215 return 1 + (w + ((bin - 1) % w)) % w;
0216
0217 }
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227 std::size_t getBin(double x) const final {
0228 return wrapBin(
0229 static_cast<int>(std::floor((x - getMin()) / getBinWidth()) + 1));
0230 }
0231
0232
0233
0234 double getBinWidth(std::size_t = 0) const { return m_width; }
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245 double getBinLowerBound(std::size_t bin) const {
0246 return getMin() + (bin - 1) * getBinWidth();
0247 }
0248
0249
0250
0251
0252
0253
0254
0255
0256 double getBinUpperBound(std::size_t bin) const {
0257 return getMin() + bin * getBinWidth();
0258 }
0259
0260
0261
0262
0263
0264
0265 double getBinCenter(std::size_t bin) const {
0266 return getMin() + (bin - 0.5) * getBinWidth();
0267 }
0268
0269
0270
0271 double getMax() const final { return m_max; }
0272
0273
0274
0275 double getMin() const final { return m_min; }
0276
0277
0278
0279 std::size_t getNBins() const final { return m_bins; }
0280
0281
0282
0283
0284
0285
0286
0287 bool isInside(double x) const { return (m_min <= x) && (x < m_max); }
0288
0289
0290
0291 std::vector<double> getBinEdges() const final {
0292 std::vector<double> binEdges;
0293 for (std::size_t i = 1; i <= m_bins; i++) {
0294 binEdges.push_back(getBinLowerBound(i));
0295 }
0296 binEdges.push_back(getBinUpperBound(m_bins));
0297 return binEdges;
0298 }
0299
0300 friend std::ostream& operator<<(std::ostream& os, const Axis& axis) {
0301 os << "Axis<Equidistant, " << bdt << ">(";
0302 os << axis.m_min << ", ";
0303 os << axis.m_max << ", ";
0304 os << axis.m_bins << ")";
0305 return os;
0306 }
0307
0308 protected:
0309 void toStream(std::ostream& os) const final { os << *this; }
0310
0311 private:
0312
0313 double m_min{};
0314
0315 double m_max{};
0316
0317 double m_width{};
0318
0319 std::size_t m_bins{};
0320 };
0321
0322
0323
0324
0325
0326 template <AxisBoundaryType bdt>
0327 class Axis<AxisType::Variable, bdt> : public IAxis {
0328 public:
0329
0330 static constexpr AxisType type = AxisType::Variable;
0331
0332
0333
0334
0335
0336
0337
0338
0339 explicit Axis(std::vector<double> binEdges,
0340 std::optional<AxisDirection> direction = std::nullopt)
0341 : IAxis(direction), m_binEdges(std::move(binEdges)) {
0342 if (m_binEdges.size() < 2) {
0343 throw std::invalid_argument(
0344 "Axis: Invalid binning, at least two bin edges are needed.");
0345 }
0346 if (!std::ranges::is_sorted(m_binEdges)) {
0347 throw std::invalid_argument(
0348 "Axis: Invalid binning, bin edges are not sorted.");
0349 }
0350 }
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360 Axis(AxisBoundaryTypeTag<bdt> typeTag, std::vector<double> binEdges,
0361 std::optional<AxisDirection> direction = std::nullopt)
0362 : Axis(std::move(binEdges), direction) {
0363 static_cast<void>(typeTag);
0364 }
0365
0366
0367
0368 bool isEquidistant() const final { return false; }
0369
0370
0371
0372 bool isVariable() const final { return true; }
0373
0374
0375
0376 AxisType getType() const final { return type; }
0377
0378
0379
0380 AxisBoundaryType getBoundaryType() const final { return bdt; }
0381
0382
0383
0384
0385
0386
0387 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0388 std::size_t size = 1) const {
0389 return neighborHoodIndices(idx,
0390 std::make_pair(-static_cast<int>(size), size));
0391 }
0392
0393
0394
0395
0396
0397
0398 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0399 std::pair<int, int> sizes = {-1,
0400 1}) const
0401 requires(bdt == AxisBoundaryType::Open)
0402 {
0403 constexpr int min = 0;
0404 const int max = getNBins() + 1;
0405 const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0406 const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0407 return NeighborHoodIndices(itmin, itmax + 1);
0408 }
0409
0410
0411
0412
0413
0414
0415
0416 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0417 std::pair<int, int> sizes = {-1,
0418 1}) const
0419 requires(bdt == AxisBoundaryType::Bound)
0420 {
0421 if (idx <= 0 || idx >= (getNBins() + 1)) {
0422 return NeighborHoodIndices();
0423 }
0424 constexpr int min = 1;
0425 const int max = getNBins();
0426 const int itmin = std::max(min, static_cast<int>(idx) + sizes.first);
0427 const int itmax = std::min(max, static_cast<int>(idx) + sizes.second);
0428 return NeighborHoodIndices(itmin, itmax + 1);
0429 }
0430
0431
0432
0433
0434
0435
0436
0437
0438 NeighborHoodIndices neighborHoodIndices(std::size_t idx,
0439 std::pair<int, int> sizes = {-1,
0440 1}) const
0441 requires(bdt == AxisBoundaryType::Closed)
0442 {
0443
0444 if (idx <= 0 || idx >= (getNBins() + 1)) {
0445 return NeighborHoodIndices();
0446 }
0447
0448
0449
0450
0451 const int max = getNBins();
0452 sizes.first = std::clamp(sizes.first, -max, max);
0453 sizes.second = std::clamp(sizes.second, -max, max);
0454 if (std::abs(sizes.first - sizes.second) >= max) {
0455 sizes.first = 1 - idx;
0456 sizes.second = max - idx;
0457 }
0458
0459
0460
0461
0462
0463
0464
0465
0466 const int itmin = idx + sizes.first;
0467 const int itmax = idx + sizes.second;
0468 const std::size_t itfirst = wrapBin(itmin);
0469 const std::size_t itlast = wrapBin(itmax);
0470 if (itfirst <= itlast) {
0471 return NeighborHoodIndices(itfirst, itlast + 1);
0472 } else {
0473 return NeighborHoodIndices(itfirst, max + 1, 1, itlast + 1);
0474 }
0475 }
0476
0477
0478
0479
0480
0481 std::size_t wrapBin(int bin) const
0482 requires(bdt == AxisBoundaryType::Open)
0483 {
0484 return std::max(std::min(bin, static_cast<int>(getNBins()) + 1), 0);
0485 }
0486
0487
0488
0489
0490
0491 std::size_t wrapBin(int bin) const
0492 requires(bdt == AxisBoundaryType::Bound)
0493 {
0494 return std::max(std::min(bin, static_cast<int>(getNBins())), 1);
0495 }
0496
0497
0498
0499
0500
0501 std::size_t wrapBin(int bin) const
0502 requires(bdt == AxisBoundaryType::Closed)
0503 {
0504 const int w = getNBins();
0505 return 1 + (w + ((bin - 1) % w)) % w;
0506
0507 }
0508
0509
0510
0511
0512
0513
0514
0515
0516
0517 std::size_t getBin(double x) const final {
0518 const auto it = std::ranges::upper_bound(m_binEdges, x);
0519 return wrapBin(
0520 static_cast<int>(std::ranges::distance(m_binEdges.begin(), it)));
0521 }
0522
0523
0524
0525
0526
0527
0528 double getBinWidth(std::size_t bin) const {
0529 return m_binEdges.at(bin) - m_binEdges.at(bin - 1);
0530 }
0531
0532
0533
0534
0535
0536
0537
0538
0539 double getBinLowerBound(std::size_t bin) const {
0540 return m_binEdges.at(bin - 1);
0541 }
0542
0543
0544
0545
0546
0547
0548
0549
0550 double getBinUpperBound(std::size_t bin) const { return m_binEdges.at(bin); }
0551
0552
0553
0554
0555
0556
0557 double getBinCenter(std::size_t bin) const {
0558 return 0.5 * (getBinLowerBound(bin) + getBinUpperBound(bin));
0559 }
0560
0561
0562
0563 double getMax() const final { return m_binEdges.back(); }
0564
0565
0566
0567 double getMin() const final { return m_binEdges.front(); }
0568
0569
0570
0571 std::size_t getNBins() const final { return m_binEdges.size() - 1; }
0572
0573
0574
0575
0576
0577
0578 bool isInside(double x) const {
0579 return (m_binEdges.front() <= x) && (x < m_binEdges.back());
0580 }
0581
0582
0583
0584 std::vector<double> getBinEdges() const final { return m_binEdges; }
0585
0586 friend std::ostream& operator<<(std::ostream& os, const Axis& axis) {
0587 os << "Axis<Variable, " << bdt << ">(";
0588 os << axis.m_binEdges.front();
0589 for (std::size_t i = 1; i < axis.m_binEdges.size(); i++) {
0590 os << ", " << axis.m_binEdges[i];
0591 }
0592 os << ")";
0593 return os;
0594 }
0595
0596 protected:
0597 void toStream(std::ostream& os) const final { os << *this; }
0598
0599 private:
0600
0601 std::vector<double> m_binEdges;
0602 };
0603
0604 }