11#undef BMN_TEST_ALGORITHM
24#include "fairlogger/Logger.h"
31#define BMN_MSE_SLOW_THRESHOLD 2.0
33template<
typename HitType>
36 const char* filesListPath,
37 const char* constraintsPath,
39 :
BmnAligner(measureModel, modelDetector, constraintsPath, nThreads)
42 LOG(state) <<
" > BmnAligner : initialization started";
45 LOG(info) <<
" ROOT files list: " << filesListPath;
48 LOG(error) <<
"<!> ROOT iterator initialization failed";
73 LOG(info) <<
Form(
" %.1f GB of RAM required", ram_size / 1073741824.0);
84template<
typename HitType>
103template<
typename HitType>
107 LOG(state) <<
" > BmnAligner : data preparation started";
109 LOG(error) <<
" <!> BmnAligner is not initialized";
118 Int_t detID, iTrack{0}, iHit{0};
126 Double_t xSum{0.0}, ySum{0.0}, zSum{0.0}, z2Sum{0.0}, zxSum{0.0}, zySum{0.0};
134 LOG(error) <<
" <!> Unknown detector module encoding:"
135 <<
"\n iterator::HitDetectorID() = " << detID
136 <<
"\n FairHit::GetDetectorID() = " << hit->GetDetectorID()
137 <<
"\n CbmStsHit::GetSystemId() = " << hit->
GetSystemId()
138 <<
"\n CbmStsHit::GetStationNr() = " << hit->
GetStationNr()
139 <<
"\n CbmStsHit::GetSectorNr() = " << hit->
GetSectorNr()
140 <<
"\n CbmStsHit::GetSensorNr() = " << hit->
GetSensorNr();
171 factor{1.0 / (hitCnt * z2Sum - zSum * zSum)};
172 arrAlpha[iTrack] =
SVectLC((xSum * z2Sum - zSum * zxSum) * factor, (hitCnt * zxSum - zSum * xSum) * factor,
173 (ySum * z2Sum - zSum * zySum) * factor, (hitCnt * zySum - zSum * ySum) * factor);
205 LOG(info) <<
" " << iTrack <<
" tracks loaded";
206 LOG(info) <<
" " << iHit <<
" hits loaded";
208 LOG(info) <<
" average hit position errors: Dx = " << Dx <<
", Dy = " << Dy;
213template<
typename HitType>
217 LOG(state) <<
" > BmnAligner : loading constraints...";
219 LOG(warning) <<
" constraints file in not provided, proceeding without constraints";
224 LOG(error) <<
" <!> LoadConstraints: failed to open JSON file " << *
fpConstraintsPath;
230 }
catch (
const std::exception& e) {
231 LOG(error) <<
" <!> LoadConstraints: failed to parse JSON file " << *
fpConstraintsPath;
232 LOG(error) << e.what();
235 if (!jsonData.is_array()) {
236 LOG(error) <<
" <!> LoadConstraints: top-level JSON is not an array in " << *
fpConstraintsPath;
241 LOG(error) <<
" <!> LoadConstraints: empty constraints array in " << *
fpConstraintsPath;
247 const auto& jsonLine = jsonData[
i];
248 if (!jsonLine.is_array()) {
249 LOG(error) <<
" <!> LoadConstraints: constraint " <<
i <<
" is not an array in " <<
fpConstraintsPath;
252 Int_t lineSize = jsonLine.size();
254 LOG(warning) <<
" constraint " <<
i <<
" size exceeds global parameters count, truncating";
257 LOG(warning) <<
" constraint " <<
i <<
" size is less then global parameters count, padding with zeroes";
259 for (
Int_t k = 0; k < lineSize; k++)
268template<
typename HitType>
274 LOG(state) <<
" > BmnAligner : loading solution from file " << jsonPath;
276 std::ifstream ifs(jsonPath);
278 LOG(error) <<
" <!> LoadSolution: failed to open JSON file";
283 Int_t elementsLoaded{0};
289 if (js.contains(
"CorrectionValues")) {
290 if (js[
"CorrectionValues"] != jsAlignmentType) {
291 LOG(error) <<
" <!> LoadSolution: wrong alignment type in JSON file\n"
292 <<
" expected: " << jsAlignmentType <<
", in file: " << js[
"CorrectionValues"];
296 LOG(warning) <<
" CorrectionValues key is missing: assuming alignment type is correct";
297 if (js.contains(
"DetectorElements")) {
299 LOG(error) <<
" <!> LoadSolution: wrong detector configuration in JSON file\n"
300 <<
" modules expected: " <<
BMN_MODULE_COUNT <<
", in file: " << js[
"DetectorElements"];
304 LOG(warning) <<
" DetectorElements key is missing: assuming detector configuration is correct";
305 if (!js.contains(
"CorrectionsPerDetector") || !js[
"CorrectionsPerDetector"].is_array()) {
306 LOG(error) <<
" LoadSolution: CorrectionsPerDetector key is missing or not an array";
311 Int_t elementsCount{
static_cast<Int_t>(js[
"CorrectionsPerDetector"].size())};
312 for (
Int_t i = 0;
i < elementsCount;
i++) {
313 json& element = js[
"CorrectionsPerDetector"][
i];
314 if (!element.contains(
"detector_id")) {
315 LOG(warning) <<
" ATTENTION! DetectorID is missing for some element, skipping.";
318 Int_t detID = element[
"detector_id"].get<
Int_t>();
320 LOG(warning) <<
" ATTENTION! Unregistered detector element ID: " << detID <<
", skipping.";
323 if (!element.contains(
"values") || !element[
"values"].is_array()
326 LOG(warning) <<
" ATTENTION! Correction values for the element " << detID
327 <<
" are invalid, skipping.";
333 json& values = element[
"values"];
337 if (element.contains(
"isFixed")) {
350 LOG(info) <<
" alignment corrections loaded for " << elementsLoaded <<
" detector elements";
355 LOG(info) <<
" loading tracks approximation from last knownsolution";
358 LOG(warning) <<
" ATTENTION! Local parameters approximation not found.";
360 }
catch (std::exception& e) {
361 LOG(error) <<
" <!> LoadSolution: failed to parse JSON file " << jsonPath;
362 LOG(error) << e.what();
372template<
typename HitType>
376 LOG(state) <<
" > BmnAligner : saving solution to file " << jsonPath;
377 std::ofstream ofs(jsonPath);
379 LOG(error) <<
" <!> SaveSolution: failed to open output file";
390 js[
"CorrectionsPerDetector"] = json::array();
394 element[
"values"] = json::array();
396 element[
"values"].push_back(solution.
A(
i)(j));
397 js[
"CorrectionsPerDetector"].push_back(element);
404 js[
"TracksParameters"] = json::array();
406 json element = json::array();
408 element.push_back(solution.
Alpha(
i)(j));
409 js[
"TracksParameters"].push_back(element);
415 ofs << std::setw(4) << js << std::endl;
416 }
catch (std::exception& e) {
417 LOG(error) <<
" <!> SaveSolution: failed to write JSON data to file\n" << e.what();
432template<
typename HitType>
436 LOG(state) <<
" > BmnAligner : iterative alignment started";
444 LOG(warning) <<
Form(
" detector element %d has only %d hits of %d required, it will be fixed",
461 if (progressAbs < 0.0) {
465 LOG(error) <<
" <!> BmnAligner::Align : algorithm is diverging";
473 LOG(error) <<
" <!> BmnAligner::Align : iteration failed";
477 progressRel = progressAbs /
fpResultLast->GetValueMSE();
478 LOG(info) <<
Form(
" = %+.3f%% relative progress, MSE change : %.3e -> %.3e\n", progressRel * 100.0,
481 retriesLast = regularRetries;
491 LOG_IF(info, retriesLast != regularRetries) <<
" " << regularRetries <<
" regularization retries left";
497 for (
const auto& [idx, mse] : MSElist)
507 LOG(info) <<
" " <<
fIterationResults.size() - 1 <<
" iterations until algorithm convergence criteria reached";
536template<
typename HitType>
541 LOG(state) <<
" > #" <<
fIteration + 1 <<
" iteration started";
543 LOG(error) <<
" <!> BmnAligner is not initialized";
551 TMatrixD mC_dash(ActiveParamsCnt, ActiveParamsCnt);
554 LOG(info) <<
" " <<
fThreadsCnt <<
" threads started to perform dimension reduction (Millepede)";
558 LOG(info) <<
Form(
" %.1fs (REAL) / %.1fs (CPU) took to complete dimension reduction", timerMille.
RealTime(),
596 mCL(k, ActiveParamsCnt + j) = weight;
597 mCL(ActiveParamsCnt + j, k) = weight;
598 constActive[j] =
kTRUE;
605 Int_t dstIdx{ActiveParamsCnt - 1},
607 for (
Int_t srcIdx = ActiveParamsCnt,
i = 0; srcIdx <= maxIdx; srcIdx++,
i++) {
610 if (++dstIdx == srcIdx)
612 mCL.
GetSub(srcIdx, srcIdx, 0, maxIdx, row);
613 mCL.
SetSub(dstIdx, 0, row);
614 mCL.
SetSub(0, dstIdx, row.
T());
619 LOG(info) <<
" " << dstIdx - ActiveParamsCnt <<
" active parameter constraints applied";
624 LOG(info) <<
" solving non-regularized system for global parameters...";
626 LOG(warning) <<
" iteration failed to improve MSE";
631 LOG(info) <<
" starting Levenberg-Marquardt regularization...";
634 Int_t LMiterarion{0};
639 for (
Int_t i = 0;
i < ActiveParamsCnt;
i++)
640 mCL(
i,
i) = mC_dash(
i,
i) * LMfactor;
654 LMfactorBest = LMfactor;
658 LOG(warning) <<
Form(
" LM regularization failed (F=%.3e)", LMfactor);
667 for (
Int_t i = 0;
i < ActiveParamsCnt;
i++)
668 mCLcopy(
i,
i) = mC_dash(
i,
i) * factor;
670 throw std::runtime_error(
Form(
"Levenberg-Marquardt failed (F=%.3e)", factor));
677 " regularization cancelled : sufficient %+.1f%% of progress achieved using non-regularized method",
680 }
else if (LMfactorBest < 1.0) {
681 LOG(warning) <<
" iteration failed to improve MSE";
684 }
else if (LMfactorBest == 1.0 || LMworking) {
688 <<
Form(
" F = %.3e : LM regularization approached gradient descent", LMfactorBest);
691 <<
" F = 1.0 : LM regularization did not improve results";
704 LOG(info) <<
Form(
" F = %.3e : optimal LM factor value", brent.
XMinimum());
706 LOG(warning) <<
Form(
" F = %.3e : rough LM factor value used, 1D optimization failed",
708 fnLM(success ? brent.
XMinimum() : LMfactorBest);
713 LOG(info) <<
Form(
" %d MSE value calculations performed during iteration",
fMSEclcCounter);
714 LOG(info) <<
Form(
" %.1fs (REAL) / %.1fs (CPU) took to complete parameters estimation", timerGlobal.
RealTime(),
723template<
typename HitType>
752 LOG(warning) <<
" <!> global matrix inversion unsuccessful";
758 if (activeConstCnt) {
778 Double_t valueMSE, stepMin{0.0}, stepMax{1.0};
779 valueMSE = fnMSE(stepMax);
780 while (valueMSE < fpResultLast->GetValueMSE()) {
783 valueMSE = fnMSE(stepMax);
787 fBrent.SetFunction(fnMSE, stepMin, stepMax);
790 LOG(warning) <<
" Brent method failed to calculate step size";
814template<
typename HitType>
818 constexpr static const Int_t SMatrMaxSize{
821 static const std::vector<Double_t> zeroes(SMatrMaxSize, 0.0);
822 static const auto zStart = zeroes.begin();
840 mC_part(ActiveParamsCnt, ActiveParamsCnt);
841 TVectorD vBfull(ActiveParamsCnt), vB_part(ActiveParamsCnt);
898 throw std::runtime_error(
"matrix inversion failed while calculating local parameters");
899 vDelta = mGamma * vBeta;
964 vBfull.SetSub(j, vaB);
965 mGfull.SetSub(j, 0, maG);
966 mCfull.SetSub(j, j, maC);
970 mC_part += mCfull - mGfull * maGamma * mGfullT;
971 vB_part += vBfull - mGfull * vaDelta;
979 *pmC_dash += mC_part;
980 *pvB_dash += vB_part;
983template<
typename HitType>
989 tempSolution.A() = solution;
990 tempSolution.SeparateA();
997template<
typename HitType>
1002 if (withResiduals) {
1012 std::cout <<
" " <<
fMSEclcCounter <<
" MSE calculations\r" << std::flush;
1016template<
typename HitType>
1020 std::array<Double_t, BMN_MODULE_COUNT> arrMSE;
1021 TH1D *pResX, *pResY;
1024 if (withResiduals) {
1033 if (threadNum == -1) {
1055 arrMSE[detectorIdx] += omega * delta * delta;
1063 arrMSE[detectorIdx] += omega * delta * delta;
1079 if (withResiduals) {
1087template<
typename HitType>
1091 Int_t idxLast{after.SVDSig().GetNrows() - 1};
1092 static const char* drawLine{
"================================================================================"};
1094 LOG(info) << drawLine;
1095 LOG(info) <<
Form(
"%.2e -> %.2e (%+.1f%%) : MSE value change", before.
GetValueMSE(), after.GetValueMSE(),
1098 "%.2e -> %.2e (%+.1f%%) : mean error in X change", before.
ResidualsX().
GetMean(), after.ResidualsX().GetMean(),
1101 "%.2e -> %.2e (%+.1f%%) : mean error in Y change", before.
ResidualsY().
GetMean(), after.ResidualsY().GetMean(),
1104 after.ResidualsX().GetStdDev(),
1108 after.ResidualsY().GetStdDev(),
1111 LOG(info) <<
Form(
"SVD minimum singular value : %.2e", after.SVDSig()(idxLast));
1112 LOG(info) <<
Form(
"SVD condition number : %.2e", after.SVDSig()(0) / after.SVDSig()(idxLast));
1113 LOG(info) << drawLine;
1115 LOG(info) <<
Form(
"total CPU time spent .......... %dh %02dm %02ds", cpuTime / 3600, (cpuTime / 60) % 60,
1117 LOG(info) <<
Form(
"total real time spent ......... %dh %02dm %02ds", realTime / 3600, (realTime / 60) % 60,
1122template<
typename HitType>
1130 LOG(warning) <<
" too many threads requested for the size of dataset, using " << max_threads
1135 LOG(warning) <<
" parallelization implemented for RAM mode only";
1150 : tracksPerThread));
1160template<
typename HitType>
1164 LOG(state) <<
Form(
" < BmnAligner : %s completed in %.1fs (REAL) / %.1fs (CPU)", stage, timer.
RealTime(),
1169template<
typename HitType>
1173 static const UInt_t ww{1900}, wh{1010};
1175 "canvas",
"BM@N alignment");
1183 fpPad3D =
new TPad(
"pad3D",
"3D", 0.0, 0.34, 0.6, 1.0);
1184 fpPadG1 =
new TPad(
"padG1",
"G1", 0.0, 0.0, 0.6, 0.34);
1185 fpPadG2 =
new TPad(
"padG2",
"G2", 0.6, 0.67, 1.0, 1.0);
1186 fpPadG3 =
new TPad(
"padG3",
"G3", 0.6, 0.34, 1.0, 0.67);
1187 fpPadG4 =
new TPad(
"padG4",
"G4", 0.6, 0.0, 1.0, 0.34);
1195template<
typename HitType>
1202 fpPadG3->SetTitle(
"Residuals by Y");
1211 pHist->
Draw(
"CSAME");
1214 fpPadG2->SetTitle(
"Residuals by X");
1223 pHist->
Draw(
"CSAME");
1226template<
typename HitType>
1234 fpPad3D->SetTitle(
"Some random tracks");
1243 Int_t iTrack{0}, iTrackDrawn{0};
1245 while (iTrackDrawn < BMN_DRAW_TRACKS_CNT && !fpIteratorMain->EndOfTracks()) {
1263 pTrackLines[iTrackDrawn].
Draw();
1267 pApproxLines[iTrackDrawn].
SetPoint(0, 0.0, Alpha(
iY0), Alpha(
iX0));
1268 pApproxLines[iTrackDrawn].
SetPoint(1, maxZdraw, Alpha(
iY0) + maxZdraw * Alpha(
iY1),
1269 Alpha(
iX0) + maxZdraw * Alpha(
iX1));
1272 pApproxLines[iTrackDrawn].
Draw();
#define BMN_MSE_SLOW_THRESHOLD
TMatrixT< Double_t > TMatrixD
char * Form(const char *fmt,...)
TVectorT< Double_t > TVectorD
#define BMN_MIN_REL_PROGRESS
ROOT::Math::SMatrix< Double_t, BMN_GLOBAL_PARAMS_PD, BMN_GLOBAL_PARAMS_PD > SMatrGL
#define BMN_BRENT_GRID_SIZE
#define BMN_RESIDUALS_BINS
ROOT::Math::SVector< Double_t, BMN_GLPARAMS_TOTAL > SVectGLT
#define BMN_LM_ITERATIONS_MAX
ROOT::Math::SMatrix< Double_t, BMN_GLOBAL_PARAMS_PD, BMN_LOCAL_PARAMS_PT > SMatrGP
#define BMN_DRAW_TRACKS_CNT
ROOT::Math::SMatrix< Double_t, BMN_LOCAL_PARAMS_PT, BMN_LOCAL_PARAMS_PT > SMatrLC
#define BMN_MIN_HITS_PORTION
ROOT::Math::SVector< Double_t, BMN_GLOBAL_PARAMS_PD > SVectGL
#define BMN_MIN_TRACKS_PER_THREAD
#define BMN_1D_STEP_TOLERANCE
#define BMN_RESIDUALS_RANGE_X
#define BMN_REL_PROGRESS_GOOD
#define BMN_MIN_HITS_PER_TRACK
static constexpr Int_t BMN_GLPARAMS_TOTAL
#define BMN_MIN_ACTIVE_DETECTORS
#define BMN_RESIDUALS_RANGE_Y
ROOT::Math::SVector< Double_t, BMN_LOCAL_PARAMS_PT > SVectLC
#define BMN_MIN_REGULAR_ITERS
#define BMN_LM_ITERATIONS_MIN
#define BMN_MAX_HIT_RAM_SIZE
#define BMN_MAX_REGULAR_RETRY
#define BMN_LOCAL_PARAMS_PT
#define BMN_GLOBAL_PARAMS_PD
#define _BMN_ALIGNER_RUN_THREADS(...)
VectorSVectLC_t & Alpha() noexcept
Double_t GetValueMSE(Int_t index) const noexcept
SVectGL & A(Int_t index) noexcept
std::array< std::pair< Int_t, Double_t >, BMN_MODULE_COUNT > IdxValuePair_t
TH1D & ResidualsX() noexcept
TH1D & ResidualsY() noexcept
DetectorFlags_t fDetectorActive
std::vector< BmnDataIterator * > fpIterators
BmnAlignResult * fpResultCurrent
std::vector< TVectorD > fConstraints
BmnAlignResult * fpResultLast
std::deque< BmnAlignResult > fIterationResults
void StoreZeroSolution(const std::vector< SVectLC > &AlphaZero)
BmnDataIterator * fpIteratorMain
BmnSimpleProgressBar fProgressBar
void ProcessTracks(TVectorD *pvB_dash, TMatrixD *pmC_dash, Int_t threadNum)
std::vector< Int_t > fFirstTracksIdx
virtual Bool_t IterateAlignment()
std::vector< Bool_t > * fpTrackUsable
virtual Bool_t PrepareData(const char *tracksPath=nullptr)
DetectorHitCount_t fHitsPerDetector
Bool_t SaveSolution(const char *jsonPath, Bool_t withAlpha=kFALSE)
BmnRamIterator< HitType > * fpRamIterator
Double_t fOmegaScaleFactor
void CalculateMSEpart(Bool_t withResiduals, Int_t threadNum)
BmnRootIterator * fpRootIterator
Bool_t LoadSolution(const char *jsonPath, Bool_t withAlpha=kTRUE, Bool_t withMSE=kTRUE)
BmnAlignResult * GetCurrentResult() const noexcept
Bool_t Align(Bool_t fixEvery=kFALSE)
BmnAligner(const BmnMeasureModel *measureModel, const BmnDetectorModel *modelDetector, const char *constraintsPath=nullptr, Int_t nThreads=1)
void DoneInTime(TStopwatch &timer, const char *stage)
Double_t CalculateMSE(const SVectGLT &solution)
const BmnDetectorModel * fpDetModel
const BmnMeasureModel * fpMeasureModel
virtual void ReportResults() const
const char * fpConstraintsPath
BmnAlignResult * GetResult(Bool_t last=kTRUE) const noexcept
Bool_t SolveGlobal(const TMatrixD &mCL, const TVectorD &vBL)
void MarkTime(TStopwatch &timer)
virtual Double_t HitX() const =0
virtual Double_t HitY() const =0
virtual Double_t HitZ() const =0
virtual Double_t HitWx() const =0
virtual void ResetHits()=0
virtual void ResetAll()=0
virtual void NextTrack()=0
Bool_t EndOfTracks() const noexcept
virtual Int_t HitDetectorID() const =0
virtual Double_t HitWy() const =0
Bool_t EndOfHits() const noexcept
Int_t GetSensorNr() const
virtual Int_t GetStationNr() const
Int_t GetSectorNr() const
Int_t GetSystemId() const
Accessors.
void SetFunction(const ROOT::Math::IGenFunction &f, double xlow, double xup)
bool Minimize(int maxIter, double absTol=1.E-8, double relTol=1.E-10) override
double XMinimum() const override
void SetElements(InputIterator begin, InputIterator end, bool triang=false, bool lower=true)
void SetElements(InputIterator begin, InputIterator end)
virtual void SetFillColor(Color_t fcolor)
virtual void SetLineStyle(Style_t lstyle)
virtual void SetLineColor(Color_t lcolor)
virtual void SetRangeUser(Double_t ufirst, Double_t ulast)
void SetWindowSize(UInt_t ww, UInt_t wh)
void SetFixedAspectRatio(Bool_t fixed=kTRUE) override
void SetCanvasSize(UInt_t ww, UInt_t wh) override
bool SetRealAspectRatio(const Int_t axis=1)
Bool_t Decompose() override
const TVectorD & GetSig()
Bool_t Decompose() override
TVectorD Solve(const TVectorD &b, Bool_t &ok) override
virtual Double_t GetStdDev(Int_t axis=1) const
virtual Double_t GetMean(Int_t axis=1) const
void Draw(Option_t *option="") override
virtual Int_t Fill(const char *name, Double_t w)
TMatrixT< Element > GetSub(Int_t row_lwb, Int_t row_upb, Int_t col_lwb, Int_t col_upb, Option_t *option="S") const
TMatrixT< Element > & T()
const TMatrixT< Element > & Use(const TMatrixT< Element > &a) const
TMatrixTBase< Element > & ResizeTo(const TMatrixT< Element > &m)
TMatrixTBase< Element > & SetSub(Int_t row_lwb, Int_t col_lwb, const TMatrixTBase< Element > &source) override
virtual void SetPoint(Int_t point, Double_t x, Double_t y, Double_t z)
void Draw(Option_t *option="") override
virtual Double_t Uniform(Double_t x1, Double_t x2)
void Start(Bool_t reset=kTRUE)
const TVectorT< Element > & Use(const TVectorT< Element > &v) const
TVectorT< Element > & ResizeTo(const TVectorT< Element > &v)
Element * GetMatrixArray()
TVectorT< Element > GetSub(Int_t row_lwb, Int_t row_upb, Option_t *option="S") const
Expr< TensorMulOp< SVector< T, D1 >, SVector< T, D2 > >, T, D1, D2 > TensorProd(const SVector< T, D1 > &lhs, const SVector< T, D2 > &rhs)
namespace for Niels Lohmann
basic_json<> json
default specialization