Skip to content

Commit a7a9f18

Browse files
committed
ITS3: harmonize study and alignment wfx for misalignment
Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent 37b4052 commit a7a9f18

File tree

10 files changed

+547
-308
lines changed

10 files changed

+547
-308
lines changed

Detectors/Upgrades/ITS3/alignment/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
o2_add_library(ITS3Align
1414
TARGETVARNAME targetName
1515
SOURCES src/AlignmentHierarchy.cxx
16+
src/AlignmentDOF.cxx
17+
src/AlignmentMath.cxx
18+
src/MisalignmentUtils.cxx
1619
src/AlignmentSensors.cxx
1720
src/AlignmentParams.cxx
1821
src/AlignmentTypes.cxx

Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,29 @@
1212
#ifndef O2_ITS3_ALIGNMENT_DOF_H
1313
#define O2_ITS3_ALIGNMENT_DOF_H
1414

15+
#include <algorithm>
1516
#include <cstdint>
1617
#include <format>
17-
#include <algorithm>
18+
#include <stdexcept>
19+
#include <string>
1820
#include <vector>
1921

22+
#include <Eigen/Dense>
23+
24+
struct DerivativeContext {
25+
int sensorID{-1};
26+
int layerID{-1};
27+
double measX{0.};
28+
double measAlpha{0.};
29+
double measZ{0.};
30+
double trkY{0.};
31+
double trkZ{0.};
32+
double snp{0.};
33+
double tgl{0.};
34+
double dydx{0.};
35+
double dzdx{0.};
36+
};
37+
2038
// Generic set of DOF
2139
class DOFSet
2240
{
@@ -30,6 +48,7 @@ class DOFSet
3048
virtual Type type() const = 0;
3149
int nDOFs() const { return static_cast<int>(mFree.size()); }
3250
virtual std::string dofName(int idx) const = 0;
51+
virtual void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const = 0;
3352
bool isFree(int idx) const { return mFree[idx]; }
3453
void setFree(int idx, bool f) { mFree[idx] = f; }
3554
void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); }
@@ -73,6 +92,7 @@ class RigidBodyDOFSet final : public DOFSet
7392
}
7493
Type type() const override { return Type::RigidBody; }
7594
std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; }
95+
void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const override;
7696
uint8_t mask() const
7797
{
7898
uint8_t m = 0;
@@ -100,6 +120,7 @@ class LegendreDOFSet final : public DOFSet
100120
int j = idx - (i * (i + 1) / 2);
101121
return std::format("L({},{})", i, j);
102122
}
123+
void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const override;
103124

104125
private:
105126
int mOrder;
@@ -146,6 +167,7 @@ class InextensionalDOFSet final : public DOFSet
146167
static constexpr const char* subNames[] = {"a", "b", "c", "d"};
147168
return std::format("{}_{}", subNames[sub], n);
148169
}
170+
void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const override;
149171

150172
private:
151173
int mMaxOrder;

Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,6 @@ namespace o2::its3::align
3333
using Matrix36 = Eigen::Matrix<double, 3, 6>;
3434
using Matrix66 = Eigen::Matrix<double, 6, 6>;
3535

36-
// return the rigid body derivatives
37-
// trk has be at in the measurment frame
38-
auto getRigidBodyDerivatives(const auto& trk)
39-
{
40-
// calculate slopes
41-
const double tgl = trk.getTgl(), snp = trk.getSnp();
42-
const double csp = 1. / sqrt(1. + (tgl * tgl));
43-
const double u = trk.getY(), v = trk.getZ();
44-
const double uP = snp * csp, vP = tgl * csp;
45-
Matrix36 der;
46-
der.setZero();
47-
// columns: Tt, Tu, Tv, Rt, Ru, Rv
48-
// (X) (Y) (Z) (RX) (RY) (RZ)
49-
der << uP, -1., 0., v, v * uP, -u * uP,
50-
vP, 0., -1., -u, v * vP, -u * vP;
51-
return der;
52-
}
53-
5436
class HierarchyConstraint
5537
{
5638
public:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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+
12+
#ifndef O2_ITS3_ALIGNMENT_MATH_H
13+
#define O2_ITS3_ALIGNMENT_MATH_H
14+
15+
#include <utility>
16+
#include <vector>
17+
18+
namespace o2::its3::align
19+
{
20+
21+
struct TrackSlopes {
22+
double dydx{0.};
23+
double dzdx{0.};
24+
};
25+
26+
std::pair<double, double> computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius);
27+
TrackSlopes computeTrackSlopes(double snp, double tgl);
28+
std::vector<double> legendrePols(int order, double x);
29+
30+
} // namespace o2::its3::align
31+
32+
#endif
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
12+
#ifndef O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H
13+
#define O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H
14+
15+
#include <array>
16+
#include <cstddef>
17+
#include <map>
18+
#include <string>
19+
#include <utility>
20+
21+
#include "ITS3Align/AlignmentMath.h"
22+
#include "MathUtils/LegendrePols.h"
23+
24+
namespace o2::its3::align
25+
{
26+
27+
struct InextensionalMisalignment {
28+
std::map<int, std::array<double, 4>> modes; // n -> (a_n, b_n, c_n, d_n)
29+
double alpha{0.};
30+
double beta{0.};
31+
};
32+
33+
struct SensorMisalignment {
34+
o2::math_utils::Legendre2DPolynominal legendre;
35+
bool hasLegendre{false};
36+
InextensionalMisalignment inextensional;
37+
bool hasInextensional{false};
38+
39+
bool empty() const noexcept { return !hasLegendre && !hasInextensional; }
40+
};
41+
42+
struct MisalignmentModel {
43+
static constexpr std::size_t NSensors = 6;
44+
std::array<SensorMisalignment, NSensors> sensors{};
45+
46+
bool empty() const noexcept;
47+
const SensorMisalignment& operator[](std::size_t idx) const { return sensors[idx]; }
48+
SensorMisalignment& operator[](std::size_t idx) { return sensors[idx]; }
49+
};
50+
51+
struct MisalignmentFrame {
52+
int sensorID{-1};
53+
int layerID{-1};
54+
double x{0.}; // tracking-frame X / nominal radius at the measurement
55+
double alpha{0.}; // tracking-frame alpha
56+
double z{0.}; // tracking-frame measurement z
57+
};
58+
59+
struct MisalignmentShift {
60+
double dy{0.};
61+
double dz{0.};
62+
bool accepted{true};
63+
64+
MisalignmentShift& operator+=(const MisalignmentShift& other)
65+
{
66+
dy += other.dy;
67+
dz += other.dz;
68+
accepted = accepted && other.accepted;
69+
return *this;
70+
}
71+
};
72+
73+
MisalignmentModel loadMisalignmentModel(const std::string& jsonPath);
74+
MisalignmentShift evaluateLegendreShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes);
75+
MisalignmentShift evaluateInextensionalShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes);
76+
77+
} // namespace o2::its3::align
78+
79+
#endif
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
12+
#include "ITS3Align/AlignmentDOF.h"
13+
14+
#include <cmath>
15+
#include <stdexcept>
16+
17+
#include "ITS3Align/AlignmentMath.h"
18+
#include "ITS3Base/SpecsV2.h"
19+
20+
namespace
21+
{
22+
23+
void validateDerivativeOutput(const DOFSet& dofSet, Eigen::Ref<Eigen::MatrixXd> out)
24+
{
25+
if (out.rows() != 3 || out.cols() != dofSet.nDOFs()) {
26+
throw std::invalid_argument(std::format("Derivative buffer shape {}x{} does not match expected 3x{}",
27+
out.rows(), out.cols(), dofSet.nDOFs()));
28+
}
29+
out.setZero();
30+
}
31+
32+
} // namespace
33+
34+
void RigidBodyDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const
35+
{
36+
validateDerivativeOutput(*this, out);
37+
38+
const double csp = 1. / std::sqrt(1. + (ctx.tgl * ctx.tgl));
39+
const double uP = ctx.snp * csp;
40+
const double vP = ctx.tgl * csp;
41+
42+
out(0, TX) = uP;
43+
out(0, TY) = -1.;
44+
out(0, RX) = ctx.trkZ;
45+
out(0, RY) = ctx.trkZ * uP;
46+
out(0, RZ) = -ctx.trkY * uP;
47+
48+
out(1, TX) = vP;
49+
out(1, TZ) = -1.;
50+
out(1, RX) = -ctx.trkY;
51+
out(1, RY) = ctx.trkZ * vP;
52+
out(1, RZ) = -ctx.trkY * vP;
53+
}
54+
55+
void LegendreDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const
56+
{
57+
validateDerivativeOutput(*this, out);
58+
if (ctx.sensorID < 0 || ctx.layerID < 0) {
59+
throw std::invalid_argument("LegendreDOFSet requires an ITS3 measurement context");
60+
}
61+
62+
const double gloX = ctx.measX * std::cos(ctx.measAlpha);
63+
const double gloY = ctx.measX * std::sin(ctx.measAlpha);
64+
const auto [u, v] = o2::its3::align::computeUV(gloX, gloY, ctx.measZ, ctx.sensorID, o2::its3::constants::radii[ctx.layerID]);
65+
const auto pu = o2::its3::align::legendrePols(mOrder, u);
66+
const auto pv = o2::its3::align::legendrePols(mOrder, v);
67+
68+
int idx = 0;
69+
for (int i = 0; i <= mOrder; ++i) {
70+
for (int j = 0; j <= i; ++j) {
71+
const double basis = pu[j] * pv[i - j];
72+
out(0, idx) = ctx.dydx * basis;
73+
out(1, idx) = ctx.dzdx * basis;
74+
++idx;
75+
}
76+
}
77+
}
78+
79+
void InextensionalDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref<Eigen::MatrixXd> out) const
80+
{
81+
validateDerivativeOutput(*this, out);
82+
if (ctx.layerID < 0) {
83+
throw std::invalid_argument("InextensionalDOFSet requires an ITS3 measurement context");
84+
}
85+
86+
const double r = o2::its3::constants::radii[ctx.layerID];
87+
const double phi = std::atan2(r * std::sin(ctx.measAlpha), r * std::cos(ctx.measAlpha));
88+
const double z = ctx.measZ;
89+
90+
for (int n = 2; n <= mMaxOrder; ++n) {
91+
const double sn = std::sin(n * phi);
92+
const double cn = std::cos(n * phi);
93+
const double n2 = static_cast<double>(n * n);
94+
const int off = modeOffset(n);
95+
96+
out(0, off + 0) = -(z / r) * (n * sn + ctx.dydx * n2 * cn);
97+
out(1, off + 0) = -cn - ctx.dzdx * (z / r) * n2 * cn;
98+
99+
out(0, off + 1) = (z / r) * (n * cn - ctx.dydx * n2 * sn);
100+
out(1, off + 1) = -sn * (1. + ctx.dzdx * (z / r) * n2);
101+
102+
out(0, off + 2) = -cn + ctx.dydx * n * sn;
103+
out(1, off + 2) = ctx.dzdx * n * sn;
104+
105+
out(0, off + 3) = -sn - ctx.dydx * n * cn;
106+
out(1, off + 3) = -ctx.dzdx * n * cn;
107+
}
108+
109+
out(0, alphaIdx()) = z / r;
110+
out(1, alphaIdx()) = -phi;
111+
112+
out(0, betaIdx()) = -phi - ctx.dydx;
113+
out(1, betaIdx()) = -ctx.dzdx;
114+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
12+
#include "ITS3Align/AlignmentMath.h"
13+
14+
#include <cmath>
15+
16+
#include <TMath.h>
17+
18+
#include "ITS3Base/SpecsV2.h"
19+
#include "MathUtils/Utils.h"
20+
21+
namespace o2::its3::align
22+
{
23+
24+
std::pair<double, double> computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius)
25+
{
26+
const bool isTop = sensorID % 2 == 0;
27+
const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX));
28+
const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius));
29+
const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius));
30+
const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.;
31+
const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.;
32+
return {u, v};
33+
}
34+
35+
TrackSlopes computeTrackSlopes(double snp, double tgl)
36+
{
37+
const double csci = 1. / std::sqrt(1. - (snp * snp));
38+
return {.dydx = snp * csci, .dzdx = tgl * csci};
39+
}
40+
41+
std::vector<double> legendrePols(int order, double x)
42+
{
43+
std::vector<double> p(order + 1);
44+
p[0] = 1.;
45+
if (order > 0) {
46+
p[1] = x;
47+
}
48+
for (int n = 1; n < order; ++n) {
49+
p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1);
50+
}
51+
return p;
52+
}
53+
54+
} // namespace o2::its3::align

0 commit comments

Comments
 (0)