9 #include <GaudiKernel/PhysicalConstants.h>
31 constexpr
double detCutOff = 1.e-8;
35 using namespace SegmentFit;
84 ATH_MSG_DEBUG(
"Somehow a measurement is on the narrow ridge of validity. Let's try if the time can be fitted now ");
99 fitResult.
nPhiMeas+= hit->measuresPhi();
100 fitResult.
nDoF+= hit->measuresPhi();
101 fitResult.
nDoF+= hit->measuresEta();
104 fitResult.
nTimeMeas+=hit->measuresTime();
106 if (!fitResult.
nDoF) {
123 const double normDot = normal.dot(
line.dir);
127 const double travelledDist = (
offset -
line.pos.dot(normal)) / normDot;
128 const double partialDist = - travelledDist / normDot * normal.dot(
line.gradient[
toInt(fitPar)]);
129 return travelledDist *
line.gradient[
toInt(fitPar)] + partialDist *
line.dir;
180 line.hessian[idxThetaSq] = -
Amg::Vector3D{phi.cs*theta.sn,phi.sn*theta.sn, theta.cs};
191 const double planeProject =
line.dir.dot(hitDir);
192 const double projDirLenSq = 1. -
std::pow(planeProject, 2);
193 if (projDirLenSq < std::numeric_limits<float>::epsilon()) {
207 const double invProjLenSq = 1. / projDirLenSq;
208 const double invProjLen = std::sqrt(invProjLenSq);
214 const double lineDist = projDir.cross(hitDir).dot(hitMinSeg);
215 const double resVal = (lineDist - hit.
driftRadius());
228 auto partPlaneProject{make_array<double, nLinePars>(0.)};
239 partPlaneProject[param] =
line.gradient[param].dot(hitDir);
240 partProjDir[param] = (
line.gradient[param] - partPlaneProject[param]*hitDir) * invProjLen
241 + partPlaneProject[param]*planeProject* projDir * invProjLenSq;
242 const double partialDist = partProjDir[param].cross(hitDir).dot(hitMinSeg);
247 ( hitMinSeg.dot(
line.gradient[param]) * planeProject +
248 hitMinSeg.dot(
line.dir)*partPlaneProject[param]) * invProjLenSq
254 const double partialDist = - projDir.cross(hitDir).dot(
line.gradient[param]);
258 line.gradient[param].dot(hitDir)) * invProjLenSq;
275 for (
int param1 = param; param1>=0; --param1) {
279 const int lineIdx = vecIdxFromSymMat<nLinePars>(param, param1);
280 const int resIdx = vecIdxFromSymMat<toInt(ParamDefs::nPars)>(param, param1);
285 const double partSqLineProject =
line.hessian[lineIdx].dot(hitDir);
286 const Amg::Vector3D projDirPartSq = (
line.hessian[lineIdx] - partSqLineProject * hitDir) * invProjLen
287 + (partPlaneProject[param1] * planeProject) * invProjLenSq * partProjDir[param]
288 + (partPlaneProject[param] * planeProject) * invProjLenSq * partProjDir[param1]
289 + (partSqLineProject*planeProject) * invProjLenSq * projDir
290 + (partPlaneProject[param1] * partPlaneProject[param]) *
std::pow(invProjLenSq, 2) * projDir;
292 const double partialSqDist = projDirPartSq.cross(hitDir).dot(hitMinSeg);
299 + hitMinSeg.dot(
line.hessian[lineIdx]) *planeProject * invProjLenSq
300 + hitMinSeg.dot(
line.dir)*partSqLineProject * invProjLenSq
301 + hitMinSeg.dot(
line.gradient[param])*partPlaneProject[param1]*invProjLenSq
302 + hitMinSeg.dot(
line.gradient[param1])*partPlaneProject[param]*invProjLenSq;
308 const double partialSqDist = - partProjDir[param].cross(hitDir).dot(
line.gradient[param1]);
311 const double partialSqAlongWire = -(
line.gradient[param1].dot(
line.gradient[param])*planeProject +
312 line.gradient[param1].dot(
line.dir)*partPlaneProject[param]) * invProjLenSq
328 const double planeOffSet = normal.dot(hitPos);
330 const double normDot = normal.dot(
line.dir);
331 if (std::abs(normDot) < std::numeric_limits<double>::epsilon()){
339 const double travelledDist = (planeOffSet -
line.pos.dot(normal)) / normDot;
343 residual.residual.block<2,1>(0,0) = (planeIsect - hitPos).block<2,1>(0,0);
345 for (
unsigned fitPar = 0; fitPar <
line.gradient.size(); ++fitPar) {
349 const double partialDist = - travelledDist / normDot * normal.dot(
line.gradient[fitPar]);
350 residual.gradient[fitPar].block<2,1>(0,0) = (travelledDist *
line.gradient[fitPar] + partialDist *
line.dir).block<2,1>(0,0);
354 residual.gradient[fitPar].block<2,1>(0,0) = (
line.gradient[fitPar] -
line.gradient[fitPar].dot(normal) / normDot *
line.dir).block<2,1>(0,0);
367 for (
int param1 = param; param1>=0; --param1) {
368 const int lineIdx = vecIdxFromSymMat<nLinePars>(param, param1);
369 const int resIdx = vecIdxFromSymMat<toInt(ParamDefs::nPars)>(param, param1);
372 residual.hessian[resIdx].block<2,1>(0,0) =(
373 travelledDist *
line.hessian[lineIdx] - travelledDist *(normal.dot(
line.hessian[lineIdx])) / normDot *
line.dir
374 - normal.dot(
line.gradient[param1]) / normDot *
residual.gradient[param]
375 - normal.dot(
line.gradient[param]) / normDot *
residual.gradient[param1]).block<2,1>(0,0);
377 const double gradientDisplace = normal.dot(
line.gradient[param1]);
378 if (gradientDisplace > std::numeric_limits<float>::epsilon()){
379 residual.hessian[resIdx].block<2,1>(0,0) = gradientDisplace*( normal.dot(
line.gradient[param]) /
std::pow(normDot,2)*
line.dir
380 -
line.gradient[param] / normDot ).block<2,1>(0,0);
398 std::stringstream hitStream{};
399 const auto [startPos, startDir] =
makeLine(startPars);
400 for (
const HitType& hit : calibHits) {
406 hitStream<<
", channel dir: "<<
Amg::toString(hit->directionInChamber())<<std::endl;
409 <<
", plane location: "<<
Amg::toString(localToGlobal)<<std::endl<<hitStream.str());
416 fitResult.calibMeasurements = std::move(calibHits);
422 const auto [segPos, segDir] =
makeLine(startPars);
433 unsigned int noChangeIter{0};
435 ATH_MSG_VERBOSE(
"Iteration: "<<fitResult.nIter<<
" parameters: "<<
toString(fitResult.segmentPars)<<
"chi2: "<<fitResult.chi2);
449 for (
const HitType& hit : fitResult.calibMeasurements) {
455 ATH_MSG_VERBOSE(
"Update chi2 from measurement "<<(hit->spacePoint() ? idHelperSvc->toString(hit->spacePoint()->identify())
460 switch (hit->type()) {
467 const Amg::Vector3D normal = hit->spacePoint()->planeNormal();
468 const double planeOffSet = normal.dot(hit->positionInChamber());
470 + Amg::intersect<3>(segmentLine.pos, segmentLine.dir, normal, planeOffSet).value_or(0)* segmentLine.dir;
473 residual.residual.block<2,1>(0,0) = (hitPos - planeIsect).block<2,1>(0,0);
475 if (fitResult.timeFit && hit->measuresTime()) {
477 const double totFlightDist = (localToGlobal * planeIsect).
mag();
485 if (fitResult.timeFit && hit->measuresTime()) {
488 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
495 if (fitResult.timeFit && hit->measuresTime()) {
498 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
505 const double planeOffSet = normal.dot(hit->positionInChamber());
507 Amg::intersect<3>(segmentLine.pos, segmentLine.dir, normal, planeOffSet).value_or(0)* segmentLine.dir;
509 residual.residual.block<2,1>(0,0) = (hitPos - planeIsect).block<2,1>(0,0);
516 ATH_MSG_VERBOSE(
"Partial derivative of "<<idHelperSvc->toString(hit->spacePoint()->identify())
523 const auto &measurement = *hit->spacePoint()->primaryMeasurement();
524 ATH_MSG_WARNING(
"MdtSegmentFitter() - Unsupported measurment type" <<
typeid(measurement).
name());
527 ATH_MSG_VERBOSE(
"Update derivatives for hit "<< (hit->spacePoint() ? idHelperSvc->toString(hit->spacePoint()->identify()) :
"beamspot"));
533 for (
int k =1;
k < 5 - (!fitResult.timeFit); ++
k){
534 for (
int l = 0;
l<
k; ++
l){
535 hessian(
l,
k) = hessian(
k,
l);
540 fitResult.converged =
true;
541 ATH_MSG_VERBOSE(
"Fit converged after "<<fitResult.nIter<<
" iterations with "<<fitResult.chi2);
547 if (!fitResult.nPhiMeas && !fitResult.timeFit) {
548 paramUpdate = updateParameters<2>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
549 }
else if (!fitResult.nPhiMeas && fitResult.timeFit) {
557 paramUpdate = updateParameters<3>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
563 }
else if (fitResult.nPhiMeas && !fitResult.timeFit) {
564 paramUpdate = updateParameters<4>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
565 }
else if (fitResult.nPhiMeas && fitResult.timeFit) {
566 paramUpdate = updateParameters<5>(fitResult.segmentPars, prevPars, gradient, prevGrad, hessian);
570 fitResult.converged =
true;
581 fitResult.nDoF-=fitResult.timeFit;
584 const auto [segPos, segDir] = fitResult.makeLine();
587 std::optional<double> toF = fitResult.timeFit ? std::make_optional<double>((localToGlobal * segPos).
mag() * c_inv) : std::nullopt;
589 std::ranges::stable_sort(fitResult.calibMeasurements, [](
const HitType&
a,
const HitType&
b){
590 return a->positionInChamber().z() < b->positionInChamber().z();
595 for (
const HitType& hit : fitResult.calibMeasurements) {
596 hit->setDriftRadius(std::abs(hit->driftRadius()));
600 if (!fitResult.nPhiMeas&& !fitResult.timeFit) {
601 blockCovariance<2>(std::move(hessian), fitResult.segmentParErrs);
602 }
else if (!fitResult.nPhiMeas && fitResult.timeFit) {
605 blockCovariance<3>(std::move(hessian), fitResult.segmentParErrs);
608 }
else if (fitResult.nPhiMeas) {
609 blockCovariance<4>(std::move(hessian), fitResult.segmentParErrs);
610 }
else if (fitResult.nPhiMeas && fitResult.timeFit) {
611 blockCovariance<5>(std::move(hessian), fitResult.segmentParErrs);
619 double&
chi2,
int startPar)
const {
623 for (
int p = startPar;
p >=0 ; --
p) {
624 gradient[
p] +=2.*covRes.dot(fitMeas.
gradient[
p]);
625 for (
int k=
p;
k>=0; --
k) {
628 const int symMatIdx = vecIdxFromSymMat<ResidualWithPartials::nPars>(
p,
k);
634 <<
toString(gradient)<<
", Hessian:\n"<<hessian<<
", measurement covariance\n"<<
toString(invCov));
637 template <
unsigned int nDim>
641 covariance.setIdentity();
642 AmgSymMatrix(nDim) miniHessian = hessian.block<nDim, nDim>(0,0);
643 if (std::abs(miniHessian.determinant()) <= detCutOff) {
644 ATH_MSG_VERBOSE(
"Boeser mini hessian ("<<miniHessian.determinant()<<
")\n"<<miniHessian<<
"\n\n"<<hessian);
647 ATH_MSG_VERBOSE(
"Hessian matrix: \n"<<hessian<<
",\nblock Hessian:\n"<<miniHessian<<
",\n determinant: "<<miniHessian.determinant());
648 covariance.block<nDim,nDim>(0,0) = miniHessian.inverse();
652 template <
unsigned int nDim>
658 AmgSymMatrix(nDim) miniHessian = currentHessian.block<nDim, nDim>(0,0);
660 <<
", hessian ("<<miniHessian.determinant()<<
")"<<std::endl<<miniHessian);
662 double changeMag{0.};
663 if (std::abs(miniHessian.determinant()) > detCutOff) {
664 prevPars.block<nDim,1>(0,0) = currPars.block<nDim,1>(0,0);
666 const AmgVector(nDim) updateMe = miniHessian.inverse()* currGrad.block<nDim, 1>(0,0);
667 changeMag = std::sqrt(updateMe.dot(updateMe));
668 currPars.block<nDim,1>(0,0) -= updateMe;
669 prevGrad.block<nDim,1>(0,0) = currGrad.block<nDim,1>(0,0);
670 ATH_MSG_VERBOSE(
"Hessian inverse:\n"<<miniHessian.inverse()<<
"\n\nUpdate the parameters by -"
673 const AmgVector(nDim) gradDiff = (currGrad - prevGrad).block<nDim,1>(0,0);
674 const double gradDiffMag = gradDiff.mag2();
675 double denom = (gradDiffMag > std::numeric_limits<float>::epsilon() ? gradDiffMag : 1.);
676 const double gamma = std::abs((currPars - prevPars).block<nDim,1>(0,0).
dot(gradDiff))
678 ATH_MSG_VERBOSE(
"Hessian determinant invalid. Try deepest descent - \nprev parameters: "
680 prevPars.block<nDim, 1>(0,0) = currPars.block<nDim, 1>(0,0);
681 currPars.block<nDim, 1>(0,0) -=
gamma* currGrad.block<nDim, 1>(0,0);
682 prevGrad.block<nDim, 1>(0,0) = currGrad.block<nDim,1>(0,0);
683 changeMag = std::abs(
gamma) * currGrad.block<nDim, 1>(0,0).
mag();
686 unsigned int nOutOfBound{0};
687 for (
unsigned int p =0;
p< nDim; ++
p) {