Belle II Software development
TreeFitterModule.cc
1/**************************************************************************
2 * basf2 (Belle II Analysis Software Framework) *
3 * Author: The Belle II Collaboration *
4 * *
5 * See git log for contributors and copyright holders. *
6 * This file is licensed under LGPL-3.0, see LICENSE.md. *
7 **************************************************************************/
8
9// Implementation of Decay Tree Fitter based on arXiv:physics/0503191
10// Main module implementation
11
12#include <analysis/modules/TreeFitter/TreeFitterModule.h>
13#include <analysis/VertexFitting/TreeFitter/FitManager.h>
14
15#include <framework/particledb/EvtGenDatabasePDG.h>
16
17#include <analysis/utility/ParticleCopy.h>
18#include <analysis/utility/PCmsLabTransform.h>
19
20#include <analysis/VertexFitting/TreeFitter/FitParameterDimensionException.h>
21
22using namespace Belle2;
23
24REG_MODULE(TreeFitter);
25
26TreeFitterModule::TreeFitterModule() : Module(), m_nCandidatesBeforeFit(-1), m_nCandidatesAfter(-1)
27{
28 setDescription("Tree Fitter module. Performs simultaneous fit of all vertices in a decay chain. Can also be used to just fit a single vertex.");
30 //
31 addParam("particleList", m_particleList,
32 "Type::[string]. Input mother of the decay tree to fit. For example 'B0:myB0particleList'.");
33 addParam("confidenceLevel", m_confidenceLevel,
34 "Type::[double]. Confidence level to accept fitted decay tree. Candidates with < confidenceLevel will be removed from the particle list! Typical Values: -1: keep all particle candidates, 0: remove all that fail the fit, 0.001: standard cut, 0.1: (too) tight cut. Optimise using a figure of merit (for example S/(sqrt{S+B}) ) for your analysis.",
35 0.0);
36 addParam("convergencePrecision", m_precision,
37 "Type::[double]. Fractional upper limit for chi2 fluctuations to accept result. Larger value = less signal rejection but also less background rejection. Optimized for FOM on different topologies - don't touch unless you REALLY want this.",
38 0.01);
39 addParam("massConstraintList", m_massConstraintList,
40 "Type::[int]. List of particles to mass constrain with int = pdg code. Note that the variables 'M': fit result for the particle and 'InvM': calculated from the daughter momenta, will look different (especially if you don't update the daughters!).", {});
41 addParam("massConstraintDecayString", m_massConstraintDecayString,
42 "Type::[string]. Decay string to select which particles' mass should be constrained to their nominal value. Note that the variables 'M': fit result for the particle and 'InvM': calculated from the daughter momenta, will look different (especially if you don't update the daughters!).", {});
43 addParam("massConstraintMassValues", m_massConstraintMassValues,
44 "Type::[double]. List of invariant masses to be used for the mass constraints of the particles selected via the decay string massConstraintDecayString", {});
45
46 addParam("geoConstraintList", m_geoConstraintListPDG,
47 "Type::[int], if 'autoSetGeoConstraintAndMergeVertices==False' you can manually set the particles that will be geometrically constrained here.", {});
48 addParam("sharedVertexList", m_fixedToMotherVertexListPDG,
49 "Type::[int], if 'autoSetGeoConstraintAndMergeVertices==False' you can manually set the particles that share the vertex with their mother here.", {});
50 addParam("autoSetGeoConstraintAndMergeVertices", m_automatic_vertex_constraining,
51 "Type::bool, shall vertices of strong resonance be merged with their mothers? Can the particles vertex be constraint geometrically?",
52 true);
53
54 addParam("customOriginVertex", m_customOriginVertex,
55 "Type::[double]. List of vertex coordinates to be used in the custom origin constraint.", {0.001, 0, 0.0116});
56 addParam("customOriginCovariance", m_customOriginCovariance,
57 "Type::[double]. List vertex covariance elements used in the custom origin constraint (as a vector). Default is meant for B0 decays and is taken from 100k generated B0 to mumu events.",
58 {
59 0.0048, 0, 0,
60 0, 0.003567, 0,
61 0, 0, 0.0400
62 }
63 );
64 addParam("customOriginConstraint", m_customOrigin,
65 "Type::[bool]. Use a custom vertex as the production point of the highest hierarchy particle (register this as the mother of the list you specify). Like the beam constraint but you can specify the position its covariance yourself. ",
66 false);
67 addParam("ipConstraint", m_ipConstraint,
68 "Type::[bool]. Use the IP as the origin of the tree. This registers an internal IP particle as the mother of the list you give. Or in other words forces the PRODUCTION vertex of your particle to be the IP and its covariance as specified in the database.",
69 false);
70 addParam("originDimension", m_originDimension,
71 "Type int, default 3. If origin or ip constraint used, specify the dimension of the constraint 3->x,y,z; 2->x,y. This also changes the dimension of the geometric constraints! So you might want to turn them off for some particles. (That means turn auto off and manually on for the ones you want to constrain)",
72 3);
73 addParam("updateAllDaughters", m_updateDaughters,
74 "Type::[bool]. Update all daughters (vertex position and momenta) in the tree. If not set only the 4-momenta for the head of the tree will be updated. We also update the vertex position of the daughters regardless of what you put here, because otherwise the default when the particle list is created is {0,0,0}.",
75 false);
76 addParam("expertBeamConstraintPDG", m_beamConstraintPDG,
77 "Type int, default 0. The 4-momentum of particles with the given PDG will be constrained to the 4-momentum of the initial e+e- system.",
78 0);
79 addParam("expertMassConstraintType", m_massConstraintType,
80 "Type::[int]. False(0): use particles parameters in mass constraint; True: use sum of daughter parameters for mass constraint. WAARNING not even guaranteed that it works.",
81 0);
82 addParam("expertRemoveConstraintList", m_removeConstraintList,
83 "Type::[string]. List of constraints that you do not want to be used in the fit. WARNING don't use if you don't know exactly what it does.", {});
84 addParam("inflationFactorCovZ", m_inflationFactorCovZ,
85 "Inflate the covariance of the beamspot by this number so that the 3d beam constraint becomes weaker in Z.And: thisnumber->infinity : dim(beamspot constr) 3d->2d.",
86 1);
87 addParam("treatAsInvisible", m_treatAsInvisible,
88 "Type::[string]. Decay string to select one particle that will be ignored in the fit.", {});
89
90 addParam("ignoreFromVertexFit", m_ignoreFromVertexFit,
91 "Type::[string]. Decay string to select particles that will be ignored to determine the vertex position while kept for kinematics determination.", {});
92}
93
95{
96 m_plist.isRequired(m_particleList);
101
102 if (!m_massConstraintDecayString.empty()) {
103 if (!m_massConstraintList.empty())
104 B2ERROR("TreeFitterModule::initialize Either provide a decay string or a list of particles to determine which particles' mass should be constrained not both!");
106 if (!valid)
107 B2ERROR("TreeFitterModule::initialize Invalid decay descriptor: " << m_massConstraintDecayString);
109 B2INFO("Use nominal PDG values for mass constraints in TreeFit.");
110 } else if (static_cast<size_t>(std::count(m_massConstraintDecayString.begin(), m_massConstraintDecayString.end(),
111 '^')) != m_massConstraintMassValues.size()) {
112 B2ERROR("TreeFitterModule::initialize The number of selected particles and given invariant masses for the mass constraint doesn't match!");
113 }
114 }
115
116 if (!m_treatAsInvisible.empty()) {
118 if (!valid)
119 B2ERROR("TreeFitterModule::initialize Invalid Decay Descriptor: " << m_treatAsInvisible);
120 else if (m_pDDescriptorInvisible.getSelectionPDGCodes().size() != 1)
121 B2ERROR("TreeFitterModule::initialize Please select exactly one particle to ignore: " << m_treatAsInvisible);
122 }
123
124 if (!m_ignoreFromVertexFit.empty()) {
126 if (!valid)
127 B2ERROR("TreeFitterModule::initialize Invalid Decay Descriptor: " << m_ignoreFromVertexFit);
128 }
129}
130
132{
134 const ROOT::Math::PxPyPzEVector cms = T.getBeamFourMomentum();
135
136 m_beamMomE(0) = cms.X();
137 m_beamMomE(1) = cms.Y();
138 m_beamMomE(2) = cms.Z();
139 m_beamMomE(3) = cms.E();
140
141 m_beamCovariance = Eigen::Matrix4d::Zero();
142 const double covE = 3.19575e-05; // TODO Avoid using the hard coded value.
143 // It is taken from the BeamParameter which was used previously. The uncertainty from the CollisionInvariantMass is a possibility.
144
145 for (size_t i = 0; i < 4; ++i) {
146 m_beamCovariance(i, i) = covE;
147 // TODO Currently, we do not get a full covariance matrix from beamparams, and the py value is zero, which means there is no constraint on py. Therefore, we approximate it by a diagonal matrix using the energy value for all components. This is based on the assumption that the components of the beam four-momentum are independent and of comparable size.
148 }
149}
150
152{
153 if (!m_plist) {
154 B2ERROR("ParticleList " << m_particleList << " not found");
155 return;
156 }
157
158 std::vector<unsigned int> toRemove;
159 const unsigned int nParticles = m_plist->getListSize();
160 m_nCandidatesBeforeFit += nParticles;
161
162 TMatrixFSym dummyCovMatrix(7);
163 for (int row = 0; row < 7; ++row) { //diag
164 dummyCovMatrix(row, row) = 10000;
165 }
166
167 TMatrixFSym dummyCovMatrix_smallMomError(dummyCovMatrix);
168 for (int row = 0; row < 4; ++row) {
169 dummyCovMatrix_smallMomError(row, row) = 1e-10;
170 }
171
172 for (unsigned iPart = 0; iPart < nParticles; iPart++) {
173 Particle* particle = m_plist->getParticle(iPart);
174
175 if (m_updateDaughters == true) {
177 }
178
179 if (!m_massConstraintDecayString.empty()) {
180 std::vector<const Particle*> selParticlesMassConstraint = m_pDDescriptorMassConstraint.getSelectionParticles(particle);
181 size_t nSelParticlesMassConstraint = selParticlesMassConstraint.size();
182 for (size_t i = 0; i < nSelParticlesMassConstraint; ++i) {
183 Particle* selParticle = m_particles[selParticlesMassConstraint[i]->getArrayIndex()];
184 Particle* selParticleCopy = ParticleCopy::copyParticle(selParticle);
185 selParticleCopy->writeExtraInfo("treeFitterMassConstraint", 1);
186 if (!m_usePDGMassForMassConstraint) selParticleCopy->writeExtraInfo("treeFitterMassConstraintValue", m_massConstraintMassValues[i]);
187
188 bool isReplaced = particle->replaceDaughterRecursively(selParticle, selParticleCopy);
189 if (!isReplaced)
190 B2ERROR("TreeFitterModule::event Cannot find particle selected in " << m_massConstraintDecayString);
191 }
192 }
193
194 if (!m_treatAsInvisible.empty()) {
195 std::vector<const Particle*> selParticlesTarget = m_pDDescriptorInvisible.getSelectionParticles(particle);
196 Particle* targetD = m_particles[selParticlesTarget[0]->getArrayIndex()];
197 Particle* daughterCopy = ParticleCopy::copyParticle(targetD);
198 daughterCopy->writeExtraInfo("treeFitterTreatMeAsInvisible", 1);
199 daughterCopy->setMomentumVertexErrorMatrix(dummyCovMatrix);
200
201 bool isReplaced = particle->replaceDaughterRecursively(targetD, daughterCopy);
202 if (!isReplaced)
203 B2ERROR("TreeFitterModule::event No target particle found for " << m_treatAsInvisible);
204 }
205
206 if (!m_ignoreFromVertexFit.empty()) {
207 std::vector<const Particle*> selParticlesTarget = m_pDDescriptorForIgnoring.getSelectionParticles(particle);
208
209 for (auto part : selParticlesTarget) {
210 Particle* targetD = m_particles[part->getArrayIndex()];
211 Particle* daughterCopy = ParticleCopy::copyParticle(targetD);
212 daughterCopy->writeExtraInfo("treeFitterTreatMeAsInvisible", 1);
213 daughterCopy->setMomentumVertexErrorMatrix(dummyCovMatrix_smallMomError);
214
215 bool isReplaced = particle->replaceDaughterRecursively(targetD, daughterCopy);
216 if (!isReplaced)
217 B2ERROR("TreeFitterModule::event No target particle found for " << m_ignoreFromVertexFit);
218 }
219 }
220
221 try {
222 const bool ok = fitTree(particle);
223 if (!ok) { particle->setPValue(-1); }
225 B2ERROR(e.what());
226 }
227
228 if (particle->getPValue() < m_confidenceLevel) {
229 toRemove.push_back(particle->getArrayIndex());
230 }
231
232 }
233 m_plist->removeParticles(toRemove);
234 m_nCandidatesAfter += m_plist->getListSize();
235}
236
237
239{
240 if (m_nCandidatesAfter > 0) {
242 } else {
243 B2WARNING("Not a single candidate survived the fit. Candidates before fit: " << m_nCandidatesBeforeFit << " after: " <<
245 }
246}
247
249{
250 const TreeFitter::ConstraintConfiguration constrConfig(
266 );
267
268 std::unique_ptr<TreeFitter::FitManager> TreeFitter(
270 head,
271 constrConfig,
274 )
275 );
276 bool rc = TreeFitter->fit();
277 return rc;
278}
279
281{
282 B2INFO("\033[1;35m================================================================================\033[0m");
283 B2INFO("\033[40;97m ,., \033[0m");
284 B2INFO("\033[40;97m ;%&M%;_ ,.., \033[0m");
285 B2INFO("\033[40;97m \"_ \"__\" % M % M %; , ..., , \033[0m");
286 B2INFO("\033[40;97m , ..., __.\" --\" , ., _ - \" % &W % WM %; \033[0m");
287 B2INFO("\033[40;97m ; % M&$M % \"___ \"_._ %M%\"_.\"\" _ \"\"\"\"\"\" \033[0m");
288 B2INFO("\033[40;97m \"\"\"\"\" \"\" , \\_. \"_. .\" \033[0m");
289 B2INFO("\033[40;97m , ., _\"__ \\__./ .\" \033[0m");
290 B2INFO("\033[40;97m ___ __ | y , .., \033[97;40mThank you for using TreeFitter. \033[0m");
291 B2INFO("\033[40;97m /)'\\ ''''| u \\ %W%W%%; \033[0m");
292 B2INFO("\033[40;97m ___)/ \"---\\_ \\ |____\" \033[97;40mPlease cite both: \033[0m");
293 B2INFO("\033[40;97m ;&&%%; (|__.|)./ ,.., \033[97;40m10.1016/j.nima.2020.164269 \033[0m");
294 B2INFO("\033[40;97m ,.., ___\\ |/ &&\" \033[97;40m 10.1016/j.nima.2005.06.078 \033[0m");
295 B2INFO("\033[40;97m &&%%& (| Uo / '\" \033[97;40mEmail: \033[0m");
296 B2INFO("\033[40;97m '''' \\ 7 \\ \033[97;40mfrancesco.tenchini@desy.de \033[0m");
297 B2INFO("\033[40;97m ._______________.-'____\"\"-.____. \033[97;40mjo-frederik.krohn@desy.de \033[0m");
298 B2INFO("\033[40;97m \\ / \033[0m");
299 B2INFO("\033[40;97m \\ \033[0m\033[32;40mTREEFITTER\033[0m\033[40;97m / \033[0m");
300 B2INFO("\033[40;97m \\_______________________/ \033[0m");
301 B2INFO("\033[40;97m (_) (_) \033[0m");
302 B2INFO("\033[40;97m \033[0m");
303 B2INFO("\033[1;35m============= TREEFIT STATISTICS ===============================================\033[0m");
304 B2INFO("\033[1;39mTarget particle list: " << m_particleList << "\033[0m");
305 B2INFO("\033[1;39mCandidates before fit: " << m_nCandidatesBeforeFit << "\033[0m");
306 B2INFO("\033[1;39mCandidates after fit: " << m_nCandidatesAfter << "\033[0m");
307 B2INFO("\033[1;39mA total of " << m_nCandidatesBeforeFit - m_nCandidatesAfter <<
308 " candidates were removed during the fit.\033[0m");
309 B2INFO("\033[1;39m" << (double)m_nCandidatesAfter / (double)m_nCandidatesBeforeFit * 100.0 <<
310 "% of candidates survived the fit.\033[0m");
311 B2INFO("\033[1;39m" << 100. - (double)m_nCandidatesAfter / (double)m_nCandidatesBeforeFit * 100.0 <<
312 "% of candidates did not.\033[0m");
313 B2INFO("\033[1;39mYou chose to drop all candidates with pValue < " << m_confidenceLevel << ".\033[0m");
314 B2INFO("\033[1;35m================================================================================\033[0m");
315}
bool init(const std::string &str)
Initialise the DecayDescriptor from given string.
std::vector< int > getSelectionPDGCodes()
Return list of PDG codes of selected particles.
std::vector< const Particle * > getSelectionParticles(const Particle *particle)
Get a vector of pointers with selected daughters in the decay tree.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
Class to hold Lorentz transformations from/to CMS and boost vector.
ROOT::Math::PxPyPzEVector getBeamFourMomentum() const
Returns LAB four-momentum of e+e-, i.e.
Class to store reconstructed particles.
Definition: Particle.h:76
void writeExtraInfo(const std::string &name, const double value)
Sets the user defined extraInfo.
Definition: Particle.cc:1393
void setMomentumVertexErrorMatrix(const TMatrixFSym &errMatrix)
Sets 7x7 error matrix.
Definition: Particle.cc:424
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
std::string m_massConstraintDecayString
decay string to select particles that will be mass-constrained
DecayDescriptor m_pDDescriptorForIgnoring
Decay descriptor of the ignored particles.
void plotFancyASCII()
plot ascii art and statistics
std::vector< double > m_massConstraintMassValues
list of values for mass constraints
std::string m_ignoreFromVertexFit
decay string to select particles that will be ignored to determine the vertex position
DecayDescriptor m_pDDescriptorInvisible
Decay descriptor of the invisible particle.
std::vector< double > m_customOriginVertex
vertex coordinates of the custom origin
virtual void initialize() override
initialize
virtual void event() override
performed for each event
Eigen::Matrix< double, 4, 1 > m_beamMomE
beam four-momentum
bool m_updateDaughters
flag if you want to update all particle momenta in the decay tree.
unsigned int m_nCandidatesAfter
after the fit
std::vector< int > m_massConstraintList
vector carrying the PDG codes of the particles to be mass constraint
std::vector< int > m_fixedToMotherVertexListPDG
list of pdg codes of particles where we use the same vertex for production and decay which is the ver...
StoreArray< Particle > m_particles
StoreArray of Particles.
virtual void terminate() override
stuff at the end
DecayDescriptor m_pDDescriptorMassConstraint
Decay descriptor of the mass constrained particles.
bool m_usePDGMassForMassConstraint
use PDG values for mass constraints
bool m_automatic_vertex_constraining
should the vertex be joined with the mother and should it be geometrically constrained?...
virtual void beginRun() override
performed at the start of run
bool m_ipConstraint
Use x-y-z beamspot constraint.
int m_originDimension
dimension to use for beam/origin constraint
int m_inflationFactorCovZ
inflate beamspot covariance of z by this number
std::vector< std::string > m_removeConstraintList
list of constraints not to be applied in tree fit
int m_massConstraintType
type of the mass constraint false: use normal one.
std::vector< int > m_geoConstraintListPDG
list of pdg codes of particles to use a geo constraint for
std::vector< double > m_customOriginCovariance
covariance of the custom origin
std::string m_particleList
name of the particle list fed to the fitter
double m_confidenceLevel
minimum confidence level to accept fit calculated as f(chiSquared, NDF) -2: accept all 0: only accept...
bool m_customOrigin
use a custom vertex as the production vertex of the highest hierarchy particle
std::string m_treatAsInvisible
decay string to select one particle that will be treated as invisible
Eigen::Matrix< double, 4, 4 > m_beamCovariance
beam covariance matrix
int m_beamConstraintPDG
PDG code of particle to be constrained to the beam 4-momentum.
bool fitTree(Particle *head)
this fits all particle candidates contained in the m_particleList
double m_precision
convergence precision for the newton method
StoreObjPtr< ParticleList > m_plist
input particle list
unsigned int m_nCandidatesBeforeFit
before the fit
constraint configuration class
exception template, runtime_error implements what()
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:559
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:649
void copyDaughters(Particle *mother)
Function copies all (grand-)^n-daughter particles of the argument mother Particle.
Definition: ParticleCopy.cc:55
Particle * copyParticle(const Particle *original)
Function takes argument Particle and creates a copy of it and copies of all its (grand-)^n-daughters.
Definition: ParticleCopy.cc:17
Abstract base class for different kinds of events.