Project
Loading...
Searching...
No Matches
MisalignmentUtils.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <algorithm>
15#include <cmath>
16#include <fstream>
17#include <vector>
18#include <array>
19
20#include <TMatrixD.h>
21#include <nlohmann/json.hpp>
22
23#include "Framework/Logger.h"
25#include "ITS3Base/SpecsV2.h"
26
27namespace o2::its3::align
28{
29
30bool MisalignmentModel::empty() const noexcept
31{
32 return std::all_of(sensors.begin(), sensors.end(), [](const auto& sensor) { return sensor.empty(); });
33}
34
35MisalignmentModel loadMisalignmentModel(const std::string& jsonPath)
36{
38 if (jsonPath.empty()) {
39 return model;
40 }
41
42 std::ifstream f(jsonPath);
43 if (!f.is_open()) {
44 LOGP(fatal, "Cannot open misalignment JSON file: {}", jsonPath);
45 }
46
47 using json = nlohmann::json;
48 const auto data = json::parse(f);
49 for (const auto& item : data) {
50 const int id = item["id"].get<int>();
51 if (id < 0 || id >= static_cast<int>(MisalignmentModel::NSensors)) {
52 LOGP(fatal, "Misalignment sensor id {} out of range [0, {}) in {}", id, MisalignmentModel::NSensors, jsonPath);
53 }
54
55 auto& sensor = model[id];
56 if (item.contains("matrix")) {
57 auto v = item["matrix"].get<std::vector<std::vector<double>>>();
58 if (v.empty()) {
59 LOGP(fatal, "Legendre matrix for sensor {} is empty in {}", id, jsonPath);
60 }
61 TMatrixD m(v.size(), v.back().size());
62 for (std::size_t r{0}; r < v.size(); ++r) {
63 for (std::size_t c{0}; c < v[r].size(); ++c) {
64 m(r, c) = v[r][c];
65 }
66 }
68 sensor.hasLegendre = true;
69 }
70 if (item.contains("inextensional")) {
71 const auto& inex = item["inextensional"];
72 sensor.hasInextensional = true;
73 if (inex.contains("modes")) {
74 for (const auto& [key, val] : inex["modes"].items()) {
75 sensor.inextensional.modes[std::stoi(key)] = val.get<std::array<double, 4>>();
76 }
77 }
78 if (inex.contains("alpha")) {
79 sensor.inextensional.alpha = inex["alpha"].get<double>();
80 }
81 if (inex.contains("beta")) {
82 sensor.inextensional.beta = inex["beta"].get<double>();
83 }
84 }
85 }
86
87 return model;
88}
89
91{
93 if (!sensor.hasLegendre) {
94 return shift;
95 }
96
97 const double gloX = frame.x * std::cos(frame.alpha);
98 const double gloY = frame.x * std::sin(frame.alpha);
99 const double gloZ = frame.z;
100 auto [u, v] = computeUV(gloX, gloY, gloZ, frame.sensorID, constants::radii[frame.layerID]);
101 const double h = sensor.legendre(u, v);
102
103 // this is the shift due to back-projection of the track on the ideal surface
104 shift.dy = slopes.dydx * h;
105 shift.dz = slopes.dzdx * h;
106
107 if (std::abs(u) > o2::constants::math::Almost0) {
108 // account for additional tangential movement due to radial shift
109 // we have to approximate the difference in arc-length from the reference pnt on the deformed surface
110 // this is done by integrating the height function via Gauss-Legendre quadrature (from Numerical recipes 4.6 [1])
111 constexpr std::array<double, 8> x = {-0.9602898564975363, -0.7966664774136267, -0.5255324099163290, -0.1834346424956498, 0.1834346424956498, 0.5255324099163290, 0.7966664774136267, 0.9602898564975363};
112 constexpr std::array<double, 8> w = {0.1012285362903763, 0.2223810344533745, 0.3137066458778873, 0.3626837833783620, 0.3626837833783620, 0.3137066458778873, 0.2223810344533745, 0.1012285362903763};
113 const double mid = 0.5 * u;
114 const double half = 0.5 * u;
115 double integral = 0.;
116 for (int i = 0; i < 8; ++i) {
117 const double up = mid + (half * x[i]);
118 integral += w[i] * sensor.legendre(up, v);
119 }
120 integral *= half;
121 shift.dy += 0.5 * getSensorPhiWidth(frame.sensorID, constants::radii[frame.layerID]) * integral;
122 }
123
124 const double newGloY = gloY + (shift.dy * std::cos(frame.alpha));
125 const double newGloX = gloX - (shift.dy * std::sin(frame.alpha));
126 const double newGloZ = gloZ + shift.dz;
127 auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, frame.sensorID, constants::radii[frame.layerID]);
128 shift.accepted = std::abs(uNew) <= 1. && std::abs(vNew) <= 1.;
129 return shift;
130}
131
133{
134 MisalignmentShift shift;
135 if (!sensor.hasInextensional) {
136 return shift;
137 }
138
139 const double r = constants::radii[frame.layerID];
140 const double phi = std::atan2(r * std::sin(frame.alpha), r * std::cos(frame.alpha));
141 const double z = frame.z;
142 const auto& inex = sensor.inextensional;
143
144 double uz = 0., uphi = 0., ur = 0.;
145 for (const auto& [n, coeffs] : inex.modes) {
146 const double a_n = coeffs[0], b_n = coeffs[1], c_n = coeffs[2], d_n = coeffs[3];
147 const double sn = std::sin(n * phi);
148 const double cn = std::cos(n * phi);
149 const int n2 = n * n;
150
151 const double fn = (a_n * cn) + (b_n * sn);
152 const double fpn = (-n * a_n * sn) + (n * b_n * cn);
153 const double fppn = (-n2 * a_n * cn) - (n2 * b_n * sn);
154 const double gn = (c_n * cn) + (d_n * sn);
155 const double gpn = (-n * c_n * sn) + (n * d_n * cn);
156
157 uz += fn;
158 uphi += -(z / r) * fpn + gn;
159 ur += (z / r) * fppn - gpn;
160 }
161
162 uz += inex.alpha * phi;
163 uphi += -(z / r) * inex.alpha + inex.beta * phi;
164 ur += -inex.beta;
165
166 shift.dy = -uphi + (slopes.dydx * ur);
167 shift.dz = -uz + (slopes.dzdx * ur);
168 return shift;
169}
170
171} // namespace o2::its3::align
int32_t i
useful math constants
uint32_t c
Definition RawData.h:2
nlohmann::json json
StringRef key
Class for time synchronization of RawReader instances.
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
const GLfloat * m
Definition glcorearb.h:4066
const GLdouble * v
Definition glcorearb.h:832
GLdouble f
Definition glcorearb.h:310
GLboolean * data
Definition glcorearb.h:298
GLuint GLfloat * val
Definition glcorearb.h:1582
GLint GLenum GLint const GLfloat * coeffs
Definition glcorearb.h:5520
GLboolean r
Definition glcorearb.h:1233
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLuint id
Definition glcorearb.h:650
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
constexpr float Almost0
MisalignmentShift evaluateInextensionalShift(const SensorMisalignment &sensor, const MisalignmentFrame &frame, const TrackSlopes &slopes)
MisalignmentModel loadMisalignmentModel(const std::string &jsonPath)
double getSensorPhiWidth(int sensorID, double radius)
MisalignmentShift evaluateLegendreShift(const SensorMisalignment &sensor, const MisalignmentFrame &frame, const TrackSlopes &slopes)
std::pair< double, double > computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius)
constexpr std::array< double, nLayers > radii
Definition SpecsV2.h:134
static constexpr std::size_t NSensors
std::array< SensorMisalignment, NSensors > sensors
o2::math_utils::Legendre2DPolynominal legendre
InextensionalMisalignment inextensional