BmnRoot 26.10.0-210-gc52d34b0
Software framework of the BM@N experiment
Loading...
Searching...
No Matches
compareS.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 4.7 (Opus)
7 Supervisor: Igor Polev, polev@jinr.int
8
9 Visual comparison of two alignment solutions produced by
10 BmnAligner::SaveSolution. Useful to compare an obtained solution
11 against a known precise solution (e.g. the truth saved by
12 BmnAligner::MakeTestTask).
13*/
14
15// headers below are for IDE syntax to work and they will be
16// turned off during execution in ROOT macro mode
17#ifndef __CLING__
18#include "TAxis.h"
19#include "TCanvas.h"
20#include "TGraph.h"
21#include "TGraphErrors.h"
22#include "TMultiGraph.h"
23#include "TPad.h"
24
25#include <cmath>
26#include <fstream>
27#include <iomanip>
28#include <iostream>
29#include <map>
30#include <vector>
31// #include "TLegend.h"
32#include "TLine.h"
33#include "TString.h"
34#endif
35
36// nlohmann/json must be visible to CLING too. Its default header search path
37// in ROOT macro mode may not include the locations used by BmnRoot.
38#ifdef __CLING__
39#pragma cling add_include_path("/home/igor/bmnroot/services")
40#pragma cling add_include_path("/usr/include")
41#endif
42#include <nlohmann/json.hpp>
43
44// number of correction parameters per detector (matches BMN_GLOBAL_PARAMS_PD)
45#define PARAM_CNT 6
46constexpr static const char* PARAM_NAMES[PARAM_CNT]{"dX", "dY", "dZ",
47 "phi", "theta", "psi"}; // matches BmnParamLayout::ParamNames()
48constexpr static const char* PARAM_TITLES[PARAM_CNT]{"X shift, cm", "Y shift, cm", "Z shift, cm",
49 "phi rotation, rad", "theta rotation, rad", "psi rotation, rad"};
50constexpr static const Double_t PARAM_RANGES[PARAM_CNT]{0.25, 0.25, 0.25, 0.02, 0.02, 0.02};
51
52// canvas and pads configuration
53#define RES_X 1920
54#define RES_Y 1080
55#define PADS_X 3
56#define PADS_Y 2
57
59{
60 std::vector<Int_t> ids;
61 std::vector<std::vector<Double_t>> values; // [detector][param]
62 std::vector<std::vector<Double_t>> errors; // [detector][param] - zeros if file omits "errors"
63};
64
65static Bool_t LoadCmpSolution(const char* path, CmpSolution& sol)
66{
67 std::ifstream ifs(path);
68 if (!ifs.good()) {
69 std::cerr << " <!> compareSolutions: failed to open " << path << std::endl;
70 return kFALSE;
71 }
72 nlohmann::json js;
73 try {
74 ifs >> js;
75 } catch (const std::exception& e) {
76 std::cerr << " <!> compareSolutions: failed to parse JSON " << path << ":\n " << e.what() << std::endl;
77 return kFALSE;
78 }
79 if (!js.contains("CorrectionsPerDetector") || !js["CorrectionsPerDetector"].is_array()) {
80 std::cerr << " <!> compareSolutions: 'CorrectionsPerDetector' missing or"
81 " not an array in "
82 << path << std::endl;
83 return kFALSE;
84 }
85 const auto& arr = js["CorrectionsPerDetector"];
86 sol.ids.clear(); // just in case
87 sol.values.clear();
88 sol.errors.clear();
89 sol.ids.reserve(arr.size());
90 sol.values.reserve(arr.size());
91 sol.errors.reserve(arr.size());
92 for (const auto& el : arr) {
93 if (!el.contains("detector_id") || !el.contains("values") || !el["values"].is_array()
94 || el["values"].size() != PARAM_CNT)
95 {
96 std::cerr << " <!> compareSolutions: malformed entry in " << path << std::endl;
97 return kFALSE;
98 }
99 std::vector<Double_t> v;
100 v.reserve(PARAM_CNT);
101 for (const auto& x : el["values"])
102 v.push_back(x.get<Double_t>());
103 // "errors" is optional: fall back to zeros
104 std::vector<Double_t> e(PARAM_CNT, 0.0);
105 if (el.contains("errors") && el["errors"].is_array() && el["errors"].size() == PARAM_CNT) {
106 for (Int_t k = 0; k < PARAM_CNT; k++)
107 e[k] = el["errors"][k].get<Double_t>();
108 }
109 sol.ids.push_back(el["detector_id"].get<Int_t>());
110 sol.values.push_back(std::move(v));
111 sol.errors.push_back(std::move(e));
112 }
113 return kTRUE;
114}
115
116/**
117 * @brief Visual comparison of two alignment solutions.
118 *
119 * Both files are read in the format BmnAligner::SaveSolution writes. Typical
120 * use is checking an obtained solution against a known truth, such as the one
121 * makett.C saves next to a synthetic task.
122 *
123 * @param obtainedPath .json solution under test
124 * @param precisePath .json solution to compare it against
125 * @param pdfPath .pdf file to save the canvas into, null to only display
126 *
127 * @return 0 on success, 1 if either solution could not be loaded
128 */
130 const char* obtainedPath = "/home/igor/bmnroot/align_new/cfg/solutions/sl9555.112e_lin3.04.json",
131 const char* precisePath =
132 "/home/igor/data.cache/tt9555.112e_lin3.04.solution.json", //"/home/igor/bmnroot/align_new/cfg/solutions/Ruf_AZ_run9.json",
133 const char* pdfPath = nullptr)
134{
135 // load both solutions
136 CmpSolution sObt, sTru;
137 if (!LoadCmpSolution(obtainedPath, sObt))
138 return 1;
139 if (!LoadCmpSolution(precisePath, sTru))
140 return 1;
141 if (sObt.ids.size() != sTru.ids.size()) {
142 std::cerr << " <!> compareSolutions: detector counts differ (" << sObt.ids.size() << " obtained vs "
143 << sTru.ids.size() << " precise)" << std::endl;
144 return 1;
145 }
146 const Int_t nDet = static_cast<Int_t>(sObt.ids.size());
147
148 // align entries by detector_id (handles possible reordering)
149 std::map<Int_t, Int_t> truIdxByID;
150 for (Int_t i = 0; i < nDet; i++)
151 truIdxByID[sTru.ids[i]] = i;
152
153 std::vector<Int_t> ids(nDet);
154 std::vector<std::vector<Double_t>> tVal(nDet, std::vector<Double_t>(PARAM_CNT, 0.0)),
155 oVal(nDet, std::vector<Double_t>(PARAM_CNT, 0.0)), oErr(nDet, std::vector<Double_t>(PARAM_CNT, 0.0));
156 for (Int_t i = 0; i < nDet; i++) {
157 Int_t id = sObt.ids[i];
158 auto it = truIdxByID.find(id);
159 if (it == truIdxByID.end()) {
160 std::cerr << " <!> compareSolutions: detector_id " << id << " not found in precise solution" << std::endl;
161 return 1;
162 }
163 ids[i] = id;
164 for (Int_t p = 0; p < PARAM_CNT; p++) {
165 oVal[i][p] = sObt.values[i][p];
166 oErr[i][p] = sObt.errors[i][p];
167 tVal[i][p] = sTru.values[it->second][p];
168 }
169 }
170
171 // numeric summary to stdout
172 using namespace std;
173 cout << "Per-parameter summary of (obtained - precise):\n";
174 cout << " " << left << setw(8) << "param"
175 << right
176 //<< setw(15) << "mean true"
177 //<< setw(15) << "mean obt"
178 //<< setw(15) << "mean diff"
179 << setw(15) << "RMS diff" << setw(19) << "mean|diff/true|" << setw(18) << "max|diff/true|" << setw(10)
180 << "det_idx" << setw(12) << "det_id"
181 << "\n";
182 constexpr Double_t TRUTH_EPS{1e-6};
183 for (Int_t p = 0; p < PARAM_CNT; p++) {
184 Double_t s{0.0}, s2{0.0}, t{0.0}, o{0.0}, sRel{0.0}, maxRel{-1.0};
185 Int_t maxIdx{-1};
186 for (Int_t i = 0; i < nDet; i++) {
187 Double_t d{oVal[i][p] - tVal[i][p]};
188 s += d;
189 s2 += d * d;
190 t += tVal[i][p];
191 o += oVal[i][p];
192 if (fabs(tVal[i][p]) < TRUTH_EPS)
193 continue; // skip near-zero truth
194 Double_t r{fabs(d / tVal[i][p])};
195 sRel += r;
196 if (r > maxRel) {
197 maxRel = r;
198 maxIdx = i;
199 }
200 }
201 cout << " " << left << setw(8) << PARAM_NAMES[p] << right << fixed
202 << setprecision(6)
203 //<< setw(15) << t / nDet
204 //<< setw(15) << o / nDet
205 //<< setw(15) << s / nDet
206 << setw(15) << sqrt(s2 / nDet) << setw(19) << sRel / nDet;
207 if (maxIdx >= 0)
208 cout << fixed << setprecision(1) << setw(18) << maxRel << setw(10) << maxIdx << setw(12) << ids[maxIdx];
209 else
210 cout << setw(18) << "n/a" << setw(10) << "-" << setw(12) << "-";
211 cout << "\n";
212 }
213
214 // canvas + pads (RAM not released - acceptable in ROOT macro mode)
215 TCanvas* pCnv = new TCanvas(
216 "canvas", "BM@N alignment solutions comparison : BAR = true value, DOT = obtained (1-sigma conf), LINE = diff",
217 RES_X, RES_Y);
218 pCnv->SetCanvasSize(RES_X - 20, RES_Y - 96);
219 constexpr const Double_t padW{1.0 / PADS_X}, padH{1.0 / PADS_Y};
220 TPad* pPads[PADS_Y][PADS_X];
221 for (UInt_t i = 0; i < PADS_Y; i++)
222 for (UInt_t j = 0; j < PADS_X; j++) {
223 pPads[i][j] = new TPad(Form("pad%u%u", i, j), Form("pad%u%u", i, j), padW * j, padH * i, padW * (j + 1),
224 padH * (i + 1));
225 pPads[i][j]->Draw();
226 }
227
228 // 6 panels: top row = shifts (dX, dY, dZ), bottom row = rotations (phi, theta, psi)
229 for (Int_t p = 0; p < PARAM_CNT; p++) {
230 Int_t col = p % PADS_X;
231 Int_t row = p / PADS_X;
232 // row 0 = shifts on top, row 1 = rotations below
233 pPads[PADS_Y - 1 - row][col]->cd();
234
235 std::vector<Double_t> x(nDet), xE(nDet, 0.0), yT(nDet), yO(nDet), yE(nDet), yD(nDet);
236 Double_t yMin{+1e30}, yMax{-1e30};
237 for (Int_t i = 0; i < nDet; i++) {
238 x[i] = static_cast<Double_t>(i);
239 yT[i] = tVal[i][p];
240 yO[i] = oVal[i][p];
241 yE[i] = oErr[i][p];
242 yD[i] = oVal[i][p] - tVal[i][p];
243 if (yT[i] < yMin)
244 yMin = yT[i];
245 if (yO[i] - yE[i] < yMin)
246 yMin = yO[i] - yE[i]; // account for error bar
247 if (yD[i] < yMin)
248 yMin = yD[i];
249 if (yT[i] > yMax)
250 yMax = yT[i];
251 if (yO[i] + yE[i] > yMax)
252 yMax = yO[i] + yE[i];
253 if (yD[i] > yMax)
254 yMax = yD[i];
255 }
256 Double_t pad{0.1 * std::max(yMax - yMin, 1e-6)};
257 if (yMax == yMin) {
258 yMin -= 1.0e-3;
259 yMax += 1.0e-3;
260 }
261
262 // per-series graphs
263 // truth: teal bars
264 TGraph* gT = new TGraph(nDet, x.data(), yT.data());
265 gT->SetFillColor(kTeal);
266 gT->SetLineColor(kTeal);
267
268 // obtained: black markers + 1-sigma vertical error bars, no connecting line
269 TGraphErrors* gO = new TGraphErrors(nDet, x.data(), yO.data(), xE.data(), yE.data());
272 gO->SetLineColor(kRed);
273
274 // diff: red line, no markers
275 TGraph* gD = new TGraph(nDet, x.data(), yD.data());
276 gD->SetLineColor(kRed);
277 gD->SetLineWidth(2);
278
279 // bundle into a multigraph - acts as the axis frame
280 TMultiGraph* mg = new TMultiGraph();
281 mg->SetName(Form("mg_%s", PARAM_NAMES[p]));
282 mg->SetTitle(Form("%s;detector index;", PARAM_TITLES[p]));
283 mg->Add(gT, "B"); // bars first (drawn at back)
284 mg->Add(gD, "L"); // diff line on top of bars
285 mg->Add(gO, "P"); // obtained markers on top of everything
286 mg->Draw("A");
287 mg->GetXaxis()->SetLimits(-0.5, nDet - 0.5);
288 mg->GetXaxis()->SetNdivisions(nDet);
289 if (PARAM_RANGES[p] > 0.0)
291 else
292 mg->GetYaxis()->SetRangeUser(yMin - pad, yMax + pad);
293
294 // zero reference line (drawn after multigraph so pad coords are set)
295 TLine* zero = new TLine(-0.5, 0.0, nDet - 0.5, 0.0);
296 zero->SetLineColor(kGray + 1);
297 zero->SetLineStyle(3);
298 zero->Draw();
299
300 // legends removed to avoid overlaping with data
301 // moved to window title in some way
302 /*
303 TLegend* leg = new TLegend(0.65, 0.74, 0.95, 0.92);
304 leg->SetTextSize(0.035);
305 leg->AddEntry(gT, "Precise (truth)", "f");
306 leg->AddEntry(gO, "Obtained", "p");
307 leg->AddEntry(gD, "Diff (obt - tru)", "l");
308 leg->Draw();
309 */
310 }
311
312 if (pdfPath && pdfPath[0])
313 pCnv->SaveAs(pdfPath);
314
315 return 0;
316}
friend F32vec4 fabs(const F32vec4 &a)
Definition P4_F32vec4.h:52
__m128 v
Definition P4_F32vec4.h:1
int i
Definition P4_F32vec4.h:22
bool Bool_t
int Int_t
unsigned int UInt_t
constexpr Bool_t kFALSE
double Double_t
constexpr Bool_t kTRUE
kTeal
kGray
kRed
kBlack
kCircle
char * Form(const char *fmt,...)
virtual void SetNdivisions(Int_t n1, Int_t n2, Int_t n3, Bool_t optim=kTRUE)
virtual void SetFillColor(Color_t fcolor)
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 SetLimits(Double_t xmin, Double_t xmax)
virtual void SetRangeUser(Double_t ufirst, Double_t ulast)
void SetCanvasSize(UInt_t ww, UInt_t wh) override
virtual void Add(TGraph *graph, Option_t *chopt="")
void Draw(Option_t *chopt="") override
TAxis * GetYaxis()
TAxis * GetXaxis()
virtual void SetTitle(const char *title="")
virtual void SetName(const char *name)
virtual void Draw(Option_t *option="")
void SaveAs(const char *filename="", Option_t *option="") const override
TVirtualPad * cd(Int_t subpadnumber=0) override
void Draw(Option_t *option="") override
#define PADS_Y
Definition compareS.C:56
#define RES_Y
Definition compareS.C:54
#define PADS_X
Definition compareS.C:55
#define RES_X
Definition compareS.C:53
static constexpr const char * PARAM_TITLES[PARAM_CNT]
Definition compareS.C:48
static constexpr const Double_t PARAM_RANGES[PARAM_CNT]
Definition compareS.C:50
static Bool_t LoadCmpSolution(const char *path, CmpSolution &sol)
Definition compareS.C:65
int compareS(const char *obtainedPath="/home/igor/bmnroot/align_new/cfg/solutions/sl9555.112e_lin3.04.json", const char *precisePath="/home/igor/data.cache/tt9555.112e_lin3.04.solution.json", const char *pdfPath=nullptr)
Visual comparison of two alignment solutions.
Definition compareS.C:129
static constexpr const char * PARAM_NAMES[PARAM_CNT]
Definition compareS.C:46
#define PARAM_CNT
Definition compareS.C:45
RVec< PromoteType< T > > sqrt(const RVec< T > &v)
std::vector< std::vector< Double_t > > values
Definition compareS.C:61
std::vector< std::vector< Double_t > > errors
Definition compareS.C:62
std::vector< Int_t > ids
Definition compareS.C:60