9 #include <GaudiKernel/PhysicalConstants.h>
29 constexpr
double detCutOff = 1.e-8;
33 using namespace SegmentFit;
58 const double normDot = normal.dot(segDir);
62 const double travelledDist = (
offset - segPos.dot(normal)) / normDot;
63 const double partialDist = - travelledDist / normDot * normal.dot(linePartials[
toInt(fitPar)]);
64 return travelledDist * linePartials[
toInt(fitPar)] + partialDist * segDir;
68 return linePartials[
toInt(fitPar)] - linePartials[
toInt(fitPar)].dot(normal) / normDot * segDir;
84 const double dirDots = hitDir.dot(segDir);
85 const double divisor = (1. - dirDots * dirDots);
93 const double AminusBdotHit = AminusB.dot(hitDir);
94 const double numerator = (AminusB.dot(segDir) - AminusBdotHit * dirDots);
95 const double travelledDist = numerator /
divisor;
96 const double partialDirDots = hitDir.dot(segDirPartial);
99 const double derivativeDist = (
divisor * (AminusB.dot(segDirPartial) - AminusBdotHit* partialDirDots)
102 return travelledDist *segDirPartial + derivativeDist * segDir;
108 const double numerator = (AminusB.dot(segDir) - AminusB.dot(hitDir) * dirDots);
109 const double travelledDist = numerator /
divisor;
110 return linePartials[
toInt(fitPar)] + travelledDist * segDir;
146 std::stringstream hitStream{};
147 const auto [startPos, startDir] =
makeLine(startPars);
148 for (
const HitType& hit : calibHits) {
154 hitStream<<
", channel dir: "<<
Amg::toString(hit->directionInChamber())<<std::endl;
157 <<
", plane location: "<<
Amg::toString(localToGlobal)<<std::endl<<hitStream.str());
164 fitResult.calibMeasurements = std::move(calibHits);
178 unsigned int noChangeIter{0};
180 ATH_MSG_VERBOSE(
"Iteration: "<<fitResult.nIter<<
" parameters: "<<
toString(fitResult.segmentPars)<<
"chi2: "<<fitResult.chi2);
182 const auto [segPos, segDir] = fitResult.makeLine();
184 fitResult.calibMeasurements =
m_cfg.
calibrator->
calibrate(ctx, std::move(fitResult.calibMeasurements), segPos, segDir,
187 fitResult.nPhiMeas = fitResult.nDoF = fitResult.nTimeMeas = 0;
189 for (
const HitType& hit : fitResult.calibMeasurements) {
193 fitResult.nPhiMeas+= hit->measuresPhi();
194 fitResult.nDoF+= hit->measuresPhi();
195 fitResult.nDoF+= hit->measuresEta();
198 fitResult.nTimeMeas+=hit->measuresTime();
200 if (!fitResult.nDoF) {
204 if (!fitResult.nPhiMeas) {
210 fitResult.nDoF = fitResult.nDoF - 2 - (fitResult.nPhiMeas > 0 ? 2 : 0);
213 if (fitResult.timeFit && fitResult.nDoF <= 1) {
214 fitResult.timeFit =
false;
215 ATH_MSG_DEBUG(
"Switch of the time fit because nDoF: "<<fitResult.nDoF);
218 fitResult.calibMeasurements =
m_cfg.
calibrator->
calibrate(ctx, std::move(fitResult.calibMeasurements), segPos, segDir,
222 ATH_MSG_DEBUG(
"Somehow a measurement is on the narrow ridge of validity. Let's try if the time can be fitted now ");
223 fitResult.timeFit =
true;
233 for (
const HitType& hit : fitResult.calibMeasurements) {
242 switch (hit->type()) {
245 const double travelledDist =
Amg::intersect(hitPos, hitDir, segPos, segDir).value_or(0);
246 const Amg::Vector3D closePointSeg = segPos + travelledDist* segDir;
248 const Amg::Vector3D closePointWire = hitPos + hitDir.dot(closePointSeg - hitPos) * hitDir;
250 const Amg::Vector3D lineConnect = (closePointWire - closePointSeg);
251 const double lineDist = lineConnect.mag();
266 const Amg::Vector3D partialClosePointW = hitDir.dot(partialClosePointOnSeg) * hitDir;
268 const double dR = -driftV * c_inv * globApproach.dot(linePartials[
p]);
271 partialsResidual[
p][
toInt(
AxisDefs::eta)] = lineConnect.dot(partialClosePointW - partialClosePointOnSeg) / lineDist;
273 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
275 <<
Amg::toString(partialsResidual[
p])<<
", drift velocity: "<<driftV
276 <<
" in terms of beta: "<<(driftV * c_inv)<<
" -> dR: "<<dR);
280 if (fitResult.timeFit) {
283 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
291 const Amg::Vector3D normal = hit->spacePoint()->planeNormal();
292 const double planeOffSet = normal.dot(hit->positionInChamber());
293 const Amg::Vector3D planeIsect = segPos + Amg::intersect<3>(segPos, segDir, normal, planeOffSet).value_or(0)* segDir;
296 residual.block<2,1>(0,0) = (hitPos - planeIsect).block<2,1>(0,0);
298 if (fitResult.timeFit && hit->measuresTime()) {
300 const double totFlightDist = (localToGlobal * planeIsect).
mag();
307 segPos, segDir,linePartials,
par).block<2,1>(0,0);
309 if (fitResult.timeFit && hit->measuresTime()) {
312 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
319 if (fitResult.timeFit && hit->measuresTime()) {
322 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
329 const double planeOffSet = normal.dot(hit->positionInChamber());
330 const Amg::Vector3D planeIsect = segPos + Amg::intersect<3>(segPos, segDir, normal, planeOffSet).value_or(0)* segDir;
332 residual.block<2,1>(0,0) = (hitPos - planeIsect).block<2,1>(0,0);
337 segPos, segDir,linePartials,
par).block<2,1>(0,0);
340 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
347 ATH_MSG_WARNING(
"MdtSegmentFitter() - Unsupported measurment type" <<
typeid(*hit->spacePoint()).name());
350 ATH_MSG_VERBOSE(
"Update derivatives for hit "<< (hit->spacePoint() ? idHelperSvc->toString(hit->spacePoint()->identify()) :
"beamspot"));
356 for (
int k =1;
k < 5 - (!fitResult.timeFit); ++
k){
357 for (
int l = 0;
l<
k; ++
l){
358 hessian(
l,
k) = hessian(
k,
l);
363 fitResult.converged =
true;
364 ATH_MSG_VERBOSE(
"Fit converged after "<<fitResult.nIter<<
" iterations with "<<fitResult.chi2);
370 if (!fitResult.nPhiMeas && !fitResult.timeFit) {
371 paramUpdate = updateParameters<2>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
372 }
else if (!fitResult.nPhiMeas && fitResult.timeFit) {
380 paramUpdate = updateParameters<3>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
386 }
else if (fitResult.nPhiMeas && !fitResult.timeFit) {
387 paramUpdate = updateParameters<4>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
388 }
else if (fitResult.nPhiMeas && fitResult.timeFit) {
389 paramUpdate = updateParameters<5>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
393 fitResult.converged =
true;
399 fitResult.chi2PerMeasurement.resize(fitResult.calibMeasurements.size(), -1.);
405 fitResult.nDoF-=fitResult.timeFit;
408 const auto [segPos, segDir] = fitResult.makeLine();
411 std::optional<double> toF = fitResult.timeFit ? std::make_optional<double>((localToGlobal * segPos).
mag() * c_inv) : std::nullopt;
413 std::ranges::stable_sort(fitResult.calibMeasurements, [](
const HitType&
a,
const HitType&
b){
414 return a->positionInChamber().z() > b->positionInChamber().z();
418 fitResult.chi2PerMeasurement = std::move(chi2Term);
419 fitResult.chi2 =
chi2;
421 if (!fitResult.nPhiMeas&& !fitResult.timeFit) {
422 blockCovariance<2>(std::move(hessian), fitResult.segmentParErrs);
423 }
else if (!fitResult.nPhiMeas && fitResult.timeFit) {
426 blockCovariance<3>(std::move(hessian), fitResult.segmentParErrs);
429 }
else if (fitResult.nPhiMeas) {
430 blockCovariance<4>(std::move(hessian), fitResult.segmentParErrs);
431 }
else if (fitResult.nPhiMeas && fitResult.timeFit) {
432 blockCovariance<5>(std::move(hessian), fitResult.segmentParErrs);
442 double&
chi2,
int startPar)
const {
448 for (
int p = startPar;
p >=0 ; --
p) {
449 gradient[
p] +=2.*covRes.dot(residualPartials[
p]);
450 for (
int k=
p;
k>=0; --
k) {
451 hessian(
p,
k)+= 2.*
contract(invCov, residualPartials[
p], residualPartials[
k]);
455 <<
toString(gradient)<<
", Hessian:\n"<<hessian<<
", measurement covariance\n"<<
toString(invCov));
458 template <
unsigned int nDim>
462 covariance.setIdentity();
463 AmgSymMatrix(nDim) miniHessian = hessian.block<nDim, nDim>(0,0);
464 if (std::abs(miniHessian.determinant()) <= detCutOff) {
465 ATH_MSG_VERBOSE(
"Boeser mini hessian ("<<miniHessian.determinant()<<
")\n"<<miniHessian<<
"\n\n"<<hessian);
468 ATH_MSG_VERBOSE(
"Hessian matrix: \n"<<hessian<<
",\nblock Hessian:\n"<<miniHessian<<
",\n determinant: "<<miniHessian.determinant());
469 covariance.block<nDim,nDim>(0,0) = miniHessian.inverse();
473 template <
unsigned int nDim>
479 AmgSymMatrix(nDim) miniHessian = currentHessian.block<nDim, nDim>(0,0);
481 <<
", hessian ("<<miniHessian.determinant()<<
")"<<std::endl<<miniHessian);
483 double changeMag{0.};
484 if (std::abs(miniHessian.determinant()) > detCutOff) {
485 prevPars.block<nDim,1>(0,0) = currPars.block<nDim,1>(0,0);
487 const AmgVector(nDim) updateMe = miniHessian.inverse()* currGrad.block<nDim, 1>(0,0);
488 changeMag = std::sqrt(updateMe.dot(updateMe));
489 currPars.block<nDim,1>(0,0) -= updateMe;
490 prevGrad.block<nDim,1>(0,0) = currGrad.block<nDim,1>(0,0);
491 ATH_MSG_VERBOSE(
"Hessian inverse:\n"<<miniHessian.inverse()<<
"\n\nUpdate the parameters by -"
494 const AmgVector(nDim) gradDiff = (currGrad - prevGrad).block<nDim,1>(0,0);
495 const double gradDiffMag = gradDiff.mag2();
496 const double gamma = std::abs((currPars - prevPars).block<nDim,1>(0,0).
dot(gradDiff))
497 / gradDiffMag > std::numeric_limits<float>::epsilon() ? gradDiffMag : 1.;
498 ATH_MSG_VERBOSE(
"Hessian determinant invalid. Try deepest descent - \nprev parameters: "
500 prevPars.block<nDim, 1>(0,0) = currPars.block<nDim, 1>(0,0);
501 currPars.block<nDim, 1>(0,0) -=
gamma* currGrad.block<nDim, 1>(0,0);
502 prevGrad.block<nDim, 1>(0,0) = currGrad.block<nDim,1>(0,0);
503 changeMag = std::abs(
gamma) * currGrad.block<nDim, 1>(0,0).
mag();
506 unsigned int nOutOfBound{0};
507 for (
unsigned int p =0;
p< nDim; ++
p) {