Belle II Software development
FitManager.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * External Contributor: Wouter Hulsbergen *
5 * *
6 * See git log for contributors and copyright holders. *
7 * This file is licensed under LGPL-3.0, see LICENSE.md. *
8 **************************************************************************/
9#include <TMath.h>
10#include <Math/Vector4D.h>
11
12#include <analysis/dataobjects/Particle.h>
13
14#include <framework/logging/Logger.h>
15#include <framework/gearbox/Const.h>
16
17#include <analysis/VertexFitting/TreeFitter/FitManager.h>
18#include <analysis/VertexFitting/TreeFitter/FitParams.h>
19#include <analysis/VertexFitting/TreeFitter/DecayChain.h>
20#include <analysis/VertexFitting/TreeFitter/ParticleBase.h>
21
22namespace TreeFitter {
23
25 const ConstraintConfiguration& config,
26 double prec,
27 bool updateDaughters
28 ) :
29 m_particle(particle),
30 m_decaychain(nullptr),
31 m_status(VertexStatus::UnFitted),
32 m_chiSquare(-1),
33 m_prec(prec),
34 m_updateDaugthers(updateDaughters),
35 m_ndf(0),
36 m_fitparams(nullptr),
37 m_config(config)
38 {
39 m_decaychain = new DecayChain(particle, config, false);
41 }
42
44 {
45 delete m_decaychain;
46 delete m_fitparams;
47 }
48
50 {
51 const int nitermax = 100;
52 const int maxndiverging = 3;
53 const double dChisqConv = m_prec;
54 m_chiSquare = -1;
56
57 if (m_status == VertexStatus::UnFitted) {
59 }
60
61 if (m_errCode.failure()) {
62 m_status = VertexStatus::BadInput;
63 } else {
64 m_status = VertexStatus::UnFitted;
65 int ndiverging = 0;
66 bool finished = false;
67 int niter = 0;
68 for (niter = 0; niter < nitermax && !finished; ++niter) {
69 if (niter == 0) {
71 } else {
72 auto* tempState = new FitParams(*m_fitparams);
74 delete tempState;
75 }
77 double chisq = m_fitparams->chiSquare();
78 double deltachisq = chisq - m_chiSquare;
79 if (m_errCode.failure()) {
80 finished = true ;
81 m_status = VertexStatus::Failed;
82 m_particle->writeExtraInfo("failed", 1);
83 } else {
84 if (niter > 0) {
85 if ((std::abs(deltachisq) / m_chiSquare < dChisqConv)) {
86 m_chiSquare = chisq;
87 m_status = VertexStatus::Success;
88 finished = true ;
89 m_particle->writeExtraInfo("failed", 0);
90 } else if (deltachisq > 0 && ++ndiverging >= maxndiverging) {
91 m_particle->writeExtraInfo("failed", 2);
92 m_status = VertexStatus::NonConverged;
93 m_errCode = ErrCode(ErrCode::Status::slowdivergingfit);
94 finished = true ;
95 }
96 }
97 if (deltachisq < 0) {
98 ndiverging = 0;
99 }
100 m_chiSquare = chisq;
101 }
102 }
103 if (niter == nitermax && m_status != VertexStatus::Success) {
104 m_particle->writeExtraInfo("failed", 3);
105 m_status = VertexStatus::NonConverged;
106 }
107 if (!(m_fitparams->testCovariance())) {
108 m_particle->writeExtraInfo("failed", 4);
109 m_status = VertexStatus::Failed;
110 }
111 }
112
113 if (m_status == VertexStatus::Success) {
114 // mass constraints comes after kine so we have to
115 // update the mothers with the values set by the mass constraint
116 if (m_config.m_massConstraintListPDG.size() != 0) {
118 }
119 updateTree(*m_particle, true);
120 }
121
122 return (m_status == VertexStatus::Success);
123 }
124
125 void FitManager::getCovFromPB(const ParticleBase* pb, TMatrixFSym& returncov) const
126 {
127
128 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> cov = m_fitparams->getCovariance().selfadjointView<Eigen::Lower>();
129 int posindex = pb->posIndex();
130 // hack: for tracks and photons, use the production vertex
131 if (posindex < 0 && pb->mother()) {
132 posindex = pb->mother()->posIndex();
133 }
134 int momindex = pb->momIndex();
135 if (pb->hasEnergy()) {
136 // if particle has energy, get full p4 from fitparams and put them directly in the return type
137 // very important! Belle2 uses p,E,x! Change order here!
138 for (int row = 0; row < 4; ++row) {
139 for (int col = 0; col < 4; ++col) {
140 returncov(row, col) = cov(momindex + row, momindex + col);
141 }
142 }
143
144 for (int row = 0; row < 3; ++row) {
145 for (int col = 0; col < 3; ++col) {
146 returncov(row + 4, col + 4) = cov(posindex + row, posindex + col);
147 }
148 }
149
150 } else {
151 Eigen::Matrix<double, 6, 6> cov6 =
152 Eigen::Matrix<double, 6, 6>::Zero(6, 6);
153
154 for (int row = 0; row < 3; ++row) {
155 for (int col = 0; col < 3; ++col) {
156 cov6(row, col) = cov(momindex + row, momindex + col);
157 cov6(row + 3, col + 3) = cov(posindex + row, posindex + col);
158 }
159 }
160
161 double mass = 0;
162 if (pb->particle()->hasExtraInfo("treeFitterMassConstraintValue")) {
163 mass = pb->particle()->getExtraInfo("treeFitterMassConstraintValue");
164 } else mass = pb->particle()->getPDGMass();
165 Eigen::Matrix<double, 3, 1> momVec =
166 m_fitparams->getStateVector().segment(momindex, 3);
167
168 double energy2 = momVec.transpose() * momVec;
169 energy2 += mass * mass;
170 double energy = sqrt(energy2);
171
172 Eigen::Matrix<double, 7, 6> jacobian =
173 Eigen::Matrix<double, 7, 6>::Zero(7, 6);
174
175 for (int col = 0; col < 3; ++col) {
176 jacobian(col, col) = 1;
177 jacobian(3, col) = m_fitparams->getStateVector()(momindex + col) / energy;
178 jacobian(col + 4, col + 3) = 1;
179 }
180
181 Eigen::Matrix<double, 7, 7> cov7
182 = jacobian * cov6.selfadjointView<Eigen::Lower>() * jacobian.transpose();
183
184 for (int row = 0; row < 7; ++row) {
185 for (int col = 0; col < 7; ++col) {
186 returncov(row, col) = cov7(row, col);
187 }
188 }
189 } // else
190 }
191
192 bool FitManager::updateCand(Belle2::Particle& cand, const bool isTreeHead) const
193 {
194 const ParticleBase* pb = m_decaychain->locate(&cand);
195 if (pb) {
196 updateCand(*pb, cand, isTreeHead);
197 } else {
198 B2ERROR("Can't find candidate " << cand.getName() << " in tree " << m_particle->getName());
199 }
200 return pb != nullptr;
201 }
202
204 Belle2::Particle& cand, const bool isTreeHead) const
205 {
206 int posindex = pb.posIndex();
207 if (posindex < 0 && pb.mother()) {
208 posindex = pb.mother()->posIndex();
209 }
210
211 if (m_updateDaugthers || isTreeHead) {
212 TMatrixFSym cov7b2(7);
213 if (posindex >= 0) {
214 const ROOT::Math::XYZVector pos(m_fitparams->getStateVector()(posindex),
215 m_fitparams->getStateVector()(posindex + 1),
216 m_fitparams->getStateVector()(posindex + 2));
217 cand.setVertex(pos);
218 if (&pb == m_decaychain->cand()) { // if head
219 const double fitparchi2 = m_fitparams->chiSquare();
220 cand.setPValue(TMath::Prob(fitparchi2, m_ndf));//if m_ndf<1, this is 0.
221 cand.writeExtraInfo("chiSquared", fitparchi2);
222 cand.writeExtraInfo("modifiedPValue", TMath::Prob(fitparchi2, 3));
223 cand.writeExtraInfo("ndf", m_ndf);
224 }
225 if (pb.mother()) {
226 int motherPosIndex = pb.mother()->posIndex();
227 if (motherPosIndex >= 0) {
228 cand.writeExtraInfo("prodVertexX", m_fitparams->getStateVector()(motherPosIndex));
229 cand.writeExtraInfo("prodVertexY", m_fitparams->getStateVector()(motherPosIndex + 1));
230 if (pb.mother()->dim() > 2)
231 cand.writeExtraInfo("prodVertexZ", m_fitparams->getStateVector()(motherPosIndex + 2));
232 if (not isTreeHead) {
233 getCovFromPB(pb.mother(), cov7b2);
234 cand.writeExtraInfo("prodVertSxx", cov7b2[4][4]);
235 cand.writeExtraInfo("prodVertSxy", cov7b2[4][5]);
236 cand.writeExtraInfo("prodVertSyx", cov7b2[5][4]);
237 cand.writeExtraInfo("prodVertSyy", cov7b2[5][5]);
238 if (pb.mother()->dim() > 2) {
239 cand.writeExtraInfo("prodVertexZ", m_fitparams->getStateVector()(motherPosIndex + 2));
240 cand.writeExtraInfo("prodVertSxz", cov7b2[4][6]);
241 cand.writeExtraInfo("prodVertSyz", cov7b2[5][6]);
242 cand.writeExtraInfo("prodVertSzx", cov7b2[6][4]);
243 cand.writeExtraInfo("prodVertSzy", cov7b2[6][5]);
244 cand.writeExtraInfo("prodVertSzz", cov7b2[6][6]);
245 }
246 }
247 }
248 }
249 }
250
251 const int momindex = pb.momIndex();
252 ROOT::Math::PxPyPzEVector p;
253 p.SetPx(m_fitparams->getStateVector()(momindex));
254 p.SetPy(m_fitparams->getStateVector()(momindex + 1));
255 p.SetPz(m_fitparams->getStateVector()(momindex + 2));
256 if (pb.hasEnergy()) {
257 p.SetE(m_fitparams->getStateVector()(momindex + 3));
259 } else {
260 double mass = 0;
261 if (cand.hasExtraInfo("treeFitterMassConstraintValue")) {
262 mass = cand.getExtraInfo("treeFitterMassConstraintValue");
263 } else mass = cand.getPDGMass();
264 p.SetE(std::sqrt(p.P2() + mass * mass));
266 }
267 getCovFromPB(&pb, cov7b2);
268 cand.setMomentumVertexErrorMatrix(cov7b2);
269 }
270
271 if (pb.tauIndex() > 0) {
272 const std::tuple<double, double>tau = getDecayLength(cand);
273 const std::tuple<double, double>life = getLifeTime(cand);
274 cand.writeExtraInfo("decayLength", std::get<0>(tau));
275 cand.writeExtraInfo("decayLengthErr", std::get<1>(tau));
276 cand.writeExtraInfo("lifeTime", std::get<0>(life));
277 cand.writeExtraInfo("lifeTimeErr", std::get<1>(life));
278 }
279 }
280
281 void FitManager::updateTree(Belle2::Particle& cand, const bool isTreeHead) const
282 {
283 const bool updateableMother = updateCand(cand, isTreeHead);
284
285 if (updateableMother and not cand.hasExtraInfo("bremsCorrected") and
286 not(cand.hasExtraInfo("treeFitterTreatMeAsInvisible") and cand.getExtraInfo("treeFitterTreatMeAsInvisible") == 1)) {
287 const int ndaughters = cand.getNDaughters();
288 for (int i = 0; i < ndaughters; i++) {
289 auto* daughter = const_cast<Belle2::Particle*>(cand.getDaughter(i));
290 updateTree(*daughter, false);
291 }
292 }
293 }
294
295 std::tuple<double, double> FitManager::getLifeTime(Belle2::Particle& cand) const
296 {
297 const ParticleBase* pb = m_decaychain->locate(&cand);
298
299 if (pb && pb->tauIndex() >= 0 && pb->mother()) {
300 const int momindex = pb->momIndex();
301 const int tauIndex = pb->tauIndex();
302 const Eigen::Matrix<double, 1, 3> mom_vec = m_fitparams->getStateVector().segment(momindex, 3);
303
304 const Eigen::Matrix<double, 3, 3> mom_cov = m_fitparams->getCovariance().block<3, 3>(momindex, momindex);
305 Eigen::Matrix<double, 4, 4> comb_cov = Eigen::Matrix<double, 4, 4>::Zero(4, 4);
306
307 const std::tuple<double, double> lenTuple = getDecayLength(cand);
308
309 const double lenErr = std::get<1>(lenTuple);
310 comb_cov(0, 0) = lenErr * lenErr;
311 comb_cov(1, 0) = m_fitparams->getCovariance()(momindex, tauIndex);
312 comb_cov(2, 0) = m_fitparams->getCovariance()(momindex + 1, tauIndex);
313 comb_cov(3, 0) = m_fitparams->getCovariance()(momindex + 2, tauIndex);
314
315 comb_cov.block<3, 3>(1, 1) = mom_cov;
316
317 double mass = 0;
318 if (pb->particle()->hasExtraInfo("treeFitterMassConstraintValue")) {
319 mass = pb->particle()->getExtraInfo("treeFitterMassConstraintValue");
320 } else mass = pb->particle()->getPDGMass();
321 const double mBYc = mass / Belle2::Const::speedOfLight;
322 const double mom = mom_vec.norm();
323 const double mom3 = mom * mom * mom;
324
325 const double len = std::get<0>(lenTuple);
326 const double t = len / mom * mBYc;
327
328 Eigen::Matrix<double, 1, 4> jac = Eigen::Matrix<double, 1, 4>::Zero();
329 jac(0) = 1. / mom * mBYc;
330 jac(1) = -1. * len * mom_vec(0) / mom3 * mBYc;
331 jac(2) = -1. * len * mom_vec(1) / mom3 * mBYc;
332 jac(3) = -1. * len * mom_vec(2) / mom3 * mBYc;
333
334 const double tErr2 = jac * comb_cov.selfadjointView<Eigen::Lower>() * jac.transpose();
335 // time in nanosec
336 return std::make_tuple(t, std::sqrt(tErr2));
337 }
338 return std::make_tuple(-999, -999);
339 }
340
341 std::tuple<double, double> FitManager::getDecayLength(const ParticleBase* pb) const
342 {
343 // returns the decaylength in the lab frame
344 return getDecayLength(pb, *m_fitparams);
345 }
346
347 std::tuple<double, double> FitManager::getDecayLength(const ParticleBase* pb, const FitParams& fitparams) const
348 {
349 if (pb->tauIndex() >= 0 && pb->mother()) {
350 const int tauindex = pb->tauIndex();
351 const double len = fitparams.getStateVector()(tauindex);
352 const double lenErr2 = fitparams.getCovariance()(tauindex, tauindex);
353 return std::make_tuple(len, std::sqrt(lenErr2));
354 }
355 return std::make_tuple(-999, -999);
356 }
357
358 std::tuple<double, double> FitManager::getDecayLength(Belle2::Particle& cand) const
359 {
360 std::tuple<double, double> rc = std::make_tuple(-999, -999);
361 const ParticleBase* pb = m_decaychain->locate(&cand) ;
362 if (pb && pb->tauIndex() >= 0 && pb->mother()) {
363 rc = getDecayLength(pb);
364 }
365 return rc;
366 }
367
368}//end module namespace
static const double speedOfLight
[cm/ns]
Definition: Const.h:695
Class to store reconstructed particles.
Definition: Particle.h:76
std::string getName() const override
Return name of this particle.
Definition: Particle.cc:1250
void writeExtraInfo(const std::string &name, const double value)
Sets the user defined extraInfo.
Definition: Particle.cc:1393
void setVertex(const ROOT::Math::XYZVector &vertex)
Sets position (decay vertex)
Definition: Particle.h:306
void set4VectorDividingByMomentumScaling(const ROOT::Math::PxPyPzEVector &p4)
Sets Lorentz vector dividing by the momentum scaling factor.
Definition: Particle.h:294
bool hasExtraInfo(const std::string &name) const
Return whether the extra info with the given name is set.
Definition: Particle.cc:1351
unsigned getNDaughters(void) const
Returns number of daughter particles.
Definition: Particle.h:747
double getPDGMass(void) const
Returns uncertainty on the invariant mass (requires valid momentum error matrix)
Definition: Particle.cc:635
void setMomentumVertexErrorMatrix(const TMatrixFSym &errMatrix)
Sets 7x7 error matrix.
Definition: Particle.cc:424
void setPValue(double pValue)
Sets chi^2 probability of fit.
Definition: Particle.h:377
const Particle * getDaughter(unsigned i) const
Returns a pointer to the i-th daughter particle.
Definition: Particle.cc:662
double getExtraInfo(const std::string &name) const
Return given value if set.
Definition: Particle.cc:1374
constraint configuration class
const std::vector< int > m_massConstraintListPDG
list of pdg codes to mass constrain
this class does a lot of stuff: Build decaytree structure allowing to index particles and handle the ...
Definition: DecayChain.h:23
ErrCode initialize(FitParams &par)
initialize the chain
Definition: DecayChain.cc:70
const ParticleBase * locate(Belle2::Particle *bc) const
convert Belle2::particle into particle base(fitter base particle)
Definition: DecayChain.cc:111
int dim() const
get dimension
Definition: DecayChain.h:49
const ParticleBase * cand()
get candidate
Definition: DecayChain.h:64
ErrCode filterWithReference(FitParams &par, const FitParams &ref)
filter with respect to a previous iteration for better stability
Definition: DecayChain.cc:93
ErrCode filter(FitParams &par)
filter down the chain
Definition: DecayChain.cc:81
abstract errorocode be aware that the default is success
Definition: ErrCode.h:14
bool failure() const
returns true if errorcode is error
Definition: ErrCode.h:58
void reset()
reset the errorcode to default (success!)
Definition: ErrCode.h:55
const ConstraintConfiguration m_config
config container
Definition: FitManager.h:109
DecayChain * m_decaychain
the decay tree
Definition: FitManager.h:85
FitManager()
constructor
Definition: FitManager.h:29
Belle2::Particle * particle()
getter for the head of the tree
Definition: FitManager.h:78
ErrCode m_errCode
errorcode
Definition: FitManager.h:97
void getCovFromPB(const ParticleBase *pb, TMatrixFSym &returncov) const
extract cov from particle base
Definition: FitManager.cc:125
void updateTree(Belle2::Particle &particle, const bool isTreeHead) const
update the Belle2::Particles with the fit results
Definition: FitManager.cc:281
FitParams * m_fitparams
parameters to be fitted
Definition: FitManager.h:106
const bool m_updateDaugthers
if this is set all daughters will be updated otherwise only the head of the tree
Definition: FitManager.h:100
~FitManager()
destructor does stuff
Definition: FitManager.cc:43
int m_ndf
number of degrees of freedom for this topology
Definition: FitManager.h:103
double m_prec
precision that is needed for status:converged (delta chi2)
Definition: FitManager.h:94
bool fit()
main fit function that uses the kalman filter
Definition: FitManager.cc:49
int m_status
status of the current iteration
Definition: FitManager.h:88
Belle2::Particle * m_particle
head of the tree
Definition: FitManager.h:82
double m_chiSquare
chi2 of the current iteration
Definition: FitManager.h:91
VertexStatus
status flag of the fit-itereation (the step in the newton method)
Definition: FitManager.h:26
std::tuple< double, double > getLifeTime(Belle2::Particle &cand) const
get lifetime
Definition: FitManager.cc:295
std::tuple< double, double > getDecayLength(const ParticleBase *pb) const
get decay length
Definition: FitManager.cc:341
bool updateCand(Belle2::Particle &particle, const bool isTreeHead) const
update particles parameters with the fit results
Definition: FitManager.cc:192
Class to store and manage fitparams (statevector)
Definition: FitParams.h:20
Eigen::Matrix< double, -1, 1, 0, MAX_MATRIX_SIZE, 1 > & getStateVector()
getter for the fit parameters/statevector
Definition: FitParams.h:65
bool testCovariance() const
test if the covariance makes sense
Definition: FitParams.cc:46
double chiSquare() const
get chi2 of statevector
Definition: FitParams.h:86
int nDof() const
get number of degrees of freedom
Definition: FitParams.cc:56
Eigen::Matrix< double, -1, -1, 0, MAX_MATRIX_SIZE, MAX_MATRIX_SIZE > & getCovariance()
getter for the states covariance
Definition: FitParams.h:53
base class for all particles
Definition: ParticleBase.h:25
Belle2::Particle * particle() const
get basf2 particle
Definition: ParticleBase.h:92
virtual int dim() const =0
get dimension of constraint
virtual void forceP4Sum(FitParams &) const
force p4 sum conservation all along the tree
Definition: ParticleBase.h:116
virtual int posIndex() const
get vertex index (in statevector!)
Definition: ParticleBase.h:122
virtual int momIndex() const
get momentum index
Definition: ParticleBase.h:128
virtual bool hasEnergy() const
get momentum dimension
Definition: ParticleBase.h:132
virtual int tauIndex() const
get tau index
Definition: ParticleBase.h:125
const ParticleBase * mother() const
getMother() / hasMother()
Definition: ParticleBase.h:98