BmnRoot 26.10.0-210-gc52d34b0
Software framework of the BM@N experiment
Loading...
Searching...
No Matches
histoGL.C
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: Claude (Opus 4.8)
7 Supervisor: Igor Polev, polev@jinr.int
8
9 Histogramming method of planar-detector alignment - WHOLE-DETECTOR view.
10
11 Companion to histo.C. Where histo.C fills and reads the residual-histogram
12 set separately for every alignable element, histoGL.C pools ALL hits of the
13 whole detector into a single set of histograms - it reports the ensemble
14 first-order corrections rather than per-element ones.
15
16 The reading rules are identical to histo.C (see there for the derivation):
17
18 histogram / profile of rx read as gives
19 -------------------------- ---------- -----------------
20 rx mean dX
21 rx vs beta_x slope dZ = -slope
22 rx vs y_bar slope psi = -slope
23 rx vs beta_x * y_bar slope phi = -slope
24 rx vs beta_x * x_bar slope theta = +slope
25
26 (dY and the alternative dZ/phi/theta/psi come from the symmetric ry
27 expansion, exactly as in histo.C.)
28
29 Two extra panels fill the top row: the running evolution of ONE selected
30 correction as more and more tracks are accounted for - the X-solution on
31 the left, the Y-solution on the right. Watching the two independent
32 readings settle (and, for the shared parameters, converge toward each
33 other) is a direct check of how much data the ensemble reading needs and
34 whether the single-covariate approximation is self-consistent.
35
36 Interactive: the Left / Right arrows no longer browse detector elements
37 (there is only one, pooled histogram set now) - they cycle which
38 correction the two evolution panels display. q or Esc quits.
39
40 Run on a DST reconstructed with the current best alignment; the numbers
41 printed are the ensemble corrections still to be applied.
42*/
43
44// headers below are for IDE syntax to work and they will be
45// turned off during execution in ROOT macro mode
46#ifndef __CLING__
47#include "BmnAlignModels.h" // IWYU pragma: keep
48#include "BmnRootIterator.h"
50#include "BmnWeightedLine.h"
51
52#include <TCanvas.h>
53#include <TF1.h>
54#include <TGraph.h>
55#include <TGraphErrors.h>
56#include <TH1D.h>
57#include <TH2D.h>
58#include <TLine.h>
59#include <TPad.h>
60#include <TPaveText.h>
61#include <TProfile.h>
62#include <TROOT.h>
63#include <TString.h>
64#include <TStyle.h>
65#include <TSystem.h>
66#include <algorithm>
67#include <array>
68#include <cmath>
69#include <iomanip>
70#include <iostream>
71#include <vector>
72#endif
73
74// POSIX terminal control for single-key (arrow) interactive parameter cycling.
75// Kept out of the CLING-skipped block: these are not autoloaded by ROOT.
76#include <termios.h>
77#include <unistd.h>
78
79// correction parameters, ordered as BmnParamLayout::ParamNames().
80// Local enum so the macro stays self-contained in interpreted ROOT mode
81#define PARAM_CNT 6
91constexpr static const char* PARAM_NAMES[PARAM_CNT]{"dX", "dY", "dZ", "phi", "theta", "psi"};
92constexpr static const char* PARAM_UNIT[PARAM_CNT]{"cm", "cm", "cm", "rad", "rad", "rad"};
93// how each parameter is read from its diagnostic: mean of residual (shift)
94// or slope of residual vs covariate, and the sign that turns the reading
95// into the correction (matches the rx expansion in the header)
97// two independent readings per rotation/dZ: from the X-residual and from the
98// Y-residual. Only the sign of psi differs between them (see dXdA / dYdA).
99constexpr static const Double_t PARAM_SIGN_X[PARAM_CNT]{+1.0, +1.0, -1.0, -1.0, +1.0, -1.0};
100constexpr static const Double_t PARAM_SIGN_Y[PARAM_CNT]{+1.0, +1.0, -1.0, -1.0, +1.0, +1.0};
101constexpr static const char* PARAM_DIAG[PARAM_CNT]{// X-residual reading
102 "mean of X-residual",
103 "mean of Y-residual",
104 "slope of X-res vs track x-slope",
105 "slope of X-res vs (x-slope * y)",
106 "slope of X-res vs (x-slope * x)",
107 "slope of X-res vs y"};
108constexpr static const char* PARAM_DIAG_Y[PARAM_CNT]{// Y-residual reading
109 "mean of X-residual",
110 "mean of Y-residual",
111 "slope of Y-res vs track y-slope",
112 "slope of Y-res vs (y-slope * y)",
113 "slope of Y-res vs (y-slope * x)",
114 "slope of Y-res vs x"};
115constexpr static const char* COV_TITLE_X[PARAM_CNT]{"X residual, cm",
116 "Y residual, cm",
117 "track x-slope #beta_{x}",
118 "#beta_{x} #upoint y_{bar}, cm",
119 "#beta_{x} #upoint x_{bar}, cm",
120 "y_{bar}, cm"};
121constexpr static const char* COV_TITLE_Y[PARAM_CNT]{"X residual, cm",
122 "Y residual, cm",
123 "track y-slope #beta_{y}",
124 "#beta_{y} #upoint y_{bar}, cm",
125 "#beta_{y} #upoint x_{bar}, cm",
126 "x_{bar}, cm"};
127
128// slope-panel drawing mode (drawMode argument):
129// DM_PROFILE - both slope rows as 1D TProfile (X in middle, Y in bottom)
130// DM_SCATTER - both slope rows as 2D TH2D (X in middle, Y in bottom)
131// DM_MIX - X-residual slopes only: 1D TProfile in middle, 2D TH2D in
132// bottom (Y-residual readings are computed but not drawn)
139
140// canvas and pads configuration: top row = shifts (dX, dY) + two evolution
141// panels, middle row = X-residual slopes, bottom row = Y-residual (alternative)
142// slopes; one param per column
143#define RES_X 3440
144#define RES_Y 1440
145#define PADS_X 4
146#define PADS_Y 3
147
148// residual histogram ranges for the drawn shift panels
149#define RESIDUAL_RANGE_X 0.02
150#define RESIDUAL_RANGE_Y 0.4
151#define RESIDUAL_BINS 201
152#define PROFILE_BINS 201
153#define PAVE_TEXT_SIZE 0.04 // ~half of the TPaveText auto-fit default
154
155// evolution sampling: record every track for the first EVO_DENSE_HEAD tracks
156// (to resolve the fast early convergence), then thin out to about
157// EVO_TAIL_POINTS samples across the remaining range
158#define EVO_DENSE_HEAD 200
159#define EVO_TAIL_POINTS 600
160
161// simple accumulator: mean of y and slope of a straight line y = a + b*x, unweighted
162struct LinAcc
163{
164 BmnWeightedLine line; // unit weights
165 void Add(Double_t x, Double_t y) { line.Add(x, y); }
166 Double_t MeanY() const { return line.N() > 0 ? line.Mean() : 0.0; }
167 Double_t RmsY() const { return line.N() < 2 ? 0.0 : line.Rms(); }
168 Double_t MeanYErr() const { return line.N() > 0 ? RmsY() / std::sqrt(line.N()) : 0.0; }
169 Double_t Slope() const { return std::abs(line.Det()) > 0.0 ? line.Slope() : 0.0; }
171 {
172 if (!(std::abs(line.Det()) > 0.0) || line.N() < 3)
173 return 0.0;
174 // unit weights: the variance scale comes from the scatter itself
175 Double_t s2{line.Chi2() / (line.N() - 2)};
176 return s2 > 0.0 ? std::sqrt(s2 * line.SlopeVar()) : 0.0;
177 }
178 // reading turned into a correction: sign array selects X- or Y-solution
179 Double_t Value(Int_t p, const Double_t* sign) const { return sign[p] * (PARAM_IS_SHIFT[p] ? MeanY() : Slope()); }
180 Double_t Error(Int_t p) const { return PARAM_IS_SHIFT[p] ? MeanYErr() : SlopeErr(); }
181};
182
183// two independent readings for the whole detector: from X- and from Y-residuals
184struct DetAcc
185{
186 std::array<LinAcc, PARAM_CNT> x, y;
187};
188
189// per-hit drawn quantities for the whole detector, pooled over all elements
190struct DetDraw
191{
192 VecDouble_t rx, ry, betaX, betaXY, betaXX, yBar, // X-residual covariates
193 betaY, betaYY, betaYX, xBar; // Y-residual covariates
194};
195
196// running-parameter history: one value/error series per parameter and solution,
197// sampled at the track counts stored in n
199{
200 VecDouble_t n; // cumulative tracks at each sample
201 std::array<VecDouble_t, PARAM_CNT> valX, errX, valY, errY;
202 void Sample(Long64_t tracks, const DetAcc& a)
203 {
204 n.push_back(static_cast<Double_t>(tracks));
205 for (Int_t p = 0; p < PARAM_CNT; p++) {
206 valX[p].push_back(a.x[p].Value(p, PARAM_SIGN_X));
207 errX[p].push_back(a.x[p].Error(p));
208 valY[p].push_back(a.y[p].Value(p, PARAM_SIGN_Y));
209 errY[p].push_back(a.y[p].Error(p));
210 }
211 }
212};
213
214// put the controlling terminal into non-canonical, no-echo mode (on == true)
215// so single keystrokes - including arrow escape sequences - are read at once
216static void histoGLSetRawMode(bool on, struct termios* saved)
217{
218 if (on) {
219 tcgetattr(STDIN_FILENO, saved);
220 struct termios raw{*saved};
221 raw.c_lflag &= ~(ICANON | ECHO);
222 raw.c_cc[VMIN] = 0; // non-blocking read
223 raw.c_cc[VTIME] = 0;
224 tcsetattr(STDIN_FILENO, TCSANOW, &raw);
225 } else {
226 tcsetattr(STDIN_FILENO, TCSANOW, saved);
227 }
228}
229
230// one drawn diagnostic panel (a shift histogram or a slope profile / 2D scatter)
231struct Panel
232{
233 Int_t row, col, p;
234 Bool_t isShift, isY, as2D; // as2D: draw as TH2D instead of TProfile
235 const VecDouble_t* res; // residual values
236 const VecDouble_t* cov; // covariate values (nullptr for shift)
238 const char* diag;
239 const char* covTitle;
240 const LinAcc* a; // accumulator for the value/error box
241 const Double_t* sign;
242};
243
244/**
245 * @brief Whole-detector histogramming estimate of the alignment corrections.
246 *
247 * Companion to histo.C: the same reading rules, but every hit of the detector
248 * is pooled into one histogram set, so the numbers are ensemble corrections
249 * rather than per-element ones. Useful for judging a global shift or rotation
250 * of the whole assembly.
251 *
252 * @param fileListPath text file listing the .root files to read
253 * @param selParam correction shown in the two evolution panels, one of the
254 * P_dX .. P_psi constants
255 * @param drawMode slope panel style: DM_PROFILE, DM_SCATTER or DM_MIX
256 * @param pdfPath .pdf file to save the canvas into, null to only display
257 *
258 * @return 0 on success, non-zero if the input data could not be read
259 */
260int histoGL(const char* fileListPath = "/home/igor/bmnroot/align_new/cfg/run8/7466.noa_rlerr.list",
261 Int_t selParam = P_dZ, // correction shown in the two evolution panels
262 Int_t drawMode = DM_MIX, // slope panels: DM_PROFILE / DM_SCATTER / DM_MIX
263 const char* pdfPath = nullptr)
264{
265 // init section
266 BmnRun8cgTopDM detModel;
267 detModel.Init();
268 if (selParam < 0 || selParam >= PARAM_CNT) {
269 std::cerr << " <!> histoGL: selParam " << selParam << " out of range [0, " << PARAM_CNT << ")" << std::endl;
270 return 1;
271 }
272 if (drawMode < DM_PROFILE || drawMode > DM_MIX) {
273 std::cerr << " <!> histoGL: drawMode " << drawMode << " out of range [" << DM_PROFILE << ", " << DM_MIX << "]"
274 << std::endl;
275 return 1;
276 }
277 BmnSimpleProgressBar progressBar;
278 BmnRootIterator itr(fileListPath, detModel, BmnDefaultTrackFilter(), BmnDefaultHitFilter(detModel), &progressBar);
279 if (!itr.Initialized()) {
280 std::cerr << " <!> histoGL: failed to initialize iterator on " << fileListPath << std::endl;
281 return 1;
282 }
283
284 std::cout << "\nInitializing ROOT reader for " << fileListPath << "\n";
285 itr.CountElements(); // count suitable tracks/hits, calibrate per-track progress
286 const Long64_t totalTracks{itr.GetTotalTracks()};
287 // thin the evolution tail to about EVO_TAIL_POINTS samples
288 const Long64_t evoStride{std::max<Long64_t>(1, totalTracks / EVO_TAIL_POINTS)};
289
290 DetAcc acc; // pooled X- and Y-residual accumulators (whole detector)
291 DetDraw draw; // pooled per-hit records for drawing (whole detector)
292 EvoHist evo; // running-correction history vs tracks accounted
293
294 // walk tracks; fit a straight line per track, then read residuals
295 std::cout << "Reading data...\n";
296 Long64_t usedTracks{0}, usedHits{0};
297 progressBar.Init();
298 itr.ResetAll();
299 while (!itr.EndOfTracks()) {
300 // weighted straight-line fit x = alpha_x + beta_x * z, y = alpha_y + beta_y * z
301 BmnWeightedLine lineX, lineY;
302 itr.ResetHits();
303 while (!itr.EndOfHits()) {
304 lineX.Add(itr.HitZ(), itr.HitX(), itr.HitWx());
305 lineY.Add(itr.HitZ(), itr.HitY(), itr.HitWy());
306 itr.NextHit();
307 }
308 if (!(lineX.Det() > 0.0) || !(lineY.Det() > 0.0)) { // degenerate track (no z spread)
309 itr.NextTrack();
310 continue;
311 }
312 Double_t betaX{lineX.Slope()}, alphaX{lineX.At0()}, betaY{lineY.Slope()}, alphaY{lineY.At0()};
313
314 // residuals and covariates (bar = straight-line prediction),
315 // pooled over the whole detector (no per-element split)
316 itr.ResetHits();
317 while (!itr.EndOfHits()) {
318 Double_t z{itr.HitZ()}, xBar{alphaX + betaX * z}, yBar{alphaY + betaY * z}, rx{itr.HitX() - xBar},
319 ry{itr.HitY() - yBar};
320
321 // X-residual readings
322 acc.x[P_dX].Add(0.0, rx); // mean -> dX
323 acc.x[P_dZ].Add(betaX, rx); // slope -> -dZ
324 acc.x[P_phi].Add(betaX * yBar, rx); // slope -> -phi
325 acc.x[P_theta].Add(betaX * xBar, rx); // slope -> +theta
326 acc.x[P_psi].Add(yBar, rx); // slope -> -psi
327 // Y-residual readings (alternative solution)
328 acc.y[P_dY].Add(0.0, ry); // mean -> dY
329 acc.y[P_dZ].Add(betaY, ry); // slope -> -dZ
330 acc.y[P_phi].Add(betaY * yBar, ry); // slope -> -phi
331 acc.y[P_theta].Add(betaY * xBar, ry); // slope -> +theta
332 acc.y[P_psi].Add(xBar, ry); // slope -> +psi
333
334 draw.rx.push_back(rx);
335 draw.ry.push_back(ry);
336 draw.betaX.push_back(betaX);
337 draw.betaXY.push_back(betaX * yBar);
338 draw.betaXX.push_back(betaX * xBar);
339 draw.yBar.push_back(yBar);
340 draw.betaY.push_back(betaY);
341 draw.betaYY.push_back(betaY * yBar);
342 draw.betaYX.push_back(betaY * xBar);
343 draw.xBar.push_back(xBar);
344 usedHits++;
345 itr.NextHit();
346 }
347 usedTracks++;
348 // sample the running corrections: dense at the start, thinned later
349 if (usedTracks <= EVO_DENSE_HEAD || usedTracks % evoStride == 0)
350 evo.Sample(usedTracks, acc);
351 itr.NextTrack();
352 }
353 progressBar.Clear();
354
355 if (usedTracks == 0) {
356 std::cerr << " <!> histoGL: no usable tracks found" << std::endl;
357 return 1;
358 }
359 // guarantee the final state is the last evolution sample
360 if (evo.n.empty() || evo.n.back() != static_cast<Double_t>(usedTracks))
361 evo.Sample(usedTracks, acc);
362
363 // numeric report: single ensemble reading for the whole detector
364
365 using namespace std;
366 cout << "\nWhole-detector histogramming: ensemble first-order corrections\n";
367 cout << usedTracks << " tracks, " << usedHits << " hits\n\n";
368
369 cout << fixed << setprecision(6);
370 cout << " " << left << setw(7) << "dX" << " = " << right << setw(11) << acc.x[P_dX].Value(P_dX, PARAM_SIGN_X)
371 << " +/- " << setw(10) << acc.x[P_dX].Error(P_dX) << " (" << PARAM_DIAG[P_dX] << ")\n";
372 cout << " " << left << setw(7) << "dY" << " = " << right << setw(11) << acc.y[P_dY].Value(P_dY, PARAM_SIGN_Y)
373 << " +/- " << setw(10) << acc.y[P_dY].Error(P_dY) << " (" << PARAM_DIAG[P_dY] << ")\n";
374 for (Int_t p = P_dZ; p < PARAM_CNT; p++)
375 cout << " " << left << setw(7) << PARAM_NAMES[p] << right << " X: " << setw(11)
376 << acc.x[p].Value(p, PARAM_SIGN_X) << " +/- " << setw(9) << acc.x[p].Error(p) << " Y: " << setw(11)
377 << acc.y[p].Value(p, PARAM_SIGN_Y) << " +/- " << setw(9) << acc.y[p].Error(p) << "\n";
378 cout << " RMS(rx) = " << acc.x[P_dX].RmsY() << " RMS(ry) = " << acc.y[P_dY].RmsY() << "\n" << flush;
379
380 // drawn objects are detached from any TDirectory and marked kCanDelete so
381 // that a pad Clear() on each redraw disposes of the previous picture
383 gStyle->SetOptFit(1);
384 gStyle->SetOptStat("emr");
385
386 TCanvas* pCnv = new TCanvas( // RAM not released - acceptable in macro mode
387 "canvas", "BM@N histogramming (whole detector)", RES_X, RES_Y);
388 pCnv->SetCanvasSize(RES_X - 20, RES_Y - 96);
389
390 // build the pad grid once; the static diagnostics never change while the
391 // user cycles the selected parameter, so only the two evolution pads are
392 // redrawn on a key press
393 pCnv->cd();
394 const Double_t padW{1.0 / PADS_X}, padH{1.0 / PADS_Y};
395 TPad* pPads[PADS_Y][PADS_X];
396 for (UInt_t i = 0; i < PADS_Y; i++)
397 for (UInt_t j = 0; j < PADS_X; j++) {
398 pPads[i][j] = new TPad(Form("pad%u%u", i, j), Form("pad%u%u", i, j), padW * j, padH * i, padW * (j + 1),
399 padH * (i + 1));
400 pPads[i][j]->Draw();
401 }
402 // top row is physical index PADS_Y-1; its two rightmost pads host the
403 // evolution panels (X-solution then Y-solution)
404 TPad* pEvoX{pPads[PADS_Y - 1][2]};
405 TPad* pEvoY{pPads[PADS_Y - 1][3]};
406
407 // static panels: top row = shifts (dX, dY), middle = X-residual slopes,
408 // bottom = Y-residual (alternative) slopes; one param per column - exactly
409 // the histo.C set, but pooled over the whole detector
410 const Int_t slopeP[4]{P_dZ, P_phi, P_theta, P_psi};
411 const VecDouble_t* covX[4]{&draw.betaX, &draw.betaXY, &draw.betaXX, &draw.yBar};
412 const VecDouble_t* covY[4]{&draw.betaY, &draw.betaYY, &draw.betaYX, &draw.xBar};
413 std::vector<Panel> panels;
414 panels.push_back({0, 0, P_dX, kTRUE, kFALSE, kFALSE, &draw.rx, nullptr, RESIDUAL_RANGE_X, PARAM_DIAG[P_dX],
416 panels.push_back({0, 1, P_dY, kTRUE, kTRUE, kFALSE, &draw.ry, nullptr, RESIDUAL_RANGE_Y, PARAM_DIAG[P_dY],
418 for (Int_t c = 0; c < 4; c++) {
419 Int_t p{slopeP[c]};
420 // middle row: X-residual slope, 2D only in pure-scatter mode
421 panels.push_back({1, c, p, kFALSE, kFALSE, drawMode == DM_SCATTER, &draw.rx, covX[c], RESIDUAL_RANGE_X,
422 PARAM_DIAG[p], COV_TITLE_X[p], &acc.x[p], PARAM_SIGN_X});
423 // bottom row: Y-residual slope normally; in mix mode the same X-residual
424 // slope drawn as 2D (Y readings stay computed, not drawn)
425 if (drawMode == DM_MIX)
426 panels.push_back({2, c, p, kFALSE, kFALSE, kTRUE, &draw.rx, covX[c], RESIDUAL_RANGE_X, PARAM_DIAG[p],
427 COV_TITLE_X[p], &acc.x[p], PARAM_SIGN_X});
428 else
429 panels.push_back({2, c, p, kFALSE, kTRUE, drawMode == DM_SCATTER, &draw.ry, covY[c], RESIDUAL_RANGE_Y,
430 PARAM_DIAG_Y[p], COV_TITLE_Y[p], &acc.y[p], PARAM_SIGN_Y});
431 }
432
433 for (const Panel& pn : panels) {
434 pPads[PADS_Y - 1 - pn.row][pn.col]->cd();
435 const char* tag{pn.isShift ? "" : (pn.isY ? " (Y)" : " (X)")};
436 const char* suff{pn.isY ? "y" : "x"};
437 const char* axis{pn.isY ? "Y" : "X"};
438
439 // per-pad report box with the extracted value
440 TPaveText* pave = new TPaveText(0.70, 0.86, 0.99, 0.92, "BR NDC");
441 pave->SetBit(kCanDelete);
442 pave->SetFillColor(0);
444 pave->AddText(
445 Form("%s%s = %+.5f #pm %.5f", PARAM_NAMES[pn.p], tag, pn.a->Value(pn.p, pn.sign), pn.a->Error(pn.p)));
446
447 if (pn.isShift) {
448 // shift: 1D residual histogram, correction = mean (dashed line)
449 TH1D* h = new TH1D(Form("h_%s", PARAM_NAMES[pn.p]), Form("%s;%s;hits", pn.diag, pn.covTitle), RESIDUAL_BINS,
450 -pn.resRange, pn.resRange);
451 h->SetBit(kCanDelete);
452 for (Double_t r : *pn.res)
453 h->Fill(r);
454 h->SetLineColor(kAzure + 2);
455 h->SetStats(0);
456 h->Draw();
457 TLine* mean = new TLine(h->GetMean(), 0.0, h->GetMean(), h->GetMaximum());
458 mean->SetBit(kCanDelete);
459 mean->SetLineColor(kRed);
460 mean->SetLineStyle(2);
461 mean->SetLineWidth(2);
462 mean->Draw();
463 } else {
464 // rotation / dZ: residual against its covariate, slope fit
465 const VecDouble_t& cov{*pn.cov};
466 const VecDouble_t& res{*pn.res};
467 Double_t lo{+1e30}, hi{-1e30};
468 for (Double_t c : cov) {
469 if (c < lo)
470 lo = c;
471 if (c > hi)
472 hi = c;
473 }
474 if (hi <= lo) {
475 lo -= 1e-3;
476 hi += 1e-3;
477 }
478
479 Double_t margin{0.05 * (hi - lo)};
480 if (pn.as2D) {
481 // 2D scatter of residual vs covariate; pol1 fit is the slope
482 // (TH2::Fit weights each column by content, like the profile)
483 TH2D* h2 = new TH2D(Form("h2_%s_%s", PARAM_NAMES[pn.p], suff),
484 Form("%s;%s;%s residual, cm", pn.diag, pn.covTitle, axis), PROFILE_BINS,
485 lo - margin, hi + margin, RESIDUAL_BINS, -pn.resRange, pn.resRange);
486 h2->SetBit(kCanDelete);
487 for (std::size_t i = 0; i < cov.size(); i++)
488 h2->Fill(cov[i], res[i]);
489 h2->Fit("pol1", "Q");
490 if (TF1* fit = h2->GetFunction("pol1")) {
491 fit->SetLineColor(kRed);
492 fit->SetLineWidth(2);
493 }
494 h2->SetStats(0);
495 h2->Draw("COLZ");
496 } else {
497 // residual profiled against its covariate, slope fit
498 TProfile* pr = new TProfile(Form("p_%s_%s", PARAM_NAMES[pn.p], suff),
499 Form("%s;%s;#LT %s residual #GT, cm", pn.diag, pn.covTitle, axis),
500 PROFILE_BINS, lo - margin, hi + margin);
501 pr->SetBit(kCanDelete);
502 for (std::size_t i = 0; i < cov.size(); i++)
503 pr->Fill(cov[i], res[i]);
505 pr->SetMarkerColor(kAzure + 2);
506 pr->SetLineColor(kAzure + 2);
507 pr->Fit("pol1", "Q");
508 pr->SetStats(0);
509 pr->Draw();
510 }
511 }
512 pave->Draw();
513 }
514
515 // (re)draw one evolution panel: the running value of parameter p (X- or
516 // Y-solution) with a statistical-error band, against the number of tracks
517 auto drawEvo = [&](TPad* pad, Int_t p, Bool_t useY) {
518 pad->cd();
519 pad->Clear();
520 pad->SetLogx();
521 pad->SetGridx();
522 pad->SetGridy();
523
524 const LinAcc& a{useY ? acc.y[p] : acc.x[p]};
525 const char* sol{useY ? "Y-solution" : "X-solution"};
526 const VecDouble_t& vv{useY ? evo.valY[p] : evo.valX[p]};
527 const VecDouble_t& ve{useY ? evo.errY[p] : evo.errX[p]};
528
529 // dX has no independent Y reading and dY none in X - say so plainly
530 if (a.line.N() <= 0 || vv.empty()) {
531 TPaveText* msg = new TPaveText(0.05, 0.42, 0.95, 0.58, "BR NDC");
532 msg->SetBit(kCanDelete);
533 msg->SetFillColor(0);
535 msg->AddText(Form("%s : no independent %s", PARAM_NAMES[p], sol));
536 msg->Draw();
537 pad->Modified();
538 pad->Update();
539 return;
540 }
541
542 const Int_t n{static_cast<Int_t>(vv.size())};
543 TGraphErrors* band = new TGraphErrors(n); // value with +/- error band
544 band->SetBit(kCanDelete);
545 TGraph* line = new TGraph(n); // central value line
546 line->SetBit(kCanDelete);
547 for (Int_t i = 0; i < n; i++) {
548 band->SetPoint(i, evo.n[i], vv[i]);
549 band->SetPointError(i, 0.0, ve[i]);
550 line->SetPoint(i, evo.n[i], vv[i]);
551 }
552 const Double_t vFinal{vv.back()}, eFinal{ve.back()};
553
554 // scale to the converged tail so the plateau - not the noisy first few
555 // tracks - fills the panel
556 Double_t ylo{+1e30}, yhi{-1e30};
557 for (Int_t i = n / 2; i < n; i++) {
558 ylo = std::min(ylo, vv[i] - ve[i]);
559 yhi = std::max(yhi, vv[i] + ve[i]);
560 }
561 if (!(yhi > ylo)) {
562 ylo = vFinal - 1e-6;
563 yhi = vFinal + 1e-6;
564 }
565 const Double_t span{0.25 * (yhi - ylo)};
566 ylo -= span;
567 yhi += span;
568
569 band->SetTitle(Form("%s (%s);tracks accounted;%s, %s", PARAM_NAMES[p], sol, PARAM_NAMES[p], PARAM_UNIT[p]));
570 band->SetFillColorAlpha(kAzure - 9, 0.7);
571 band->SetLineColor(kAzure - 9);
572 band->SetMinimum(ylo);
573 band->SetMaximum(yhi);
574 band->Draw("A3");
575 band->GetXaxis()->SetLimits(evo.n.front(), evo.n.back());
576
577 line->SetLineColor(kAzure + 2);
578 line->SetLineWidth(2);
579 line->Draw("L SAME");
580
581 // final value as a red dashed reference
582 TLine* ref = new TLine(evo.n.front(), vFinal, evo.n.back(), vFinal);
583 ref->SetBit(kCanDelete);
584 ref->SetLineColor(kRed);
585 ref->SetLineStyle(2);
586 ref->SetLineWidth(2);
587 ref->Draw();
588
589 TPaveText* box = new TPaveText(0.50, 0.82, 0.99, 0.92, "BR NDC");
590 box->SetBit(kCanDelete);
591 box->SetFillColor(0);
593 box->AddText(Form("%s = %+.5f #pm %.5f", PARAM_NAMES[p], vFinal, eFinal));
594 box->Draw();
595
596 pad->Modified();
597 pad->Update();
598 };
599
600 // redraw both evolution panels for the currently selected parameter
601 auto showEvo = [&](Int_t p) {
602 drawEvo(pEvoX, p, kFALSE);
603 drawEvo(pEvoY, p, kTRUE);
604 pCnv->Update();
605 };
606
607 showEvo(selParam);
608
609 if (pdfPath && pdfPath[0])
610 pCnv->SaveAs(pdfPath);
611
612 // interactive: Left/Right arrows cycle the selected correction shown in the
613 // two evolution panels (wrapping around), q or Esc quits. Skipped in batch
614 // mode or when stdin is not a terminal (e.g. piped input) - there the
615 // default parameter is drawn once.
616 if (!gROOT->IsBatch() && isatty(STDIN_FILENO)) {
617 cout << "\n[ Right / Left arrows: next / previous correction"
618 " (evolution panels) | q: quit ]\n"
619 << flush;
620 struct termios saved;
621 histoGLSetRawMode(true, &saved);
622 Bool_t running{kTRUE};
623 while (running) {
624 gSystem->ProcessEvents();
625 // leave if the user closed the canvas window
626 if (!gROOT->GetListOfCanvases()->FindObject(pCnv))
627 break;
628 unsigned char c{0};
629 if (read(STDIN_FILENO, &c, 1) <= 0) { // nothing pending
630 gSystem->Sleep(20);
631 continue;
632 }
633 if (c == 'q' || c == 'Q') {
634 running = kFALSE;
635 } else if (c == 27) { // ESC: arrow escape sequence or a bare Esc
636 unsigned char a{0}, b{0};
637 gSystem->Sleep(1); // let the rest of the sequence arrive
638 if (read(STDIN_FILENO, &a, 1) <= 0) {
639 running = kFALSE;
640 continue;
641 }
642 read(STDIN_FILENO, &b, 1);
643 if (a == '[' && b == 'C')
644 selParam = (selParam + 1) % PARAM_CNT;
645 else if (a == '[' && b == 'D')
646 selParam = (selParam - 1 + PARAM_CNT) % PARAM_CNT;
647 else
648 continue;
649 cout << " selected correction: " << PARAM_NAMES[selParam] << "\n" << flush;
650 showEvo(selParam);
651 }
652 }
653 histoGLSetRawMode(false, &saved);
654 }
655
656 return 0;
657}
std::vector< Double_t > VecDouble_t
TrackFilter_t BmnDefaultTrackFilter(Bool_t wholeTrackOnly=kTRUE)
HitFilter_t BmnDefaultHitFilter(const BmnDetectorModel &model)
int i
Definition P4_F32vec4.h:22
bool Bool_t
int Int_t
unsigned int UInt_t
constexpr Bool_t kFALSE
double Double_t
long long Long64_t
constexpr Bool_t kTRUE
kRed
kAzure
kFullCircle
kCanDelete
#define gROOT
char * Form(const char *fmt,...)
R__EXTERN TStyle * gStyle
R__EXTERN TSystem * gSystem
Bool_t EndOfTracks() const noexcept
Bool_t EndOfHits() const noexcept
Bool_t Initialized() const noexcept
Bool_t Init(const char *targetGeoFile=nullptr)
virtual Double_t HitY() const override
virtual void ResetAll() override
virtual Double_t HitWy() const override
virtual Double_t HitZ() const override
virtual Double_t HitX() const override
Int_t GetTotalTracks() const noexcept
virtual Double_t HitWx() const override
virtual void ResetHits() override
virtual void NextTrack() override
virtual void NextHit() override
Double_t Det() const noexcept
Double_t At0() const noexcept
Int_t N() const noexcept
void Add(Double_t z, Double_t v, Double_t w=1.0) noexcept
Double_t Slope() const noexcept
virtual void SetFillColor(Color_t fcolor)
virtual void SetFillColorAlpha(Color_t fcolor, Float_t falpha)
virtual void SetLineStyle(Style_t lstyle)
virtual void SetLineWidth(Width_t lwidth)
virtual void SetLineColor(Color_t lcolor)
virtual void SetMarkerColor(Color_t mcolor=1)
virtual void SetMarkerStyle(Style_t mstyle=1)
virtual void SetTextSize(Float_t tsize=1)
virtual void SetLimits(Double_t xmin, Double_t xmax)
TVirtualPad * cd(Int_t subpadnumber=0) override
void SetCanvasSize(UInt_t ww, UInt_t wh) override
void Update() override
virtual void SetPointError(Double_t ex, Double_t ey)
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
virtual void SetMaximum(Double_t maximum=-1111)
void Draw(Option_t *chopt="") override
TAxis * GetXaxis() const
void SetTitle(const char *title="") override
virtual void SetMinimum(Double_t minimum=-1111)
virtual Double_t GetMean(Int_t axis=1) const
static void AddDirectory(Bool_t add=kTRUE)
virtual TFitResultPtr Fit(const char *formula, Option_t *option="", Option_t *goption="", Double_t xmin=0, Double_t xmax=0)
virtual Double_t GetMaximum(Double_t maxval=FLT_MAX) const
void Draw(Option_t *option="") override
virtual Int_t Fill(const char *name, Double_t w)
virtual TF1 * GetFunction(const char *name) const
virtual void SetStats(Bool_t stats=kTRUE)
virtual Int_t Fill(const char *namex, const char *namey, Double_t w)
void SetBit(UInt_t f)
virtual void Draw(Option_t *option="")
void SetGridx(Int_t value=1) override
void Update() override
void Clear(Option_t *option="") override
void Modified(Bool_t flag=true) override
void SetGridy(Int_t value=1) override
void SaveAs(const char *filename="", Option_t *option="") const override
TVirtualPad * cd(Int_t subpadnumber=0) override
void Draw(Option_t *option="") override
void SetLogx(Int_t value=1) override
virtual TText * AddText(const char *label)
void Draw(Option_t *option="") override
Int_t Fill(const char *namex, Double_t y) override
#define PADS_Y
Definition histoGL.C:146
#define RES_Y
Definition histoGL.C:144
#define PADS_X
Definition histoGL.C:145
#define RES_X
Definition histoGL.C:143
static constexpr const char * PARAM_NAMES[PARAM_CNT]
Definition histoGL.C:91
static constexpr const Double_t PARAM_SIGN_X[PARAM_CNT]
Definition histoGL.C:99
#define EVO_DENSE_HEAD
Definition histoGL.C:158
static constexpr const char * PARAM_UNIT[PARAM_CNT]
Definition histoGL.C:92
static void histoGLSetRawMode(bool on, struct termios *saved)
Definition histoGL.C:216
int histoGL(const char *fileListPath="/home/igor/bmnroot/align_new/cfg/run8/7466.noa_rlerr.list", Int_t selParam=P_dZ, Int_t drawMode=DM_MIX, const char *pdfPath=nullptr)
Whole-detector histogramming estimate of the alignment corrections.
Definition histoGL.C:260
#define EVO_TAIL_POINTS
Definition histoGL.C:159
static constexpr const Double_t PARAM_SIGN_Y[PARAM_CNT]
Definition histoGL.C:100
#define PARAM_CNT
Definition histoGL.C:81
static constexpr const Bool_t PARAM_IS_SHIFT[PARAM_CNT]
Definition histoGL.C:96
#define PAVE_TEXT_SIZE
Definition histoGL.C:153
static constexpr const char * PARAM_DIAG_Y[PARAM_CNT]
Definition histoGL.C:108
#define RESIDUAL_RANGE_X
Definition histoGL.C:149
eParam
@ P_psi
Definition histoGL.C:82
@ P_theta
@ P_dX
@ P_dZ
@ P_dY
@ P_phi
static constexpr const char * COV_TITLE_Y[PARAM_CNT]
Definition histoGL.C:121
static constexpr const char * PARAM_DIAG[PARAM_CNT]
Definition histoGL.C:101
static constexpr const char * COV_TITLE_X[PARAM_CNT]
Definition histoGL.C:115
eDrawMode
@ DM_MIX
Definition histoGL.C:133
@ DM_SCATTER
@ DM_PROFILE
#define RESIDUAL_RANGE_Y
Definition histoGL.C:150
#define PROFILE_BINS
Definition histoGL.C:152
#define RESIDUAL_BINS
Definition histoGL.C:151
std::array< LinAcc, PARAM_CNT > y
Definition histo.C:172
std::array< LinAcc, PARAM_CNT > x
Definition histo.C:172
VecDouble_t ry
Definition histo.C:179
VecDouble_t yBar
Definition histo.C:179
VecDouble_t xBar
Definition histo.C:180
VecDouble_t betaYX
Definition histo.C:180
VecDouble_t betaX
Definition histo.C:179
VecDouble_t betaYY
Definition histo.C:180
VecDouble_t betaXX
Definition histo.C:179
VecDouble_t betaY
Definition histo.C:180
VecDouble_t rx
Definition histo.C:179
VecDouble_t betaXY
Definition histo.C:179
std::array< VecDouble_t, PARAM_CNT > errX
Definition histoGL.C:201
std::array< VecDouble_t, PARAM_CNT > valY
Definition histoGL.C:201
std::array< VecDouble_t, PARAM_CNT > errY
Definition histoGL.C:201
std::array< VecDouble_t, PARAM_CNT > valX
Definition histoGL.C:201
VecDouble_t n
Definition histoGL.C:200
void Sample(Long64_t tracks, const DetAcc &a)
Definition histoGL.C:202
Double_t RmsY() const
Definition histoGL.C:167
Double_t SlopeErr() const
Definition histoGL.C:170
BmnWeightedLine line
Definition histo.C:150
Double_t Value(Int_t p, const Double_t *sign) const
Definition histoGL.C:179
Double_t Slope() const
Definition histoGL.C:169
Double_t MeanYErr() const
Definition histoGL.C:168
Double_t MeanY() const
Definition histoGL.C:166
Double_t Error(Int_t p) const
Definition histoGL.C:180
void Add(Double_t x, Double_t y)
Definition histoGL.C:165
Definition histo.C:201
const char * diag
Definition histo.C:207
Double_t resRange
Definition histo.C:206
const Double_t * sign
Definition histo.C:210
const char * covTitle
Definition histo.C:208
Bool_t as2D
Definition histo.C:203
Bool_t isY
Definition histo.C:203
Bool_t isShift
Definition histo.C:203
const VecDouble_t * res
Definition histo.C:204
Int_t p
Definition histo.C:202
Int_t col
Definition histo.C:202
const LinAcc * a
Definition histo.C:209
Int_t row
Definition histo.C:202
const VecDouble_t * cov
Definition histo.C:205