Belle II Software development
ParticleBase.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
10//Creates a particle. Base class for all other particle classes.
11
12#include <TDatabasePDG.h>
13
14#include <analysis/VertexFitting/TreeFitter/ParticleBase.h>
15#include <analysis/VertexFitting/TreeFitter/InternalParticle.h>
16#include <analysis/VertexFitting/TreeFitter/Composite.h>
17#include <analysis/VertexFitting/TreeFitter/RecoResonance.h>
18#include <analysis/VertexFitting/TreeFitter/RecoTrack.h>
19#include <analysis/VertexFitting/TreeFitter/RecoNeutral.h>
20#include <analysis/VertexFitting/TreeFitter/Resonance.h>
21#include <analysis/VertexFitting/TreeFitter/Origin.h>
22#include <analysis/VertexFitting/TreeFitter/FitParams.h>
23#include <analysis/VertexFitting/TreeFitter/ConstraintConfiguration.h>
24#include <analysis/VertexFitting/TreeFitter/Projection.h>
25
26namespace TreeFitter {
27
32 m_config(config),
33 m_index(0),
34 m_name("Unknown")
35 {
36 if (particle) {
38 const int pdgcode = particle->getPDGCode();
39 if (pdgcode) { // PDG code != 0
40 m_name = particle->getName();
41 }
42 }
43 }
44
45 ParticleBase::ParticleBase(const std::string& name) :
46 m_particle(nullptr),
47 m_mother(nullptr),
49 m_config(nullptr),
50 m_index(0),
51 m_name(name)
52 {}
53
54
56 {
57 for (auto daughter : m_daughters) {
58 delete daughter;
59 }
60 m_daughters.clear();
61 }
62
64 {
65 auto newDaughter = ParticleBase::createParticle(cand, this, config, forceFitAll);
66 m_daughters.push_back(newDaughter);
67 return m_daughters.back();
68 }
69
70
72 {
73 auto iter = std::find(m_daughters.begin(), m_daughters.end(), pb);
74 if (iter != m_daughters.end()) {
75 delete *iter;
76 m_daughters.erase(iter);
77 } else {
78 B2ERROR("Cannot remove particle, because not found ...");
79 }
80 }
81
82 void ParticleBase::updateIndex(int& offset)
83 {
84 for (auto* daughter : m_daughters) {
85 daughter->updateIndex(offset);
86 }
87 m_index = offset;
88 offset += dim();
89 }
90
92 Belle2::Particle* daughter,
93 const ConstraintConfiguration& config,
94 bool forceFitAll
95 )
96 {
97 return new Origin(daughter, config, forceFitAll);
98 }
99
101 const ConstraintConfiguration& config, bool forceFitAll)
102 {
103 ParticleBase* rc = nullptr;
104
105 if (!mother) { // If there is no mother, this is the 'head of tree' particle (is never a resonance)
106 rc = new InternalParticle(particle, nullptr, config, forceFitAll);
107 } else if (particle->hasExtraInfo("bremsCorrected") // Has Bremsstrahlungs-recovery
108 && particle->getExtraInfo("bremsCorrected") != 0) { // and gammas are attached
109 rc = new Composite(particle, mother, config, true);
110 // if no gamma is attached, it is treated as a RecoTrack
111 } else if (particle->hasExtraInfo("treeFitterTreatMeAsInvisible")
112 && particle->getExtraInfo("treeFitterTreatMeAsInvisible") == 1) { // dummy particles with invisible flag
113 rc = new RecoResonance(particle, mother, config);
114 } else if (particle->getTrack()) { // external reconstructed track
115 rc = new RecoTrack(particle, mother);
116 } else if (particle->getParticleSource() == Belle2::Particle::EParticleSourceObject::c_ECLCluster
117 or particle->getParticleSource() == Belle2::Particle::EParticleSourceObject::c_KLMCluster) {
118 // external reconstructed neutral final-state particle
119 rc = new RecoNeutral(particle, mother);
120 } else if (particle->getMdstArrayIndex()) { // external composite e.g. V0
121 rc = new InternalParticle(particle, mother, config, forceFitAll);
122 } else { // 'internal' particles
123 if (isAResonance(particle)) {
124 rc = new Resonance(particle, mother, config, forceFitAll);
125 } else {
126 rc = new InternalParticle(particle, mother, config, forceFitAll);
127 }
128 }
129 return rc;
130 }
131
133 {
134 bool rc = false ;
135 const int pdgcode = std::abs(particle->getPDGCode());
136
137 if (pdgcode && !(particle->getMdstArrayIndex())) {
138 switch (pdgcode) {
139 case 22: //photon conversion
140 rc = false;
141 break ;
142
143 case -11: //bremsstrahlung
144 case 11:
145 rc = true ;
146 break ;
147 default: //everything with boosted flight length less than 1 micrometer
148 rc = (pdgcode && particle->getPDGLifetime() < 1e-14);
149 }
150 }
151 return rc ;
152 }
153
154 void ParticleBase::collectVertexDaughters(std::vector<ParticleBase*>& particles, int posindex)
155 {
156 if (mother() && mother()->posIndex() == posindex) {
157 particles.push_back(this);
158 }
159
160 for (auto* daughter : m_daughters) {
161 daughter->collectVertexDaughters(particles, posindex);
162 }
163 }
164
166 {
167 // this is very sensitive and can heavily affect the efficiency of the fit
168 ErrCode status;
169
170 const int posindex = posIndex();
171 if (posindex >= 0) {
172 for (int i = 0; i < 3; ++i) {
173 fitparams.getCovariance()(posindex + i, posindex + i) = 1;
174 }
175 }
176
177 const int momindex = momIndex();
178 if (momindex >= 0) {
179 const int maxrow = hasEnergy() ? 4 : 3;
180 for (int i = 0; i < maxrow; ++i) {
181 fitparams.getCovariance()(momindex + i, momindex + i) = 10.;
182 }
183 }
184
185 const int tauindex = tauIndex();
186 if (tauindex >= 0) {
187 fitparams.getCovariance()(tauindex, tauindex) = 1.;
188 }
189 return status;
190 }
191
192 std::string ParticleBase::parname(int thisindex) const
193 {
194 std::string rc = m_name;
195 switch (thisindex) {
196 case 0: rc += "_x "; break;
197 case 1: rc += "_y "; break;
198 case 2: rc += "_z "; break;
199 case 3: rc += "_tau"; break;
200 case 4: rc += "_px "; break;
201 case 5: rc += "_py "; break;
202 case 6: rc += "_pz "; break;
203 case 7: rc += "_E "; break;
204 default:;
205 }
206 return rc;
207 }
208
210 {
211 const ParticleBase* rc = (m_particle == particle) ? this : nullptr;
212 if (!rc) {
213 for (auto* daughter : m_daughters) {
214 rc = daughter->locate(particle);
215 if (rc) {break;}
216 }
217 }
218 return rc;
219 }
220
222 {
223 map.push_back(std::pair<const ParticleBase*, int>(this, index()));
224 for (auto* daughter : m_daughters) {
225 daughter->retrieveIndexMap(map);
226 }
227 }
228
229 double ParticleBase::chiSquare(const FitParams& fitparams) const
230 {
231 double rc = 0;
232 for (auto* daughter : m_daughters) {
233 rc += daughter->chiSquare(fitparams);
234 }
235 return rc;
236 }
237
239 {
240 int rc = 0;
241 for (auto* daughter : m_daughters) {
242 rc += daughter->nFinalChargedCandidates();
243 }
244 return rc;
245 }
246
248 {
249 assert(m_config);
250 // only allow 2d for head of tree particles that are beam constrained
251 const int dim = m_config->m_originDimension == 2 && std::abs(m_particle->getPDGCode()) == m_config->m_headOfTreePDG ? 2 : 3;
252 const int posindexmother = mother()->posIndex();
253 const int posindex = posIndex();
254 const int tauindex = tauIndex();
255 const int momindex = momIndex();
256
257 const double tau = fitparams.getStateVector()(tauindex);
258 Eigen::VectorXd x_vec = fitparams.getStateVector().segment(posindex, dim);
259 Eigen::VectorXd x_m = fitparams.getStateVector().segment(posindexmother, dim);
260 Eigen::VectorXd p_vec = fitparams.getStateVector().segment(momindex, dim);
261 const double mom = p_vec.norm();
262 const double mom3 = mom * mom * mom;
263
264 if (3 == dim) {
265 // we can already set these
266 //diagonal momentum
267 p.getH()(0, momindex) = tau * (p_vec(1) * p_vec(1) + p_vec(2) * p_vec(2)) / mom3 ;
268 p.getH()(1, momindex + 1) = tau * (p_vec(0) * p_vec(0) + p_vec(2) * p_vec(2)) / mom3 ;
269 p.getH()(2, momindex + 2) = tau * (p_vec(0) * p_vec(0) + p_vec(1) * p_vec(1)) / mom3 ;
270
271 //offdiagonal momentum
272 p.getH()(0, momindex + 1) = - tau * p_vec(0) * p_vec(1) / mom3 ;
273 p.getH()(0, momindex + 2) = - tau * p_vec(0) * p_vec(2) / mom3 ;
274
275 p.getH()(1, momindex + 0) = - tau * p_vec(1) * p_vec(0) / mom3 ;
276 p.getH()(1, momindex + 2) = - tau * p_vec(1) * p_vec(2) / mom3 ;
277
278 p.getH()(2, momindex + 0) = - tau * p_vec(2) * p_vec(0) / mom3 ;
279 p.getH()(2, momindex + 1) = - tau * p_vec(2) * p_vec(1) / mom3 ;
280
281 } else if (2 == dim) {
282
283 // NOTE THAT THESE ARE DIFFERENT IN 2d
284 p.getH()(0, momindex) = tau * (p_vec(1) * p_vec(1)) / mom3 ;
285 p.getH()(1, momindex + 1) = tau * (p_vec(0) * p_vec(0)) / mom3 ;
286
287 //offdiagonal momentum
288 p.getH()(0, momindex + 1) = - tau * p_vec(0) * p_vec(1) / mom3 ;
289 p.getH()(1, momindex + 0) = - tau * p_vec(1) * p_vec(0) / mom3 ;
290 } else {
291 B2FATAL("Dimension of Geometric constraint is not 2 or 3. This will crash many things. You should feel bad.");
292 }
293
294 // --- Fill residuals and remaining Jacobian entries ---
298 Eigen::VectorXd residuals = x_m + tau * p_vec / mom - x_vec;
299 p.getResiduals().segment(0, dim) = residuals;
300
301 for (int row = 0; row < dim; ++row) {
302 p.getH()(row, posindexmother + row) = 1;
303 p.getH()(row, posindex + row) = -1;
304 p.getH()(row, tauindex) = p_vec(row) / mom;
305 }
306
307 return ErrCode(ErrCode::Status::success);
308 }
309
311 Projection& p) const
312 {
313 double mass = 0;
314 if (particle()->hasExtraInfo("treeFitterMassConstraintValue")) {
315 mass = particle()->getExtraInfo("treeFitterMassConstraintValue");
316 } else mass = particle()->getPDGMass();
317 const double mass2 = mass * mass;
318 double px = 0;
319 double py = 0;
320 double pz = 0;
321 double E = 0;
322
323 // the parameters of the daughters must be used otherwise the mass constraint does not have an effect on the extracted daughter momenta
324 for (const auto* daughter : m_daughters) {
325 const int momindex = daughter->momIndex();
326 // in most cases the daughters will be final states so we cache the value to use it in the energy column
327 const double px_daughter = fitparams.getStateVector()(momindex);
328 const double py_daughter = fitparams.getStateVector()(momindex + 1);
329 const double pz_daughter = fitparams.getStateVector()(momindex + 2);
330
331 px += px_daughter;
332 py += py_daughter;
333 pz += pz_daughter;
334 if (daughter->hasEnergy()) {
335 E += fitparams.getStateVector()(momindex + 3);
336 } else {
337 // final states dont have an energy index
338 double m = 0;
339 if (daughter->particle()->hasExtraInfo("treeFitterMassConstraintValue")) {
340 m = daughter->particle()->getExtraInfo("treeFitterMassConstraintValue");
341 } else m = daughter->particle()->getPDGMass();
342 E += std::sqrt(m * m + px_daughter * px_daughter + py_daughter * py_daughter + pz_daughter * pz_daughter);
343 }
344 }
345
349 p.getResiduals()(0) = mass2 - E * E + px * px + py * py + pz * pz;
350
351 for (const auto* daughter : m_daughters) {
352 //dr/dx = d/dx m2-{E1+E2+...}^2+{p1+p2+...}^2 = 2*x (x= E or p)
353 const int momindex = daughter->momIndex();
354 Eigen::Vector3d mom(px, py, pz);
355 p.getH().block<1, 3>(0, momindex) = 2.0 * mom;
356
357 if (daughter->hasEnergy()) {
358 p.getH()(0, momindex + 3) = -2.0 * E;
359 } else {
360 Eigen::Vector3d mom_daughter = fitparams.getStateVector().segment<3>(momindex);
361 double m = 0;
362 if (daughter->particle()->hasExtraInfo("treeFitterMassConstraintValue")) {
363 m = daughter->particle()->getExtraInfo("treeFitterMassConstraintValue");
364 } else m = daughter->particle()->getPDGMass();
365
366 const double E_daughter = std::sqrt(m * m + mom_daughter.squaredNorm());
367 const double E_by_E_daughter = E / E_daughter;
368 p.getH().block<1, 3>(0, momindex) -= 2.0 * E_by_E_daughter * mom_daughter;
369 }
370
371 }
372 return ErrCode(ErrCode::Status::success);
373 }
374
376 Projection& p) const
377 {
378 double mass = 0;
379 if (particle()->hasExtraInfo("treeFitterMassConstraintValue")) mass = particle()->getExtraInfo("treeFitterMassConstraintValue");
380 else mass = particle()->getPDGMass();
381 const double mass2 = mass * mass;
382 const int momindex = momIndex();
383 Eigen::Vector4d state = fitparams.getStateVector().segment<4>(momindex);
384 Eigen::Vector3d p3 = state.head<3>();
385 const double px = state(0);
386 const double py = state(1);
387 const double pz = state(2);
388 const double E = state(3);
389
393 p.getResiduals()(0) = mass2 - E * E + px * px + py * py + pz * pz;
394
395 p.getH().block<1, 3>(0, momindex) = 2.0 * p3;
396 p.getH()(0, momindex + 3) = -2.0 * E;
397
398 // TODO 0 in most cases -> needs special treatment if width=0 to not crash chi2 calculation
399 // const double width = TDatabasePDG::Instance()->GetParticle(particle()->getPDGCode())->Width();
400 // transport measurement uncertainty into residual system
401 // f' = sigma_x^2 * (df/dx)^2
402 // p.getV()(0) = width * width * 4 * mass2;
403
404 return ErrCode(ErrCode::Status::success);
405 }
406
408 Projection& p) const
409 {
410 assert(m_config);
411 if (m_config->m_massConstraintType == 0) {
412 return projectMassConstraintParticle(fitparams, p);
413 } else {
414 return projectMassConstraintDaughters(fitparams, p);
415 }
416 }
417
419 {
420 if (type == Constraint::mass) {
421 return projectMassConstraint(fitparams, p);
422 } else {
423 B2FATAL("Trying to project constraint of ParticleBase type. This is undefined.");
424 }
425 return ErrCode(ErrCode::Status::badsetup);
426 }
427
429 {
430 const int tauindex = tauIndex();
431 if (tauindex >= 0 && hasPosition()) {
432
433 const int posindex = posIndex();
434 const int mother_ps_index = mother()->posIndex();
435 const int dim = m_config->m_originDimension; // TODO can we configure this to be particle specific?
436
437 // tau has different meaning depending on the dimension of the constraint
438 // 2-> use x-y projection
439 const Eigen::Matrix < double, 1, -1, 1, 1, 3 > vertex_dist =
440 fitparams.getStateVector().segment(posindex, dim) - fitparams.getStateVector().segment(mother_ps_index, dim);
441 const Eigen::Matrix < double, 1, -1, 1, 1, 3 >
442 mom = fitparams.getStateVector().segment(posindex, dim);
443
444 // if an intermediate vertex is not well defined by a track or so it will be initialised with 0
445 // same for the momentum of for example B0, it might be initialised with 0
446 // in those cases use pdg value
447 const double mom_norm = mom.norm();
448 const double dot = std::abs(vertex_dist.dot(mom));
449 const double tau = dot / mom_norm;
450 if (0 == mom_norm || 0 == dot) {
451 const double mass = m_particle->getPDGMass();
452 if (mass > 0)
453 fitparams.getStateVector()(tauindex) = m_particle->getPDGLifetime() * 1e9 * Belle2::Const::speedOfLight / mass;
454 else
455 fitparams.getStateVector()(tauindex) = 0;
456 } else {
457 fitparams.getStateVector()(tauindex) = tau;
458 }
459 }
460
461 return ErrCode(ErrCode::Status::success);
462 }
463} //end namespace TreeFitter
464
R E
internal precision of FFTW codelets
static const double speedOfLight
[cm/ns]
Definition Const.h:695
Class to store reconstructed particles.
Definition Particle.h:76
double getPDGMass(void) const
Returns uncertainty on the invariant mass (requires valid momentum error matrix)
Definition Particle.cc:635
double getExtraInfo(const std::string &name) const
Return given value if set.
Definition Particle.cc:1374
A class for composite particles, where the daughters must be ignored by the fitter.
Definition Composite.h:19
Type
type of constraints the order of these constraints is important: it is the order in which they are ap...
Definition Constraint.h:27
abstract errorocode be aware that the default is success
Definition ErrCode.h:14
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
Eigen::Matrix< double, -1, -1, 0, MAX_MATRIX_SIZE, MAX_MATRIX_SIZE > & getCovariance()
getter for the states covariance
Definition FitParams.h:53
another unnecessary layer of abstraction
representation of the beamspot as a particle
Definition Origin.h:21
virtual void updateIndex(int &offset)
this sets the index for momentum, position, etc.
virtual ErrCode projectMassConstraintParticle(const FitParams &, Projection &) const
project mass constraint using the particles parameters
Belle2::Particle * particle() const
get basf2 particle
ErrCode initTau(FitParams &par) const
initialises tau as a length
virtual ParticleBase * addDaughter(Belle2::Particle *, const ConstraintConfiguration &config, bool forceFitAll=false)
add daughter
static ParticleBase * createOrigin(Belle2::Particle *daughter, const ConstraintConfiguration &config, bool forceFitAll)
create a custom origin particle or a beamspot
static ParticleBase * createParticle(Belle2::Particle *particle, const ParticleBase *mother, const ConstraintConfiguration &config, bool forceFitAll=false)
create the according treeFitter particle obj for a basf2 particle type
virtual int dim() const =0
get dimension of constraint
virtual int nFinalChargedCandidates() const
number of charged candidates
virtual void retrieveIndexMap(indexmap &anindexmap) const
get index map
virtual ErrCode projectMassConstraint(const FitParams &, Projection &) const
project mass constraint abstract
const ConstraintConfiguration * m_config
has all the constraint config
virtual std::string parname(int index) const
get name of parameter i
static bool isAResonance(Belle2::Particle *particle)
controls if a particle is treated as a resonance(lifetime=0) or a particle that has a finite lifetime...
virtual ErrCode projectConstraint(Constraint::Type, const FitParams &, Projection &) const
project constraint.
virtual ErrCode projectMassConstraintDaughters(const FitParams &, Projection &) const
project mass constraint using the parameters of the daughters
const ParticleBase * locate(Belle2::Particle *particle) const
get particle base from basf2 particle
virtual void removeDaughter(const ParticleBase *pb)
remove daughter
const ParticleBase * m_mother
motherparticle
bool m_isStronglyDecayingResonance
decay length less than 1 micron
virtual ErrCode initCovariance(FitParams &) const
init covariance matrix
virtual int type() const =0
get particle type
Belle2::Particle * m_particle
pointer to framework type
ParticleBase(Belle2::Particle *particle, const ParticleBase *mother, const ConstraintConfiguration *config=nullptr)
default constructor
virtual ErrCode projectGeoConstraint(const FitParams &, Projection &) const
project geometrical constraint
virtual double chiSquare(const FitParams &) const
get chi2
virtual int posIndex() const
get vertex index (in statevector!)
std::vector< std::pair< const ParticleBase *, int > > indexmap
alias
virtual int momIndex() const
get momentum index
int index() const
get index
virtual bool hasEnergy() const
get momentum dimension
std::vector< ParticleBase * > m_daughters
daughter container
virtual ~ParticleBase()
destructor, actually does something
void collectVertexDaughters(std::vector< ParticleBase * > &particles, int posindex)
get vertex daughters
virtual int tauIndex() const
get tau index
const ParticleBase * mother() const
getMother() / hasMother()
virtual bool hasPosition() const
get false
class to store the projected residuals and the corresponding jacobian as well as the covariance matri...
Definition Projection.h:18
representation of the neutral particle constraint
Definition RecoNeutral.h:18
A class for resonances.
representation of all charged final states FIXME rename since this name is taken in tracking
Definition RecoTrack.h:18
class for resonances as internal particles
Definition Resonance.h:18
STL class.