BmnRoot 26.10.0-210-gc52d34b0
Software framework of the BM@N experiment
Loading...
Searching...
No Matches
BmnAlignTestTask.cxx
Go to the documentation of this file.
1/*
2 BM@N alignment routine
3 BM@N experiment at NICA complex, JINR, 2025
4
5 Department: Math & Soft Group of HEP lab
6 Author: Igor Polev, polev@jinr.int
7
8 BmnAlignTestTask class implementation
9*/
10
11#include "BmnAlignTestTask.h"
12
13#include "BmnAlignDefines.h"
14#include "BmnHit.h"
15#include "BmnRootIterator.h"
16#include "BmnWeightedLine.h"
17#include "fairlogger/Logger.h"
18
19#include <TArrayI.h>
20#include <TClonesArray.h>
21#include <TDecompSVD.h>
22#include <TFile.h>
23#include <TMath.h>
24#include <TMatrixD.h>
25#include <TRandom.h>
26#include <TStopwatch.h>
27#include <TTree.h>
28#include <TVector3.h>
29#include <array>
30#include <cmath>
31#include <map>
32#include <memory>
33
34namespace
35{
36// truth solution
37constexpr Double_t RANK_TOL{1.0E-10}; // singular values below this share of the largest are null
38constexpr Double_t CONSTRAINT_TOL{1.0E-9}; // cm or rad, largest constraint violation the truth may keep
39// vertex tracks
40constexpr Int_t VERTEX_STEPS{3}; // Gauss-Newton steps of the vertex condition, see Make()
41} // anonymous namespace
42
43/*
44 * The test task
45 * -------------
46 * Real tracks give the directions and positions, the truth solution gives the
47 * misalignment, the measurement model makes the hits. Make() goes in three
48 * stages, all on the data in RAM except the last one, which reads the source
49 * files once more to copy what the output needs.
50 *
51 * 1. Measurements on the real data, its hits taken in the base geometry (the
52 * aligner's last result, the nominal one without it):
53 * - the mean hit error of every element, reported and used as a noise option;
54 * - the vertex hit test of the aligner splits the tracks into vertex tracks
55 * (their vertex hit is accepted) and free tracks (rejected, or none);
56 * - z0, where the tracks meet the beam, see BmnAligner::FitTargetZ().
57 * 2. The truth solution, see below.
58 * 3. The hits. A free track keeps its free line: it misses the vertex as it
59 * does in the real data, and the aligner's vertex test has it to reject. A
60 * vertex track is the line closest to its real hits (in the metric of the
61 * free fit covariance) under one hard condition: the measurement model of
62 * the target, with its truth correction, reproduces the vertex hit plus the
63 * noise of the event. For Lin3 the condition is linear in the track and one
64 * step is exact; Lin6 adds terms quadratic in it, so VERTEX_STEPS
65 * Gauss-Newton steps are taken, the Jacobian from central differences, which
66 * are exact for a quadratic function. Every tracker hit is the measurement
67 * model at the source hit z plus Gauss noise in the element frame. The SiBT
68 * hits are copied, so the reader builds the same beam line and vertex hit.
69 *
70 * z0
71 * --
72 * The truth puts the target at z0: its dZ gets z_c - z0 on top of the draw.
73 * The measurement model reads the vertex hit at z_c as the track at z_c - dZ
74 * (Lin3: X = track(z - dZ) + dX), so the tracks then meet the beam at z0.
75 *
76 * The truth solution
77 * ------------------
78 * A test of the solver needs a truth the solver can reach. So the truth must
79 * satisfy every constraint the alignment will use, and be as likely as the
80 * truth sigmas - the aligner's presigmas - say. It is drawn from that prior,
81 * conditioned on the constraints.
82 *
83 * The draw q is the own share of every parameter: q_k = Ref_k + Gaus(0, s_k),
84 * s_k = sigmaScale * Sigma_k. A parameter without a sigma is not drawn.
85 * The stored solution p holds effective leaf corrections, p = S q with
86 * S = [ I J ; 0 I ], and the constraints are written on p: C p = r, i.e.
87 * G q = r with G = C S.
88 *
89 * 1. A constraint with a single coefficient fixes that parameter, e.g. every
90 * rotation in a Lin3 alignment. It is not drawn but set to r / c.
91 * 2. The rest is the conditional Gaussian draw, a projection in the metric of
92 * the prior covariance V = diag(s^2):
93 *
94 * q' = q - K (G q - r), K = V G^T (G V G^T)^+
95 *
96 * Each parameter moves in proportion to its variance, so whatever was not
97 * drawn stays in place. The fixes of step 1 stay in G: on a leaf, they
98 * still have to cancel what the ancestors of the element bring.
99 *
100 * Why q' is the draw under the constraints: q' is linear in q, so q' and G q
101 * are jointly Gaussian, and Cov(q', G q) = (I - K G) V G^T = 0. So they are
102 * independent: the law of q' does not depend on the value of G q, and where
103 * G q = r it is q itself. Hence law(q') = law(q | G q = r). q' is also the
104 * projection of q onto the constraints in the metric of V^-1, the minimum of
105 * (x - q)^T V^-1 (x - q) under G x = r. All constraint types are plain rows
106 * of C by now, so one step serves them all at once.
107 */
108
110 const char* dataPath,
111 const char* solutionPath,
112 Double_t sigmaScale,
113 ENoise noise,
114 const TVector2& stdDevXY)
115{
116 TStopwatch timer;
117 LOG(state) << " > BmnAlignTestTask : preparing test task";
118 if (!aligner.Initialized()) {
119 LOG(error) << "<!> BmnAligner is not initialized";
120 LOG(state) << " < BmnAlignTestTask : test task preparation terminated";
121 return kFALSE;
122 }
123 if (!aligner.Hierarchy().Ready()) {
124 LOG(error) << " <!> BmnAlignTestTask::Make: no parameter layout, PrepareData() must run first";
125 return kFALSE;
126 }
127 if (sigmaScale < 0.0) {
128 LOG(error) << " <!> BmnAlignTestTask::Make: sigmaScale " << sigmaScale << " is negative";
129 return kFALSE;
130 }
131 if (kUserValues == noise && (stdDevXY.X() < 0.0 || stdDevXY.Y() < 0.0)) {
132 LOG(error) << " <!> BmnAlignTestTask::Make: noise StdDev (" << stdDevXY.X() << ", " << stdDevXY.Y()
133 << ") is negative";
134 return kFALSE;
135 }
136
137 const BmnAlignHierarchy& hier{aligner.Hierarchy()};
138 const BmnDetectorModel& model{*aligner.DetectorModel()};
139 const BmnMeasureModel& measure{*aligner.MeasureModel()};
140 const Int_t nElem{hier.LeafCount()}, trgID{model.TargetID()}, trgIdx{trgID >= 0 ? model.Idx(trgID) : -1};
141 const Bool_t withBeam{trgID >= 0};
142 const Double_t zC{0.5 * (model.Target().zStart + model.Target().zEnd)};
143
144 // prepare output file
145 TFile outFile(dataPath, "RECREATE");
146 if (outFile.IsZombie()) {
147 LOG(error) << " <!> BmnAlignTestTask::Make: failed to create output file " << dataPath;
148 return kFALSE;
149 }
150 outFile.cd();
151
152 std::unique_ptr<BmnAlignResult> pResultCopy;
153 auto RestoreResults = [&]() {
154 if (pResultCopy)
155 aligner.ResultLast() = *pResultCopy;
156 else
157 aligner.DropLastResult(); // it was null at the beginning of the method
158 };
159 VecSVectGL_t baseA(nElem); // the base geometry, see the comment above
160 if (aligner.Results().empty())
161 aligner.AddBlankSolution();
162 else {
163 pResultCopy = std::make_unique<BmnAlignResult>(aligner.ResultLast());
164 for (Int_t i = 0; i < nElem; i++)
165 baseA[i] = aligner.ResultLast().A(i);
166 }
167
168 // Free lines through the tracker hits of the current track, in the base
169 // geometry. Returns kTRUE if the track has a vertex hit, and gives its
170 // position and weights (zero weights: disabled by the aligner's test).
171 // Leaves the iterator past the last hit.
172 auto FitFree = [&](BmnDataIterator& itr, BmnWeightedLine& lineX, BmnWeightedLine& lineY, SVect3& vertex,
173 SVect2& vertexW) {
174 Bool_t hasVertex{kFALSE};
175 for (; !itr.EndOfHits(); itr.NextHit()) {
176 const Int_t detID{itr.HitDetectorID()};
177 if (detID == trgID) {
178 vertex = SVect3(itr.HitX(), itr.HitY(), itr.HitZ());
179 vertexW = SVect2(itr.HitWx(), itr.HitWy());
180 hasVertex = kTRUE;
181 continue;
182 }
183 SVect3 hit{itr.HitX(), itr.HitY(), itr.HitZ()};
184 measure.AlignHitApproximately(hit, baseA[model.Idx(detID)]);
185 lineX.Add(hit(iZ), hit(iX), itr.HitWx());
186 lineY.Add(hit(iZ), hit(iY), itr.HitWy());
187 }
188 return hasVertex;
189 };
190
191 // ==== stage 1: measurements on the real data ====
192 BmnRamIterator& ram{*aligner.RamIterator()};
193 VecDouble_t errX(nElem, 0.0), errY(nElem, 0.0);
194 VecInt_t errN(nElem, 0);
195 aligner.ProgressBar()->Init();
196 for (ram.ResetAll(); !ram.EndOfTracks(); ram.NextTrack())
197 for (; !ram.EndOfHits(); ram.NextHit()) {
198 const Int_t idx{model.Idx(ram.HitDetectorID())};
199 if (idx == trgIdx)
200 continue;
201 errX[idx] += 1.0 / std::sqrt(ram.HitWx());
202 errY[idx] += 1.0 / std::sqrt(ram.HitWy());
203 errN[idx]++;
204 }
205 aligner.ProgressBar()->Clear();
206 LOG(info) << " mean hit errors of the elements in the source data, x and y (cm):";
207 for (Int_t i = 0; i < nElem; i++) {
208 if (i == trgIdx)
209 continue;
210 errX[i] /= errN[i]; // PrepareData() refuses an element without hits
211 errY[i] /= errN[i];
212 LOG(info) << Form(" det %3d (%s) : %.2e %.2e", i, model.DecodeID(model.EncodedID(i)).c_str(), errX[i],
213 errY[i]);
214 }
215
216 VecBool_t vertexTrack(aligner.TotalTracks(), kFALSE);
217 Double_t z0{zC};
218 if (withBeam) {
219 // runs the vertex hit test too, whose hit states the flags below read
220 Double_t z0Err;
221 if (!aligner.FitTargetZ(z0, z0Err)) {
222 z0 = zC;
223 LOG(warning) << " ! z0 is left at the target centre";
224 }
225 Int_t iTrack{0}, vertexTracks{0};
226 aligner.ProgressBar()->Init();
227 for (ram.ResetAll(); !ram.EndOfTracks(); ram.NextTrack(), iTrack++) {
228 // the vertex hit comes first, and a disabled one has zero weights
229 vertexTrack[iTrack] =
230 !ram.EndOfHits() && ram.HitDetectorID() == trgID && ram.HitWx() > 0.0 && ram.HitWy() > 0.0;
231 vertexTracks += vertexTrack[iTrack];
232 }
233 aligner.ProgressBar()->Clear();
234 LOG(info) << Form(" %d vertex tracks, %zu free tracks (vertex hit rejected or absent)", vertexTracks,
235 vertexTrack.size() - vertexTracks);
236 } // if(withBeam)
237
238 // ==== stage 2: the truth solution, see the comment above ====
239 const BmnAlignPriors& priors{aligner.Priors()};
240 const Int_t nFull{hier.AllParams()}, nCnstr{priors.ConstCount()};
241 TMatrixD mS(nFull, nFull);
242 mS.UnitMatrix();
243 if (hier.CompositeParams()) // SetSub refuses the empty J of a flat model
244 mS.SetSub(0, hier.CompositeParamBase(), hier.J());
245
246 TVectorD vQ(nFull), // own shares
247 vVar(nFull); // their prior variance, zero where nothing is drawn
248 for (const ParamRef& p : hier.Layout().All()) {
249 const Double_t sigma{sigmaScale * priors.SigmaAt(p.full)};
250 if (sigma <= 0.0)
251 continue;
252 vQ(p.full) = priors.ref[p.full] + gRandom->Gaus(0.0, sigma);
253 vVar(p.full) = sigma * sigma;
254 }
255 if (withBeam)
256 vQ(hier.Layout().ByNode()[trgIdx].params[idZ].full) += zC - z0;
257
258 // RMS of the effective element corrections and of the composite own shares,
259 // per correction. The target is left out: its dZ carries z_c - z0.
260 auto LogRms = [&](const TVectorD& q, const char* stage) {
261 const TVectorD p{mS * q};
262 SVectGL leaf, comp;
263 for (const ParamRef& r : hier.Layout().All()) {
264 if (!r.IsComposite() && r.leaf == trgIdx)
265 continue; // works for targetless model also
266 (r.IsComposite() ? comp : leaf)(r.prm) += p(r.full) * p(r.full);
267 }
268 const Int_t nLeaf{nElem - withBeam}, nComp{hier.CompositeCount()};
269 auto Row = [](const SVectGL& sum, Int_t n) {
270 TString row;
271 for (Int_t k = 0; k < BMN_GLOBAL_PARAMS_PD; k++)
272 row += Form(" %10.3e", n ? std::sqrt(sum(k) / n) : 0.0);
273 return row;
274 };
275 LOG(info) << Form(" %-12s elements ", stage) << Row(leaf, nLeaf);
276 LOG_IF(info, nComp) << Form(" %-12s composites", stage) << Row(comp, nComp);
277 };
278 TString head;
279 for (Int_t k = 0; k < BMN_GLOBAL_PARAMS_PD; k++)
280 head += Form(" %10s", BmnParamLayout::ParamName(k));
281 LOG(info) << Form(" %-12s %-10s", "truth RMS", "(cm, rad)") << head;
282 LogRms(vQ, "drawn");
283
284 VecBool_t isFixed(nFull, kFALSE);
285 for (Int_t j = 0; j < nCnstr; j++) {
286 const TMatrixDRow_const c(priors.constraints, j);
287 Int_t k{-1}, terms{0};
288 for (Int_t i = 0; i < nFull; i++)
289 if (0.0 != c(i)) {
290 k = i;
291 terms++;
292 }
293 if (1 != terms)
294 continue;
295 vQ(k) = priors.rhs(j) / c(k);
296 vVar(k) = 0.0;
297 isFixed[k] = kTRUE;
298 }
299 Int_t drawn{0}, fixed{0};
300 for (Int_t i = 0; i < nFull; i++) {
301 if (vVar(i) > 0.0)
302 drawn++;
303 if (isFixed[i])
304 fixed++;
305 }
306
307 if (nCnstr) {
308 const TVectorD& vR{priors.rhs};
309 const TMatrixD mG{priors.constraints, TMatrixD::kMult, mS};
310 TMatrixD mGV{mG}; // G V
311 mGV.NormByRow(vVar, "M"); // despite the name: column i times vVar(i)
312 const TMatrixD mM{mGV, TMatrixD::kMultTranspose, mG}; // G V G^T
313 // all-zero when nothing was drawn, and SVD does not survive that
314 if (mM.NonZeros()) {
315 TVectorD vRes{mG * vQ};
316 vRes -= vR;
317 TDecompSVD svd(mM);
318 if (!svd.Decompose()) {
319 LOG(error) << " <!> BmnAlignTestTask::Make: SVD failed, the truth cannot be projected"
320 " onto the constraints";
321 RestoreResults();
322 return kFALSE;
323 }
324 // pseudo-inverse: redundant constraints leave null singular values
325 const TVectorD& sig{svd.GetSig()}; // TDecompSVD returns them in decreasing order
326 TVectorD vX{TMatrixD(TMatrixD::kTransposed, svd.GetU()) * vRes};
327 for (Int_t j = 0; j < nCnstr; j++)
328 vX(j) = sig(j) > sig(0) * RANK_TOL ? vX(j) / sig(j) : 0.0;
329 vX = svd.GetV() * vX;
330 vQ -= TMatrixD(TMatrixD::kTransposed, mGV) * vX;
331 }
332 TVectorD vViol{mG * vQ};
333 vViol -= vR;
334 Int_t violated{0};
335 for (Int_t j = 0; j < nCnstr; j++)
336 if (std::fabs(vViol(j)) > CONSTRAINT_TOL) {
337 LOG(error) << " <!> BmnAlignTestTask::Make: constraint #" << j + 1 << " cannot hold, violated by "
338 << vViol(j) << " - it rests on parameters without a truth sigma";
339 violated++;
340 }
341 if (violated) {
342 RestoreResults();
343 return kFALSE;
344 }
345 }
346 LogRms(vQ, "constrained");
347
348 BmnAlignResult& truth{aligner.ResultLast()};
349 truth.A() = mS * vQ;
350 truth.SeparateA();
351 LOG(info) << " truth drawn: " << drawn << " parameter(s) from the truth sigmas times " << sigmaScale << ", "
352 << fixed << " fixed by single-parameter constraints, all " << nCnstr << " constraint(s) hold";
353 LOG_IF(warning, !drawn && sigmaScale > 0.0)
354 << " ! no parameter carries a truth sigma: the truth is the nominal geometry";
355 if (withBeam) {
356 const SVectGL& aTrg{truth.A(trgIdx)};
357 LOG(info) << Form(" target truth : dX %+.3e, dY %+.3e, dZ %+.3e cm", aTrg(idX), aTrg(idY), aTrg(idZ));
358 }
359
360 // ==== stage 3: the synthetic hits ====
361 const Bool_t noiseX{kUserValues != noise || 0.0 != stdDevXY.X()},
362 noiseY{kUserValues != noise || 0.0 != stdDevXY.Y()};
363 TClonesArray trackData("CbmStsTrack", 200), hitData("CbmStsHit", 200 * model.Alignables()), beamData("BmnHit"),
364 *tdataPtr{&trackData}, *hdataPtr{&hitData}, *bdataPtr{&beamData};
365 BmnRootIterator& root{*aligner.RootIterator()};
366 // Note ------------------------------------------------------------------------
367 // When copying a TTree structure with CloneTree(0) if data is governed by
368 // TChain holding more than one file, modification of data will be lost and
369 // replaced by original data.
370 // Cause -----------------------------------------------------------------------
371 // CloneTree(0) registers the clone for TChain::Notify() callbacks.
372 // When the chain transitions between files (including on ResetAll/Restart),
373 // Notify() resets the clone's branch addresses back to the chain's own buffers,
374 // silently undoing SetBranchAddress overrides. This causes Fill() to write the
375 // original (unmodified) chain data instead of our modified trackData/hitData.
376 // Conclusion ------------------------------------------------------------------
377 // Can't use CloneTree(0) to copy structure of TTree from original data,
378 // must create an independent TTree instead.
379 // Aknowledgement --------------------------------------------------------------
380 // Thanks to Sonnet 4.6 for this non-trivial issue diagnostic.
381 TTree outTree(root.GetChain().GetTree()->GetName(), root.GetChain().GetTree()->GetTitle());
382 outTree.SetDirectory(&outFile);
383 outTree.Branch(BMN_TRACKS_BRANCH_NAME, "TClonesArray", &tdataPtr);
384 outTree.Branch(BMN_HITS_BRANCH_NAME, "TClonesArray", &hdataPtr);
385 if (withBeam)
386 outTree.Branch(BMN_BEAM_BRANCH_NAME, "TClonesArray", &bdataPtr);
387
388 std::map<Int_t, std::array<Double_t, 3>> sibtErr; // per SiBT station: sum of dx, of dy, hit count
389 Int_t iTrack{0}, freeWithVertex{0};
390 Double_t vertexNoiseX{0.0}, // beam error of the current event, common to its tracks
391 vertexNoiseY{0.0};
392 aligner.ProgressBar()->Init();
393 root.ResetAll();
394 while (!root.EndOfTracks()) { // `while` instead of `for` is necessary here, see comment below
395 if (withBeam && root.IsNewEvent()) {
396 const TClonesArray* beamHits{root.GetBeamHits()};
397 for (Int_t i = 0; i < beamHits->GetEntriesFast(); i++) {
398 const BmnHit& hit{*static_cast<const BmnHit*>(beamHits->At(i))};
399 new (beamData[i]) BmnHit(hit);
400 std::array<Double_t, 3>& err{sibtErr[hit.GetStation()]};
401 err[0] += hit.GetDx();
402 err[1] += hit.GetDy();
403 err[2] += 1.0;
404 }
405 vertexNoiseX = noiseX ? gRandom->Gaus(0.0, std::sqrt(root.VertexVarX())) : 0.0;
406 vertexNoiseY = noiseY ? gRandom->Gaus(0.0, std::sqrt(root.VertexVarY())) : 0.0;
407 }
408
409 // the true track
410 BmnWeightedLine lineX, lineY;
411 SVect3 vertex;
412 SVect2 vertexW;
413 const Bool_t hasVertex{FitFree(root, lineX, lineY, vertex, vertexW)};
414 if (lineX.Det() <= 0.0 || lineY.Det() <= 0.0) {
415 LOG(error) << " <!> BmnAlignTestTask::Make: track #" << iTrack << " has too few hits for a straight line";
416 RestoreResults();
417 return kFALSE;
418 }
419 const SVectLC alphaFree{lineX.At0(), lineX.Slope(), lineY.At0(), lineY.Slope()};
420 SVectLC alpha{alphaFree};
421 if (vertexTrack[iTrack]) {
422 const SVectGL& aTrg{truth.A(trgIdx)};
423 const Double_t zV{vertex(iZ)};
424 auto Vertex = [&](const SVectLC& a) { return SVect2(measure.X(zV, aTrg, a), measure.Y(zV, aTrg, a)); };
425 const SVect2 vB{vertex(iX) + vertexNoiseX, vertex(iY) + vertexNoiseY};
426 SMatrLC mCov; // of the free lines, x and y apart
427 mCov(iX0, iX0) = lineX.Var(0.0);
428 mCov(iX0, iX1) = lineX.Cov(); // symmetric storage: (iX1, iX0) is the same element
429 mCov(iX1, iX1) = lineX.SlopeVar();
430 mCov(iY0, iY0) = lineY.Var(0.0);
431 mCov(iY0, iY1) = lineY.Cov();
432 mCov(iY1, iY1) = lineY.SlopeVar();
433 for (Int_t step = 0; step < VERTEX_STEPS; step++) {
434 RM::SMatrix<Double_t, 2, BMN_LOCAL_PARAMS_PT> mA; // Jacobian of the condition at alpha
435 for (Int_t j = 0; j < BMN_LOCAL_PARAMS_PT; j++) {
436 SVectLC up{alpha}, down{alpha};
437 up(j) += 1.0;
438 down(j) -= 1.0;
439 const SVect2 d{0.5 * (Vertex(up) - Vertex(down))};
440 mA(0, j) = d(0);
441 mA(1, j) = d(1);
442 }
445 if (!mM.InvertChol()) {
446 LOG(error) << " <!> BmnAlignTestTask::Make: track #" << iTrack
447 << " cannot be put through the vertex";
448 RestoreResults();
449 return kFALSE;
450 }
451 // the free line projected onto the condition linearized at alpha
452 alpha = alphaFree - mCAt * (mM * (mA * (alphaFree - alpha) + Vertex(alpha) - vB));
453 }
454 } else if (hasVertex)
455 freeWithVertex++;
456
457 // the synthetic hits
458 Int_t arrIdx{0};
459 TArrayI hitArray(root.HitsInTrack() - root.HasVertexHit());
460 for (root.ResetHits(); !root.EndOfHits(); root.NextHit()) {
461 const BmnRootIterator::Hit_t* srcHit{root.GetHit()};
462 if (!srcHit)
463 continue; // the vertex hit: the reader builds it again from the SiBT hits
464 const Int_t hitIdx{hitData.GetEntriesFast()}, hitDet{srcHit->GetDetectorID()},
465 detIdx{model.IdxFromHit(hitDet)};
466 const Double_t z0hit{srcHit->GetZ()},
467 dx{kHitErrors == noise ? srcHit->GetDx()
468 : kElementMeans == noise ? errX[detIdx]
469 : stdDevXY.X()},
470 dy{kHitErrors == noise ? srcHit->GetDy()
471 : kElementMeans == noise ? errY[detIdx]
472 : stdDevXY.Y()};
473 TVector3 pos{// not const: CbmStsHit takes them by non-const reference
474 measure.X(z0hit, truth.A(detIdx), alpha) + (dx ? gRandom->Gaus(0, dx) : 0.0),
475 measure.Y(z0hit, truth.A(detIdx), alpha) + (dy ? gRandom->Gaus(0, dy) : 0.0), z0hit},
476 dpos{dx ? dx : BmnHitRecord::MIN_HIT_ERROR, dy ? dy : BmnHitRecord::MIN_HIT_ERROR, 0.0};
477 new (hitData[hitIdx]) CbmStsHit(hitDet, pos, dpos, 0.0, 0, 0);
478 hitArray[arrIdx++] = hitIdx;
479 }
480
481 // the synthetic track
482 BmnRootIterator::Track_t* srcTrack{const_cast<BmnRootIterator::Track_t*>(root.GetTrack())};
483 CbmStsTrack* newTrack = new (trackData[trackData.GetEntriesFast()]) CbmStsTrack();
484 newTrack->SetStsHits(hitArray);
485 newTrack->SetTrkID(srcTrack->GetTrkID());
486 newTrack->SetPidHypo(srcTrack->GetPidHypo());
487 newTrack->SetParamFirst(*srcTrack->GetParamFirst());
488 newTrack->SetParamLast(*srcTrack->GetParamLast());
489 newTrack->SetFlag(srcTrack->GetFlag());
490 newTrack->SetChi2(srcTrack->GetChi2());
491 newTrack->SetNDF(srcTrack->GetNDF());
492 newTrack->SetB(srcTrack->GetB());
493 newTrack->SetStsEv(srcTrack->GetStsEv());
494
495 root.NextTrack();
496 iTrack++;
497 // flush accumulated event if the next track opens a new one or there is none.
498 // CAUTION: the test looks one track ahead - NextTrack() must stay right before it
499 if (root.IsNewEvent() || root.EndOfTracks()) {
500 outFile.cd(); // just in case...
501 outTree.Fill();
502 trackData.Delete(); // not using Clear() to avoid memory leaks:
503 hitData.Delete(); // maybe it is safe now, but can't be sure in future changes
504 beamData.Delete();
505 }
506 } // loop over tracks
507 aligner.ProgressBar()->Clear();
508
509 for (const auto& [station, err] : sibtErr)
510 LOG(info) << Form(" SiBT station %d : mean hit errors x %.2e, y %.2e cm", station, err[0] / err[2],
511 err[1] / err[2]);
512 LOG_IF(info, withBeam) << " " << freeWithVertex
513 << " free tracks take a vertex hit in the task:"
514 " the aligner's vertex test should reject about as many";
515
516 outFile.Write("", TObject::kOverwrite);
517 outFile.Close();
518 LOG(info) << " test data successfully written to " << dataPath;
519 if (aligner.SaveSolution(solutionPath)) {
520 LOG(info) << " test solution successfully written to " << solutionPath;
521 } else {
522 LOG(error) << " <!> BmnAlignTestTask::Make: failed to save solution to " << solutionPath;
523 RestoreResults();
524 return kFALSE;
525 }
526
527 RestoreResults();
528 LOG(state) << Form(" < BmnAlignTestTask : test task prepared in %.1fs", timer.RealTime());
529 return kTRUE;
530}
std::vector< Double_t > VecDouble_t
std::vector< Bool_t > VecBool_t
@ iY1
@ iY0
@ iX1
@ iX0
RM::SVector< Double_t, 3 > SVect3
@ idX
@ idZ
@ idY
std::vector< SVectGL > VecSVectGL_t
RM::SVector< Double_t, BMN_LOCAL_PARAMS_PT > SVectLC
RM::SVector< Double_t, 2 > SVect2
RM::SVector< Double_t, BMN_GLOBAL_PARAMS_PD > SVectGL
std::vector< Int_t > VecInt_t
@ iZ
@ iX
@ iY
RM::SMatrix< Double_t, BMN_LOCAL_PARAMS_PT, BMN_LOCAL_PARAMS_PT, RM::MatRepSym< Double_t, BMN_LOCAL_PARAMS_PT > > SMatrLC
#define BMN_LOCAL_PARAMS_PT
#define BMN_GLOBAL_PARAMS_PD
#define BMN_BEAM_BRANCH_NAME
#define BMN_TRACKS_BRANCH_NAME
#define BMN_HITS_BRANCH_NAME
int i
Definition P4_F32vec4.h:22
bool Bool_t
int Int_t
constexpr Bool_t kFALSE
double Double_t
constexpr Bool_t kTRUE
TMatrixTRow_const< Double_t > TMatrixDRow_const
TMatrixT< Double_t > TMatrixD
R__EXTERN TRandom * gRandom
char * Form(const char *fmt,...)
TVectorT< Double_t > TVectorD
Int_t LeafCount() const noexcept
const TMatrixD & J() const noexcept
Int_t CompositeCount() const noexcept
Int_t AllParams() const noexcept
const BmnParamLayout & Layout() const noexcept
Int_t CompositeParams() const noexcept
Bool_t Ready() const noexcept
Int_t CompositeParamBase() const noexcept
Double_t SigmaAt(Int_t full) const noexcept
VecDouble_t ref
TMatrixD constraints
Int_t ConstCount() const noexcept
SVectGL & A(Int_t index) noexcept
static Bool_t Make(BmnAligner &aligner, const char *dataPath, const char *solutionPath, Double_t sigmaScale=1.0, ENoise noise=kHitErrors, const TVector2 &stdDevXY=TVector2())
void AddBlankSolution()
Definition BmnAligner.h:226
BmnSimpleProgressBar * ProgressBar() const noexcept
Definition BmnAligner.h:85
const BmnDetectorModel * DetectorModel() const noexcept
Definition BmnAligner.h:73
Bool_t Initialized() const noexcept
Definition BmnAligner.h:66
const BmnAlignResult & ResultLast() const
Definition BmnAligner.h:272
void DropLastResult()
Definition BmnAligner.h:236
BmnRamIterator * RamIterator() const noexcept
Definition BmnAligner.h:76
Results_t & Results() noexcept
Definition BmnAligner.h:69
const BmnMeasureModel * MeasureModel() const noexcept
Definition BmnAligner.h:74
BmnRootIterator * RootIterator() noexcept
Definition BmnAligner.h:75
Int_t TotalTracks() const noexcept
Definition BmnAligner.h:67
const BmnAlignPriors & Priors() const noexcept
Definition BmnAligner.h:77
Bool_t SaveSolution(const char *jsonPath, Bool_t withAlpha=kFALSE)
const BmnAlignHierarchy & Hierarchy() const noexcept
Definition BmnAligner.h:71
Bool_t FitTargetZ(Double_t &z0, Double_t &z0Err)
virtual Double_t HitX() const =0
virtual Double_t HitY() const =0
virtual Double_t HitZ() const =0
virtual Double_t HitWx() const =0
Bool_t EndOfTracks() const noexcept
virtual Int_t HitDetectorID() const =0
virtual void NextHit()=0
virtual Double_t HitWy() const =0
Bool_t EndOfHits() const noexcept
const Layer & Target() const
Int_t EncodedID(Int_t idx) const
virtual std::string DecodeID(Int_t id) const =0
virtual Int_t Alignables() const =0
virtual Int_t TargetID() const
Int_t Idx(Int_t ID) const
Int_t IdxFromHit(Int_t hitID) const
static constexpr Double_t MIN_HIT_ERROR
Short_t GetStation() const
Definition BmnHit.h:35
virtual Double_t Y(Double_t z0, const SVectGL &gl, const SVectLC &lc) const noexcept=0
virtual void AlignHitApproximately(SVect3 &hit, const SVectGL &a) const noexcept=0
virtual Double_t X(Double_t z0, const SVectGL &gl, const SVectLC &lc) const noexcept=0
const Nodes_t & ByNode() const noexcept
static const char * ParamName(Int_t p)
const Range_t & All() const noexcept
virtual void NextHit() override
virtual void ResetAll() override
virtual Int_t HitDetectorID() const override
virtual Double_t HitWy() const override
virtual Double_t HitWx() const override
virtual void NextTrack() override
virtual void ResetAll() override
TChain & GetChain() const noexcept
const Hit_t * GetHit() const noexcept
Bool_t IsNewEvent() const noexcept
const Track_t * GetTrack() const noexcept
Double_t VertexVarY() const noexcept
virtual Int_t HitsInTrack() const override
Bool_t HasVertexHit() const noexcept
virtual void ResetHits() override
CbmStsTrack Track_t
virtual void NextTrack() override
Double_t VertexVarX() const noexcept
virtual void NextHit() override
const TClonesArray * GetBeamHits() const
Double_t Det() const noexcept
Double_t At0() const noexcept
Double_t Cov() const noexcept
Double_t Var(Double_t z) const noexcept
Double_t Slope() const noexcept
Double_t SlopeVar() const noexcept
Double_t GetB() const
Definition CbmStsTrack.h:68
void SetNDF(Int_t ndf)
Definition CbmStsTrack.h:87
FairTrackParam * GetParamLast()
Definition CbmStsTrack.h:70
int GetTrkID()
Definition CbmStsTrack.h:79
Int_t GetPidHypo() const
Definition CbmStsTrack.h:64
void SetParamLast(FairTrackParam &par)
Definition CbmStsTrack.h:84
void SetStsHits(TArrayI &hitsArr)
Definition CbmStsTrack.h:89
void SetStsEv(Int_t evNo)
Definition CbmStsTrack.h:90
void SetParamFirst(FairTrackParam &par)
Definition CbmStsTrack.h:83
Int_t GetFlag() const
Definition CbmStsTrack.h:65
void SetChi2(Double_t chi2)
Definition CbmStsTrack.h:86
void SetFlag(Int_t flag)
Definition CbmStsTrack.h:85
Double_t GetChi2() const
Definition CbmStsTrack.h:66
void SetTrkID(int id)
Definition CbmStsTrack.h:78
Int_t GetStsEv() const
Definition CbmStsTrack.h:72
FairTrackParam * GetParamFirst()
Definition CbmStsTrack.h:69
Int_t GetNDF() const
Definition CbmStsTrack.h:67
void SetPidHypo(Int_t pid)
Modifiers.
Definition CbmStsTrack.h:82
void SetB(Double_t b)
Definition CbmStsTrack.h:88
TTree * GetTree() const override
const TVectorD & GetSig()
const TMatrixD & GetV()
Bool_t Decompose() override
const TMatrixD & GetU()
Bool_t cd() override
Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) const override
void Close(Option_t *option="") override
TMatrixT< Element > & NormByRow(const TVectorT< Element > &v, Option_t *option="D")
TMatrixTBase< Element > & SetSub(Int_t row_lwb, Int_t col_lwb, const TMatrixTBase< Element > &source) override
const char * GetName() const override
const char * GetTitle() const override
Int_t GetEntriesFast() const
TObject * At(Int_t idx) const override
R__ALWAYS_INLINE Bool_t IsZombie() const
Double_t RealTime()
virtual Int_t Fill()
virtual void SetDirectory(TDirectory *dir)
virtual Int_t Branch(const char *folder, Int_t bufsize=32000, Int_t splitlevel=99)
Double_t Y() const
Double_t X() const
Expr< TransposeOp< Expr< A, T, D1, D2, R >, T, D1, D2 >, T, D2, D1, typename TranspPolicy< T, D1, D2, R >::RepType > Transpose(const Expr< A, T, D1, D2, R > &rhs)
T Similarity(const Expr< A, T, D, D, R > &lhs, const SVector< T, D > &rhs)