1+ std ::string getEnergyPath ()
2+ {
3+ auto env = std ::getenv ("VMCWORKDIR" );
4+ if (env == nullptr ) {
5+ return "../../resources/energy_loss/HinH.txt" ; // Default path assuming cwd is build/AtTools
6+ }
7+ return std ::string (env ) + "/resources/energy_loss/HinH.txt" ; // Use environment variable
8+ }
9+ using ROOT ::Math ::XYZPoint ;
10+ using ROOT ::Math ::XYZVector ;
11+
12+ const double mass_p = 938.272 ; // Mass of proton in MeV/c^2
13+ const double charge_p = 1.602176634e-19 ; // Charge of proton
14+
15+ // Vectors to store the simulated points to compare to
16+ std ::vector < double > x_sim , y_sim , z_sim , Eloss_sim ;
17+ TH1F * hMom = nullptr ;
18+ TH1F * hMomSampled = nullptr ;
19+ TH1F * hMomError = nullptr ;
20+ TH1F * hMomError2 = nullptr ;
21+
22+ TCanvas * c1 = new TCanvas ();
23+ TCanvas * c2 = new TCanvas ();
24+
25+ // Parameters for model
26+ const int pointsToCluster = 5 ;
27+ const double sigma_pos = 1 ; // Position uncertainty of 10 mm
28+ const double sigma_mom = 0.1 ; // Momentum uncertainty in percentage
29+ const double sigma_mom_sample = 0.05 ; // Sampled momentum uncertainty in percentage
30+ const double sigma_theta = 1 * M_PI / 180 ; // Angular uncertainty of 1 degree
31+ const double sigma_phi = 1 * M_PI / 180 ; // Angular uncertainty of 1 degree
32+ const double gasDensity = 3.553e-5 ; // g/cm^3
33+ const double fAlpha = 1e-1 ;
34+ const double fBeta = 2 ;
35+ const double fKappa = 0 ;
36+
37+ // Global variables
38+ kf ::TrackFitterUKF * ukf = nullptr ;
39+
40+ // Functions in file
41+ /// Loads hits from an input file
42+ void LoadHits ();
43+ /// Run a single UKF for the passed initial state
44+ void SingleUKF (XYZPoint initialPos , XYZVector initialMom , TMatrixD initialCov );
45+ /// @brief Create the ukf pointer to reuse.
46+ void CreateUKF ();
47+
48+ TMatrixD CalculateInitialCov (double p );
49+ TMatrixD CalculatePosCov ();
50+
51+ /// @brief Test many tracks, saving stat properties
52+ /// @param n
53+ /// @param bias
54+ void TestManyTracks (int n , double bias = 0 )
55+ {
56+ LoadHits ();
57+ CreateUKF ();
58+
59+ XYZPoint fTruePos (- 3.40046e-04 , -1.49863e-04 , 1.0018 );
60+ XYZVector fTrueMom (0.00935463 , -0.0454279 , 0.00826042 );
61+ fTrueMom *= 1e3 ;
62+ double prevKE = AtTools ::Kinematics ::KE (fTrueMom .R (), mass_p ) - Eloss_sim [0 ];
63+ double fMomPrev = AtTools ::Kinematics ::GetRelMomFromKE (prevKE , mass_p );
64+ std ::cout << "Initial mom: " << fTrueMom .R () << " past mom " << fMomPrev << std ::endl ;
65+
66+ double fSigmaMom = fTrueMom .R () * sigma_mom_sample ;
67+
68+ hMom = new TH1F ("hMom" , "Reconstructed Momentum (MeV/c)" , 100 , fTrueMom .R () - 4 * fSigmaMom ,
69+ fTrueMom .R () + 4 * fSigmaMom );
70+ hMomSampled = new TH1F ("hMom" , "Reconstructed Momentum (MeV/c)" , 100 , fTrueMom .R () - 4 * fSigmaMom ,
71+ fTrueMom .R () + 4 * fSigmaMom );
72+ hMomError = new TH1F ("hMomError" , "Error (%)" , 100 , -2 , 2 );
73+ hMomError2 =
74+ new TH1F ("hMomError2" , "Error (%)" , 100 , -4 * fSigmaMom / fTrueMom .R () * 100 , 4 * fSigmaMom / fTrueMom .R () * 100 );
75+
76+ for (int i = 0 ; i < n ; ++ i ) {
77+
78+ if (i % 100 == 0 )
79+ std ::cout << "On iteration " << i << std ::endl ;
80+
81+ double pSampled = gRandom -> Gaus (fTrueMom .R (), sigma_mom_sample * fTrueMom .R ());
82+ pSampled += bias ;
83+ hMomSampled -> Fill (pSampled );
84+
85+ // pSampled = fTrueMom.R();
86+
87+ ROOT ::Math ::Polar3DVector sampledMom (pSampled , fTrueMom .Theta (), fTrueMom .Phi ());
88+ SingleUKF (fTruePos , XYZVector (sampledMom ), CalculateInitialCov (pSampled ));
89+
90+ auto filtState = ukf -> GetFilteredStates ()[0 ];
91+ auto smoothState = ukf -> GetSmoothedStates ()[0 ];
92+ auto smoothStatePrev = ukf -> GetSmoothedStates ()[1 ];
93+
94+ double pReco = (smoothState [3 ] + smoothStatePrev [3 ]) / 2 ; // Average of current and previous state
95+ hMom -> Fill (pReco );
96+ double error = (pReco - fTrueMom .R ()) / fTrueMom .R () * 100 ;
97+ hMomError -> Fill (error );
98+ double errorPrev = (ukf -> GetSmoothedStates ()[1 ][3 ] - fMomPrev ) / fMomPrev * 100 ;
99+ hMomError2 -> Fill (errorPrev );
100+
101+ std ::cout << std ::endl
102+ << std ::endl
103+ << "With initial momentum " << pSampled << " reconstructed " << pReco << " Error: " << error << "%"
104+ << std ::endl
105+ << std ::endl ;
106+ }
107+
108+ // Draw results
109+ c1 -> cd ();
110+ hMomSampled -> Scale (10 );
111+ hMomSampled -> SetStats (0 );
112+ hMom -> SetStats (0 );
113+ hMom -> Draw ("hist" );
114+ hMomSampled -> SetLineColor (kRed );
115+ hMomSampled -> Draw ("same hist" );
116+
117+ auto legend = new TLegend (0.65 , 0.75 , 0.88 , 0.88 );
118+ legend -> AddEntry (hMom , "Reconstructed" , "l" );
119+ legend -> AddEntry (hMomSampled , "Sampled (scale x10)" , "l" );
120+ legend -> Draw ();
121+
122+ c2 -> cd ();
123+ hMomError -> Draw ("hist" );
124+ // hMomError2->SetLineColor(kRed);
125+ // hMomError2->Draw("same hist");
126+ }
127+
128+ /*********** Function implementations **************/
129+ void LoadHits ()
130+ {
131+ if (x_sim .size () != 0 )
132+ return ;
133+ Eloss_sim .clear ();
134+ std ::ifstream infile ("hits.txt" );
135+ double xi , yi , zi , Ei ;
136+ int i = 0 ;
137+ double eLoss = 0 ;
138+
139+ // Save first point.
140+ infile >> xi >> yi >> zi >> Ei ;
141+ eLoss = Ei ; // Initialize energy loss
142+ x_sim .push_back (xi * 10 ); // Convert to mm
143+ y_sim .push_back (yi * 10 ); // Convert to mm
144+ z_sim .push_back (zi * 10 ); // Convert to mm
145+
146+ while (infile >> xi >> yi >> zi >> Ei ) {
147+ // Ei *= 1e3; // Convert to MeV
148+
149+ if (++ i % pointsToCluster != 0 ) {
150+ eLoss += Ei ;
151+ continue ; // Skip every 5th point
152+ }
153+ x_sim .push_back (xi * 10 );
154+ y_sim .push_back (yi * 10 );
155+ z_sim .push_back (zi * 10 );
156+ Eloss_sim .push_back (eLoss );
157+ eLoss = 0 ; // Reset energy loss for the next segment
158+ }
159+ }
160+
161+ void CreateUKF ()
162+ {
163+ auto elossModel2 = std ::make_unique < AtTools ::AtELossCATIMA > (gasDensity );
164+ elossModel2 -> SetProjectile (1 , 1 , 1 );
165+ std ::vector < std ::tuple < int , int , int >> mat ;
166+ mat .push_back ({1 , 1 , 1 });
167+ elossModel2 -> SetMaterial (mat );
168+
169+ auto elossModel = std ::make_unique < AtTools ::AtELossTable > (0 );
170+ elossModel -> LoadSrimTable (getEnergyPath ()); // Use the function to get the path
171+ elossModel -> SetDensity (gasDensity );
172+
173+ AtTools ::AtPropagator propagator (charge_p , mass_p , std ::move (elossModel ));
174+ propagator .SetEField ({0 , 0 , 0 }); // No electric field
175+ propagator .SetBField ({0 , 0 , 2.85 }); // Magnetic field
176+
177+ // Setup stepper for UKF
178+ auto stepper = std ::make_unique < AtTools ::AtRK4Stepper > ();
179+
180+ // Setup UKF
181+ ukf = new kf ::TrackFitterUKF (std ::move (propagator ), std ::move (stepper ));
182+ ukf -> setParameters (fAlpha , fBeta , fKappa );
183+ }
184+
185+ TMatrixD CalculateInitialCov (double p )
186+ {
187+ TMatrixD cov (6 , 6 );
188+ cov .Zero ();
189+ for (int i = 0 ; i < 3 ; ++ i ) {
190+
191+ cov (i , i ) = sigma_pos * sigma_pos ; // Set diagonal covariance to some small number
192+ }
193+ cov (3 , 3 ) = p * p * sigma_mom * sigma_mom ; // Momentum uncertainty
194+ cov (4 , 4 ) = sigma_theta * sigma_theta ; // Angular uncertainty
195+ cov (5 , 5 ) = sigma_phi * sigma_phi ; // Angular uncertainty
196+
197+ return cov ;
198+ }
199+ TMatrixD CalculatePosCov ()
200+ {
201+ TMatrixD cov (3 , 3 );
202+ cov .Zero ();
203+ for (int i = 0 ; i < 3 ; ++ i ) {
204+
205+ cov (i , i ) = sigma_pos * sigma_pos ; // Set diagonal covariance to some small number
206+ }
207+ return cov ;
208+ }
209+
210+ void SingleUKF (XYZPoint intitialPos , XYZVector initialMom , TMatrixD intitialCov )
211+ {
212+ ukf -> SetInitialState (intitialPos , initialMom , intitialCov );
213+
214+ for (int i = 1 ; i < x_sim .size (); ++ i ) {
215+ ukf -> SetMeasCov (CalculatePosCov ());
216+ XYZPoint point (x_sim [i ], y_sim [i ], z_sim [i ]);
217+
218+ ukf -> predictUKF (point );
219+ ukf -> correctUKF (point );
220+ }
221+
222+ ukf -> smoothUKF ();
223+ }
0 commit comments