BmnRoot
Loading...
Searching...
No Matches
BmnAlignerTest.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.ru
7
8 BmnAlignerTest class implementation
9*/
10
11#include "BmnAlignerTest.h"
12
13#include "BmnAlignDefines.h"
14#include "Math/Functions.h"
15#include "TGraph.h"
16#include "TStopwatch.h"
17#include "fairlogger/Logger.h"
18
19#include <fstream>
20#include <nlohmann/json.hpp>
21
23 const BmnATestDetector* modelDetector,
24 Int_t nTracks,
25 const char* constraintsPath,
26 Int_t nThreads)
27 : BmnAligner(measureModel, modelDetector, constraintsPath, nThreads)
28 , fpDetectors(modelDetector)
29{
30 LOG(state) << " > BmnAlignerTest : initialization started";
31
32 // BmnAligner protected fields initialization
33 fTotalTracks = nTracks;
34 fTracksUsableCnt = nTracks;
35 // each track penetrates every (infinite) detector plane
37 LOG(detail) << " total hits: " << fTotalHits << ", " << sizeof(BmnATestHit) << " bytes per hit";
38 auto ram_size = fTotalHits * sizeof(BmnATestHit);
39 LOG(info) << " RAM required: " << ram_size / 1073741824.0 << " GB, ";
40 if (ram_size > BMN_MAX_HIT_RAM_SIZE) {
41 LOG(error) << "<!> required RAM size is too large for test:" << std::endl
42 << "<!> decrease number of tracks or detector planes" << std::endl
43 << "<!> max RAM allowed: " << BMN_MAX_HIT_RAM_SIZE / 1073741824.0 << " GB";
44 return;
45 }
46 // all dynamic memory allocated here is released in BmnAligner destructor
47 fpFirstHits = new Int_t[fTotalTracks + 1]; // one extra element for iterator convenience
48 fpTrackUsable = new std::vector<Bool_t>(fTracksUsableCnt, kTRUE);
49 fpHitsData = new Byte_t[ram_size];
50 fpHits = reinterpret_cast<BmnATestHit*>(fpHitsData);
53
54 // prepare set of iterators for parallelization by data
56
57 fInitialized = kTRUE;
58 LOG(state) << " < BmnAlignerTest : initialization completed";
59}
60
62{
63 if (fpTestTracksData)
64 delete[] fpTestTracksData;
65 // the rest of dynamic memory is released in BmnAligner destructor
66}
67
68Bool_t BmnAlignerTest::PrepareData(const char* tracksPath) // virtual override
69{
70 LOG(state) << " > BmnAlignerTest : data preparation started";
71 if (!fInitialized) {
72 LOG(error) << " <!> BmnAlignerTest is not initialized";
73 return kFALSE;
74 }
75 TStopwatch Timer;
76 Bool_t loadTracks{tracksPath && std::filesystem::exists(tracksPath)};
77
78 // tracks data preparation
79 if (loadTracks) {
80 if (!LoadTracks(tracksPath)) {
81 LOG(error) << " <!> failed to load tracks from file " << tracksPath;
82 return kFALSE;
83 }
84 } else {
85 LOG(info) << " Generating random tracks";
86 fpTestTracksData = new Byte_t[fTotalTracks * sizeof(BmnATestTrack)];
87 fpTestTracks = reinterpret_cast<BmnATestTrack*>(fpTestTracksData);
88 for (auto iTrack = 0; iTrack < fTotalTracks; iTrack++)
89 new (fpTestTracks + iTrack)
91 BMN_MODULE_COUNT // in test procedure each track penetrates every detector plane
92 );
93 if (tracksPath && !SaveTracks(tracksPath))
94 LOG(warning) << " failed to save tracks to file " << tracksPath;
95 }
96
97 // calculate hits as intersections of track and detector planes
98 SVect3 HitPoint, HitPointLocal;
99 Double_t Z;
100 Int_t iTrack, iHit{0};
101 for (iTrack = 0; iTrack < fTotalTracks; iTrack++) {
102 fpFirstHits[iTrack] = iHit;
103 BmnATestTrack& Track = fpTestTracks[iTrack];
104 for (Int_t iDet = 0; iDet < BMN_MODULE_COUNT; iDet++, iHit++) {
105 const BmnATestDetectorPlane& Detector = fpDetectors->GetDetectors()[iDet];
106 // generate true hit in lab coordinates
107 Z = (-Detector.GetConst() - ROOT::Math::Dot(Detector.GetNormal(), Track.GetVertex()))
108 / ROOT::Math::Dot(Detector.GetNormal(), Track.GetDirection());
109 HitPoint = {Track.X(Z), Track.Y(Z), Z};
110 // recalculate hit to local coordinates of detector
111 HitPointLocal = Detector.GetCrdInvRotation() * (HitPoint - Detector.GetCrdShift());
112 LOG_IF(error, HitPointLocal(2) > BMN_CLOSE_TO_ZERO)
113 << " <!> non-zero local z coordinate of test hit: " << HitPointLocal(2);
114 // store hit as data measured by detector
115 auto pHit = new (fpHits + iHit)
116 BmnATestHit(HitPointLocal(0) + BMN_RND.Gaus(0.0, BMN_TEST_AVERAGE_DX), // x measurement + noise
117 HitPointLocal(1) + BMN_RND.Gaus(0.0, BMN_TEST_AVERAGE_DY), // y measurement + noise
118 Detector.GetPosition(), // z = nominal position of detector
120 Track.AddHit(*pHit); // tracks should remember its hits for visualization purposes
121 } // for iDet
122
123 } // for iTrack
124 fpFirstHits[iTrack] = iHit; // last syntetic element for iterator convenience
125
126 // calculate zero approximation of linear tracks
127 std::vector<SVectLC> arrAlpha(fTotalTracks);
128 for (iTrack = 0; iTrack < fTotalTracks; iTrack++) {
129 BmnATestHit &firstHit = fpHits[fpFirstHits[iTrack]], &lastHit = fpHits[fpFirstHits[iTrack + 1] - 1];
130 Double_t dX{lastHit.GetX() - firstHit.GetX()}, dY{lastHit.GetY() - firstHit.GetY()},
131 dZ{lastHit.GetZ() - firstHit.GetZ()}, AlphaX1{dX / dZ}, AlphaY1{dY / dZ};
132 arrAlpha[iTrack] = SVectLC(firstHit.GetX() - AlphaX1 * firstHit.GetZ(), AlphaX1,
133 firstHit.GetY() - AlphaY1 * firstHit.GetZ(), AlphaY1);
134 }
135
136 // MSE scale factors calculation
139
140 StoreZeroSolution(arrAlpha);
141
142 /*
143 // DEBUG : Test on precise solution
144 for (Int_t i = 0; i < BMN_MODULE_COUNT; i++)
145 fpResultCurrent->pA[i] = fpDetectors->GetDetectors()[i].GetAlignShift();
146 for (Int_t i = 0; i < fTotalTracks; i++)
147 fpResultCurrent->pAlpha[i] = fpTestTracks[i].GetAlpha();
148 CalculateMSE(kTRUE);
149 */
150
151 Timer.Stop();
152 LOG(info) << " " << fTotalTracks << " tracks ready, total hits: " << iHit << ", time spent: " << Timer.CpuTime()
153 << " s (CPU)";
154 LOG(state) << " < BmnAlignerTest : data preparation completed";
155 return LoadConstraints();
156}
157
158Bool_t BmnAlignerTest::IterateAlignment() // virtual override
159{
160 Bool_t success = BmnAligner::IterateAlignment();
161 if (!success)
162 return kFALSE;
163
164 // monitor local parameters convergence
166 for (auto i = 0; i < TotalTracks(); i++) {
167 const SVectLC& Alpha = GetResult()->Alpha(i);
168 delta += ROOT::Math::fabs(Alpha - GetTestTrack(i).GetAlpha());
169 }
170 delta /= TotalTracks();
171 LOG(info) << " === average local parameters convergence:";
172 LOG(info) << " error by X = (" << delta(iX0) << ", " << delta(iX1) << ")";
173 LOG(info) << " error by Y = (" << delta(iY0) << ", " << delta(iY1) << ")";
174
175 return kTRUE;
176}
177
178Bool_t BmnAlignerTest::SaveTracks(const char* path) const
179{
180 LOG(info) << " Saving tracks data to " << path;
181 if (!fpTestTracks || fTotalTracks <= 0)
182 return kFALSE;
183 std::ofstream ofs(path);
184 if (!ofs.good()) {
185 LOG(error) << " <!> failed to open JSON file " << path;
186 return kFALSE;
187 }
188
190 j["tracks"] = fTotalTracks;
191 j["alphas"] = nlohmann::json::array();
192 for (Int_t i = 0; i < fTotalTracks; i++) {
193 const auto& tr = fpTestTracks[i];
194 const auto& Alpha = tr.GetAlpha();
195 j["alphas"].push_back({Alpha(iX0), Alpha(iX1), Alpha(iY0), Alpha(iY1)});
196 }
197
198 ofs << j.dump(2);
199 return kTRUE;
200}
201
202Bool_t BmnAlignerTest::LoadTracks(const char* path)
203{
204 LOG(info) << " Loading tracks data from " << path;
205 std::ifstream ifs(path);
206 if (!ifs.good()) {
207 LOG(error) << " <!> failed to open JSON file " << path;
208 return kFALSE;
209 }
210
212 ifs >> j;
213
214 // re-allocate new buffer and construct records
215 fTotalTracks = j.value("tracks", 0);
216 if (fpTestTracksData)
217 delete[] fpTestTracksData;
218 fpTestTracksData = new Byte_t[fTotalTracks * sizeof(BmnATestTrack)];
219 fpTestTracks = reinterpret_cast<BmnATestTrack*>(fpTestTracksData);
220
221 const auto& arr = j["alphas"];
222 for (Int_t i = 0; i < fTotalTracks; i++) {
223 const auto& a = arr[i];
224 SVectLC Alpha(a[iX0].get<double>(), a[iX1].get<double>(), a[iY0].get<double>(), a[iY1].get<double>());
225 new (fpTestTracks + i) BmnATestTrack(Alpha, BMN_MODULE_COUNT);
226 }
227 return kTRUE;
228}
229
230// visualization works in ROOT macro mode only
232{
234
235 // Draw tracks
236 fpPad3D->cd();
237 fpPad3D->SetTitle("Some random tracks");
238 const Double_t zMax = fpDetectors->GetMaxZ() * 1.1;
239 for (Int_t i = 0; i < BMN_MODULE_COUNT; i++)
240 fpDetectors->GetDetectors()[i].Draw();
241 TRandom trueRnd{0};
242 std::vector<Int_t> drawn_tracks(BMN_DRAW_TRACKS_CNT, -1);
243 for (Int_t i = 0, j; i < BMN_DRAW_TRACKS_CNT; i++) {
244 j = trueRnd.Uniform(0, TotalTracks());
245 if (drawn_tracks.end() == std::find(drawn_tracks.begin(), drawn_tracks.end(), j) && fpTrackUsable->at(i)) {
246 GetTestTrack(j).Draw(kTRUE, zMax);
247 BmnATestTrack trk(GetResult()->Alpha(j), BMN_MODULE_COUNT);
248 trk.Draw(kFALSE, zMax);
249 drawn_tracks[i] = j;
250 } else
251 i--;
252 }
253
254 double xValues[BMN_GLPARAMS_TOTAL];
255 for (auto i = 0; i < BMN_GLPARAMS_TOTAL; i++)
256 xValues[i] = static_cast<double>(i + 1);
257
258 // Draw solution
259 Double_t A[BMN_GLPARAMS_TOTAL];
260 for (Int_t i = 0, k = 0; i < BMN_MODULE_COUNT; i++)
261 for (Int_t j = 0; j < BMN_GLOBAL_PARAMS_PD; j++, k++)
262 A[k] = GetResult()->A(i)(j);
263 fpPadG1->cd();
264 fpPadG1->SetTitle("Solution");
265 TGraph* graphSolution = new TGraph(BMN_GLPARAMS_TOTAL, xValues, A);
266 graphSolution->SetTitle("Alignment values");
267 graphSolution->SetFillColor(kRed);
268 graphSolution->SetMinimum(-0.25); // DEBUG
269 graphSolution->SetMaximum(0.35); // DEBUG
270 graphSolution->GetXaxis()->SetRangeUser(xValues[0], xValues[BMN_GLPARAMS_TOTAL - 1]);
271 graphSolution->Draw("AB");
272 Double_t T[BMN_GLPARAMS_TOTAL];
273 for (Int_t i = 0, k = 0; i < BMN_MODULE_COUNT; i++)
274 for (Int_t j = 0; j < BMN_GLOBAL_PARAMS_PD; j++, k++)
275 T[k] = fpDetectors->GetTestA()[i](j);
276 TGraph* graphTest = new TGraph(BMN_GLPARAMS_TOTAL, xValues, T);
277 graphTest->SetLineColor(kBlack);
278 graphTest->Draw("*SAME");
279
280 /*
281 // Draw SVD singularity values
282 TVectorD SVDsig {GetResult()->SVDSig()};
283 fpPadG4->cd();
284 fpPadG4->SetTitle("SVD singularity values");
285 fpPadG4->SetLogy();
286 TGraph* graphSVD = new TGraph(
287 BMN_GLPARAMS_TOTAL,
288 xValues,
289 SVDsig.GetMatrixArray()
290 );
291 // narrow drawing range to interesting part
292 Int_t SVDdrawIdx;
293 for (SVDdrawIdx = 0; SVDdrawIdx < SVDsig.GetNrows(); SVDdrawIdx++)
294 if (SVDsig(SVDdrawIdx) <= BMN_SVD_LOW) break;
295 SVDdrawIdx = std::max(
296 0,
297 std::min(
298 SVDdrawIdx,
299 BMN_GLPARAMS_TOTAL - 10
300 )
301 );
302 graphSVD->GetXaxis()->SetRangeUser(
303 static_cast<Double_t>(SVDdrawIdx),
304 static_cast<Double_t>(BMN_GLPARAMS_TOTAL)
305 );
306 graphSVD->SetTitle("SVD singularity values (tale)");
307 graphSVD->SetFillColor(kGray);
308 graphSVD->Draw("AB");
309 */
310
312}
313
315{
317 const SVectGL* Atest{fpDetectors->GetTestA()};
318 BmnAlignResult *pBeforeResult{GetResult(kFALSE)}, *pAfterResult{GetResult(kTRUE)};
319 Double_t beforeValue{0.0}, afterValue{0.0};
320 for (Int_t i = 0; i < BMN_MODULE_COUNT; i++) {
321 beforeValue += ROOT::Math::Mag2(Atest[i] - pBeforeResult->A(i));
322 afterValue += ROOT::Math::Mag2(Atest[i] - pAfterResult->A(i));
323 }
324 LOG(info) << " === distance to test A before/after alignment: " << std::sqrt(beforeValue) << " / "
325 << std::sqrt(afterValue);
326}
327
328Double_t BmnAlignerTest::CalculateMSE(Bool_t withResiduals)
329{
331 if (withResiduals) {
332 fpResultCurrent->ResidualsX().Reset();
333 fpResultCurrent->ResidualsY().Reset();
334 }
335 for (Int_t iTrack = 0; iTrack < fTotalTracks; iTrack++) {
336 if (!fpTrackUsable->at(iTrack))
337 continue;
338 fpTestTracks[iTrack].AddMSE(this, withResiduals);
339 }
341}
@ iY1
@ iY0
@ iX1
@ iX0
#define BMN_DRAW_TRACKS_CNT
#define BMN_CLOSE_TO_ZERO
#define BMN_TEST_AVERAGE_DY
ROOT::Math::SVector< Double_t, BMN_GLOBAL_PARAMS_PD > SVectGL
#define BMN_TEST_ALPHA2_MAX
#define BMN_TEST_AVERAGE_DX
#define BMN_TEST_ALPHA1_SIGMA
#define BMN_TEST_ALPHA2_MIN
ROOT::Math::SVector< Double_t, 3 > SVect3
ROOT::Math::SVector< Double_t, BMN_LOCAL_PARAMS_PT > SVectLC
#define BMN_MODULE_COUNT
#define BMN_MAX_HIT_RAM_SIZE
#define BMN_GLOBAL_PARAMS_PD
const Float_t delta
Distance between GEM-stations.
Definition BmnMwpcHit.cxx:8
int i
Definition P4_F32vec4.h:22
const SVect3 & GetNormal() const noexcept
const SVect3 & GetCrdShift() const noexcept
Double_t GetPosition() const noexcept
const SMatr3x3 & GetCrdInvRotation() const noexcept
void Draw(Bool_t showNormal=kFALSE) const
Double_t GetConst() const noexcept
Double_t GetMaxZ() const
const SVectGL * GetTestA() const
const BmnATestDetectorPlane * GetDetectors() const
const SVect3 & GetVertex() const noexcept
const SVectLC & GetAlpha() const noexcept
void AddMSE(BmnAlignerClass *pAligner, Bool_t withResiduals=kFALSE) const
Double_t Y(Double_t z) const noexcept
Double_t X(Double_t z) const noexcept
void Draw(Bool_t solid=kTRUE, Double_t maxZ=BMN_TEST_TRACK_DEF_MAX_Z) const
const SVect3 & GetDirection() const noexcept
void AddHit(BmnATestHit &hit)
VectorSVectLC_t & Alpha() noexcept
Double_t GetValueMSE(Int_t index) const noexcept
SVectGL & A(Int_t index) noexcept
TH1D & ResidualsX() noexcept
TH1D & ResidualsY() noexcept
virtual void ReportResults() const override
virtual Double_t CalculateMSE(Bool_t withResiduals) override
virtual Bool_t PrepareData(const char *tracksPath=nullptr) override
virtual Bool_t IterateAlignment() override
virtual ~BmnAlignerTest()
virtual void Draw() override
BmnAlignerTest()=delete
Bool_t LoadTracks(const char *path)
Bool_t SaveTracks(const char *path) const
const BmnATestTrack & GetTestTrack(Int_t trackID) const noexcept
BmnAlignResult * fpResultCurrent
Definition BmnAligner.h:122
void StoreZeroSolution(const std::vector< SVectLC > &AlphaZero)
Definition BmnAligner.h:187
BmnDataIterator * fpIteratorMain
Definition BmnAligner.h:116
virtual Bool_t IterateAlignment()
std::vector< Bool_t > * fpTrackUsable
Definition BmnAligner.h:103
BmnRamIterator< BmnATestHit > * fpRamIterator
Definition BmnAligner.h:115
Int_t TotalTracks() const noexcept
Definition BmnAligner.h:60
virtual void ReportResults() const
BmnAlignResult * GetResult(Bool_t last=kTRUE) const noexcept
Definition BmnAligner.h:64
Double_t GetY() const noexcept
Double_t GetX() const noexcept
Double_t GetZ() const noexcept
a class to store JSON values
Definition json.hpp:17282
ValueType value(const typename object_t::key_type &key, const ValueType &default_value) const
access specified object element with default value
Definition json.hpp:19319
string_t dump(const int indent=-1, const char indent_char=' ', const bool ensure_ascii=false, const error_handler_t error_handler=error_handler_t::strict) const
serialization
Definition json.hpp:18426
void push_back(basic_json &&val)
add an object to an array
Definition json.hpp:19986