File indexing completed on 2026-08-30 08:14:27
0001 #include "StripeComparison.h"
0002
0003 #include "GlobalFieldFitter.h"
0004 #include "helpers.h"
0005 #include "parameters.h"
0006
0007 #include <TGraph.h>
0008 #include <TGraph2D.h>
0009 #include <TH1.h>
0010 #include <TH2.h>
0011 #include <TTree.h>
0012
0013 #include <algorithm>
0014 #include <array>
0015 #include <cmath>
0016 #include <cstddef>
0017 #include <iostream>
0018 #include <limits>
0019 #include <string>
0020 #include <tuple>
0021 #include <utility>
0022 #include <array>
0023 #include <vector>
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 constexpr std::array<const char *, 2> kComparisonSideNames = {"negz", "posz"};
0034
0035 bool nearly_same_control_position(double lhs, double rhs)
0036 {
0037 return std::abs(lhs - rhs) < 1e-3;
0038 }
0039
0040 std::vector<std::array<double, 3>> remove_topology_region_edge_rows(const std::vector<std::array<double, 3>> &stripes, size_t &removed);
0041
0042 StripeComparison::~StripeComparison()
0043 {
0044 clear();
0045 }
0046
0047 bool StripeComparison::initialize(const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference, int side, const std::vector<double> &controlRPositions)
0048 {
0049
0050
0051 if (measured.empty() || reference.empty())
0052 {
0053 std::cout << "ComputeStripeComparisonMaps: Empty measured or reference stripes" << std::endl;
0054 return false;
0055 }
0056
0057 clear();
0058 m_sideName = kComparisonSideNames.at(side);
0059 m_controlRPositions = controlRPositions;
0060 std::sort(m_controlRPositions.begin(), m_controlRPositions.end());
0061 m_controlRPositions.erase(std::unique(m_controlRPositions.begin(), m_controlRPositions.end(), nearly_same_control_position), m_controlRPositions.end());
0062 return true;
0063 }
0064
0065 bool StripeComparison::filter_isolated_inputs(const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference)
0066 {
0067
0068 filter_isolated_stripes(measured, m_measuredFiltered);
0069 filter_isolated_stripes(reference, m_referenceFiltered);
0070 size_t removedMeasuredEdgeStripes = 0;
0071 size_t removedReferenceEdgeStripes = 0;
0072 m_measuredFiltered = remove_topology_region_edge_rows(m_measuredFiltered, removedMeasuredEdgeStripes);
0073 m_referenceFiltered = remove_topology_region_edge_rows(m_referenceFiltered, removedReferenceEdgeStripes);
0074 std::cout << "Topology edge-row filter for side " << m_sideName << ": removed measured=" << removedMeasuredEdgeStripes << " reference=" << removedReferenceEdgeStripes << std::endl;
0075 if (m_measuredFiltered.empty() || m_referenceFiltered.empty())
0076 {
0077 std::cout << "ComputeStripeComparisonMaps: Empty measured or reference "
0078 "after isolation/topology edge filtering"
0079 << std::endl;
0080 return false;
0081 }
0082 return true;
0083 }
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093 inline constexpr int candidate_measured_idx = 0;
0094 inline constexpr int candidate_reference_idx = 1;
0095 inline constexpr int candidate_delta_r = 2;
0096 inline constexpr int candidate_delta_phi = 3;
0097 inline constexpr int candidate_r_delta_phi = 4;
0098 inline constexpr int candidate_distance = 5;
0099
0100
0101
0102 inline constexpr int region_begin = 0;
0103 inline constexpr int region_end = 1;
0104
0105
0106
0107
0108 inline constexpr int hypothesis_candidates = 0;
0109 inline constexpr int hypothesis_score = 1;
0110 inline constexpr int hypothesis_median_residual = 2;
0111 inline constexpr int hypothesis_unmatched_fraction = 3;
0112 inline constexpr int hypothesis_cleaning_fraction = 4;
0113 inline constexpr int hypothesis_rejected_by_cleaning = 5;
0114 inline constexpr int hypothesis_iterations = 6;
0115
0116
0117 void sort_topology_row(std::pair<double, std::vector<size_t>> &row, const std::vector<std::array<double, 3>> &stripes)
0118 {
0119
0120
0121
0122 for (size_t i = 0; i < row.second.size(); i++)
0123 {
0124 for (size_t j = i + 1; j < row.second.size(); j++)
0125 {
0126 if (stripes[row.second[j]][stripe_phi] < stripes[row.second[i]][stripe_phi])
0127 {
0128 std::swap(row.second[i], row.second[j]);
0129 }
0130 }
0131 }
0132 if (row.second.empty())
0133 {
0134 return;
0135 }
0136 double sum = 0.0;
0137 for (size_t index : row.second)
0138 {
0139 sum += stripes[index][stripe_r];
0140 }
0141 row.first = sum / static_cast<double>(row.second.size());
0142 }
0143
0144 std::vector<std::pair<double, std::vector<size_t>>> build_topology_rows(const std::vector<std::array<double, 3>> &stripes)
0145 {
0146
0147
0148
0149 std::vector<size_t> radialOrder(stripes.size());
0150 for (size_t i = 0; i < stripes.size(); ++i)
0151 {
0152 radialOrder[i] = i;
0153 }
0154 for (size_t i = 0; i < radialOrder.size(); i++)
0155 {
0156 for (size_t j = i + 1; j < radialOrder.size(); j++)
0157 {
0158 if (stripes[radialOrder[j]][stripe_r] < stripes[radialOrder[i]][stripe_r])
0159 {
0160 std::swap(radialOrder[i], radialOrder[j]);
0161 }
0162 }
0163 }
0164 std::vector<std::pair<double, std::vector<size_t>>> rows;
0165 for (size_t index : radialOrder)
0166 {
0167 if (rows.empty() || std::abs(stripes[index][stripe_r] - rows.back().first) > topology_row_tolerance_cm)
0168 {
0169 rows.push_back({stripes[index][stripe_r], {index}});
0170 continue;
0171 }
0172 auto &row = rows.back();
0173 row.second.push_back(index);
0174 row.first += (stripes[index][stripe_r] - row.first) / static_cast<double>(row.second.size());
0175 }
0176 for (auto &row : rows)
0177 {
0178 sort_topology_row(row, stripes);
0179 }
0180 std::vector<std::pair<double, std::vector<size_t>>> keptRows;
0181 for (const auto &row : rows)
0182 {
0183 if (static_cast<int>(row.second.size()) >= topology_min_stripes_per_row)
0184 {
0185 keptRows.push_back(row);
0186 }
0187 }
0188 rows.swap(keptRows);
0189 return rows;
0190 }
0191
0192 std::vector<std::array<size_t, 2>> build_topology_regions(const std::vector<std::pair<double, std::vector<size_t>>> &rows)
0193 {
0194
0195
0196 if (rows.empty())
0197 {
0198 return {};
0199 }
0200
0201 std::vector<size_t> regionStarts{0};
0202 if (rows.size() > 1 && topology_region_count > 1)
0203 {
0204 std::vector<size_t> gapOrder(rows.size() - 1);
0205 for (size_t i = 0; i < gapOrder.size(); ++i)
0206 {
0207 gapOrder[i] = i;
0208 }
0209 for (size_t i = 0; i < gapOrder.size(); i++)
0210 {
0211 for (size_t j = i + 1; j < gapOrder.size(); j++)
0212 {
0213 const double gapI = rows[gapOrder[i] + 1].first - rows[gapOrder[i]].first;
0214 const double gapJ = rows[gapOrder[j] + 1].first - rows[gapOrder[j]].first;
0215 if (gapJ > gapI)
0216 {
0217 std::swap(gapOrder[i], gapOrder[j]);
0218 }
0219 }
0220 }
0221
0222 const size_t splitCount = std::min(gapOrder.size(), static_cast<size_t>(std::max(0, topology_region_count - 1)));
0223 for (size_t i = 0; i < splitCount; ++i)
0224 {
0225 regionStarts.push_back(gapOrder[i] + 1);
0226 }
0227 std::sort(regionStarts.begin(), regionStarts.end());
0228 }
0229 regionStarts.push_back(rows.size());
0230
0231 std::vector<std::array<size_t, 2>> regions;
0232 regions.reserve(regionStarts.size() - 1);
0233 for (size_t region = 0; region + 1 < regionStarts.size(); ++region)
0234 {
0235 regions.push_back({regionStarts[region], regionStarts[region + 1]});
0236 }
0237 return regions;
0238 }
0239
0240 std::vector<std::array<double, 3>> remove_topology_region_edge_rows(const std::vector<std::array<double, 3>> &stripes, size_t &removed)
0241 {
0242
0243
0244 removed = 0;
0245 const auto rows = build_topology_rows(stripes);
0246 if (rows.empty())
0247 {
0248 removed = stripes.size();
0249 return {};
0250 }
0251
0252 const auto regions = build_topology_regions(rows);
0253 std::vector<bool> keep(stripes.size(), false);
0254 for (const auto &row : rows)
0255 {
0256 for (size_t index : row.second)
0257 {
0258 if (index < keep.size())
0259 {
0260 keep[index] = true;
0261 }
0262 }
0263 }
0264 for (const auto ®ion : regions)
0265 {
0266 const size_t begin = region[region_begin];
0267 const size_t end = region[region_end];
0268 const size_t regionRows = end - begin;
0269 const size_t innerRows = std::min(static_cast<size_t>(std::max(0, topology_excluded_inner_rows_per_region)), regionRows);
0270 const size_t outerRows = std::min(static_cast<size_t>(std::max(0, topology_excluded_outer_rows_per_region)), regionRows);
0271 for (size_t offset = 0; offset < innerRows; ++offset)
0272 {
0273 for (size_t index : rows[begin + offset].second)
0274 {
0275 keep[index] = false;
0276 }
0277 }
0278 for (size_t offset = 0; offset < outerRows; ++offset)
0279 {
0280 for (size_t index : rows[end - 1 - offset].second)
0281 {
0282 keep[index] = false;
0283 }
0284 }
0285 }
0286
0287 std::vector<std::array<double, 3>> filtered;
0288 filtered.reserve(stripes.size());
0289 for (size_t i = 0; i < stripes.size(); ++i)
0290 {
0291 if (keep[i])
0292 {
0293 filtered.push_back(stripes[i]);
0294 }
0295 else
0296 {
0297 ++removed;
0298 }
0299 }
0300 return filtered;
0301 }
0302
0303 std::array<double, 6> make_assignment_candidate(size_t measuredIndex, size_t referenceIndex, const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference)
0304 {
0305
0306
0307
0308
0309 std::array<double, 6> candidate{};
0310 candidate[candidate_measured_idx] = static_cast<double>(measuredIndex);
0311 candidate[candidate_reference_idx] = static_cast<double>(referenceIndex);
0312 candidate[candidate_delta_r] = measured[measuredIndex][stripe_r] - reference[referenceIndex][stripe_r];
0313 candidate[candidate_delta_phi] = wrap_delta_phi(measured[measuredIndex][stripe_phi] - reference[referenceIndex][stripe_phi]);
0314 candidate[candidate_r_delta_phi] = measured[measuredIndex][stripe_r] * candidate[candidate_delta_phi];
0315 candidate[candidate_distance] = std::sqrt(candidate[candidate_delta_r] * candidate[candidate_delta_r] + candidate[candidate_r_delta_phi] * candidate[candidate_r_delta_phi]);
0316 return candidate;
0317 }
0318
0319 bool assignment_candidate_within_gates(const std::array<double, 6> &candidate)
0320 {
0321
0322
0323
0324
0325 return std::abs(candidate[candidate_delta_r]) <= global_assignment_max_abs_delta_r_cm && std::abs(candidate[candidate_delta_phi]) <= global_assignment_max_abs_delta_phi_rad;
0326 }
0327
0328
0329
0330
0331
0332
0333 std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> build_topology_initialization(const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference)
0334 {
0335
0336
0337
0338 return {build_topology_rows(measured), build_topology_rows(reference)};
0339 }
0340
0341 std::vector<std::array<double, 6>> keep_unique_assignment_candidates(std::vector<std::array<double, 6>> candidates, size_t measuredCount, size_t referenceCount)
0342 {
0343
0344
0345 for (size_t i = 0; i < candidates.size(); i++)
0346 {
0347 for (size_t j = i + 1; j < candidates.size(); j++)
0348 {
0349 if (candidates[j][candidate_distance] < candidates[i][candidate_distance])
0350 {
0351 std::swap(candidates[i], candidates[j]);
0352 }
0353 }
0354 }
0355 std::vector<bool> usedMeasured(measuredCount, false);
0356 std::vector<bool> usedReference(referenceCount, false);
0357 std::vector<std::array<double, 6>> unique;
0358 unique.reserve(candidates.size());
0359 for (const auto &candidate : candidates)
0360 {
0361 if (!usedMeasured[static_cast<size_t>(candidate[candidate_measured_idx])] && !usedReference[static_cast<size_t>(candidate[candidate_reference_idx])])
0362 {
0363 usedMeasured[static_cast<size_t>(candidate[candidate_measured_idx])] = true;
0364 usedReference[static_cast<size_t>(candidate[candidate_reference_idx])] = true;
0365 unique.push_back(candidate);
0366 }
0367 }
0368 return unique;
0369 }
0370
0371 int nearest_reference_row_for_measured_row(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, size_t measuredRow)
0372 {
0373
0374
0375
0376 if (measuredRow >= topology.first.size() || topology.second.empty())
0377 {
0378 return -1;
0379 }
0380
0381 int nearestReferenceRow = 0;
0382 double nearestRadialDistance = std::abs(topology.first[measuredRow].first - topology.second.front().first);
0383 for (size_t referenceRow = 1; referenceRow < topology.second.size(); ++referenceRow)
0384 {
0385 const double radialDistance = std::abs(topology.first[measuredRow].first - topology.second[referenceRow].first);
0386 if (radialDistance < nearestRadialDistance)
0387 {
0388 nearestRadialDistance = radialDistance;
0389 nearestReferenceRow = static_cast<int>(referenceRow);
0390 }
0391 }
0392 return nearestReferenceRow;
0393 }
0394
0395 int expected_reference_row_ordinal(size_t measuredOrdinal, size_t measuredCount, size_t referenceCount, int branchShift)
0396 {
0397
0398
0399
0400 if (referenceCount == 0)
0401 {
0402 return -1;
0403 }
0404
0405 size_t referenceOrdinal = 0;
0406 if (measuredCount > 1 && referenceCount > 1)
0407 {
0408 const double rowFraction = static_cast<double>(measuredOrdinal) / static_cast<double>(measuredCount - 1);
0409 referenceOrdinal = std::min(static_cast<size_t>(std::lround(rowFraction * static_cast<double>(referenceCount - 1))), referenceCount - 1);
0410 }
0411 return static_cast<int>(referenceOrdinal) + branchShift;
0412 }
0413
0414 std::vector<int> solve_monotone_region_row_map(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, const std::array<size_t, 2> &measuredRegion, const std::array<size_t, 2> &referenceRegion, int branchShift, double &medianDeltaR)
0415 {
0416
0417
0418
0419 const size_t measuredCount = measuredRegion[region_end] > measuredRegion[region_begin] ? measuredRegion[region_end] - measuredRegion[region_begin] : 0;
0420 const size_t referenceCount = referenceRegion[region_end] > referenceRegion[region_begin] ? referenceRegion[region_end] - referenceRegion[region_begin] : 0;
0421 medianDeltaR = 0.0;
0422 std::vector<int> mappedReferenceByMeasured(measuredCount, -1);
0423 if (measuredCount == 0 || referenceCount == 0)
0424 {
0425 return mappedReferenceByMeasured;
0426 }
0427
0428 std::vector<int> expectedReference(measuredCount, -1);
0429 std::vector<double> deltaRSeeds;
0430 deltaRSeeds.reserve(measuredCount);
0431 for (size_t measuredOrdinal = 0; measuredOrdinal < measuredCount; ++measuredOrdinal)
0432 {
0433
0434
0435 expectedReference[measuredOrdinal] = expected_reference_row_ordinal(measuredOrdinal, measuredCount, referenceCount, branchShift);
0436 if (expectedReference[measuredOrdinal] < 0 || expectedReference[measuredOrdinal] >= static_cast<int>(referenceCount))
0437 {
0438 continue;
0439 }
0440 const auto &measuredRow = topology.first[measuredRegion[region_begin] + measuredOrdinal];
0441 const auto &referenceRow = topology.second[referenceRegion[region_begin] + static_cast<size_t>(expectedReference[measuredOrdinal])];
0442 deltaRSeeds.push_back(measuredRow.first - referenceRow.first);
0443 }
0444 if (!deltaRSeeds.empty())
0445 {
0446 medianDeltaR = median_value(deltaRSeeds);
0447 }
0448
0449 const double sigmaDeltaR = std::max(topology_row_mapping_sigma_delta_r_cm, 1e-6);
0450 const double indexWeight = std::max(0.0, topology_row_mapping_index_weight);
0451 const double measuredSkipCost = std::max(0.0, topology_row_mapping_measured_skip_cost);
0452 const double infinity = 1.0e100;
0453
0454 std::vector<std::vector<double>> dp(measuredCount + 1, std::vector<double>(referenceCount + 1, infinity));
0455 std::vector<std::vector<char>> previous(measuredCount + 1, std::vector<char>(referenceCount + 1, 0));
0456
0457
0458
0459 dp[0][0] = 0.0;
0460 for (size_t j = 1; j <= referenceCount; ++j)
0461 {
0462 dp[0][j] = 0.0;
0463 previous[0][j] = 'r';
0464 }
0465 for (size_t i = 1; i <= measuredCount; ++i)
0466 {
0467 dp[i][0] = dp[i - 1][0] + measuredSkipCost;
0468 previous[i][0] = 'm';
0469 }
0470
0471 for (size_t i = 1; i <= measuredCount; ++i)
0472 {
0473 for (size_t j = 1; j <= referenceCount; ++j)
0474 {
0475 double best = dp[i][j - 1];
0476 char choice = 'r';
0477
0478 const double skipMeasured = dp[i - 1][j] + measuredSkipCost;
0479 if (skipMeasured < best)
0480 {
0481 best = skipMeasured;
0482 choice = 'm';
0483 }
0484
0485 const auto &measuredRow = topology.first[measuredRegion[region_begin] + i - 1];
0486 const auto &referenceRow = topology.second[referenceRegion[region_begin] + j - 1];
0487
0488
0489
0490 const double deltaRResidual = ((measuredRow.first - referenceRow.first) - medianDeltaR) / sigmaDeltaR;
0491 const double expectedResidual = static_cast<double>(static_cast<int>(j - 1) - expectedReference[i - 1]);
0492 const double rowPairCost = deltaRResidual * deltaRResidual + indexWeight * expectedResidual * expectedResidual;
0493 const double match = dp[i - 1][j - 1] + rowPairCost;
0494 if (match < best)
0495 {
0496 best = match;
0497 choice = 'x';
0498 }
0499
0500 dp[i][j] = best;
0501 previous[i][j] = choice;
0502 }
0503 }
0504
0505 size_t i = measuredCount;
0506 size_t j = referenceCount;
0507 while (i > 0 || j > 0)
0508 {
0509
0510 const char choice = previous[i][j];
0511 if (choice == 'x')
0512 {
0513 mappedReferenceByMeasured[i - 1] = static_cast<int>(referenceRegion[region_begin] + j - 1);
0514 --i;
0515 --j;
0516 }
0517 else if (choice == 'm')
0518 {
0519 --i;
0520 }
0521 else
0522 {
0523 --j;
0524 }
0525 }
0526
0527 return mappedReferenceByMeasured;
0528 }
0529
0530 std::vector<int> build_monotone_row_mapping(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, int branchShift)
0531 {
0532
0533
0534
0535 std::vector<int> mappedReferenceByMeasured(topology.first.size(), -1);
0536 const auto measuredRegions = build_topology_regions(topology.first);
0537 const auto referenceRegions = build_topology_regions(topology.second);
0538 const size_t regionCount = std::min(measuredRegions.size(), referenceRegions.size());
0539 for (size_t region = 0; region < regionCount; ++region)
0540 {
0541 double medianDeltaR = 0.0;
0542 const auto regionMap = solve_monotone_region_row_map(topology, measuredRegions[region], referenceRegions[region], branchShift, medianDeltaR);
0543 size_t mappedRows = 0;
0544 for (size_t localMeasuredRow = 0; localMeasuredRow < regionMap.size(); ++localMeasuredRow)
0545 {
0546 if (regionMap[localMeasuredRow] < 0)
0547 {
0548 continue;
0549 }
0550 mappedReferenceByMeasured[measuredRegions[region][region_begin] + localMeasuredRow] = regionMap[localMeasuredRow];
0551 ++mappedRows;
0552 }
0553 std::cout << "Row map branch=" << branchShift << " region=" << region << " measuredRows=" << regionMap.size() << " referenceRows=" << (referenceRegions[region][region_end] - referenceRegions[region][region_begin]) << " mappedRows=" << mappedRows << " unmappedRows=" << (regionMap.size() - mappedRows) << " medianDeltaR=" << medianDeltaR << std::endl;
0554 }
0555 return mappedReferenceByMeasured;
0556 }
0557
0558 std::vector<int> build_nearest_row_mapping(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, int branchShift)
0559 {
0560
0561
0562
0563 std::vector<int> mappedReferenceByMeasured(topology.first.size(), -1);
0564 for (size_t measuredRow = 0; measuredRow < topology.first.size(); ++measuredRow)
0565 {
0566 const int nearestReferenceRow = nearest_reference_row_for_measured_row(topology, measuredRow);
0567 const int lockedReferenceRow = nearestReferenceRow + branchShift;
0568 if (lockedReferenceRow < 0 || lockedReferenceRow >= static_cast<int>(topology.second.size()))
0569 {
0570 continue;
0571 }
0572 mappedReferenceByMeasured[measuredRow] = lockedReferenceRow;
0573 }
0574 return mappedReferenceByMeasured;
0575 }
0576
0577 void repair_edge_row_branch_mapping(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, int branchShift, std::vector<int> &mappedReferenceByMeasuredRow)
0578 {
0579
0580
0581
0582 const auto measuredRegions = build_topology_regions(topology.first);
0583 const auto referenceRegions = build_topology_regions(topology.second);
0584 const size_t regionCount = std::min(measuredRegions.size(), referenceRegions.size());
0585 const size_t edgeRows = static_cast<size_t>(std::max(0, topology_edge_row_branch_repair_edge_rows));
0586 if (edgeRows == 0)
0587 {
0588 return;
0589 }
0590
0591 for (size_t region = 0; region < regionCount; ++region)
0592 {
0593 const auto &measuredRegion = measuredRegions[region];
0594 const auto &referenceRegion = referenceRegions[region];
0595 const size_t measuredCount = measuredRegion[region_end] > measuredRegion[region_begin] ? measuredRegion[region_end] - measuredRegion[region_begin] : 0;
0596 const size_t referenceCount = referenceRegion[region_end] > referenceRegion[region_begin] ? referenceRegion[region_end] - referenceRegion[region_begin] : 0;
0597 if (measuredCount == 0 || referenceCount == 0)
0598 {
0599 continue;
0600 }
0601
0602 std::vector<int> rowBranchLabels(measuredCount, 999999);
0603 for (size_t measuredOrdinal = 0; measuredOrdinal < measuredCount; ++measuredOrdinal)
0604 {
0605
0606
0607 const size_t measuredRow = measuredRegion[region_begin] + measuredOrdinal;
0608 if (measuredRow >= mappedReferenceByMeasuredRow.size() || mappedReferenceByMeasuredRow[measuredRow] < 0)
0609 {
0610 continue;
0611 }
0612 const int localReferenceRow = mappedReferenceByMeasuredRow[measuredRow] - static_cast<int>(referenceRegion[region_begin]);
0613 const int expectedReferenceRow = expected_reference_row_ordinal(measuredOrdinal, measuredCount, referenceCount, 0);
0614 if (localReferenceRow < 0 || localReferenceRow >= static_cast<int>(referenceCount) || expectedReferenceRow < 0 || expectedReferenceRow >= static_cast<int>(referenceCount))
0615 {
0616 continue;
0617 }
0618 rowBranchLabels[measuredOrdinal] = localReferenceRow - expectedReferenceRow;
0619 }
0620
0621 std::vector<int> branchVotes;
0622 branchVotes.reserve(measuredCount);
0623 for (size_t measuredOrdinal = 0; measuredOrdinal < measuredCount; ++measuredOrdinal)
0624 {
0625
0626
0627 const bool edgeRow = measuredOrdinal < edgeRows || measuredOrdinal + edgeRows >= measuredCount;
0628 if (!edgeRow && rowBranchLabels[measuredOrdinal] != 999999)
0629 {
0630 branchVotes.push_back(rowBranchLabels[measuredOrdinal]);
0631 }
0632 }
0633 if (branchVotes.empty())
0634 {
0635 for (int label : rowBranchLabels)
0636 {
0637 if (label != 999999)
0638 {
0639 branchVotes.push_back(label);
0640 }
0641 }
0642 }
0643 if (branchVotes.empty())
0644 {
0645 continue;
0646 }
0647
0648 int majorityBranch = branchVotes.front();
0649 int majorityCount = 0;
0650 for (int candidate : branchVotes)
0651 {
0652 int count = 0;
0653 for (int label : branchVotes)
0654 {
0655 if (label == candidate)
0656 {
0657 ++count;
0658 }
0659 }
0660 if (count > majorityCount || (count == majorityCount && std::abs(candidate - branchShift) < std::abs(majorityBranch - branchShift)))
0661 {
0662 majorityBranch = candidate;
0663 majorityCount = count;
0664 }
0665 }
0666
0667 size_t repairedRows = 0;
0668 for (size_t measuredOrdinal = 0; measuredOrdinal < measuredCount; ++measuredOrdinal)
0669 {
0670 const bool edgeRow = measuredOrdinal < edgeRows || measuredOrdinal + edgeRows >= measuredCount;
0671 if (!edgeRow || rowBranchLabels[measuredOrdinal] == 999999 || rowBranchLabels[measuredOrdinal] == majorityBranch)
0672 {
0673 continue;
0674 }
0675 const int expectedReferenceRow = expected_reference_row_ordinal(measuredOrdinal, measuredCount, referenceCount, 0);
0676 const int repairedLocalReferenceRow = expectedReferenceRow + majorityBranch;
0677 if (repairedLocalReferenceRow < 0 || repairedLocalReferenceRow >= static_cast<int>(referenceCount))
0678 {
0679 continue;
0680 }
0681 const size_t measuredRow = measuredRegion[region_begin] + measuredOrdinal;
0682 mappedReferenceByMeasuredRow[measuredRow] = static_cast<int>(referenceRegion[region_begin]) + repairedLocalReferenceRow;
0683 ++repairedRows;
0684 }
0685
0686 std::cout << "Row branch repair testedBranch=" << branchShift << " region=" << region << " orderBranchMajority=" << majorityBranch << " relativeToTested=" << (majorityBranch - branchShift) << " votes=" << branchVotes.size() << " repairedEdgeRows=" << repairedRows << std::endl;
0687 }
0688 }
0689
0690 std::vector<std::array<double, 6>> build_ordered_row_branch_seed(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference, const std::pair<std::vector<int>, std::vector<int>> &allowedCandidates, int rowShift, int phiShift)
0691 {
0692
0693
0694
0695
0696 (void) rowShift;
0697 std::vector<std::array<double, 6>> seed;
0698 for (size_t measuredRow = 0; measuredRow < topology.first.size(); ++measuredRow)
0699 {
0700 const auto &measuredIndices = topology.first[measuredRow].second;
0701 int referenceRow = -1;
0702 for (size_t measuredIndex : measuredIndices)
0703 {
0704 if (measuredIndex < allowedCandidates.second.size() && allowedCandidates.second[measuredIndex] >= 0)
0705 {
0706 referenceRow = allowedCandidates.second[measuredIndex];
0707 break;
0708 }
0709 }
0710 if (referenceRow < 0 || referenceRow >= static_cast<int>(topology.second.size()))
0711 {
0712 continue;
0713 }
0714
0715 const auto &referenceIndices = topology.second[static_cast<size_t>(referenceRow)].second;
0716 if (measuredIndices.empty() || referenceIndices.empty())
0717 {
0718 continue;
0719 }
0720
0721 for (size_t measuredPosition = 0; measuredPosition < measuredIndices.size(); ++measuredPosition)
0722 {
0723 const size_t measuredIndex = measuredIndices[measuredPosition];
0724 size_t referencePosition = 0;
0725 if (measuredIndices.size() == 1 || referenceIndices.size() == 1)
0726 {
0727 referencePosition = 0;
0728 }
0729 else
0730 {
0731
0732
0733 const double rowFraction = static_cast<double>(measuredPosition) / static_cast<double>(measuredIndices.size() - 1);
0734 referencePosition = static_cast<size_t>(std::lround(rowFraction * static_cast<double>(referenceIndices.size() - 1)));
0735 referencePosition = std::min(referencePosition, referenceIndices.size() - 1);
0736 }
0737
0738 int shiftedReferencePosition = static_cast<int>(referencePosition) + phiShift;
0739 shiftedReferencePosition %= static_cast<int>(referenceIndices.size());
0740 if (shiftedReferencePosition < 0)
0741 {
0742 shiftedReferencePosition += static_cast<int>(referenceIndices.size());
0743 }
0744 const size_t referenceIndex = referenceIndices[static_cast<size_t>(shiftedReferencePosition)];
0745 if (!(measuredIndex < allowedCandidates.second.size() && referenceIndex < allowedCandidates.first.size() && allowedCandidates.second[measuredIndex] >= 0 && allowedCandidates.second[measuredIndex] == allowedCandidates.first[referenceIndex]))
0746 {
0747 continue;
0748 }
0749
0750 const auto candidate = make_assignment_candidate(measuredIndex, referenceIndex, measured, reference);
0751 if (assignment_candidate_within_gates(candidate))
0752 {
0753 seed.push_back(candidate);
0754 }
0755 }
0756 }
0757 return keep_unique_assignment_candidates(std::move(seed), measured.size(), reference.size());
0758 }
0759
0760 std::pair<std::vector<int>, std::vector<int>> build_full_r_branch_locked_candidate_mask(const std::pair<std::vector<std::pair<double, std::vector<size_t>>>, std::vector<std::pair<double, std::vector<size_t>>>> &topology, size_t measuredCount, size_t referenceCount, int branchShift, bool applyEdgeRepair)
0761 {
0762
0763
0764
0765 std::pair<std::vector<int>, std::vector<int>> allowed;
0766 allowed.first.assign(referenceCount, -1);
0767 allowed.second.assign(measuredCount, -1);
0768 for (size_t referenceRow = 0; referenceRow < topology.second.size(); ++referenceRow)
0769 {
0770 for (size_t referenceIndex : topology.second[referenceRow].second)
0771 {
0772 if (referenceIndex < referenceCount)
0773 {
0774 allowed.first[referenceIndex] = static_cast<int>(referenceRow);
0775 }
0776 }
0777 }
0778 std::vector<int> mappedReferenceByMeasuredRow;
0779 if (use_topology_row_mapping_dp)
0780 {
0781 mappedReferenceByMeasuredRow = build_monotone_row_mapping(topology, branchShift);
0782 }
0783 else
0784 {
0785 mappedReferenceByMeasuredRow = build_nearest_row_mapping(topology, branchShift);
0786 if (applyEdgeRepair && use_topology_edge_row_branch_repair)
0787 {
0788 repair_edge_row_branch_mapping(topology, branchShift, mappedReferenceByMeasuredRow);
0789 }
0790 }
0791
0792 for (size_t measuredRow = 0; measuredRow < topology.first.size(); ++measuredRow)
0793 {
0794 const int lockedReferenceRow = measuredRow < mappedReferenceByMeasuredRow.size() ? mappedReferenceByMeasuredRow[measuredRow] : -1;
0795 if (lockedReferenceRow < 0 || lockedReferenceRow >= static_cast<int>(topology.second.size()))
0796 {
0797 continue;
0798 }
0799 for (size_t measuredIndex : topology.first[measuredRow].second)
0800 {
0801 if (measuredIndex >= measuredCount)
0802 {
0803 continue;
0804 }
0805 allowed.second[measuredIndex] = lockedReferenceRow;
0806 }
0807 }
0808 return allowed;
0809 }
0810
0811
0812
0813
0814
0815 std::vector<int> solve_hungarian_assignment(const std::vector<std::vector<double>> &cost)
0816 {
0817
0818
0819 const int n = static_cast<int>(cost.size());
0820 if (n == 0)
0821 {
0822 return {};
0823 }
0824
0825 const double inf = std::numeric_limits<double>::infinity();
0826 std::vector<double> u(n + 1, 0.0);
0827 std::vector<double> v(n + 1, 0.0);
0828 std::vector<int> p(n + 1, 0);
0829 std::vector<int> way(n + 1, 0);
0830
0831
0832 for (int i = 1; i <= n; i++)
0833 {
0834 p[0] = i;
0835 int j0 = 0;
0836 std::vector<double> minv(n + 1, inf);
0837 std::vector<char> used(n + 1, false);
0838
0839
0840 do
0841 {
0842 used[j0] = true;
0843 const int i0 = p[j0];
0844 double delta = inf;
0845 int j1 = 0;
0846
0847 for (int j = 1; j <= n; j++)
0848 {
0849 if (used[j])
0850 {
0851 continue;
0852 }
0853
0854 const double cur = cost[i0 - 1][j - 1] - u[i0] - v[j];
0855 if (cur < minv[j])
0856 {
0857 minv[j] = cur;
0858 way[j] = j0;
0859 }
0860 if (minv[j] < delta)
0861 {
0862 delta = minv[j];
0863 j1 = j;
0864 }
0865 }
0866
0867 if (!std::isfinite(delta))
0868 {
0869 break;
0870 }
0871
0872
0873 for (int j = 0; j <= n; j++)
0874 {
0875 if (used[j])
0876 {
0877 u[p[j]] += delta;
0878 v[j] -= delta;
0879 }
0880 else
0881 {
0882 minv[j] -= delta;
0883 }
0884 }
0885 j0 = j1;
0886 } while (p[j0] != 0);
0887
0888
0889 do
0890 {
0891 const int j1 = way[j0];
0892 p[j0] = p[j1];
0893 j0 = j1;
0894 } while (j0 != 0);
0895 }
0896
0897 std::vector<int> row_to_col(n, -1);
0898 for (int j = 1; j <= n; j++)
0899 {
0900 if (p[j] > 0)
0901 {
0902 row_to_col[p[j] - 1] = j - 1;
0903 }
0904 }
0905
0906 return row_to_col;
0907 }
0908
0909 double global_assignment_candidate_cost(const std::array<double, 3> &measured, const std::array<double, 3> &reference, const GlobalFieldFitter &field, std::array<double, 6> &candidate)
0910 {
0911
0912
0913
0914
0915
0916
0917
0918
0919 candidate[candidate_delta_r] = measured[stripe_r] - reference[stripe_r];
0920 candidate[candidate_delta_phi] = wrap_delta_phi(measured[stripe_phi] - reference[stripe_phi]);
0921 candidate[candidate_r_delta_phi] = measured[stripe_r] * candidate[candidate_delta_phi];
0922 candidate[candidate_distance] = std::sqrt(candidate[candidate_delta_r] * candidate[candidate_delta_r] + candidate[candidate_r_delta_phi] * candidate[candidate_r_delta_phi]);
0923
0924 if (std::abs(candidate[candidate_delta_r]) > global_assignment_max_abs_delta_r_cm)
0925 {
0926 return std::numeric_limits<double>::infinity();
0927 }
0928 if (std::abs(candidate[candidate_delta_phi]) > global_assignment_max_abs_delta_phi_rad)
0929 {
0930 return std::numeric_limits<double>::infinity();
0931 }
0932 if (!field.is_valid())
0933 {
0934 return std::numeric_limits<double>::infinity();
0935 }
0936
0937
0938
0939 const double fieldDeltaR = field.evaluate_delta_r(measured[stripe_phi], measured[stripe_r]);
0940 const double fieldDeltaPhi = field.evaluate_delta_phi(measured[stripe_phi], measured[stripe_r]);
0941 const double deltaRSigma = std::max(global_assignment_field_sigma_delta_r_cm, 1e-6);
0942 const double deltaPhiSigma = std::max(global_assignment_field_sigma_delta_phi_rad, 1e-6);
0943 const double deltaRResidual = (candidate[candidate_delta_r] - fieldDeltaR) / deltaRSigma;
0944 const double deltaPhiResidual = wrap_delta_phi(candidate[candidate_delta_phi] - fieldDeltaPhi) / deltaPhiSigma;
0945 return global_assignment_field_delta_r_weight * deltaRResidual * deltaRResidual + global_assignment_field_delta_phi_weight * deltaPhiResidual * deltaPhiResidual;
0946 }
0947
0948 std::vector<std::array<double, 6>> solve_global_assignment_once(const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference, const GlobalFieldFitter &field, const std::pair<std::vector<int>, std::vector<int>> &allowedCandidates)
0949 {
0950
0951
0952
0953
0954 if (measured.empty() || reference.empty())
0955 {
0956 return {};
0957 }
0958
0959 int rowCount = 0;
0960 for (int row : allowedCandidates.first)
0961 {
0962 rowCount = std::max(rowCount, row + 1);
0963 }
0964 std::vector<std::vector<size_t>> measuredByRow(static_cast<size_t>(rowCount));
0965 std::vector<std::vector<size_t>> referenceByRow(static_cast<size_t>(rowCount));
0966 for (size_t i = 0; i < allowedCandidates.second.size(); ++i)
0967 {
0968 const int row = allowedCandidates.second[i];
0969 if (row >= 0 && row < rowCount)
0970 {
0971 measuredByRow[static_cast<size_t>(row)].push_back(i);
0972 }
0973 }
0974 for (size_t j = 0; j < allowedCandidates.first.size(); ++j)
0975 {
0976 const int row = allowedCandidates.first[j];
0977 if (row >= 0 && row < rowCount)
0978 {
0979 referenceByRow[static_cast<size_t>(row)].push_back(j);
0980 }
0981 }
0982
0983 const double largeCost = 1.0e9;
0984 std::vector<std::array<double, 6>> matches;
0985 matches.reserve(std::min(measured.size(), reference.size()));
0986 for (int row = 0; row < rowCount; ++row)
0987 {
0988 const auto &measuredIndices = measuredByRow[static_cast<size_t>(row)];
0989 const auto &referenceIndices = referenceByRow[static_cast<size_t>(row)];
0990 const size_t nMeasured = measuredIndices.size();
0991 const size_t nReference = referenceIndices.size();
0992 const size_t n = nMeasured + nReference;
0993 if (nMeasured == 0 || nReference == 0)
0994 {
0995 continue;
0996 }
0997
0998 std::vector<std::vector<double>> cost(n, std::vector<double>(n, largeCost));
0999 for (size_t localMeasured = 0; localMeasured < nMeasured; ++localMeasured)
1000 {
1001 const size_t measuredIndex = measuredIndices[localMeasured];
1002 for (size_t localReference = 0; localReference < nReference; ++localReference)
1003 {
1004 const size_t referenceIndex = referenceIndices[localReference];
1005 std::array<double, 6> candidate{};
1006 candidate[candidate_measured_idx] = static_cast<double>(measuredIndex);
1007 candidate[candidate_reference_idx] = static_cast<double>(referenceIndex);
1008 const double candidateCost = global_assignment_candidate_cost(measured[measuredIndex], reference[referenceIndex], field, candidate);
1009 if (std::isfinite(candidateCost))
1010 {
1011 cost[localMeasured][localReference] = candidateCost;
1012 }
1013 }
1014 cost[localMeasured][nReference + localMeasured] = global_assignment_unmatched_cost;
1015 }
1016 for (size_t localReference = 0; localReference < nReference; ++localReference)
1017 {
1018 cost[nMeasured + localReference][localReference] = 0.0;
1019 }
1020 for (size_t i = nMeasured; i < n; ++i)
1021 {
1022 for (size_t j = nReference; j < n; ++j)
1023 {
1024 cost[i][j] = 0.0;
1025 }
1026 }
1027
1028 const auto assignment = solve_hungarian_assignment(cost);
1029 for (size_t localMeasured = 0; localMeasured < nMeasured && localMeasured < assignment.size(); ++localMeasured)
1030 {
1031 const int localReference = assignment[localMeasured];
1032 if (localReference < 0 || localReference >= static_cast<int>(nReference))
1033 {
1034 continue;
1035 }
1036 const size_t measuredIndex = measuredIndices[localMeasured];
1037 const size_t referenceIndex = referenceIndices[static_cast<size_t>(localReference)];
1038 std::array<double, 6> candidate{};
1039 candidate[candidate_measured_idx] = static_cast<double>(measuredIndex);
1040 candidate[candidate_reference_idx] = static_cast<double>(referenceIndex);
1041 const double candidateCost = global_assignment_candidate_cost(measured[measuredIndex], reference[referenceIndex], field, candidate);
1042 if (std::isfinite(candidateCost) && candidateCost <= global_assignment_max_final_cost)
1043 {
1044 matches.push_back(candidate);
1045 }
1046 }
1047 }
1048 return matches;
1049 }
1050
1051
1052
1053
1054
1055 std::vector<std::array<double, 6>> make_observations(const std::vector<std::array<double, 6>> &candidates, const std::vector<std::array<double, 3>> &measured, bool useFixedSigma)
1056 {
1057
1058
1059
1060 std::vector<std::array<double, 6>> observations;
1061 observations.reserve(candidates.size());
1062 for (const auto &candidate : candidates)
1063 {
1064 std::array<double, 6> observation;
1065 observation[observation_phi] = measured[static_cast<size_t>(candidate[candidate_measured_idx])][stripe_phi];
1066 observation[observation_r] = measured[static_cast<size_t>(candidate[candidate_measured_idx])][stripe_r];
1067 observation[observation_delta_r] = candidate[candidate_delta_r];
1068 observation[observation_r_delta_phi] = candidate[candidate_r_delta_phi];
1069 if (useFixedSigma)
1070 {
1071
1072
1073 observation[observation_sigma_delta_r] = fallback_sigma_prior_delta_r_cm;
1074 observation[observation_sigma_r_delta_phi] = fallback_sigma_prior_r_delta_phi_cm;
1075 }
1076 observations.push_back(observation);
1077 }
1078 return observations;
1079 }
1080
1081 GlobalFieldFitter *fit_assignment_field(const std::vector<std::array<double, 6>> &candidates, const std::vector<std::array<double, 3>> &measured, const std::vector<double> &controlRPositions)
1082 {
1083
1084
1085 if (static_cast<int>(candidates.size()) < min_ml_seed_neighbors)
1086 {
1087 return nullptr;
1088 }
1089
1090 auto observations = make_observations(candidates, measured, true);
1091 auto *field = new GlobalFieldFitter(observations, controlRPositions);
1092 if (!field->fit())
1093 {
1094 delete field;
1095 return nullptr;
1096 }
1097
1098 return field;
1099 }
1100
1101 GlobalFieldFitter *fit_robust_field(std::vector<std::array<double, 6>> &observations, const std::vector<double> &controlRPositions)
1102 {
1103
1104
1105 if (static_cast<int>(observations.size()) < min_ml_seed_neighbors)
1106 {
1107 return nullptr;
1108 }
1109 std::vector<double> deltaRValues;
1110 std::vector<double> rDeltaPhiValues;
1111 deltaRValues.reserve(observations.size());
1112 rDeltaPhiValues.reserve(observations.size());
1113 for (const auto &observation : observations)
1114 {
1115 deltaRValues.push_back(observation[observation_delta_r]);
1116 rDeltaPhiValues.push_back(observation[observation_r_delta_phi]);
1117 }
1118
1119
1120 const double sigmaDeltaR = std::clamp(robust_mad_sigma(deltaRValues, fallback_sigma_prior_delta_r_cm), min_sigma_prior_delta_r_cm, max_sigma_prior_delta_r_cm);
1121 const double sigmaRDeltaPhi = std::clamp(robust_mad_sigma(rDeltaPhiValues, fallback_sigma_prior_r_delta_phi_cm), min_sigma_prior_r_delta_phi_cm, max_sigma_prior_r_delta_phi_cm);
1122 for (auto &observation : observations)
1123 {
1124 observation[observation_sigma_delta_r] = sigmaDeltaR;
1125 observation[observation_sigma_r_delta_phi] = sigmaRDeltaPhi;
1126 }
1127 auto *field = new GlobalFieldFitter(observations, controlRPositions);
1128 if (!field->fit())
1129 {
1130 delete field;
1131 return nullptr;
1132 }
1133 return field;
1134 }
1135
1136 GlobalFieldFitter *fit_robust_assignment_field(const std::vector<std::array<double, 6>> &candidates, const std::vector<std::array<double, 3>> &measured, const std::vector<double> &controlRPositions)
1137 {
1138
1139
1140 auto observations = make_observations(candidates, measured, false);
1141 return fit_robust_field(observations, controlRPositions);
1142 }
1143
1144 double assignment_change_fraction(const std::vector<std::array<double, 6>> &previous, const std::vector<std::array<double, 6>> ¤t, size_t measuredCount)
1145 {
1146
1147
1148 if (measuredCount == 0)
1149 {
1150 return 0.0;
1151 }
1152 std::vector<int> previousReference(measuredCount, -1);
1153 std::vector<int> currentReference(measuredCount, -1);
1154 for (const auto &candidate : previous)
1155 {
1156 if (candidate[candidate_measured_idx] < measuredCount)
1157 {
1158 previousReference[static_cast<size_t>(candidate[candidate_measured_idx])] = static_cast<int>(candidate[candidate_reference_idx]);
1159 }
1160 }
1161 for (const auto &candidate : current)
1162 {
1163 if (candidate[candidate_measured_idx] < measuredCount)
1164 {
1165 currentReference[static_cast<size_t>(candidate[candidate_measured_idx])] = static_cast<int>(candidate[candidate_reference_idx]);
1166 }
1167 }
1168 size_t changed = 0;
1169 for (size_t measuredIndex = 0; measuredIndex < measuredCount; ++measuredIndex)
1170 {
1171 if (previousReference[measuredIndex] != currentReference[measuredIndex])
1172 {
1173 ++changed;
1174 }
1175 }
1176 return static_cast<double>(changed) / static_cast<double>(measuredCount);
1177 }
1178
1179 std::vector<std::array<double, 6>> clean_assignment_candidates(const std::vector<std::array<double, 6>> &candidates, const std::vector<std::array<double, 3>> &measured, size_t &rejected)
1180 {
1181
1182
1183 rejected = 0;
1184 std::vector<bool> keep(candidates.size(), true);
1185 for (size_t a = 0; a < candidates.size(); ++a)
1186 {
1187 std::vector<double> neighborDeltaR;
1188 std::vector<double> neighborRDeltaPhi;
1189 for (size_t b = 0; b < candidates.size(); ++b)
1190 {
1191 if (a == b)
1192 {
1193 continue;
1194 }
1195 const auto &measuredA = measured[candidates[a][candidate_measured_idx]];
1196 const auto &measuredB = measured[candidates[b][candidate_measured_idx]];
1197 if (std::abs(measuredA[stripe_r] - measuredB[stripe_r]) < seed_clean_radial_window_cm && std::abs(wrap_delta_phi(measuredA[stripe_phi] - measuredB[stripe_phi])) < seed_clean_phi_window_rad)
1198 {
1199 neighborDeltaR.push_back(candidates[b][candidate_delta_r]);
1200 neighborRDeltaPhi.push_back(candidates[b][candidate_r_delta_phi]);
1201 }
1202 }
1203
1204 if (static_cast<int>(neighborDeltaR.size()) < seed_clean_min_neighbors)
1205 {
1206 keep[a] = false;
1207 ++rejected;
1208 continue;
1209 }
1210 const double deltaRResidual = candidates[a][candidate_delta_r] - median_value(neighborDeltaR);
1211 const double rDeltaPhiResidual = candidates[a][candidate_r_delta_phi] - median_value(neighborRDeltaPhi);
1212 if (std::hypot(deltaRResidual, rDeltaPhiResidual) > seed_clean_max_local_residual_cm)
1213 {
1214 keep[a] = false;
1215 ++rejected;
1216 }
1217 }
1218
1219 std::vector<std::array<double, 6>> cleaned;
1220 cleaned.reserve(candidates.size() - rejected);
1221 for (size_t i = 0; i < candidates.size(); ++i)
1222 {
1223 if (keep[i])
1224 {
1225 cleaned.push_back(candidates[i]);
1226 }
1227 }
1228 return cleaned;
1229 }
1230
1231 std::tuple<std::vector<std::array<double, 6>>, double, double, double, double, size_t, int> run_branch_probe(const std::vector<std::array<double, 6>> &seed, const std::pair<std::vector<int>, std::vector<int>> &allowedCandidates, const std::vector<std::array<double, 3>> &measured, const std::vector<std::array<double, 3>> &reference, const std::vector<double> &controlRPositions)
1232 {
1233
1234
1235 std::tuple<std::vector<std::array<double, 6>>, double, double, double, double, size_t, int> result{std::vector<std::array<double, 6>>{}, std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), 1.0, 1.0, 0, 0};
1236 std::get<hypothesis_candidates>(result) = seed;
1237 if (std::get<hypothesis_candidates>(result).empty())
1238 {
1239 return result;
1240 }
1241
1242 const int maximumIterations = std::max(1, global_assignment_max_iterations);
1243 for (int iteration = 0; iteration < maximumIterations; ++iteration)
1244 {
1245 GlobalFieldFitter *field = fit_assignment_field(std::get<hypothesis_candidates>(result), measured, controlRPositions);
1246 if (!field)
1247 {
1248 std::get<hypothesis_candidates>(result).clear();
1249 return result;
1250 }
1251 auto assigned = solve_global_assignment_once(measured, reference, *field, allowedCandidates);
1252 delete field;
1253
1254
1255 const double changedFraction = assignment_change_fraction(std::get<hypothesis_candidates>(result), assigned, measured.size());
1256 std::get<hypothesis_iterations>(result) = iteration + 1;
1257 if (assigned.empty())
1258 {
1259 std::get<hypothesis_candidates>(result).clear();
1260 return result;
1261 }
1262 std::get<hypothesis_candidates>(result) = std::move(assigned);
1263 if (changedFraction < global_assignment_convergence_fraction)
1264 {
1265 break;
1266 }
1267 }
1268
1269
1270
1271 const size_t preCleaningCount = std::get<hypothesis_candidates>(result).size();
1272 std::get<hypothesis_candidates>(result) = clean_assignment_candidates(std::get<hypothesis_candidates>(result), measured, std::get<hypothesis_rejected_by_cleaning>(result));
1273 if (std::get<hypothesis_candidates>(result).empty())
1274 {
1275 return result;
1276 }
1277 GlobalFieldFitter *finalField = fit_robust_assignment_field(std::get<hypothesis_candidates>(result), measured, controlRPositions);
1278 if (!finalField)
1279 {
1280 std::get<hypothesis_candidates>(result).clear();
1281 return result;
1282 }
1283
1284
1285 std::vector<double> residuals;
1286 residuals.reserve(std::get<hypothesis_candidates>(result).size());
1287 for (const auto &candidate : std::get<hypothesis_candidates>(result))
1288 {
1289 const auto &stripe = measured[static_cast<size_t>(candidate[candidate_measured_idx])];
1290 const double deltaRResidual = candidate[candidate_delta_r] - finalField->evaluate_delta_r(stripe[stripe_phi], stripe[stripe_r]);
1291 const double rDeltaPhiResidual = candidate[candidate_r_delta_phi] - finalField->evaluate_r_delta_phi(stripe[stripe_phi], stripe[stripe_r]);
1292 residuals.push_back(std::hypot(deltaRResidual, rDeltaPhiResidual));
1293 }
1294 std::get<hypothesis_median_residual>(result) = median_value(residuals);
1295 std::get<hypothesis_unmatched_fraction>(result) = 1.0 - static_cast<double>(std::get<hypothesis_candidates>(result).size()) / static_cast<double>(std::max<size_t>(1, measured.size()));
1296 std::get<hypothesis_cleaning_fraction>(result) = static_cast<double>(std::get<hypothesis_rejected_by_cleaning>(result)) / static_cast<double>(std::max<size_t>(1, preCleaningCount));
1297 std::get<hypothesis_score>(result) = matching_hypothesis_residual_weight * std::get<hypothesis_median_residual>(result) + matching_hypothesis_unmatched_weight * std::get<hypothesis_unmatched_fraction>(result) + matching_hypothesis_cleaning_weight * std::get<hypothesis_cleaning_fraction>(result);
1298 delete finalField;
1299 return result;
1300 }
1301
1302 bool StripeComparison::build_global_pattern_matches()
1303 {
1304
1305
1306
1307 auto &measured = m_measuredFiltered;
1308 auto &reference = m_referenceFiltered;
1309 m_seedMatches.clear();
1310
1311 const auto topology = build_topology_initialization(measured, reference);
1312 const int minShift = matching_branch_probe_min_shift;
1313 const int maxShift = matching_branch_probe_max_shift;
1314 const std::string rowMappingMode = use_topology_row_mapping_dp ? "monotone_dp" : (use_topology_edge_row_branch_repair ? "nearest_r_selected_edge_repair" : "nearest_r");
1315 std::cout << "Branch probe setup for side " << m_sideName << ": measuredRows=" << topology.first.size() << " referenceRows=" << topology.second.size() << " branchProbes=" << (maxShift - minShift + 1) << " rowMapping=" << rowMappingMode << std::endl;
1316
1317 std::tuple<std::vector<std::array<double, 6>>, double, double, double, double, size_t, int> bestViableResult{std::vector<std::array<double, 6>>{}, std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), 1.0, 1.0, 0, 0};
1318 std::tuple<std::vector<std::array<double, 6>>, double, double, double, double, size_t, int> bestFallbackResult{std::vector<std::array<double, 6>>{}, std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity(), 1.0, 1.0, 0, 0};
1319 std::string bestViableName;
1320 std::string bestFallbackName;
1321 int bestViableBranch = 0;
1322 int bestFallbackBranch = 0;
1323
1324
1325
1326
1327 for (int branchShift = minShift; branchShift <= maxShift; ++branchShift)
1328 {
1329 const auto branchMask = build_full_r_branch_locked_candidate_mask(topology, measured.size(), reference.size(), branchShift, false);
1330 const auto branchSeed = build_ordered_row_branch_seed(topology, measured, reference, branchMask, branchShift, 0);
1331 const auto result = run_branch_probe(branchSeed, branchMask, measured, reference, m_controlRPositions);
1332 const std::string name = "branch_probe_row_" + std::to_string(branchShift);
1333 const double matchedFraction = static_cast<double>(std::get<hypothesis_candidates>(result).size()) / static_cast<double>(std::max<size_t>(1, measured.size()));
1334 const double cleaningSurvival = 1.0 - std::get<hypothesis_cleaning_fraction>(result);
1335 const bool viable = matchedFraction >= matching_branch_probe_min_matched_fraction && cleaningSurvival >= matching_branch_probe_min_cleaning_survival;
1336
1337 std::cout << "Branch probe " << name << " for side " << m_sideName << ": seed=" << branchSeed.size() << " final=" << std::get<hypothesis_candidates>(result).size() << " iterations=" << std::get<hypothesis_iterations>(result) << " cleaningRejected=" << std::get<hypothesis_rejected_by_cleaning>(result) << " residualMedian=" << std::get<hypothesis_median_residual>(result) << " unmatchedFraction=" << std::get<hypothesis_unmatched_fraction>(result) << " cleaningFraction=" << std::get<hypothesis_cleaning_fraction>(result) << " totalScore=" << std::get<hypothesis_score>(result) << std::endl;
1338 std::cout << "Branch summary for side " << m_sideName << ": branch=" << branchShift << " bestScore=" << std::get<hypothesis_score>(result) << " bestResidual=" << std::get<hypothesis_median_residual>(result) << " matchedFraction=" << matchedFraction << " cleaningSurvival=" << cleaningSurvival << " viable=" << viable << std::endl;
1339
1340 if (std::get<hypothesis_score>(result) < std::get<hypothesis_score>(bestFallbackResult))
1341 {
1342 bestFallbackResult = result;
1343 bestFallbackName = name;
1344 bestFallbackBranch = branchShift;
1345 }
1346 if (viable && std::get<hypothesis_score>(result) < std::get<hypothesis_score>(bestViableResult))
1347 {
1348 bestViableResult = result;
1349 bestViableName = name;
1350 bestViableBranch = branchShift;
1351 }
1352 }
1353
1354 const bool hasViable = !std::get<hypothesis_candidates>(bestViableResult).empty();
1355 auto selectedResult = hasViable ? bestViableResult : bestFallbackResult;
1356 std::string selectedName = hasViable ? bestViableName : bestFallbackName;
1357 const int selectedBranchShift = hasViable ? bestViableBranch : bestFallbackBranch;
1358 std::pair<std::vector<int>, std::vector<int>> selectedMask = build_full_r_branch_locked_candidate_mask(topology, measured.size(), reference.size(), selectedBranchShift, false);
1359 if (std::get<hypothesis_candidates>(selectedResult).empty())
1360 {
1361 std::cout << "ComputeStripeComparisonMaps: Selected branch probe produced no usable assignment" << std::endl;
1362 return false;
1363 }
1364
1365 if (!use_topology_row_mapping_dp && use_topology_edge_row_branch_repair)
1366 {
1367
1368
1369 const auto repairedMask = build_full_r_branch_locked_candidate_mask(topology, measured.size(), reference.size(), selectedBranchShift, true);
1370 const auto repairedSeed = build_ordered_row_branch_seed(topology, measured, reference, repairedMask, selectedBranchShift, 0);
1371 auto repairedResult = run_branch_probe(repairedSeed, repairedMask, measured, reference, m_controlRPositions);
1372 std::cout << "Selected branch edge repair for side " << m_sideName << ": branch=" << selectedBranchShift << " seed=" << repairedSeed.size() << " final=" << std::get<hypothesis_candidates>(repairedResult).size() << " iterations=" << std::get<hypothesis_iterations>(repairedResult) << " cleaningRejected=" << std::get<hypothesis_rejected_by_cleaning>(repairedResult) << " residualMedian=" << std::get<hypothesis_median_residual>(repairedResult) << " unmatchedFraction=" << std::get<hypothesis_unmatched_fraction>(repairedResult) << " cleaningFraction=" << std::get<hypothesis_cleaning_fraction>(repairedResult) << " totalScore=" << std::get<hypothesis_score>(repairedResult) << std::endl;
1373 if (!std::get<hypothesis_candidates>(repairedResult).empty() && std::get<hypothesis_score>(repairedResult) <= std::get<hypothesis_score>(selectedResult))
1374 {
1375 selectedResult = std::move(repairedResult);
1376 selectedMask = repairedMask;
1377 selectedName += "_edge_repaired";
1378 }
1379 else if (std::get<hypothesis_candidates>(repairedResult).empty())
1380 {
1381 std::cout << "Selected branch edge repair produced no usable assignment; keeping unrepaired selected branch" << std::endl;
1382 }
1383 else
1384 {
1385 std::cout << "Selected branch edge repair worsened score from " << std::get<hypothesis_score>(selectedResult) << " to " << std::get<hypothesis_score>(repairedResult) << "; keeping unrepaired selected branch" << std::endl;
1386 }
1387 }
1388
1389 const auto &selectedCandidates = std::get<hypothesis_candidates>(selectedResult);
1390 const double selectedScore = std::get<hypothesis_score>(selectedResult);
1391 m_selectedBranchShift = selectedBranchShift;
1392 m_selectedReferenceRowByIndex = selectedMask.first;
1393 m_selectedAllowedReferenceRowByMeasured = selectedMask.second;
1394 std::vector<bool> measuredMatched(measured.size(), false);
1395 for (const auto &candidate : selectedCandidates)
1396 {
1397
1398
1399
1400 measuredMatched[static_cast<size_t>(candidate[candidate_measured_idx])] = true;
1401 std::array<double, 9> seed{};
1402 seed[seed_measured_idx] = candidate[candidate_measured_idx];
1403 seed[seed_reference_idx] = candidate[candidate_reference_idx];
1404 seed[seed_phi] = measured[static_cast<size_t>(candidate[candidate_measured_idx])][stripe_phi];
1405 seed[seed_r] = measured[static_cast<size_t>(candidate[candidate_measured_idx])][stripe_r];
1406 seed[seed_reference_phi] = reference[static_cast<size_t>(candidate[candidate_reference_idx])][stripe_phi];
1407 seed[seed_reference_r] = reference[static_cast<size_t>(candidate[candidate_reference_idx])][stripe_r];
1408 seed[seed_delta_r] = seed[seed_r] - seed[seed_reference_r];
1409 seed[seed_delta_phi] = wrap_delta_phi(seed[seed_phi] - seed[seed_reference_phi]);
1410 seed[seed_r_delta_phi] = seed[seed_r] * seed[seed_delta_phi];
1411 m_seedMatches.push_back(seed);
1412 }
1413
1414 const size_t unmatched = static_cast<size_t>(std::count(measuredMatched.begin(), measuredMatched.end(), false));
1415 std::cout << "Selected matching hypothesis " << selectedName << " for side " << m_sideName << ": score=" << selectedScore << " matches=" << m_seedMatches.size() << " unmatchedMeasured=" << unmatched << "/" << measured.size() << " selectedBranch=" << selectedBranchShift << std::endl;
1416 return true;
1417 }
1418
1419 bool StripeComparison::build_global_field_estimates()
1420 {
1421
1422
1423 m_globalObservations.clear();
1424 m_globalObservations.reserve(m_seedMatches.size());
1425 for (const auto &seed : m_seedMatches)
1426 {
1427 std::array<double, 6> observation;
1428 observation[observation_phi] = seed[seed_phi];
1429 observation[observation_r] = seed[seed_r];
1430 observation[observation_delta_r] = seed[seed_delta_r];
1431 observation[observation_r_delta_phi] = seed[seed_r_delta_phi];
1432 m_globalObservations.push_back(observation);
1433 }
1434 delete m_globalFieldFitter;
1435 m_globalFieldFitter = fit_robust_field(m_globalObservations, m_controlRPositions);
1436 std::cout << "Global field estimates: built " << m_globalObservations.size() << " observations" << std::endl;
1437 return m_globalFieldFitter && m_globalFieldFitter->is_valid();
1438 }
1439
1440 void StripeComparison::fill_candidate_diagnostics(size_t diagnosticMeasuredIndex, size_t diagnosticReferenceIndex, double &referencePhi, double &referenceR, double &deltaR, double &deltaPhi, double &rDeltaPhi, double &cost, double &residualDeltaR, double &residualDeltaPhi, double &residualRDeltaPhi, int &withinGate)
1441 {
1442
1443
1444
1445 std::array<double, 6> candidate = make_assignment_candidate(diagnosticMeasuredIndex, diagnosticReferenceIndex, m_measuredFiltered, m_referenceFiltered);
1446 withinGate = assignment_candidate_within_gates(candidate) ? 1 : 0;
1447 cost = global_assignment_candidate_cost(m_measuredFiltered[diagnosticMeasuredIndex], m_referenceFiltered[diagnosticReferenceIndex], *m_globalFieldFitter, candidate);
1448 referencePhi = m_referenceFiltered[diagnosticReferenceIndex][stripe_phi];
1449 referenceR = m_referenceFiltered[diagnosticReferenceIndex][stripe_r];
1450 deltaR = candidate[candidate_delta_r];
1451 deltaPhi = candidate[candidate_delta_phi];
1452 rDeltaPhi = candidate[candidate_r_delta_phi];
1453 residualDeltaR = m_globalFieldFitter->evaluate_delta_r(m_measuredFiltered[diagnosticMeasuredIndex][stripe_phi], m_measuredFiltered[diagnosticMeasuredIndex][stripe_r]) - candidate[candidate_delta_r];
1454 residualDeltaPhi = wrap_delta_phi(m_globalFieldFitter->evaluate_delta_phi(m_measuredFiltered[diagnosticMeasuredIndex][stripe_phi], m_measuredFiltered[diagnosticMeasuredIndex][stripe_r]) - candidate[candidate_delta_phi]);
1455 residualRDeltaPhi = m_globalFieldFitter->evaluate_r_delta_phi(m_measuredFiltered[diagnosticMeasuredIndex][stripe_phi], m_measuredFiltered[diagnosticMeasuredIndex][stripe_r]) - candidate[candidate_r_delta_phi];
1456 }
1457
1458 void StripeComparison::write_output_maps()
1459 {
1460
1461
1462 if (!m_globalFieldFitter || !m_globalFieldFitter->is_valid())
1463 {
1464 return;
1465 }
1466
1467 TGraph prematchMeasured;
1468
1469
1470 prematchMeasured.SetName((std::string("gr_prematch_measured_") + m_sideName).c_str());
1471 prematchMeasured.SetTitle((std::string("Pre-match measured stripes after isolation and topology edge-row filtering - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1472 prematchMeasured.SetMarkerStyle(20);
1473 for (size_t i = 0; i < m_measuredFiltered.size(); i++)
1474 {
1475 prematchMeasured.SetPoint(static_cast<int>(i), m_measuredFiltered[i][stripe_phi], m_measuredFiltered[i][stripe_r]);
1476 }
1477 safe_write_object(&prematchMeasured);
1478
1479 TGraph prematchReference;
1480 prematchReference.SetName((std::string("gr_prematch_reference_") + m_sideName).c_str());
1481 prematchReference.SetTitle((std::string("Pre-match reference stripes after isolation and topology edge-row filtering - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1482 prematchReference.SetMarkerStyle(24);
1483 for (size_t i = 0; i < m_referenceFiltered.size(); i++)
1484 {
1485 prematchReference.SetPoint(static_cast<int>(i), m_referenceFiltered[i][stripe_phi], m_referenceFiltered[i][stripe_r]);
1486 }
1487 safe_write_object(&prematchReference);
1488
1489 TGraph matchedMeasured;
1490
1491
1492 matchedMeasured.SetName((std::string("gr_hungarian_matched_measured_") + m_sideName).c_str());
1493 matchedMeasured.SetTitle((std::string("Hungarian matched measured stripes in original coordinates - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1494 matchedMeasured.SetMarkerStyle(20);
1495 for (size_t i = 0; i < m_seedMatches.size(); i++)
1496 {
1497 matchedMeasured.SetPoint(static_cast<int>(i), m_seedMatches[i][seed_phi], m_seedMatches[i][seed_r]);
1498 }
1499 safe_write_object(&matchedMeasured);
1500
1501 TGraph matchedReference;
1502 matchedReference.SetName((std::string("gr_hungarian_matched_reference_") + m_sideName).c_str());
1503 matchedReference.SetTitle((std::string("Hungarian matched reference stripes - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1504 matchedReference.SetMarkerStyle(24);
1505 for (size_t i = 0; i < m_seedMatches.size(); i++)
1506 {
1507 matchedReference.SetPoint(static_cast<int>(i), m_seedMatches[i][seed_reference_phi], m_seedMatches[i][seed_reference_r]);
1508 }
1509 safe_write_object(&matchedReference);
1510
1511 const auto referenceRows = build_topology_rows(m_referenceFiltered);
1512
1513
1514 std::vector<int> referenceRowByIndex(m_referenceFiltered.size(), -1);
1515 std::vector<int> referencePositionByIndex(m_referenceFiltered.size(), -1);
1516 for (size_t row = 0; row < referenceRows.size(); ++row)
1517 {
1518 for (size_t position = 0; position < referenceRows[row].second.size(); ++position)
1519 {
1520 const size_t referenceIndex = referenceRows[row].second[position];
1521 if (referenceIndex < m_referenceFiltered.size())
1522 {
1523 referenceRowByIndex[referenceIndex] = static_cast<int>(row);
1524 referencePositionByIndex[referenceIndex] = static_cast<int>(position);
1525 }
1526 }
1527 }
1528
1529 const auto measuredRows = build_topology_rows(m_measuredFiltered);
1530 std::vector<int> measuredRowByIndex(m_measuredFiltered.size(), -1);
1531 for (size_t row = 0; row < measuredRows.size(); ++row)
1532 {
1533 for (size_t index : measuredRows[row].second)
1534 {
1535 if (index < m_measuredFiltered.size())
1536 {
1537 measuredRowByIndex[index] = static_cast<int>(row);
1538 }
1539 }
1540 }
1541
1542 std::vector<int> matchedReferenceStripesByRow(referenceRows.size(), 0);
1543
1544
1545 std::vector<int> matchedMeasuredStripesByRow(measuredRows.size(), 0);
1546 std::vector<int> allowedMeasuredStripesByReferenceRow(referenceRows.size(), 0);
1547 std::vector<int> allowedMeasuredRowsByReferenceRow(referenceRows.size(), 0);
1548 std::vector<std::vector<char>> allowedMeasuredRowSeen(referenceRows.size(), std::vector<char>(measuredRows.size(), false));
1549 for (const auto &match : m_seedMatches)
1550 {
1551 if (static_cast<size_t>(match[seed_reference_idx]) < referenceRowByIndex.size() && referenceRowByIndex[static_cast<size_t>(match[seed_reference_idx])] >= 0)
1552 {
1553 ++matchedReferenceStripesByRow[static_cast<size_t>(referenceRowByIndex[static_cast<size_t>(match[seed_reference_idx])])];
1554 }
1555 if (static_cast<size_t>(match[seed_measured_idx]) < measuredRowByIndex.size() && measuredRowByIndex[static_cast<size_t>(match[seed_measured_idx])] >= 0)
1556 {
1557 ++matchedMeasuredStripesByRow[static_cast<size_t>(measuredRowByIndex[static_cast<size_t>(match[seed_measured_idx])])];
1558 }
1559 }
1560 for (size_t measuredIndex = 0; measuredIndex < m_selectedAllowedReferenceRowByMeasured.size(); ++measuredIndex)
1561 {
1562 const int referenceRowForMeasured = m_selectedAllowedReferenceRowByMeasured[measuredIndex];
1563 if (referenceRowForMeasured < 0 || referenceRowForMeasured >= static_cast<int>(referenceRows.size()))
1564 {
1565 continue;
1566 }
1567 ++allowedMeasuredStripesByReferenceRow[static_cast<size_t>(referenceRowForMeasured)];
1568 if (measuredIndex < measuredRowByIndex.size() && measuredRowByIndex[measuredIndex] >= 0)
1569 {
1570 allowedMeasuredRowSeen[static_cast<size_t>(referenceRowForMeasured)][static_cast<size_t>(measuredRowByIndex[measuredIndex])] = true;
1571 }
1572 }
1573 for (size_t referenceRowIndex = 0; referenceRowIndex < allowedMeasuredRowSeen.size(); ++referenceRowIndex)
1574 {
1575 allowedMeasuredRowsByReferenceRow[referenceRowIndex] = static_cast<int>(std::count(allowedMeasuredRowSeen[referenceRowIndex].begin(), allowedMeasuredRowSeen[referenceRowIndex].end(), true));
1576 }
1577
1578 TTree rowDiagnostics((std::string("t_row_assignment_diagnostics_") + m_sideName).c_str(), (std::string("Row assignment diagnostics - ") + m_sideName).c_str());
1579
1580
1581 int diagnosticKind = 0;
1582 int rowIndex = -1;
1583 double rowCenterR = 0.0;
1584 int rowSize = 0;
1585 int matchedStripeCount = 0;
1586 int allowedStripeCount = 0;
1587 int allowedRowCount = 0;
1588 int selectedBranch = 0;
1589 rowDiagnostics.Branch("kind", &diagnosticKind);
1590 rowDiagnostics.Branch("row_index", &rowIndex);
1591 rowDiagnostics.Branch("row_center_r", &rowCenterR);
1592 rowDiagnostics.Branch("row_size", &rowSize);
1593 rowDiagnostics.Branch("matched_stripe_count", &matchedStripeCount);
1594 rowDiagnostics.Branch("allowed_stripe_count", &allowedStripeCount);
1595 rowDiagnostics.Branch("allowed_row_count", &allowedRowCount);
1596 rowDiagnostics.Branch("selected_branch", &selectedBranch);
1597 selectedBranch = m_selectedBranchShift;
1598 diagnosticKind = 0;
1599 for (size_t row = 0; row < referenceRows.size(); ++row)
1600 {
1601 rowIndex = static_cast<int>(row);
1602 rowCenterR = referenceRows[row].first;
1603 rowSize = static_cast<int>(referenceRows[row].second.size());
1604 matchedStripeCount = matchedReferenceStripesByRow[row];
1605 allowedStripeCount = allowedMeasuredStripesByReferenceRow[row];
1606 allowedRowCount = allowedMeasuredRowsByReferenceRow[row];
1607 rowDiagnostics.Fill();
1608 }
1609 diagnosticKind = 1;
1610 for (size_t row = 0; row < measuredRows.size(); ++row)
1611 {
1612 rowIndex = static_cast<int>(row);
1613 rowCenterR = measuredRows[row].first;
1614 rowSize = static_cast<int>(measuredRows[row].second.size());
1615 matchedStripeCount = matchedMeasuredStripesByRow[row];
1616 allowedStripeCount = 0;
1617 allowedRowCount = 0;
1618 rowDiagnostics.Fill();
1619 }
1620 safe_write_object(&rowDiagnostics);
1621
1622 TTree rowMapDiagnostics((std::string("t_row_mapping_diagnostics_") + m_sideName).c_str(), (std::string("Selected row mapping diagnostics - ") + m_sideName).c_str());
1623
1624
1625
1626 int measuredRowIndex = -1;
1627 double measuredRowCenterR = 0.0;
1628 int measuredRowSize = 0;
1629 int allowedReferenceRow = -1;
1630 double allowedReferenceRowCenterR = 0.0;
1631 int allowedReferenceRowSize = 0;
1632 int measuredRowMatchedStripes = 0;
1633 int allowedReferenceRowMatchedStripes = 0;
1634 rowMapDiagnostics.Branch("measured_row_index", &measuredRowIndex);
1635 rowMapDiagnostics.Branch("measured_row_center_r", &measuredRowCenterR);
1636 rowMapDiagnostics.Branch("measured_row_size", &measuredRowSize);
1637 rowMapDiagnostics.Branch("allowed_reference_row", &allowedReferenceRow);
1638 rowMapDiagnostics.Branch("allowed_reference_row_center_r", &allowedReferenceRowCenterR);
1639 rowMapDiagnostics.Branch("allowed_reference_row_size", &allowedReferenceRowSize);
1640 rowMapDiagnostics.Branch("measured_row_matched_stripes", &measuredRowMatchedStripes);
1641 rowMapDiagnostics.Branch("allowed_reference_row_matched_stripes", &allowedReferenceRowMatchedStripes);
1642 rowMapDiagnostics.Branch("selected_branch", &selectedBranch);
1643 for (size_t measuredRow = 0; measuredRow < measuredRows.size(); ++measuredRow)
1644 {
1645 measuredRowIndex = static_cast<int>(measuredRow);
1646 measuredRowCenterR = measuredRows[measuredRow].first;
1647 measuredRowSize = static_cast<int>(measuredRows[measuredRow].second.size());
1648 measuredRowMatchedStripes = matchedMeasuredStripesByRow[measuredRow];
1649 allowedReferenceRow = -1;
1650 allowedReferenceRowCenterR = std::numeric_limits<double>::quiet_NaN();
1651 allowedReferenceRowSize = 0;
1652 allowedReferenceRowMatchedStripes = 0;
1653 for (size_t measuredIndex : measuredRows[measuredRow].second)
1654 {
1655 if (measuredIndex < m_selectedAllowedReferenceRowByMeasured.size() && m_selectedAllowedReferenceRowByMeasured[measuredIndex] >= 0)
1656 {
1657 allowedReferenceRow = m_selectedAllowedReferenceRowByMeasured[measuredIndex];
1658 break;
1659 }
1660 }
1661 if (allowedReferenceRow >= 0 && allowedReferenceRow < static_cast<int>(referenceRows.size()))
1662 {
1663 allowedReferenceRowCenterR = referenceRows[static_cast<size_t>(allowedReferenceRow)].first;
1664 allowedReferenceRowSize = static_cast<int>(referenceRows[static_cast<size_t>(allowedReferenceRow)].second.size());
1665 allowedReferenceRowMatchedStripes = matchedReferenceStripesByRow[static_cast<size_t>(allowedReferenceRow)];
1666 }
1667 rowMapDiagnostics.Fill();
1668 }
1669 safe_write_object(&rowMapDiagnostics);
1670
1671 TTree phiDiagnostics((std::string("t_phi_assignment_diagnostics_") + m_sideName).c_str(), (std::string("Phi assignment diagnostics - ") + m_sideName).c_str());
1672
1673
1674
1675 int matchIndex = 0;
1676 int measuredIndex = -1;
1677 int chosenReferenceIndex = -1;
1678 int referenceRow = -1;
1679 int referencePhiIndex = -1;
1680 int referenceRowSize = 0;
1681 double measuredPhi = 0.0;
1682 double measuredR = 0.0;
1683 double fittedDeltaR = 0.0;
1684 double fittedDeltaPhi = 0.0;
1685 double fittedRDeltaPhi = 0.0;
1686 double chosenReferencePhi = 0.0;
1687 double chosenReferenceR = 0.0;
1688 double chosenDeltaR = 0.0;
1689 double chosenDeltaPhi = 0.0;
1690 double chosenRDeltaPhi = 0.0;
1691 double chosenCost = 0.0;
1692 double chosenResidualDeltaR = 0.0;
1693 double chosenResidualDeltaPhi = 0.0;
1694 double chosenResidualRDeltaPhi = 0.0;
1695 int chosenWithinGate = 0;
1696 int prevReferenceIndex = -1;
1697 int prevWithinGate = 0;
1698 double prevReferencePhi = 0.0;
1699 double prevReferenceR = 0.0;
1700 double prevDeltaR = 0.0;
1701 double prevDeltaPhi = 0.0;
1702 double prevRDeltaPhi = 0.0;
1703 double prevCost = 0.0;
1704 double prevResidualDeltaR = 0.0;
1705 double prevResidualDeltaPhi = 0.0;
1706 double prevResidualRDeltaPhi = 0.0;
1707 int nextReferenceIndex = -1;
1708 int nextWithinGate = 0;
1709 double nextReferencePhi = 0.0;
1710 double nextReferenceR = 0.0;
1711 double nextDeltaR = 0.0;
1712 double nextDeltaPhi = 0.0;
1713 double nextRDeltaPhi = 0.0;
1714 double nextCost = 0.0;
1715 double nextResidualDeltaR = 0.0;
1716 double nextResidualDeltaPhi = 0.0;
1717 double nextResidualRDeltaPhi = 0.0;
1718
1719 phiDiagnostics.Branch("match_index", &matchIndex);
1720 phiDiagnostics.Branch("measured_index", &measuredIndex);
1721 phiDiagnostics.Branch("chosen_reference_index", &chosenReferenceIndex);
1722 phiDiagnostics.Branch("reference_row", &referenceRow);
1723 phiDiagnostics.Branch("reference_phi_index", &referencePhiIndex);
1724 phiDiagnostics.Branch("reference_row_size", &referenceRowSize);
1725 phiDiagnostics.Branch("measured_phi", &measuredPhi);
1726 phiDiagnostics.Branch("measured_r", &measuredR);
1727 phiDiagnostics.Branch("fitted_delta_r", &fittedDeltaR);
1728 phiDiagnostics.Branch("fitted_delta_phi", &fittedDeltaPhi);
1729 phiDiagnostics.Branch("fitted_r_delta_phi", &fittedRDeltaPhi);
1730 phiDiagnostics.Branch("chosen_reference_phi", &chosenReferencePhi);
1731 phiDiagnostics.Branch("chosen_reference_r", &chosenReferenceR);
1732 phiDiagnostics.Branch("chosen_delta_r", &chosenDeltaR);
1733 phiDiagnostics.Branch("chosen_delta_phi", &chosenDeltaPhi);
1734 phiDiagnostics.Branch("chosen_r_delta_phi", &chosenRDeltaPhi);
1735 phiDiagnostics.Branch("chosen_cost", &chosenCost);
1736 phiDiagnostics.Branch("chosen_residual_delta_r", &chosenResidualDeltaR);
1737 phiDiagnostics.Branch("chosen_residual_delta_phi", &chosenResidualDeltaPhi);
1738 phiDiagnostics.Branch("chosen_residual_r_delta_phi", &chosenResidualRDeltaPhi);
1739 phiDiagnostics.Branch("chosen_within_gate", &chosenWithinGate);
1740 phiDiagnostics.Branch("prev_reference_index", &prevReferenceIndex);
1741 phiDiagnostics.Branch("prev_within_gate", &prevWithinGate);
1742 phiDiagnostics.Branch("prev_reference_phi", &prevReferencePhi);
1743 phiDiagnostics.Branch("prev_reference_r", &prevReferenceR);
1744 phiDiagnostics.Branch("prev_delta_r", &prevDeltaR);
1745 phiDiagnostics.Branch("prev_delta_phi", &prevDeltaPhi);
1746 phiDiagnostics.Branch("prev_r_delta_phi", &prevRDeltaPhi);
1747 phiDiagnostics.Branch("prev_cost", &prevCost);
1748 phiDiagnostics.Branch("prev_residual_delta_r", &prevResidualDeltaR);
1749 phiDiagnostics.Branch("prev_residual_delta_phi", &prevResidualDeltaPhi);
1750 phiDiagnostics.Branch("prev_residual_r_delta_phi", &prevResidualRDeltaPhi);
1751 phiDiagnostics.Branch("next_reference_index", &nextReferenceIndex);
1752 phiDiagnostics.Branch("next_within_gate", &nextWithinGate);
1753 phiDiagnostics.Branch("next_reference_phi", &nextReferencePhi);
1754 phiDiagnostics.Branch("next_reference_r", &nextReferenceR);
1755 phiDiagnostics.Branch("next_delta_r", &nextDeltaR);
1756 phiDiagnostics.Branch("next_delta_phi", &nextDeltaPhi);
1757 phiDiagnostics.Branch("next_r_delta_phi", &nextRDeltaPhi);
1758 phiDiagnostics.Branch("next_cost", &nextCost);
1759 phiDiagnostics.Branch("next_residual_delta_r", &nextResidualDeltaR);
1760 phiDiagnostics.Branch("next_residual_delta_phi", &nextResidualDeltaPhi);
1761 phiDiagnostics.Branch("next_residual_r_delta_phi", &nextResidualRDeltaPhi);
1762
1763 for (size_t i = 0; i < m_seedMatches.size(); ++i)
1764 {
1765 const auto &match = m_seedMatches[i];
1766 matchIndex = static_cast<int>(i);
1767 measuredIndex = static_cast<int>(static_cast<size_t>(match[seed_measured_idx]));
1768 chosenReferenceIndex = static_cast<int>(static_cast<size_t>(match[seed_reference_idx]));
1769 measuredPhi = match[seed_phi];
1770 measuredR = match[seed_r];
1771 fittedDeltaR = m_globalFieldFitter->evaluate_delta_r(match[seed_phi], match[seed_r]);
1772 fittedDeltaPhi = m_globalFieldFitter->evaluate_delta_phi(match[seed_phi], match[seed_r]);
1773 fittedRDeltaPhi = m_globalFieldFitter->evaluate_r_delta_phi(match[seed_phi], match[seed_r]);
1774 referenceRow = static_cast<size_t>(match[seed_reference_idx]) < referenceRowByIndex.size() ? referenceRowByIndex[static_cast<size_t>(match[seed_reference_idx])] : -1;
1775 referencePhiIndex = static_cast<size_t>(match[seed_reference_idx]) < referencePositionByIndex.size() ? referencePositionByIndex[static_cast<size_t>(match[seed_reference_idx])] : -1;
1776 referenceRowSize = referenceRow >= 0 ? static_cast<int>(referenceRows[static_cast<size_t>(referenceRow)].second.size()) : 0;
1777 fill_candidate_diagnostics(static_cast<size_t>(match[seed_measured_idx]), static_cast<size_t>(match[seed_reference_idx]), chosenReferencePhi, chosenReferenceR, chosenDeltaR, chosenDeltaPhi, chosenRDeltaPhi, chosenCost, chosenResidualDeltaR, chosenResidualDeltaPhi, chosenResidualRDeltaPhi, chosenWithinGate);
1778
1779 prevReferenceIndex = -1;
1780 prevWithinGate = 0;
1781 prevReferencePhi = prevReferenceR = prevDeltaR = prevDeltaPhi = prevRDeltaPhi = prevCost = prevResidualDeltaR = prevResidualDeltaPhi = prevResidualRDeltaPhi = std::numeric_limits<double>::quiet_NaN();
1782 nextReferenceIndex = -1;
1783 nextWithinGate = 0;
1784 nextReferencePhi = nextReferenceR = nextDeltaR = nextDeltaPhi = nextRDeltaPhi = nextCost = nextResidualDeltaR = nextResidualDeltaPhi = nextResidualRDeltaPhi = std::numeric_limits<double>::quiet_NaN();
1785 if (referenceRow >= 0 && referenceRowSize > 1 && referencePhiIndex >= 0)
1786 {
1787
1788 const auto &rowIndices = referenceRows[static_cast<size_t>(referenceRow)].second;
1789 prevReferenceIndex = static_cast<int>(rowIndices[static_cast<size_t>((referencePhiIndex + referenceRowSize - 1) % referenceRowSize)]);
1790 nextReferenceIndex = static_cast<int>(rowIndices[static_cast<size_t>((referencePhiIndex + 1) % referenceRowSize)]);
1791 fill_candidate_diagnostics(static_cast<size_t>(match[seed_measured_idx]), static_cast<size_t>(prevReferenceIndex), prevReferencePhi, prevReferenceR, prevDeltaR, prevDeltaPhi, prevRDeltaPhi, prevCost, prevResidualDeltaR, prevResidualDeltaPhi, prevResidualRDeltaPhi, prevWithinGate);
1792 fill_candidate_diagnostics(static_cast<size_t>(match[seed_measured_idx]), static_cast<size_t>(nextReferenceIndex), nextReferencePhi, nextReferenceR, nextDeltaR, nextDeltaPhi, nextRDeltaPhi, nextCost, nextResidualDeltaR, nextResidualDeltaPhi, nextResidualRDeltaPhi, nextWithinGate);
1793 }
1794 phiDiagnostics.Fill();
1795 }
1796 safe_write_object(&phiDiagnostics);
1797
1798
1799
1800 TGraph controlPoints;
1801 controlPoints.SetName((std::string("gr_global_field_control_points_") + m_sideName).c_str());
1802 controlPoints.SetTitle((std::string("Global field control points - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1803 controlPoints.SetMarkerStyle(24);
1804
1805 TGraph activeControlPoints;
1806 activeControlPoints.SetName((std::string("gr_global_field_active_control_points_") + m_sideName).c_str());
1807 activeControlPoints.SetTitle((std::string("Active global field control points - ") + m_sideName + ";#phi [rad];R [cm]").c_str());
1808 activeControlPoints.SetMarkerStyle(20);
1809
1810 TGraph2D controlSupport;
1811 controlSupport.SetName((std::string("gr2_global_field_control_support_") + m_sideName).c_str());
1812 controlSupport.SetTitle((std::string("Global field control support - ") + m_sideName + ";#phi [rad];R [cm];Support").c_str());
1813
1814 TGraph2D controlDeltaR;
1815 controlDeltaR.SetName((std::string("gr2_global_field_control_delta_r_") + m_sideName).c_str());
1816 controlDeltaR.SetTitle((std::string("Global field control #DeltaR - ") + m_sideName + ";#phi [rad];R [cm];#DeltaR [cm]").c_str());
1817
1818 TGraph2D controlRDeltaPhi;
1819 controlRDeltaPhi.SetName((std::string("gr2_global_field_control_r_delta_phi_") + m_sideName).c_str());
1820 controlRDeltaPhi.SetTitle((std::string("Global field control R#Delta#phi - ") + m_sideName + ";#phi [rad];R [cm];R#Delta#phi [cm]").c_str());
1821
1822 int activeIndex = 0;
1823 const auto fittedControlPoints = m_globalFieldFitter->control_points();
1824 for (size_t i = 0; i < fittedControlPoints.size(); ++i)
1825 {
1826 const auto &point = fittedControlPoints[i];
1827 controlPoints.SetPoint(static_cast<int>(i), point[control_point_phi], point[control_point_r]);
1828 controlSupport.SetPoint(static_cast<int>(i), point[control_point_phi], point[control_point_r], point[control_point_support]);
1829 controlDeltaR.SetPoint(static_cast<int>(i), point[control_point_phi], point[control_point_r], point[control_point_delta_r]);
1830 controlRDeltaPhi.SetPoint(static_cast<int>(i), point[control_point_phi], point[control_point_r], point[control_point_r_delta_phi]);
1831 if (point[control_point_active])
1832 {
1833 activeControlPoints.SetPoint(activeIndex++, point[control_point_phi], point[control_point_r]);
1834 }
1835 }
1836
1837 safe_write_object(&controlPoints);
1838 safe_write_object(&activeControlPoints);
1839 safe_write_object(&controlSupport);
1840 safe_write_object(&controlDeltaR);
1841 safe_write_object(&controlRDeltaPhi);
1842
1843
1844
1845 const std::string suffix = "_" + m_sideName;
1846 TH1D stripeResidualDeltaR(("h_stripe_residual_delta_r" + suffix).c_str(), ("Stripe fit residual #DeltaR" + suffix + ";fitted-observed #DeltaR [cm];Counts").c_str(), 160, -3.0, 3.0);
1847 TH1D stripeResidualDeltaPhi(("h_stripe_residual_delta_phi" + suffix).c_str(), ("Stripe fit residual #Delta#phi" + suffix + ";fitted-observed #Delta#phi [rad];Counts").c_str(), 160, -0.05, 0.05);
1848 TH1D stripeResidualRDeltaPhi(("h_stripe_residual_r_delta_phi" + suffix).c_str(), ("Stripe fit residual R#Delta#phi" + suffix + ";fitted-observed R#Delta#phi [cm];Counts").c_str(), 160, -3.0, 3.0);
1849 TH1D stripeResidualMagnitude(("h_stripe_residual_magnitude" + suffix).c_str(), ("Stripe fit residual magnitude" + suffix + ";sqrt((#delta#DeltaR)^{2}+(#deltaR#Delta#phi)^{2}) [cm];Counts").c_str(), 120, 0.0, 3.0);
1850 TH2D stripeResidualMagnitudeVsPosition(("h_stripe_residual_magnitude_vs_position" + suffix).c_str(), ("Stripe fit residual magnitude vs position" + suffix + ";measured #phi [rad];R [cm];Residual magnitude [cm]").c_str(), 80, 0.0, 2.0 * M_PI, 52, 20.0, 80.0);
1851 TH2D stripeResidualDeltaRVsR(("h_stripe_residual_delta_r_vs_r" + suffix).c_str(), ("Stripe #DeltaR residual vs R" + suffix + ";R [cm];fitted-observed #DeltaR [cm]").c_str(), 52, 20.0, 80.0, 160, -3.0, 3.0);
1852 TH2D stripeResidualDeltaPhiVsR(("h_stripe_residual_delta_phi_vs_r" + suffix).c_str(), ("Stripe #Delta#phi residual vs R" + suffix + ";R [cm];fitted-observed #Delta#phi [rad]").c_str(), 52, 20.0, 80.0, 160, -0.05, 0.05);
1853
1854 stripeResidualDeltaR.SetDirectory(nullptr);
1855 stripeResidualDeltaPhi.SetDirectory(nullptr);
1856 stripeResidualRDeltaPhi.SetDirectory(nullptr);
1857 stripeResidualMagnitude.SetDirectory(nullptr);
1858 stripeResidualMagnitudeVsPosition.SetDirectory(nullptr);
1859 stripeResidualDeltaRVsR.SetDirectory(nullptr);
1860 stripeResidualDeltaPhiVsR.SetDirectory(nullptr);
1861 stripeResidualMagnitudeVsPosition.SetStats(false);
1862 stripeResidualDeltaRVsR.SetStats(false);
1863 stripeResidualDeltaPhiVsR.SetStats(false);
1864
1865 for (const auto &match : m_seedMatches)
1866 {
1867 const double residualDeltaR = m_globalFieldFitter->evaluate_delta_r(match[seed_phi], match[seed_r]) - match[seed_delta_r];
1868 const double residualDeltaPhi = m_globalFieldFitter->evaluate_delta_phi(match[seed_phi], match[seed_r]) - match[seed_delta_phi];
1869 const double residualRDeltaPhi = m_globalFieldFitter->evaluate_r_delta_phi(match[seed_phi], match[seed_r]) - match[seed_r_delta_phi];
1870 const double magnitude = std::hypot(residualDeltaR, residualRDeltaPhi);
1871 stripeResidualDeltaR.Fill(residualDeltaR);
1872 stripeResidualDeltaPhi.Fill(residualDeltaPhi);
1873 stripeResidualRDeltaPhi.Fill(residualRDeltaPhi);
1874 stripeResidualMagnitude.Fill(magnitude);
1875 stripeResidualMagnitudeVsPosition.Fill(match[seed_phi], match[seed_r], magnitude);
1876 stripeResidualDeltaRVsR.Fill(match[seed_r], residualDeltaR);
1877 stripeResidualDeltaPhiVsR.Fill(match[seed_r], residualDeltaPhi);
1878 }
1879
1880 safe_write_object(&stripeResidualDeltaR);
1881 safe_write_object(&stripeResidualDeltaPhi);
1882 safe_write_object(&stripeResidualRDeltaPhi);
1883 safe_write_object(&stripeResidualMagnitude);
1884 safe_write_object(&stripeResidualMagnitudeVsPosition);
1885 safe_write_object(&stripeResidualDeltaRVsR);
1886 safe_write_object(&stripeResidualDeltaPhiVsR);
1887
1888
1889 auto *deltaR = new TH2D((std::string("hIntDistortionR_") + m_sideName).c_str(), (std::string("#DeltaR map, global field estimate - ") + m_sideName + ";#phi [rad];R [cm];#DeltaR [cm]").c_str(), 80, 0.0, 2.0 * M_PI, 52, 20.0, 80.0);
1890 auto *deltaPhi = new TH2D((std::string("hIntDistortionP_") + m_sideName).c_str(), (std::string("#Delta#phi map, global field estimate - ") + m_sideName + ";#phi [rad];R [cm];#Delta#phi [rad]").c_str(), 80, 0.0, 2.0 * M_PI, 52, 20.0, 80.0);
1891 auto *deltaZ = new TH2D((std::string("hIntDistortionZ_") + m_sideName).c_str(), (std::string("#DeltaZ map, global field estimate - ") + m_sideName + ";#phi [rad];R [cm];#DeltaZ [cm]").c_str(), 80, 0.0, 2.0 * M_PI, 52, 20.0, 80.0);
1892
1893 for (int phiBin = 1; phiBin <= deltaR->GetNbinsX(); ++phiBin)
1894 {
1895 const double phi = deltaR->GetXaxis()->GetBinCenter(phiBin);
1896 for (int rBin = 1; rBin <= deltaR->GetNbinsY(); ++rBin)
1897 {
1898 const double r = deltaR->GetYaxis()->GetBinCenter(rBin);
1899 if (r < fit_r_min_cm || r > fit_r_max_cm)
1900 {
1901 continue;
1902 }
1903 deltaR->SetBinContent(phiBin, rBin, m_globalFieldFitter->evaluate_delta_r(phi, r));
1904 deltaPhi->SetBinContent(phiBin, rBin, m_globalFieldFitter->evaluate_r_delta_phi(phi, r) / r);
1905 }
1906 }
1907
1908 deltaR->SetStats(false);
1909 deltaPhi->SetStats(false);
1910 deltaZ->SetStats(false);
1911 safe_write_object(deltaR);
1912 safe_write_object(deltaPhi);
1913 safe_write_object(deltaZ);
1914 delete deltaR;
1915 delete deltaPhi;
1916 delete deltaZ;
1917 }
1918
1919 void StripeComparison::write_shifted_histogram(TH2 *sourceHistogram, const std::string &name, const std::string &title, double shiftSign)
1920 {
1921
1922
1923
1924 if (!sourceHistogram || !m_globalFieldFitter || !m_globalFieldFitter->is_valid())
1925 {
1926 return;
1927 }
1928
1929 const int nPhiBins = sourceHistogram->GetNbinsX();
1930 const int nRBins = sourceHistogram->GetNbinsY();
1931 auto *shifted = new TH2D(name.c_str(), title.c_str(), nPhiBins, sourceHistogram->GetXaxis()->GetXmin(), sourceHistogram->GetXaxis()->GetXmax(), nRBins, sourceHistogram->GetYaxis()->GetXmin(), sourceHistogram->GetYaxis()->GetXmax());
1932 shifted->SetDirectory(nullptr);
1933
1934 for (int phiBin = 1; phiBin <= nPhiBins; ++phiBin)
1935 {
1936 const double phi = sourceHistogram->GetXaxis()->GetBinCenter(phiBin);
1937 for (int rBin = 1; rBin <= nRBins; ++rBin)
1938 {
1939 const double content = sourceHistogram->GetBinContent(phiBin, rBin);
1940 if (content == 0.0)
1941 {
1942 continue;
1943 }
1944
1945 const double r = sourceHistogram->GetYaxis()->GetBinCenter(rBin);
1946 const double shiftedR = r + shiftSign * m_globalFieldFitter->evaluate_delta_r(phi, r);
1947 double shiftedPhi = phi + shiftSign * m_globalFieldFitter->evaluate_delta_phi(phi, r);
1948
1949
1950 const double phiMin = sourceHistogram->GetXaxis()->GetXmin();
1951 const double phiMax = sourceHistogram->GetXaxis()->GetXmax();
1952 const double phiWidth = phiMax - phiMin;
1953 while (shiftedPhi < phiMin)
1954 {
1955 shiftedPhi += phiWidth;
1956 }
1957 while (shiftedPhi >= phiMax)
1958 {
1959 shiftedPhi -= phiWidth;
1960 }
1961
1962 const int shiftedPhiBin = shifted->GetXaxis()->FindBin(shiftedPhi);
1963 const int shiftedRBin = shifted->GetYaxis()->FindBin(shiftedR);
1964 if (shiftedPhiBin < 1 || shiftedPhiBin > nPhiBins || shiftedRBin < 1 || shiftedRBin > nRBins)
1965 {
1966 continue;
1967 }
1968 shifted->SetBinContent(shiftedPhiBin, shiftedRBin, shifted->GetBinContent(shiftedPhiBin, shiftedRBin) + content);
1969 }
1970 }
1971
1972 shifted->SetStats(false);
1973 safe_write_object(shifted);
1974 delete shifted;
1975 }
1976
1977 void StripeComparison::write_corrected_measured_histogram(TH2 *measuredHistogram)
1978 {
1979
1980 write_shifted_histogram(measuredHistogram, "hPetal_measured_corrected_" + m_sideName, "Measured cluster histogram shifted by fitted distortion map - " + m_sideName + ";#phi [rad];R [cm]", -1.0);
1981 }
1982
1983 void StripeComparison::write_distorted_reference_histogram(TH2 *referenceHistogram)
1984 {
1985
1986 write_shifted_histogram(referenceHistogram, "hPetal_reference_distorted_" + m_sideName, "Reference cluster histogram shifted by fitted distortion map - " + m_sideName + ";#phi [rad];R [cm]", 1.0);
1987 }
1988
1989 void StripeComparison::clear()
1990 {
1991
1992
1993 delete m_globalFieldFitter;
1994 m_globalFieldFitter = nullptr;
1995 m_sideName.clear();
1996 m_controlRPositions.clear();
1997 m_measuredFiltered.clear();
1998 m_referenceFiltered.clear();
1999 m_seedMatches.clear();
2000 m_globalObservations.clear();
2001 m_selectedBranchShift = 0;
2002 m_selectedReferenceRowByIndex.clear();
2003 m_selectedAllowedReferenceRowByMeasured.clear();
2004 }