Skip to content

Commit 969d4d3

Browse files
committed
align calib parameters
Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent 65a443b commit 969d4d3

File tree

5 files changed

+139
-43
lines changed

5 files changed

+139
-43
lines changed

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

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,52 +85,63 @@ auto getRigidBodyDerivatives(const auto& trk)
8585
class GlobalLabel
8686
{
8787
// Millepede label is any positive integer [1....)
88+
// Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int)
8889
public:
8990
using T = uint32_t;
90-
static constexpr int DOF_BITS = 5; // 0...4
91-
static constexpr int ID_BITS = 23; // 5...27
92-
static constexpr int SENS_BITS = 1; // 28
91+
static constexpr int DOF_BITS = 5; // bits 0-4
92+
static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration
93+
static constexpr int ID_BITS = 22; // bits 6-27
94+
static constexpr int SENS_BITS = 1; // bit 28
9395
static constexpr int TOTAL_BITS = sizeof(T) * 8;
94-
static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int!
96+
static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int!
9597
static constexpr T bitMask(int b) noexcept
9698
{
9799
return (T(1) << b) - T(1);
98100
}
99101
static constexpr int DOF_SHIFT = 0;
100102
static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1);
101103
static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT;
102-
static constexpr int ID_SHIFT = DOF_BITS;
104+
static constexpr int CALIB_SHIFT = DOF_BITS;
105+
static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1);
106+
static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT;
107+
static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS;
103108
static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1);
104109
static constexpr T ID_MASK = ID_MAX << ID_SHIFT;
105-
static constexpr int SENS_SHIFT = ID_BITS + DOF_BITS;
110+
static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS;
106111
static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1);
107112
static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT;
108-
static constexpr int DET_SHIFT = DOF_BITS + ID_BITS + SENS_BITS;
113+
static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS;
109114
static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1);
110115
static constexpr T DET_MASK = DET_MAX << DET_SHIFT;
111116

112-
GlobalLabel(T det, T id, bool sens) : mID(((((id + 1) & ID_MAX) << ID_SHIFT) | ((det & DET_MAX) << DET_SHIFT) | ((sens & SENS_MAX) << SENS_SHIFT)))
117+
GlobalLabel(T det, T id, bool sens, bool calib = false)
118+
: mID((((id + 1) & ID_MAX) << ID_SHIFT) |
119+
((det & DET_MAX) << DET_SHIFT) |
120+
((T(sens) & SENS_MAX) << SENS_SHIFT) |
121+
((T(calib) & CALIB_MAX) << CALIB_SHIFT))
113122
{
114123
}
115124

125+
/// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label)
116126
constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); }
117127
constexpr int rawGBL(T dof) const noexcept { return static_cast<int>(raw(dof)); }
118-
constexpr T id() const noexcept
119-
{
120-
return ((mID - 1) & ID_MASK) >> ID_SHIFT;
121-
}
122-
constexpr T det() const noexcept
123-
{
124-
return (mID & DET_MASK) >> DET_SHIFT;
125-
}
126-
constexpr bool sens() const noexcept
128+
129+
/// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume)
130+
GlobalLabel asCalib() const noexcept
127131
{
128-
return (mID & SENS_MASK) >> SENS_SHIFT;
132+
GlobalLabel c{*this};
133+
c.mID |= (T(1) << CALIB_SHIFT);
134+
return c;
129135
}
130136

137+
constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; }
138+
constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; }
139+
constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; }
140+
constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; }
141+
131142
std::string asString() const
132143
{
133-
return std::format("Det:{} Id:{} Sens:{}", det(), id(), sens());
144+
return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib());
134145
}
135146

136147
constexpr auto operator<=>(const GlobalLabel&) const noexcept = default;
@@ -215,6 +226,10 @@ class AlignableVolume
215226
void setRigidBodyDOF(RigidBodyDOFMask m) noexcept { mDOF = m; }
216227
RigidBodyDOFMask getRigidBodyDOF() const noexcept { return mDOF; }
217228

229+
// calibration DOFs (e.g. Legendre deformation coefficients)
230+
void setNCalibDOFs(int n) noexcept { mNCalibDOFs = n; }
231+
int getNCalibDOFs() const noexcept { return mNCalibDOFs; }
232+
218233
// transformation matrices
219234
virtual void defineMatrixL2G() {}
220235
virtual void defineMatrixT2L() {}
@@ -241,6 +256,7 @@ class AlignableVolume
241256
GlobalLabel mLabel; // internal global idetenifier
242257
uint8_t mLevel{0}; // depth-in tree
243258
RigidBodyDOFMask mDOF{RigidBodyDOFAll}; // allowed dofs
259+
int mNCalibDOFs{0}; // number of calibration DOFs (0 = none)
244260

245261
AlignableVolume* setParent(Ptr c)
246262
{

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ struct AlignmentParams : public o2::conf::ConfigurableParamHelper<AlignmentParam
3636
float extraClsErrY[7] = {0};
3737
float extraClsErrZ[7] = {0};
3838

39-
// misalignment
39+
// misalignment simulation
4040
bool doMisalignment = false; // simulate Legendre deformation on ITS3 layers
4141
bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers
4242
std::string misAlgJson; // JSON file with deformation and/or rigid body params
4343

44+
// Legendre DOFs for Millepede
45+
int legendreOrder = 3; // max Legendre order in phi (u) direction (0..N -> N+1 terms)
46+
4447
// Ridder options
4548
int ridderMaxExtrap = 10;
4649
double ridderRelIniStep[5] = {0.01, 0.01, 0.02, 0.02, 0.02};

Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,17 @@ void AlignableVolume::writeParameters(std::ostream& os) const
170170
if (isRoot()) {
171171
os << "Parameter\n";
172172
}
173+
// rigid body DOFs (CALIB=0)
173174
for (uint8_t iDOF{0}; iDOF < RigidBodyDOF::NDOF; ++iDOF) {
174175
os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", mLabel.raw(iDOF), mPreSigma, (hasRigidBodyDOF(mDOF, iDOF) ? 0.0 : -1.0), (hasRigidBodyDOF(mDOF, iDOF) ? 'V' : 'F'), RigidBodyDOFNames[iDOF]) << mSymName << '\n';
175176
}
177+
// calibration DOFs (CALIB=1)
178+
if (mNCalibDOFs > 0) {
179+
auto calibLbl = mLabel.asCalib();
180+
for (int iDOF{0}; iDOF < mNCalibDOFs; ++iDOF) {
181+
os << std::format("{:<10} {:>+15g} {:>+15g} ! C_{}", calibLbl.raw(iDOF), 0.0, 0.0, iDOF) << mSymName << '\n';
182+
}
183+
}
176184
for (const auto& c : mChildren) {
177185
c->writeParameters(os);
178186
}

Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <TGeoPhysicalNode.h>
1717

1818
#include "ITSMFTBase/SegmentationAlpide.h"
19+
#include "ITS3Align/AlignmentParams.h"
1920
#include "ITS3Align/AlignmentSensors.h"
2021
#include "ITSBase/GeometryTGeo.h"
2122

@@ -80,6 +81,9 @@ AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap
8081
AlignableVolume *volHB{nullptr}, *volSt{nullptr}, *volHSt{nullptr}, *volMod{nullptr};
8182
std::unordered_map<std::string, AlignableVolume*> sym2vol;
8283

84+
const int N = AlignmentParams::Instance().legendreOrder;
85+
const int nLeg = (N + 1) * (N + 2) / 2;
86+
8387
auto root = std::make_unique<AlignableVolume>(geom->composeSymNameITS(), gLbl++, det, false, RigidBodyDOFNone);
8488
sym2vol[root->getSymName()] = root.get();
8589
for (int ilr = 0; ilr < geom->getNumberOfLayers(); ilr++) {
@@ -88,6 +92,7 @@ AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap
8892
volHB = root->addChild(geom->composeSymNameHalfBarrel(ilr, ihb, isLayITS3), gLbl++, det, false, RigidBodyDOFNone);
8993
sym2vol[volHB->getSymName()] = volHB;
9094
if (isLayITS3) { // for its3 there are no more alignable volumes after this!
95+
volHB->setNCalibDOFs(nLeg);
9196
continue;
9297
}
9398
int nstavesHB = geom->getNumberOfStaves(ilr) / 2;

Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,32 @@ using VTIndex = o2::dataformats::VtxTrackIndex;
6161
using GTrackID = o2::dataformats::GlobalTrackID;
6262
using TrackD = o2::track::TrackParCovD;
6363

64+
// compute normalized (u,v) in [-1,1] from global position on a sensor
65+
static std::pair<double, double> computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius)
66+
{
67+
const bool isTop = sensorID % 2 == 0;
68+
const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX));
69+
const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius));
70+
const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius));
71+
const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.;
72+
const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.;
73+
return {u, v};
74+
}
75+
76+
// evaluate Legendre polynomials P_0(x) through P_order(x) via recurrence
77+
static std::vector<double> legendrePols(int order, double x)
78+
{
79+
std::vector<double> p(order + 1);
80+
p[0] = 1.;
81+
if (order > 0) {
82+
p[1] = x;
83+
}
84+
for (int n = 1; n < order; ++n) {
85+
p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1);
86+
}
87+
return p;
88+
}
89+
6490
class AlignmentSpec final : public Task
6591
{
6692
public:
@@ -129,8 +155,8 @@ class AlignmentSpec final : public Task
129155
GTrackID::mask_t mTracksSrc;
130156
int mNThreads{1};
131157
const AlignmentParams* mParams{nullptr};
132-
std::array<o2::math_utils::Legendre2DPolynominal, 6> mDeformations; // one per sensorID (0-5)
133-
std::array<Eigen::Matrix<double, 6, 1>, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID
158+
std::array<o2::math_utils::Legendre2DPolynominal, 6> mDeformations; // one per sensorID (0-5)
159+
std::array<Eigen::Matrix<double, 6, 1>, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID
134160
};
135161

136162
void AlignmentSpec::init(InitContext& ic)
@@ -295,37 +321,45 @@ void AlignmentSpec::process()
295321
// derivatives for all sensitive volumes and their parents
296322
// this is the derivative in TRK but we want to align in LOC
297323
// so dr/da_(LOC) = dr/da_(TRK) * da_(TRK)/da_(LOC)
298-
const auto* sensorVol = mChip2Hiearchy.at(lbl);
324+
const auto* tileVol = mChip2Hiearchy.at(lbl);
299325
Matrix36 der = getRigidBodyDerivatives(wTrk);
300326

301-
// count columns: only volumes with real DOFs (not DOFPseudo)
302-
int nCol{0};
303-
for (const auto* v = sensorVol; v && !v->isRoot(); v = v->getParent()) {
327+
// count rigid body columns: only volumes with real DOFs (not DOFPseudo)
328+
int nColRB{0};
329+
for (const auto* v = tileVol; v && !v->isRoot(); v = v->getParent()) {
304330
if (v->getRigidBodyDOF() != RigidBodyDOFPseudo) {
305-
nCol += RigidBodyDOF::NDOF;
331+
nColRB += RigidBodyDOF::NDOF;
306332
}
307333
}
334+
335+
// count Legendre columns for ITS3 sensors (applied at sensor = tile's parent)
336+
const bool isITS3 = constants::detID::isDetITS3(frame.sens);
337+
const int N = mParams->legendreOrder;
338+
const int nLeg = isITS3 ? (N + 1) * (N + 2) / 2 : 0;
339+
const int nCol = nColRB + nLeg;
340+
308341
std::vector<int> gLabels;
309342
gLabels.reserve(nCol);
310343
Eigen::MatrixXd gDer(3, nCol);
344+
gDer.setZero();
311345
Eigen::Index curCol{0};
312346

313-
// 1) sensor: TRK -> LOC via precomputed T2L and J_L2T
347+
// 1) tile: TRK -> LOC via precomputed T2L and J_L2T
314348
const double posTrk[3] = {frame.x, 0., 0.};
315349
double posLoc[3];
316-
sensorVol->getT2L().LocalToMaster(posTrk, posLoc);
350+
tileVol->getT2L().LocalToMaster(posTrk, posLoc);
317351
Matrix66 jacL2T;
318-
sensorVol->computeJacobianL2T(posLoc, jacL2T);
352+
tileVol->computeJacobianL2T(posLoc, jacL2T);
319353
der *= jacL2T;
320-
if (sensorVol->getRigidBodyDOF() != RigidBodyDOFPseudo) {
354+
if (tileVol->getRigidBodyDOF() != RigidBodyDOFPseudo) {
321355
for (uint8_t iDOF{0}; iDOF < RigidBodyDOF::NDOF; ++iDOF) {
322-
gLabels.push_back(sensorVol->getLabel().rawGBL(iDOF));
356+
gLabels.push_back(tileVol->getLabel().rawGBL(iDOF));
323357
}
324358
gDer.middleCols(curCol++ * RigidBodyDOF::NDOF, RigidBodyDOF::NDOF) = der;
325359
}
326360

327361
// 2) chain through parents: child's J_L2P
328-
for (const auto* child = sensorVol; child->getParent() && !child->getParent()->isRoot(); child = child->getParent()) {
362+
for (const auto* child = tileVol; child->getParent() && !child->getParent()->isRoot(); child = child->getParent()) {
329363
der *= child->getJL2P();
330364
const auto* parent = child->getParent();
331365
if (parent->getRigidBodyDOF() != RigidBodyDOFPseudo) {
@@ -335,6 +369,46 @@ void AlignmentSpec::process()
335369
gDer.middleCols(curCol++ * RigidBodyDOF::NDOF, RigidBodyDOF::NDOF) = der;
336370
}
337371
}
372+
373+
// 3) Legendre global derivatives for ITS3 sensors
374+
// h(u,v) = sum_{i=0}^{N} sum_{j=0}^{i} c_{ij} P_{i-j}(u) P_j(v)
375+
// dres/dc_{ij} = slope * P_{i-j}(u) * P_j(v)
376+
// Legendre DOFs live on the sensor volume (tile's direct parent)
377+
if (isITS3) {
378+
const int sensorID = constants::detID::getSensorID(frame.sens);
379+
const int layerID = constants::detID::getDetID2Layer(frame.sens);
380+
const auto* sensorVol = tileVol->getParent();
381+
382+
// compute (u,v) from global position
383+
const double r = frame.x;
384+
const double gX = r * std::cos(frame.alpha);
385+
const double gY = r * std::sin(frame.alpha);
386+
const double gZ = frame.positionTrackingFrame[1];
387+
auto [u, v] = computeUV(gX, gY, gZ, sensorID, constants::radii[layerID]);
388+
389+
// track slopes at this cluster (reconstructed track)
390+
const double snp = wTrk.getSnp();
391+
const double tgl = wTrk.getTgl();
392+
const double csci = 1. / std::sqrt(1. - (snp * snp));
393+
const double dydx = snp * csci;
394+
const double dzdx = tgl * csci;
395+
396+
// evaluate Legendre polynomials P_0..P_N for both u and v
397+
auto pu = legendrePols(N, u);
398+
auto pv = legendrePols(N, v);
399+
400+
int legIdx = 0;
401+
const int legColStart = nColRB;
402+
for (int i = 0; i <= N; ++i) {
403+
for (int j = 0; j <= i; ++j) {
404+
const double basis = pu[i - j] * pv[j];
405+
gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(legIdx));
406+
gDer(0, legColStart + legIdx) = dydx * basis; // dres_y / dc_{ij}
407+
gDer(1, legColStart + legIdx) = dzdx * basis; // dres_z / dc_{ij}
408+
++legIdx;
409+
}
410+
}
411+
}
338412
point.addGlobals(gLabels, gDer);
339413
}
340414

@@ -771,16 +845,6 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt&
771845
// --- Legendre deformation (non-rigid-body) ---
772846
if (mParams->doMisalignment && mIT3Dict && mUseMC) {
773847
const auto prop = o2::base::PropagatorD::Instance();
774-
// compute normalized (u,v) in [-1,1] from global position
775-
auto computeUV = [](double gloX, double gloY, double gloZ, int sensorID, double radius) -> std::pair<double, double> {
776-
const bool isTop = sensorID % 2 == 0;
777-
const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX));
778-
const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius));
779-
const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius));
780-
const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.;
781-
const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.;
782-
return {u, v};
783-
};
784848

785849
const auto lbl = mRecoData->getITSTracksMCLabels()[iTrk];
786850
const auto mcTrk = mcReader->getTrack(lbl);

0 commit comments

Comments
 (0)