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"
4748#include " ITS3Align/AlignmentTypes.h"
4849#include " ITS3Align/AlignmentHierarchy.h"
4950#include " ITS3Align/AlignmentSensors.h"
51+ #include " MathUtils/LegendrePols.h"
5052
5153namespace 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
128136void 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, " \t GBL 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+
726861void AlignmentSpec::endOfStream (EndOfStreamContext& /* ec*/ )
727862{
728863 mDBGOut ->Close ();
0 commit comments