Project
Loading...
Searching...
No Matches
MisalignmentHits.cxx
Go to the documentation of this file.
1// Copyright 2020-2022 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#include "ITS3Base/ITS3Params.h"
16#include "Framework/Logger.h"
17
18#include "Math/Factory.h"
19#include "Math/UnaryOperators.h"
20#include "TGeoNode.h"
21#include "TGeoBBox.h"
22#include "TString.h"
23
24#include <memory>
25#include <string>
26#include <cstring>
27#include <algorithm>
28
29namespace o2::its3::align
30{
31
33{
34 if (o2::its3::ITS3Params::Instance().misalignmentHitsUseProp) {
35 mMethod = PropMethod::Propagator;
36 } else {
37 mMethod = PropMethod::Line;
38 }
39
41
42 mMinimizer.reset(ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad"));
43 if (mMinimizer == nullptr) {
44 LOGP(fatal, "Cannot create minimizer");
45 }
46 mMinimizer->SetMaxFunctionCalls(1'000'000'000);
47 mMinimizer->SetStrategy(0);
48 mMinimizer->SetPrintLevel(0);
49
50 if (mMethod == PropMethod::Propagator) {
51 LOGP(info, "Using propagator to find intersection");
53 mMCReader = std::make_unique<o2::steer::MCKinematicsReader>(prefix, o2::steer::MCKinematicsReader::Mode::kMCKine);
54 mMinimizer->SetFunction(mPropagator);
55 } else {
56 LOGP(info, "Using local straight-line to find intersection");
57 mMinimizer->SetFunction(mLine);
58 }
59
60 resetStats();
61
62 if (auto file = o2::its3::ITS3Params::Instance().misalignmentHitsParams; file.empty()) {
63 LOGP(fatal, "No parameter file specified");
64 } else {
65 mDeformations.init(file);
66 }
67}
68
69std::optional<o2::itsmft::Hit> MisAlignmentHits::processHit(int iEvent, const o2::itsmft::Hit& hit)
70{
71 ++mStats[Stats::kHitTotal];
72
74 ++mStats[Stats::kHitIsOB];
75 return hit;
76 }
77 ++mStats[Stats::kHitIsIB];
78
79 // Set the working hits
80 mCurHit = hit;
81 mCurWorkingHits[WorkingHit::kEntering] = WorkingHit(iEvent, WorkingHit::kEntering, hit);
82 mCurWorkingHits[WorkingHit::kExiting] = WorkingHit(iEvent, WorkingHit::kExiting, hit);
83
84 // Do work
85 if (!deformHit(WorkingHit::kEntering) || !deformHit(WorkingHit::kExiting)) {
86 ++mStats[Stats::kHitDead];
87 return std::nullopt;
88 }
89 ++mStats[Stats::kHitAlive];
90
91 // Set the possibly new detectorIDs with mid point approximation
92 auto midPointOrig = mCurWorkingHits[WorkingHit::kEntering].mPoint + (mCurWorkingHits[WorkingHit::kExiting].mPoint - mCurWorkingHits[WorkingHit::kEntering].mPoint) * 0.5;
93 auto midPointDef = mCurWorkingHits[WorkingHit::kEntering].mPointDef + (mCurWorkingHits[WorkingHit::kExiting].mPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef) * 0.5;
94 const short idDef = getDetID(midPointDef), idOrig = getDetID(midPointOrig);
95 if (idDef == -1) {
96 return std::nullopt;
97 }
98
99 if (idDef != idOrig) {
100 ++mStats[Stats::kHitMigrated];
101 } else {
102 ++mStats[Stats::kHitNotMigrated];
103 }
104
105 if constexpr (false) {
108 bool crossesBoundary{false};
109 TGeoNode *nEnt{nullptr}, *nExt{nullptr};
110 {
111 auto dirEnt = mCurWorkingHits[WorkingHit::kEntering].mPointDef - midPointDef;
112 auto stepEnt = std::min(static_cast<double>(dirEnt.R()), std::abs(dirEnt.R() - 5.e-4));
113 auto dirEntU = dirEnt.Unit();
114 gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z());
115 gGeoManager->SetCurrentDirection(dirEntU.X(), dirEntU.Y(), dirEntU.Z());
116 nEnt = gGeoManager->FindNextBoundaryAndStep(stepEnt, false);
117 if (gGeoManager->IsOnBoundary()) {
118 ++mStats[Stats::kHitEntBoundary];
119 crossesBoundary = true;
120 }
121 }
122 {
123 auto dirExt = midPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef;
124 auto stepExt = std::min(static_cast<double>(dirExt.R()), std::abs(dirExt.R() - 5.e-4));
125 auto dirExtU = dirExt.Unit();
126 gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z());
127 gGeoManager->SetCurrentDirection(dirExtU.X(), dirExtU.Y(), dirExtU.Z());
128 nExt = gGeoManager->FindNextBoundaryAndStep(stepExt, false);
129 if (gGeoManager->IsOnBoundary()) {
130 ++mStats[Stats::kHitExtBoundary];
131 crossesBoundary = true;
132 }
133 }
134
135 if (crossesBoundary && nEnt != nullptr && nExt != nullptr) {
136 if (nEnt != nExt) {
137 return std::nullopt;
138 } else {
139 ++mStats[Stats::kHitSameBoundary]; // indicates that the step size is too large and we end up in the mother volume; just pretend that his fine for now
140 }
141 }
142 ++mStats[Stats::kHitNoBoundary];
143 }
144
145 // Get new postion
146 mCurHit.SetPosStart(mCurWorkingHits[WorkingHit::kEntering].mPointDef);
147 mCurHit.SetPos(mCurWorkingHits[WorkingHit::kExiting].mPointDef);
148 mCurHit.SetDetectorID(idDef);
149
150 ++mStats[Stats::kHitSuccess];
151 return mCurHit;
152}
153
154bool MisAlignmentHits::deformHit(WorkingHit::HitType t)
155{
156 auto& wHit = mCurWorkingHits[t];
157
158 mMinimizer->Clear(); // clear for next iteration
159 constexpr double minStep{1e-5};
160 constexpr double zMargin{4.0};
161 constexpr double phiMargin{0.4};
162 if (mMethod == PropMethod::Line) {
163 prepareLineMethod(t);
164 mMinimizer->SetVariable(0, "t", 0.0, minStep); // this is left as a free parameter on since t is very small since start and end of hit are close
165 } else {
166 if (!preparePropagatorMethod(t)) {
167 return false;
168 }
169 mMinimizer->SetVariable(0, "r", mPropagator.mTrack.getX(), minStep); // this is left as a free parameter on since t is very small since start and end of hit are close
170 }
171 mMinimizer->SetLimitedVariable(1, "phiStar", wHit.mPhi, minStep,
172 std::max(static_cast<double>(wHit.mPhiBorder1), static_cast<double>(wHit.mPhi) - phiMargin),
173 std::min(static_cast<double>(wHit.mPhiBorder2), static_cast<double>(wHit.mPhi) + phiMargin));
174 mMinimizer->SetLimitedVariable(2, "zStar", wHit.mPoint.Z(), minStep,
175 std::max(static_cast<double>(-constants::segment::lengthSensitive / 2.f), static_cast<double>(wHit.mPoint.Z()) - zMargin),
176 std::min(static_cast<double>(constants::segment::lengthSensitive / 2.f), static_cast<double>(wHit.mPoint.Z()) + zMargin));
177
178 mMinimizer->Minimize(); // perform the actual minimization
179
180 auto ss = mMinimizer->Status();
181 if (ss == 1) {
182 ++mStats[Stats::kMinimizerCovPos];
183 } else if (ss == 2) {
184 ++mStats[Stats::kMinimizerHesse];
185 } else if (ss == 3) {
186 ++mStats[Stats::kMinimizerEDM];
187 } else if (ss == 4) {
188 ++mStats[Stats::kMinimizerLimit];
189 } else if (ss == 5) {
190 ++mStats[Stats::kMinimizerOther];
191 } else {
192 ++mStats[Stats::kMinimizerConverged];
193 }
194
195 if (ss == 0 || ss == 1) { // for Minuit2 0=ok, 1=ok with pos. forced hesse
196 ++mStats[Stats::kMinimizerStatusOk];
197 if (mMinimizer->MinValue() < 2e-4) { // within 2 um considering the pixel pitch this good enough
198 ++mStats[Stats::kMinimizerValueOk];
199 } else {
200 ++mStats[Stats::kMinimizerValueBad];
201 return false;
202 }
203 } else {
204 ++mStats[Stats::kMinimizerStatusBad];
205 return false;
206 }
207
208 // Valid solution found; calculate new position on ideal geo
209 wHit.recalculateIdeal(static_cast<float>(mMinimizer->X()[1]), static_cast<float>(mMinimizer->X()[2]));
210
211 return true;
212}
213
214short MisAlignmentHits::getDetID(const o2::math_utils::Point3D<float>& point)
215{
216 // Do not modify the path, I do not know if this is needed but lets be safe
217 gGeoManager->PushPath();
218 auto id = getDetIDFromCords(point);
219 gGeoManager->PopPath();
220 return id;
221}
222
223short MisAlignmentHits::getDetIDFromCords(const o2::math_utils::Point3D<float>& point)
224{
225 // retrive if any the node which constains the point
226 const auto node = gGeoManager->FindNode(point.X(), point.Y(), point.Z());
227 if (node == nullptr) {
228 ++mStats[Stats::kFindNodeFailed];
229 return -1;
230 }
231 ++mStats[Stats::kFindNodeSuccess];
232
233 // check if this node is a sensitive volume
234 const std::string path = gGeoManager->GetPath();
235 if (path.find(o2::its::GeometryTGeo::getITS3SensorPattern()) == std::string::npos) {
236 ++mStats[Stats::kProjNonSensitive];
237 return -1;
238 }
239 ++mStats[Stats::kProjSensitive];
240
241 return getDetIDFromPath(path);
242}
243
244short MisAlignmentHits::getDetIDFromPath(const std::string& path) const
245{
246 static const std::regex pattern{R"(/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITS3Layer(\d+)_(\d+)/ITS3CarbonForm(\d+)_(\d+)/ITS3Chip(\d+)_(\d+)/ITS3Segment(\d+)_(\d+)/ITS3RSU(\d+)_(\d+)/ITS3Tile(\d+)_(\d+)/ITS3PixelArray(\d+)_(\d+))"};
247 if (std::smatch matches; std::regex_search(path, matches, pattern)) {
248 if (matches.size() == 15) {
249 int iLayer = std::stoi(matches[1]);
250 int iCarbonForm = std::stoi(matches[4]);
251 int iSegment = std::stoi(matches[8]);
252 int iRSU = std::stoi(matches[10]);
253 int iTile = std::stoi(matches[12]);
254 return mGeo->getChipIndex(iLayer, iCarbonForm, 0, iSegment, iRSU, iTile);
255 } else {
256 LOGP(fatal, "Path did not contain expected number of matches ({})!", matches.size());
257 }
258 } else {
259 LOGP(fatal, "Path was not matched ({})!", path);
260 }
261 __builtin_unreachable();
262}
263
265{
266 auto makeFraction = [&](Stats n, Stats d) -> float { return static_cast<float>(mStats[n]) / static_cast<float>(mStats[d] + mStats[n]); };
267 LOGP(info, "Processed {} Hits (IB:{}; OB:{}) ({:.2f}%):", mStats[Stats::kHitTotal], mStats[Stats::kHitIsIB], mStats[Stats::kHitIsOB], makeFraction(Stats::kHitIsIB, Stats::kHitIsOB));
268 LOGP(info, " - Minimizer Status: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerStatusOk], mStats[Stats::kMinimizerStatusBad], makeFraction(Stats::kMinimizerStatusOk, Stats::kMinimizerStatusBad));
269 LOGP(info, " - Minimizer Value: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerValueOk], mStats[Stats::kMinimizerValueBad], makeFraction(Stats::kMinimizerValueOk, Stats::kMinimizerValueBad));
270 LOGP(info, " - Minimizer Detailed: {} Converged {} pos. forced Hesse ({:.2f}%)", mStats[Stats::kMinimizerConverged], mStats[Stats::kMinimizerHesse], makeFraction(Stats::kMinimizerConverged, Stats::kMinimizerHesse));
271 LOGP(info, " - Minimizer Detailed: {} EDM {} call limit {} other ({:.2f}%)", mStats[Stats::kMinimizerEDM], mStats[Stats::kMinimizerLimit], mStats[Stats::kMinimizerOther], makeFraction(Stats::kMinimizerEDM, Stats::kMinimizerLimit));
272 LOGP(info, " - FindNode: {} ok {} failed", mStats[Stats::kFindNodeSuccess], mStats[Stats::kFindNodeFailed]);
273 LOGP(info, " - IsSensitve: {} yes {} no ({:.2f}%)", mStats[Stats::kProjSensitive], mStats[Stats::kProjNonSensitive], makeFraction(Stats::kProjSensitive, Stats::kProjNonSensitive));
274 LOGP(info, " - IsAlive: {} yes {} no ({:.2f}%)", mStats[Stats::kHitAlive], mStats[Stats::kHitDead], makeFraction(Stats::kHitAlive, Stats::kHitDead));
275 LOGP(info, " - HasMigrated: {} yes {} no ({:.2f}%)", mStats[Stats::kHitMigrated], mStats[Stats::kHitNotMigrated], makeFraction(Stats::kHitMigrated, Stats::kHitNotMigrated));
276 // LOGP(info, " - Crosses Boundary: {} entering {} exiting {} same {} no", mStats[Stats::kHitEntBoundary], mStats[Stats::kHitExtBoundary], mStats[Stats::kHitSameBoundary], mStats[Stats::kHitNoBoundary]);
277 if (mMethod == PropMethod::Propagator) {
278 LOGP(info, " - Propagator: {} null track {} null pdg", mStats[Stats::kPropTrackNull], mStats[Stats::kPropPDGNull]);
279 }
280 LOGP(info, " --> Good Hits {} ({:.2f}%)", mStats[Stats::kHitSuccess], makeFraction(Stats::kHitSuccess, Stats::kHitIsIB));
281}
282
283void MisAlignmentHits::prepareLineMethod(WorkingHit::HitType from)
284{
285 // Set the starint point and radius
286 // always start from the entering hit that way t is always pos. defined
287 mLine.mStart = mCurWorkingHits[WorkingHit::kEntering].mPoint;
288 mLine.mRadius = mCurWorkingHits[from].mRadius;
289 mLine.mSensorID = mCurWorkingHits[from].mSensorID;
290 mLine.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1;
291 mLine.mPhi1 = mCurWorkingHits[from].mPhiBorder1;
292 // Calculate the direction vector
293 mLine.mD[0] = mCurWorkingHits[WorkingHit::kExiting].mPoint.X() - mCurWorkingHits[WorkingHit::kEntering].mPoint.X();
294 mLine.mD[1] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Y() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Y();
295 mLine.mD[2] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Z() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Z();
296}
297
298double MisAlignmentHits::StraightLine::DoEval(const double* x) const
299{
300 const double t = x[0];
301 const double phi = x[1];
302 const double z = x[2];
303 const double nphi = std::clamp((phi - mPhi1) * 2.0 / mPhiTot - 1.0, -1.0, 1.0);
304 const double nz = std::clamp((z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0, -1.0, 1.0);
305
307 double xline = mStart.X() + t * mD[0],
308 yline = mStart.Y() + t * mD[1],
309 zline = mStart.Z() + t * mD[2];
310
311 // Find the point of the deformed geometry given a certain phi' and z'
312 double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi),
313 zideal = z;
314 const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz);
315 double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz;
316
317 // Minimize the euclidean distance of the line point and the deformed point
318 return std::hypot(xline - xdef, yline - ydef, zline - zdef);
319}
320
321bool MisAlignmentHits::preparePropagatorMethod(WorkingHit::HitType from)
322{
323 mPropagator.mRadius = mCurWorkingHits[from].mRadius;
324 mPropagator.mSensorID = mCurWorkingHits[from].mSensorID;
325 mPropagator.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1;
326 mPropagator.mPhi1 = mCurWorkingHits[from].mPhiBorder1;
327 const auto mcTrack = mMCReader->getTrack(mCurWorkingHits[from].mEvent, mCurWorkingHits[from].mTrackID);
328 if (mcTrack == nullptr) {
329 ++mStats[Stats::kPropTrackNull];
330 return false;
331 }
332 const std::array<float, 3> xyz{(float)mcTrack->GetStartVertexCoordinatesX(), (float)mcTrack->GetStartVertexCoordinatesY(), (float)mcTrack->GetStartVertexCoordinatesZ()},
333 pxyz{(float)mcTrack->GetStartVertexMomentumX(), (float)mcTrack->GetStartVertexMomentumY(), (float)mcTrack->GetStartVertexMomentumZ()};
334 const TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrack->GetPdgCode());
335 if (pPDG == nullptr) {
336 ++mStats[Stats::kPropPDGNull];
337 return false;
338 }
339 mPropagator.mTrack = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false);
340 mPropagator.mBz = o2::base::Propagator::Instance()->getNominalBz();
341 return true;
342}
343
344double MisAlignmentHits::Propagator::DoEval(const double* x) const
345{
346 const double r = x[0];
347 const double phi = x[1];
348 const double z = x[2];
349 const double nphi = (phi - mPhi1) * 2.0 / mPhiTot - 1.0;
350 const double nz = (z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0;
351
352 auto trc = mTrack;
353 if (!trc.propagateTo(r, mBz)) {
354 return 999;
355 }
356 const auto glo = trc.getXYZGlo();
357
358 // Find the point of the deformed geometry given a certain phi' and z'
359 double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi),
360 zideal = z;
361 const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz);
362 double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz;
363
364 // Minimize the euclidean distance of the propagator point and the deformed point
365 return std::hypot(glo.X() - xdef, glo.Y() - ydef, glo.Z() - zdef);
366}
367
368} // namespace o2::its3::align
void SetDetectorID(short detID)
Definition BaseHits.h:78
short GetDetectorID() const
Definition BaseHits.h:73
void SetPos(math_utils::Point3D< T > const &p)
Definition BaseHits.h:88
GPUd() value_type estimateLTFast(o2 static GPUd() float estimateLTIncrement(const o2 PropagatorImpl * Instance(bool uninitialized=false)
Definition Propagator.h:143
void init(const std::filesystem::path &)
std::optional< o2::itsmft::Hit > processHit(int iEvent, const o2::itsmft::Hit &hit)
static const char * getITS3SensorPattern()
static GeometryTGeo * Instance()
int getChipIndex(int lay, int detInLay) const
void SetPosStart(const math_utils::Point3D< Float_t > &p)
Definition Hit.h:98
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLboolean r
Definition glcorearb.h:1233
GLuint id
Definition glcorearb.h:650
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
bool isDetITS3(T detID)
Definition SpecsV2.h:210
TrackParF TrackPar
Definition Track.h:29
std::string digitizationgeometry_prefix
Definition DigiParams.h:30
std::array< uint16_t, 5 > pattern