SND@LHC Software
Loading...
Searching...
No Matches
sndShowerTools.cxx
Go to the documentation of this file.
1#include "sndShowerTools.h"
2
3#include <vector>
4#include <algorithm>
5#include <utility>
6
7#include "sndConfiguration.h"
8#include "sndScifiPlane.h"
9#include "sndUSPlane.h"
10#include "TMatrixD.h"
11#include "TMatrixDSym.h"
12#include "TMatrixDSymEigen.h"
13#include "TVectorD.h"
14#include "TDecompChol.h"
15#include "Math/Vector3D.h"
16#include "Math/Point3D.h"
17
18int snd::analysis_tools::GetScifiShowerStart(const std::vector<snd::analysis_tools::ScifiPlane> &scifi_planes)
19{
20 // Assuming planes are ordered
21 for (const auto &p : scifi_planes)
22 {
23 if (p.HasShower())
24 {
25 return p.GetStation();
26 }
27 }
28 return -1;
29}
30
31int snd::analysis_tools::GetScifiShowerEnd(const std::vector<snd::analysis_tools::ScifiPlane> &scifi_planes)
32{
33 // Assuming planes are ordered
34 for (auto p = scifi_planes.rbegin(); p != scifi_planes.rend(); p++) {
35 if (p->HasShower())
36 {
37 return p->GetStation();
38 }
39 }
40 return -1;
41}
42
43int snd::analysis_tools::GetUSShowerStart(const std::vector<snd::analysis_tools::USPlane> &us_planes)
44{
45 // Assuming planes are ordered
46 for (const auto &p : us_planes)
47 {
48 if (p.HasShower())
49 {
50 return p.GetStation();
51 }
52 }
53 return -1;
54}
55
56int snd::analysis_tools::GetUSShowerEnd(const std::vector<snd::analysis_tools::USPlane> &us_planes)
57{
58 // Assuming planes are ordered
59 for (auto p = us_planes.rbegin(); p != us_planes.rend(); p++) {
60 if (p->HasShower())
61 {
62 return p->GetStation();
63 }
64 }
65 return -1;
66}
67
68std::pair<ROOT::Math::XYZPoint, ROOT::Math::XYZVector> snd::analysis_tools::GetShowerInterceptAndDirection(const snd::Configuration &configuration, const std::vector<snd::analysis_tools::ScifiPlane> &scifi_planes, const std::vector<snd::analysis_tools::USPlane> &us_planes)
69{
70 std::vector<double> positions_x;
71 std::vector<double> positions_y;
72 std::vector<double> positions_z;
73 std::vector<double> err_positions_x;
74 std::vector<double> err_positions_y;
75 std::vector<double> err_positions_z;
76
77 auto collect_centroids = [&](const auto &planes) {
78 for (const auto &p : planes) {
79 auto centroid = p.GetCentroid();
80 auto centroid_error = p.GetCentroidError();
81 // Check that centroid is valid
82 if (!(std::isnan(centroid.X()) || std::isnan(centroid.Y()) || std::isnan(centroid.Z()))) {
83 positions_x.push_back(centroid.X());
84 positions_y.push_back(centroid.Y());
85 positions_z.push_back(centroid.Z());
86 err_positions_x.push_back(centroid_error.X());
87 err_positions_y.push_back(centroid_error.Y());
88 err_positions_z.push_back(centroid_error.Z());
89 }
90 }
91 };
92
93 collect_centroids(scifi_planes);
94 collect_centroids(us_planes);
95
96 if (static_cast<int>(positions_z.size()) < configuration.centroid_min_valid_station) {
97 return std::make_pair(ROOT::Math::XYZPoint(std::nan(""), std::nan(""), std::nan("")), ROOT::Math::XYZVector(std::nan(""), std::nan(""), std::nan("")));
98 }
99
100 auto weighted_linear_fit = [](const std::vector<double> &z, const std::vector<double> &val, const std::vector<double> &err)
101 {
102 const int n_points = z.size();
103 const int n_variables = 2; // intercept + slope
104
105 // Wrap existing memory into TVectorD
106 TVectorD v_z;
107 v_z.Use(n_points, const_cast<double*>(z.data()));
108 TVectorD v_y;
109 v_y.Use(n_points, const_cast<double*>(val.data()));
110 TVectorD v_e;
111 v_e.Use(n_points, const_cast<double*>(err.data()));
112
113 TMatrixD matrix(n_points, n_variables);
114 TMatrixDColumn(matrix,0) = 1.0;
115 TMatrixDColumn(matrix,1) = v_z;
116
117 // Solve normal equations (weighted least squares)
118 TVectorD coeffs = NormalEqn(matrix, v_y, v_e);
119
120 return std::make_pair(coeffs[0], coeffs[1]); // (intercept, slope)
121 };
122
123 auto [intercept_x, slope_x] = weighted_linear_fit(positions_z, positions_x, err_positions_x);
124 auto [intercept_y, slope_y] = weighted_linear_fit(positions_z, positions_y, err_positions_y);
125
126 ROOT::Math::XYZPoint shower_intercept(intercept_x, intercept_y, 0.0);
127 ROOT::Math::XYZVector shower_direction(slope_x, slope_y, 1.0);
128
129 return std::make_pair(shower_intercept, shower_direction.Unit());
130}
131
132std::pair<std::vector<snd::analysis_tools::ScifiPlane>, std::vector<snd::analysis_tools::USPlane>> snd::analysis_tools::GetShoweringPlanes(const std::vector<snd::analysis_tools::ScifiPlane> &scifi_planes, const std::vector<snd::analysis_tools::USPlane> &us_planes)
133{
134 std::vector<snd::analysis_tools::ScifiPlane> sh_scifi_planes;
135 std::vector<snd::analysis_tools::USPlane> sh_us_planes;
136
137 // Filter showering Scifi planes
138 std::copy_if(scifi_planes.begin(), scifi_planes.end(), std::back_inserter(sh_scifi_planes), [](const snd::analysis_tools::ScifiPlane& p) { return p.HasShower(); });
139 // Filter showering US planes
140 std::copy_if(us_planes.begin(), us_planes.end(), std::back_inserter(sh_us_planes), [](const snd::analysis_tools::USPlane& p) { return p.HasShower(); });
141
142 for (auto &p: sh_scifi_planes) {
143 p.FindCentroid();
144 }
145 for (auto &p: sh_us_planes) {
146 p.FindCentroid();
147 }
148
149 return std::make_pair(sh_scifi_planes, sh_us_planes);
150}
151
152std::pair<double, double>PCA(const std::vector<double>& u, const std::vector<double>& z)
153{
154 // Need at least 3 measurements
155 assert(u.size() == z.size() && u.size() >= 2);
156 const size_t n_samples = u.size();
157 const int n_variables = 2;
158
159 // Find the mean values
160 double mean_u = 0.0, mean_z = 0.0;
161 for (size_t i = 0; i < n_samples; ++i)
162 {
163 mean_u += u[i];
164 mean_z += z[i];
165 }
166 mean_u /= n_samples;
167 mean_z /= n_samples;
168
169 // Find the covariance components
170 double cov_uu = 0.0, cov_zz = 0.0, cov_uz = 0.0;
171 for (size_t i = 0; i < n_samples; ++i)
172 {
173 double du = u[i] - mean_u;
174 double dz = z[i] - mean_z;
175 cov_uu += du * du;
176 cov_zz += dz * dz;
177 cov_uz += du * dz;
178 }
179
180 const double denom = (n_samples > 1) ? (n_samples - 1.0) : 1.0;
181 cov_uu /= denom;
182 cov_zz /= denom;
183 cov_uz /= denom;
184
185 // Fill the covariance matrix
186 TMatrixDSym cov_matrix(n_variables);
187 cov_matrix[0][0] = cov_uu;
188 cov_matrix[0][1] = cov_uz;
189 cov_matrix[1][0] = cov_uz;
190 cov_matrix[1][1] = cov_zz;
191
192 // Find the eigenvalues and eigenvectors. For now we only need the former.
193 // Perhaps the eigenvectors could be used in the future, so left that part in comments.
194 TMatrixDSymEigen eig_decomp(cov_matrix);
195 TVectorD evals = eig_decomp.GetEigenValues();
196 //TMatrixD evecs = eig_decomp.GetEigenVectors();
197
198 // Make sure the order is descending, swap if needed
199 if (evals[0] < evals[1])
200 {
201 std::swap(evals[0], evals[1]);
202 //evecs[0] = eig_decomp.GetEigenVectors()[1];
203 //evecs[1] = eig_decomp.GetEigenVectors()[0];
204 }
205
206 return {evals[0], evals[1]};
207}
208
209std::pair<double, double> snd::analysis_tools::GetSciFiSpatialAnisotropy(const std::vector<ScifiPlane> &scifi_planes, bool use_all_centroids)
210{
211 std::vector<double> x, y, zx, zy;
212 for (auto &p : scifi_planes) {
213 // Add measurements per projection and in the respective coordinate vectors
214 if (use_all_centroids == false) {
215 for (auto &hit : p.GetHits()) {
216 if (hit.is_x) {
217 x.push_back(hit.x);
218 zx.push_back(hit.z);
219 } else {
220 y.push_back(hit.y);
221 zy.push_back(hit.z);
222 }
223 }
224 } else {
225 auto centroid = p.GetCentroid();
226 // Check that centroid is valid
227 if (!std::isnan(centroid.Z())) {
228 if (!std::isnan(centroid.X())) {
229 x.push_back(centroid.X());
230 zx.push_back(centroid.Z());
231 }
232 if (!std::isnan(centroid.Y())) {
233 y.push_back(centroid.Y());
234 zy.push_back(centroid.Z());
235 }
236 }
237 }
238 }
239
240 // Need at least 3 measurements
241 if (x.size() < 2 || y.size() < 2 || zx.size() < 2 || zy.size() < 2) {
242 return {1., 1.};
243 }
244 double lambda1, lambda2;
245 std::tie(lambda1, lambda2) = PCA(x, zx);
246 double anisotropy_xz = (lambda1 > 0) ? 1.0 - lambda2 / lambda1 : 0.0;
247 auto pca_result_yz = PCA(y, zy);
248 std::tie(lambda1, lambda2) = PCA(y, zy);
249 double anisotropy_yz = (lambda1 > 0) ? 1.0 - lambda2 / lambda1 : 0.0;
250 return {anisotropy_xz, anisotropy_yz};
251}
252
253double snd::analysis_tools::GetUSSpatialAnisotropy(const std::vector<USPlane> &us_planes, bool use_all_centroids)
254{
255 std::vector<double> y, zy;
256 for (auto &p : us_planes) {
257 // Add measurements per projection and in the respective coordinate vectors
258 if (use_all_centroids == false) {
259 for (auto &hit : p.GetHits()) {
260 y.push_back(hit.y);
261 zy.push_back(hit.z);
262 }
263 } else {
264 auto centroid = p.GetCentroid();
265 // Check that centroid is valid
266 if (!(std::isnan(centroid.Z()) || std::isnan(centroid.Y()))) {
267 y.push_back(centroid.Y());
268 zy.push_back(centroid.Z());
269 }
270 }
271 }
272
273 // Need at least 3 measurements
274 if (y.size() < 2 || zy.size() < 2) {
275 return 1.;
276 }
277 double lambda1, lambda2;
278 std::tie(lambda1, lambda2) = PCA(y, zy);
279 return (lambda1 > 0) ? 1.0 - lambda2 / lambda1 : 0.0;
280}
std::pair< std::vector< ScifiPlane >, std::vector< USPlane > > GetShoweringPlanes(const std::vector< ScifiPlane > &scifi_planes, const std::vector< USPlane > &us_planes)
int GetUSShowerEnd(const std::vector< USPlane > &us_planes)
double GetUSSpatialAnisotropy(const std::vector< USPlane > &us_planes, bool use_all_centroids=false)
int GetScifiShowerStart(const std::vector< ScifiPlane > &scifi_planes)
int GetUSShowerStart(const std::vector< USPlane > &us_planes)
int GetScifiShowerEnd(const std::vector< ScifiPlane > &scifi_planes)
std::pair< ROOT::Math::XYZPoint, ROOT::Math::XYZVector > GetShowerInterceptAndDirection(const Configuration &configuration, const std::vector< ScifiPlane > &scifi_planes, const std::vector< USPlane > &us_planes)
std::pair< double, double > GetSciFiSpatialAnisotropy(const std::vector< ScifiPlane > &scifi_planes, bool use_all_centroids=false)
std::pair< double, double > PCA(const std::vector< double > &u, const std::vector< double > &z)