Skip to content

Commit 65a443b

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

File tree

4 files changed

+165
-25
lines changed

4 files changed

+165
-25
lines changed

Detectors/Upgrades/ITS3/alignment/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ o2_add_library(ITS3Align
2727
O2::GlobalTrackingWorkflowHelpers
2828
O2::DataFormatsGlobalTracking
2929
O2::DetectorsVertexing
30+
nlohmann_json::nlohmann_json
3031
GBL::GBL)
3132
if (OpenMP_CXX_FOUND)
3233
target_compile_definitions(${targetName} PRIVATE WITH_OPENMP)

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

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

39+
// misalignment
40+
bool doMisalignment = false; // simulate Legendre deformation on ITS3 layers
41+
bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers
42+
std::string misAlgJson; // JSON file with deformation and/or rigid body params
43+
3944
// Ridder options
4045
int ridderMaxExtrap = 10;
4146
double ridderRelIniStep[5] = {0.01, 0.01, 0.02, 0.02, 0.02};
4247
double ridderMaxIniStep[5] = {0.1, 0.1, 0.05, 0.05, 0.05};
4348
double ridderShrinkFac = 2.0;
4449
double ridderEps = 1e-16;
45-
double ridderMaxJacDiagTol = 0.2; // max tolerance of diagonal elements away from 1
4650

4751
// MillePede output
4852
std::string milleBinFile = "mp2data.bin";

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

Lines changed: 158 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <GblPoint.h>
2424
#include <GblMeasurement.h>
2525
#include <MilleBinary.h>
26+
#include <nlohmann/json.hpp>
2627

2728
#include "Framework/ConfigParamRegistry.h"
2829
#include "Framework/DataProcessorSpec.h"
@@ -47,6 +48,7 @@
4748
#include "ITS3Align/AlignmentTypes.h"
4849
#include "ITS3Align/AlignmentHierarchy.h"
4950
#include "ITS3Align/AlignmentSensors.h"
51+
#include "MathUtils/LegendrePols.h"
5052

5153
namespace o2::its3::align
5254
{
@@ -97,6 +99,11 @@ class AlignmentSpec final : public Task
9799
// build track to vertex association
98100
void buildT2V();
99101

102+
// apply some misalignment on inner ITS3 layers
103+
// it can happen that a measurement is pushed outside of
104+
// ITS3 acceptance so false is to discard track
105+
bool applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk);
106+
100107
// Steering debug
101108
enum class OutputOpt : uint8_t {
102109
VerboseGBL = 0,
@@ -119,10 +126,11 @@ class AlignmentSpec final : public Task
119126
AlignableVolume::SensorMapping mChip2Hiearchy; // global label mapping to leaves in the tree
120127
bool mUseMC{false};
121128
bool mWithPV{false};
122-
FrameInfoExt mPVInfo;
123129
GTrackID::mask_t mTracksSrc;
124130
int mNThreads{1};
125131
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
126134
};
127135

128136
void AlignmentSpec::init(InitContext& ic)
@@ -264,8 +272,9 @@ void AlignmentSpec::process()
264272
res << frame.positionTrackingFrame[0] - wTrk.getY(), frame.positionTrackingFrame[1] - wTrk.getZ();
265273

266274
// here we can apply some misalignment on the measurment
267-
if (constants::detID::getSensorID(frame.sens) == 2) {
268-
res[0] += 0.01;
275+
if (!applyMisalignment(res, frame, wTrk, iTrk)) {
276+
failed = true;
277+
break;
269278
}
270279

271280
prec << 1. / resTrack.points[ip].sig2y, 1. / resTrack.points[ip].sig2z;
@@ -274,7 +283,7 @@ void AlignmentSpec::process()
274283
if (msErr > mParams->minMS && ip < np - 1) {
275284
Eigen::Vector2d scat(0., 0.), scatPrec = Eigen::Vector2d::Constant(1. / (msErr * msErr));
276285
point.addScatterer(scat, scatPrec);
277-
lt.clearFast(); // only clear if accounted
286+
lt.clearFast(); // clear if accounted
278287
}
279288

280289
if (frame.lr >= 0) {
@@ -356,9 +365,8 @@ void AlignmentSpec::process()
356365
LOGP(info, "GBL FIT chi2 {} ndf {}", chi2, ndf);
357366
traj.printTrajectory(5);
358367
}
359-
if (chi2 / ndf > mParams->maxChi2Ndf) {
368+
if (chi2 / ndf > mParams->maxChi2Ndf && cGBLChi2Rej < 10) {
360369
LOGP(error, "GBL fit exceeded red chi2 {}", chi2 / ndf);
361-
++cGBLChi2Rej;
362370
if (std::abs(resTrack.kfFit.chi2Ndf - 1) < 0.02) {
363371
LOGP(error, "\tGBL is far away from good KF fit!!!!");
364372
continue;
@@ -431,6 +439,39 @@ void AlignmentSpec::updateTimeDependentParams(ProcessingContext& pc)
431439
mParams->printKeyValues(true, true);
432440

433441
buildHierarchy();
442+
443+
if (mParams->doMisalignment || mParams->doMisalignmentRB) {
444+
TMatrixD null(1, 1);
445+
null(0, 0) = 0;
446+
for (int i = 0; i < 6; ++i) {
447+
mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null);
448+
mRigidBodyParams[i].setZero();
449+
}
450+
if (!mParams->misAlgJson.empty()) {
451+
using json = nlohmann::json;
452+
std::ifstream f(mParams->misAlgJson);
453+
auto data = json::parse(f);
454+
for (const auto& item : data) {
455+
int id = item["id"].get<int>();
456+
if (mParams->doMisalignment && item.contains("matrix")) {
457+
auto v = item["matrix"].get<std::vector<std::vector<double>>>();
458+
TMatrixD m(v.size(), v[0].size());
459+
for (size_t r{0}; r < v.size(); ++r) {
460+
for (size_t c{0}; c < v[r].size(); ++c) {
461+
m(r, c) = v[r][c];
462+
}
463+
}
464+
mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m);
465+
}
466+
if (mParams->doMisalignmentRB && item.contains("rigidBody")) {
467+
auto rb = item["rigidBody"].get<std::vector<double>>();
468+
for (int k = 0; k < 6 && k < (int)rb.size(); ++k) {
469+
mRigidBodyParams[id](k) = rb[k];
470+
}
471+
}
472+
}
473+
}
474+
}
434475
}
435476
}
436477

@@ -536,12 +577,6 @@ bool AlignmentSpec::getTransportJacobian(const TrackD& track, double xTo, double
536577
LOGP(error, "Near jacobian idendity for taking track from {} to {}", track.getX(), xTo);
537578
return false;
538579
}
539-
// only check for elements where there is no change expected q/pt, tgl
540-
if (std::abs(1. - jac(4, 4)) > mParams->ridderMaxJacDiagTol || std::abs(1. - jac(3, 3)) > mParams->ridderMaxJacDiagTol) {
541-
LOGP(error, "Diagonal element not expected to change in propagation jacobian is to far away from 1");
542-
LOG(debug) << jac;
543-
return false;
544-
}
545580

546581
return true;
547582
}
@@ -584,6 +619,7 @@ bool AlignmentSpec::prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack,
584619
return 1;
585620
};
586621

622+
FrameInfoExt pvInfo;
587623
if (mWithPV) { // add PV as constraint
588624
const int iPV = mT2PV[iTrk];
589625
if (iPV < 0) {
@@ -594,18 +630,18 @@ bool AlignmentSpec::prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack,
594630
if (!prop->propagateToDCA(pv, tmp, bz)) {
595631
return false;
596632
}
597-
mPVInfo.alpha = (float)tmp.getAlpha();
633+
pvInfo.alpha = (float)tmp.getAlpha();
598634
double ca{0}, sa{0};
599-
o2::math_utils::bringTo02Pid(mPVInfo.alpha);
600-
o2::math_utils::sincosd(mPVInfo.alpha, sa, ca);
601-
mPVInfo.x = tmp.getX();
602-
mPVInfo.positionTrackingFrame[0] = -pv.getX() * sa + pv.getY() * ca;
603-
mPVInfo.positionTrackingFrame[1] = pv.getZ();
604-
mPVInfo.covarianceTrackingFrame[0] = 0.5 * (pv.getSigmaX2() + pv.getSigmaY2());
605-
mPVInfo.covarianceTrackingFrame[2] = pv.getSigmaY2();
606-
mPVInfo.sens = -1;
607-
mPVInfo.lr = -1;
608-
frameArr[0] = &mPVInfo;
635+
o2::math_utils::bringToPMPid(pvInfo.alpha);
636+
o2::math_utils::sincosd(pvInfo.alpha, sa, ca);
637+
pvInfo.x = tmp.getX();
638+
pvInfo.positionTrackingFrame[0] = -pv.getX() * sa + pv.getY() * ca;
639+
pvInfo.positionTrackingFrame[1] = pv.getZ();
640+
pvInfo.covarianceTrackingFrame[0] = 0.5 * (pv.getSigmaX2() + pv.getSigmaY2());
641+
pvInfo.covarianceTrackingFrame[2] = pv.getSigmaY2();
642+
pvInfo.sens = -1;
643+
pvInfo.lr = -1;
644+
frameArr[0] = &pvInfo;
609645
}
610646

611647
// collect all track clusters to array, placing them to layer+1 slot
@@ -723,6 +759,105 @@ void AlignmentSpec::buildT2V()
723759
}
724760
}
725761

762+
bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk)
763+
{
764+
if (!constants::detID::isDetITS3(frame.sens)) {
765+
return true;
766+
}
767+
768+
const int sensorID = constants::detID::getSensorID(frame.sens);
769+
const int layerID = constants::detID::getDetID2Layer(frame.sens);
770+
771+
// --- Legendre deformation (non-rigid-body) ---
772+
if (mParams->doMisalignment && mIT3Dict && mUseMC) {
773+
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+
};
784+
785+
const auto lbl = mRecoData->getITSTracksMCLabels()[iTrk];
786+
const auto mcTrk = mcReader->getTrack(lbl);
787+
if (!mcTrk) {
788+
return false;
789+
}
790+
std::array<double, 3> xyz{mcTrk->GetStartVertexCoordinatesX(), mcTrk->GetStartVertexCoordinatesY(), mcTrk->GetStartVertexCoordinatesZ()};
791+
std::array<double, 3> pxyz{mcTrk->GetStartVertexMomentumX(), mcTrk->GetStartVertexMomentumY(), mcTrk->GetStartVertexMomentumZ()};
792+
TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode());
793+
if (!pPDG) {
794+
return false;
795+
}
796+
o2::track::TrackParD mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
797+
798+
const double r = frame.x;
799+
const double gloX = r * std::cos(frame.alpha);
800+
const double gloY = r * std::sin(frame.alpha);
801+
const double gloZ = frame.positionTrackingFrame[1];
802+
auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]);
803+
const double h = mDeformations[sensorID](u, v);
804+
805+
auto mcAtCl = mcPar;
806+
if (!mcAtCl.rotate(frame.alpha) || !prop->PropagateToXBxByBz(mcAtCl, frame.x)) {
807+
return false;
808+
}
809+
810+
const double snp = mcAtCl.getSnp();
811+
const double tgl = mcAtCl.getTgl();
812+
const double csci = 1. / std::sqrt(1. - (snp * snp));
813+
const double dydx = snp * csci;
814+
const double dzdx = tgl * csci;
815+
const double dy = dydx * h;
816+
const double dz = dzdx * h;
817+
818+
const double newGloY = (r * std::sin(frame.alpha)) + (dy * std::cos(frame.alpha));
819+
const double newGloX = (r * std::cos(frame.alpha)) - (dy * std::sin(frame.alpha));
820+
const double newGloZ = gloZ + dz;
821+
auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]);
822+
if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) {
823+
return false;
824+
}
825+
826+
res[0] += dy;
827+
res[1] += dz;
828+
}
829+
830+
// --- Rigid body misalignment ---
831+
// Uses the same derivative chain as GBL global derivatives:
832+
// dres/da_LOC = dres/da_TRK * J_L2T
833+
// so the residual shift is der_LOC * delta_a
834+
if (mParams->doMisalignmentRB) {
835+
GlobalLabel lbl(0, frame.sens, true);
836+
if (mChip2Hiearchy.find(lbl) == mChip2Hiearchy.end()) {
837+
return true; // sensor not in hierarchy, skip
838+
}
839+
const auto* sensorVol = mChip2Hiearchy.at(lbl);
840+
841+
// derivative in TRK frame (3x6: rows = dy, dz, dsnp)
842+
Matrix36 der = getRigidBodyDerivatives(wTrk);
843+
844+
// transform TRK -> LOC
845+
const double posTrk[3] = {frame.x, 0., 0.};
846+
double posLoc[3];
847+
sensorVol->getT2L().LocalToMaster(posTrk, posLoc);
848+
Matrix66 jacL2T;
849+
sensorVol->computeJacobianL2T(posLoc, jacL2T);
850+
der *= jacL2T;
851+
852+
// apply: delta_res = der(row 0,1) * delta_a_LOC
853+
Eigen::Vector3d shift = der * mRigidBodyParams[sensorID];
854+
res[0] += shift[0]; // dy
855+
res[1] += shift[1]; // dz
856+
}
857+
858+
return true;
859+
}
860+
726861
void AlignmentSpec::endOfStream(EndOfStreamContext& /*ec*/)
727862
{
728863
mDBGOut->Close();

Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,7 +1103,7 @@ void TrackingStudySpec::doMisalignmentStudy()
11031103
writeTree("idealRes", clArr, extrapOut, extrapInw, lbl);
11041104

11051105
// Propagate MC truth to each cluster's (alpha, x) to get true track direction,
1106-
// then compute δy = dydx·h(u,v), δz = dzdx·h(u,v) first Newton step.
1106+
// then compute dy = dydx*h(u,v), dz = dzdx*h(u,v) - first Newton step.
11071107
const auto mcTrk = mMCReader.getTrack(lbl);
11081108
if (!mcTrk) {
11091109
continue;

0 commit comments

Comments
 (0)