Estimate the full track parameters from three space points.
This method is based on the conformal map transformation. It estimates the full bound track parameters, i.e. (loc0, loc1, phi, theta, q/p, t) at the bottom space point. The bottom space is assumed to be the first element in the range defined by the iterators. The magnetic field (which might be along any direction) is also necessary for the momentum estimation.
96 {
97
98 size_t numSP = std::distance(spBegin, spEnd);
99 if (numSP != 3) {
100 std::cout << "ERROR::less than 3 point provided" << std::endl;
101 return std::nullopt;
102 }
103
104
105 Acts::ActsScalar bFieldInTesla = bField.norm() / Acts::UnitConstants::T;
106 Acts::ActsScalar bFieldMinInTesla = bFieldMin / Acts::UnitConstants::T;
107
108 if (bFieldInTesla < bFieldMinInTesla) {
109
110
111 std::cout << "The magnetic field at the bottom space point: B = "
112 << bFieldInTesla
113 << " T is smaller than |B|_min = " << bFieldMinInTesla
114 << " T. Estimation is not performed." << std::endl;
115 return std::nullopt;
116 }
117
118
119 std::array<Acts::Vector3, 3> spGlobalPositions = {
120 Acts::Vector3::Zero(), Acts::Vector3::Zero(), Acts::Vector3::Zero()};
121
122
123 for (size_t isp = 0; isp < 3; ++isp) {
124 spacepoint_iterator_t it = std::next(spBegin, isp);
125 if (*it == nullptr) {
126 std::cout << "Empty space point found. This should not happen."
127 << std::endl;
128 return std::nullopt;
129 }
130 const auto& sp = *it;
131 spGlobalPositions[isp] = Acts::Vector3(sp->x(), sp->y(), sp->z());
132 }
133
134
135
136
137
138
139 Acts::Vector3 relVec = spGlobalPositions[1] - spGlobalPositions[0];
140 Acts::Vector3 newZAxis = bField.normalized();
141 Acts::Vector3 newYAxis = newZAxis.cross(relVec).normalized();
142 Acts::Vector3 newXAxis = newYAxis.cross(newZAxis);
143 Acts::RotationMatrix3 rotation;
144 rotation.col(0) = newXAxis;
145 rotation.col(1) = newYAxis;
146 rotation.col(2) = newZAxis;
147
148 Acts::Translation3 trans(spGlobalPositions[0]);
149
150 Acts::Transform3 transform(trans * rotation);
151
152
153 Acts::Vector3 local1 = transform.inverse() * spGlobalPositions[1];
154 Acts::Vector3 local2 = transform.inverse() * spGlobalPositions[2];
155
156
157 auto uvTransform = [](const Acts::Vector3& local) -> Acts::Vector2 {
158 Acts::Vector2 uv;
159 Acts::ActsScalar denominator =
160 local.x() * local.x() + local.y() * local.y();
161 uv.x() = local.x() / denominator;
162 uv.y() = local.y() / denominator;
163 return uv;
164 };
165
166 Acts::Vector2 uv1 = uvTransform(local1);
167 Acts::Vector2 uv2 = uvTransform(local2);
168
169
170
171 Acts::ActsScalar A = (uv2.y() - uv1.y()) / (uv2.x() - uv1.x());
172 Acts::ActsScalar B = uv2.y() - A * uv2.x();
173
174 Acts::ActsScalar rho = -2.0 * B / std::hypot(1., A);
175
176
177 Acts::ActsScalar rn = local2.x() * local2.x() + local2.y() * local2.y();
178
179 Acts::ActsScalar invTanTheta =
180 local2.z() * std::sqrt(1. / rn) / (1. + rho * rho * rn);
181
182
183 Acts::Vector3 transDirection(1., A, std::hypot(1, A) * invTanTheta);
184
185 [[maybe_unused]] Acts::Vector3 direction =
186 rotation * transDirection.normalized();
187
188
189 Acts::BoundVector params = Acts::BoundVector::Zero();
190
191
192
193
194
195 Acts::Vector3 bottomLocalPos = Tp.inverse() * spGlobalPositions[0];
196
197
198 params[Acts::eBoundLoc0] = bottomLocalPos.x();
199 params[Acts::eBoundLoc1] = bottomLocalPos.y();
200
201
202
203 Acts::ActsScalar qOverPt =
204 rho * (Acts::UnitConstants::m) / (0.3 * bFieldInTesla);
205
206 params[Acts::eBoundQOverP] = qOverPt / std::hypot(1., invTanTheta);
207
208
209
210 Acts::ActsScalar pInGeV = std::abs(1.0 / params[Acts::eBoundQOverP]);
211 Acts::ActsScalar pzInGeV = 1.0 / std::abs(qOverPt) * invTanTheta;
212 Acts::ActsScalar massInGeV = mass / Acts::UnitConstants::GeV;
213
214
215 Acts::ActsScalar v = pInGeV / std::hypot(pInGeV, massInGeV);
216 Acts::ActsScalar vz = pzInGeV / std::hypot(pInGeV, massInGeV);
217
218
219 Acts::ActsScalar pathz = spGlobalPositions[0].dot(bField) / bField.norm();
220
221
222 if (pathz != 0) {
223 params[Acts::eBoundTime] = pathz / vz;
224 } else {
225 params[Acts::eBoundTime] = spGlobalPositions[0].norm() / v;
226 }
227
228 return params;
229 }