Belle II Software development
MCDecayFinderModule.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#include <analysis/modules/MCDecayFinder/MCDecayFinderModule.h>
10#include <framework/gearbox/Const.h>
11#include <analysis/DecayDescriptor/ParticleListName.h>
12#include <analysis/utility/EvtPDLUtil.h>
13#include <analysis/utility/MCMatching.h>
14#include <analysis/modules/MCDecayFinder/DecayTree.h>
15
16#include <string>
17#include <memory>
18
19using namespace std;
20using namespace Belle2;
21
22// Register module in the framework
23REG_MODULE(MCDecayFinder);
24
26{
27 //Set module properties
28 setDescription("Find decays in MCParticle list matching a given DecayString and create Particles from them.");
30 //Parameter definition
31 addParam("decayString", m_strDecay, "DecayDescriptor string.");
32 addParam("listName", m_listName, "Name of the output particle list");
33 addParam("appendAllDaughters", m_appendAllDaughters,
34 "If true, all daughters of the matched MCParticle will be added in the order defined at the MCParticle. "
35 "If false, only the daughters described in the given decayString will be appended to the output particle.",
36 false);
37 addParam("skipNonPrimaryDaughters", m_skipNonPrimaryDaughters,
38 "If true, the secondary MC daughters will be skipped to append to the output particles. Default: true",
39 true);
40 addParam("writeOut", m_writeOut,
41 "If true, the output ParticleList will be saved by RootOutput. If false, it will be ignored when writing the file.", false);
42}
43
45{
46 bool valid = m_decaydescriptor.init(m_strDecay);
47 if (!valid)
48 B2ERROR("Invalid input DecayString: " << m_strDecay);
49
52
53 B2DEBUG(10, "particle list name: " << m_listName);
54 B2DEBUG(10, "antiparticle list name: " << m_antiListName);
55
56
57 // Register output particle list, particle store and relation to MCParticles
58 m_mcparticles.isRequired();
59
61 m_outputList.registerInDataStore(m_listName, flags);
62 m_particles.registerInDataStore(flags);
63 m_extraInfoMap.registerInDataStore();
64 m_particles.registerRelationTo(m_mcparticles, DataStore::c_Event, flags);
65
67 m_antiOutputList.registerInDataStore(m_antiListName, flags);
68 }
69}
70
72{
73 if (not m_extraInfoMap)
74 m_extraInfoMap.create();
75
76 // Create output particle list
77 const int motherPDG = m_decaydescriptor.getMother()->getPDGCode();
78 m_outputList.create();
79 m_outputList->initialize(motherPDG, m_listName);
80
82 m_antiOutputList.create();
83 m_antiOutputList->initialize(-1 * motherPDG, m_antiListName);
84 m_outputList->bindAntiParticleList(*(m_antiOutputList));
85 }
86
87 // loop over all MCParticles
88 int nMCParticles = m_mcparticles.getEntries();
89 for (int i = 0; i < nMCParticles; i++) {
90
91 if (abs(m_mcparticles[i]->getPDG()) != abs(motherPDG))
92 continue;
93
94 for (int iCC = 0; iCC < 2; iCC++) {
95 int arrayIndex = -1;
96 std::unique_ptr<DecayTree<MCParticle>> decay(match(m_mcparticles[i], m_decaydescriptor, iCC, arrayIndex));
97
98 MCParticle* mcp = decay->getObj();
99 if (!mcp) continue;
100
101 B2DEBUG(19, "Match!");
102
104 // if m_appendAllDaughters is True, create a new Particle appending all daughters
105 Particle* newParticle = m_particles.appendNew(mcp);
106 newParticle->addRelationTo(mcp);
107
108 for (auto mcDaughter : mcp->getDaughters()) {
109 if (mcDaughter->hasStatus(MCParticle::c_PrimaryParticle) or not m_skipNonPrimaryDaughters
110 or abs(mcp->getPDG()) == Const::Lambda.getPDGCode()) { // Lambda's daughters are not primary but it is not FSP at mcmatching
111
112 auto partDaughter = createParticleRecursively(mcDaughter, m_skipNonPrimaryDaughters);
113 newParticle->appendDaughter(partDaughter, /* updateType = */ false); // particleSource remain c_MCParticle
114 }
115 }
116 addUniqueParticleToList(newParticle);
117
118 } else if (arrayIndex == -1) {
119 // Particle is not created when no daughter is described in decayString
120 Particle* newParticle = m_particles.appendNew(mcp);
121 newParticle->addRelationTo(mcp);
122
123 addUniqueParticleToList(newParticle);
124 } else {
125 // Particle is already created
127
128 }
129
130 }
131 }
132}
133
134DecayTree<MCParticle>* MCDecayFinderModule::match(const MCParticle* mcp, const DecayDescriptor* d, bool isCC, int& arrayIndex)
135{
136 // Suffixes used in this method:
137 // P = Information from MCParticle
138 // D = Information from DecayDescriptor
139
140 // Create empty DecayTree as return value
141 auto* decay = new DecayTree<MCParticle>();
142
143 // Load PDG codes and compare,
144 const int iPDGD = d->getMother()->getPDGCode();
145 const int iPDGP = mcp->getPDG();
146 const bool isSelfConjugatedParticle = !(EvtPDLUtil::hasAntiParticle(iPDGD));
147
148 if (!isCC && iPDGD != iPDGP) return decay;
149 else if (isCC && (iPDGD != -iPDGP && !isSelfConjugatedParticle)) return decay;
150 else if (isCC && (iPDGD != iPDGP && isSelfConjugatedParticle)) return decay;
151 B2DEBUG(19, "PDG code matched: " << iPDGP);
152
153 // Get number of daughters in the decay descriptor.
154 // If no daughters in decay descriptor, no more checks needed.
155 const int nDaughtersD = d->getNDaughters();
156 if (nDaughtersD == 0) {
157 B2DEBUG(19, "DecayDescriptor has no Daughters, everything OK!");
158 decay->setObj(const_cast<MCParticle*>(mcp));
159 return decay; // arrayIndex is not set
160 }
161
162 // Get daughters of MCParticle
163 vector<const MCParticle*> daughtersP;
164 int nDaughtersRecursiveD = 0;
165 if (d->isIgnoreIntermediate()) {
166 appendParticles(mcp, daughtersP);
167 // Get number of daughter recursively if missing intermediate states are accepted.
168 nDaughtersRecursiveD = getNDaughtersRecursive(d);
169 } else {
170 const vector<MCParticle*>& tmpDaughtersP = mcp->getDaughters();
171 for (auto daug : tmpDaughtersP)
172 daughtersP.push_back(daug);
173 nDaughtersRecursiveD = nDaughtersD;
174 }
175
176 // The MCParticle must have at least as many daughters as the decaydescriptor
177 if (nDaughtersRecursiveD > (int)daughtersP.size()) {
178 B2DEBUG(10, "DecayDescriptor has more daughters than MCParticle!");
179 return decay;
180 }
181
182 // nested daughter has to be called at first to avoid overlap
183 std::vector<std::pair<int, int>> daughtersDepthMapD; // first: -1*depth, second:iDD
184 for (int iDD = 0; iDD < nDaughtersD; iDD++) {
185 int depth = 1; // to be incremented
186 countMaxDepthOfNest(d->getDaughter(iDD), depth);
187
188 daughtersDepthMapD.push_back({-1 * depth, iDD});
189 }
190 // sorted in ascending order = the deepest nested daughter will come first
191 std::sort(daughtersDepthMapD.begin(), daughtersDepthMapD.end());
192
193 // loop over all daughters of the decay descriptor
194 for (auto pair : daughtersDepthMapD) {
195 int iDD = pair.second;
196 // check if there is an unmatched particle daughter matching this decay descriptor daughter
197 bool isMatchDaughter = false;
198 auto itDP = daughtersP.begin();
199 while (itDP != daughtersP.end()) {
200 const MCParticle* daugP = *itDP;
201
202 int tmp; // array index of a created daughter Particle (not to be used)
203 DecayTree<MCParticle>* daughter = match(daugP, d->getDaughter(iDD), isCC, tmp);
204 if (!daughter->getObj()) {
205 ++itDP;
206 delete daughter;
207 continue;
208 }
209 // Matching daughter found, remove it from list of unmatched particle daughters
210 decay->append(daughter);
211 isMatchDaughter = true;
212 itDP = daughtersP.erase(itDP);
213
214 // if the matched daughter has daughters, they are also removed from the list
215 if (d->isIgnoreIntermediate() and d->getDaughter(iDD)->getNDaughters() != 0) {
216 vector<const MCParticle*> grandDaughtersP;
217 if (d->getDaughter(iDD)->isIgnoreIntermediate()) {
218 appendParticles(daugP, grandDaughtersP);
219 } else {
220 const vector<MCParticle*>& tmpGrandDaughtersP = daugP->getDaughters();
221 for (auto grandDaugP : tmpGrandDaughtersP)
222 grandDaughtersP.push_back(grandDaugP);
223 }
224
225 for (auto grandDaugP : grandDaughtersP) {
226 auto jtDP = itDP;
227 while (jtDP != daughtersP.end()) {
228 const MCParticle* daugP_j = *jtDP;
229 // if a grand-daughter matched a daughter, remove it.
230 if (grandDaugP == daugP_j)
231 jtDP = daughtersP.erase(jtDP);
232 else
233 ++jtDP;
234 }
235 }
236 }
237
238 break;
239 }
240 if (!isMatchDaughter) {
241 return decay;
242 }
243 }
244
245 // Ok, it seems that everything from the DecayDescriptor could be matched.
246 decay->setObj(const_cast<MCParticle*>(mcp));
247
248 Particle* newParticle = buildParticleFromDecayTree(decay, d);
249 int status = MCMatching::getMCErrors(newParticle);
250 if (status != MCMatching::c_Correct) { // isSignal != 1
251 B2DEBUG(10, "isSignal is not True. There was an additional particle left.");
252 decay->setObj(nullptr);
253 return decay;
254 }
255
256 arrayIndex = newParticle->getArrayIndex();
257
258 B2DEBUG(19, "Match found!");
259 return decay;
260}
261
262void MCDecayFinderModule::appendParticles(const MCParticle* gen, vector<const MCParticle*>& children)
263{
264 if (MCMatching::isFSP(gen->getPDG())) {
265 if (gen->getPDG() != Const::Kshort.getPDGCode()) // exception for K_S0
266 return; //stop at the bottom of the MC decay tree (ignore secondaries)
267
268 // Currently the decay of "FSP" cannot be specified except for K_S0,
269 // e.g. photon-conversion: gamma -> e+ e-, decay-in-flight: K+ -> mu+ nu_mu
270 }
271
272 vector<const MCParticle*> tmp_children;
273 const vector<MCParticle*>& genDaughters = gen->getDaughters();
274 for (auto daug : genDaughters) {
275 tmp_children.push_back(daug);
276 children.push_back(daug);
277 }
278 for (auto child : tmp_children) {
279 appendParticles(child, children);
280 }
281}
282
284{
285 const int nDaughter = d->getNDaughters();
286 if (nDaughter == 0) return nDaughter;
287
288 int nDaughterRecursive = nDaughter;
289 for (int iDaug = 0; iDaug < nDaughter; iDaug++) {
290 const DecayDescriptor* dDaug = d->getDaughter(iDaug);
291
292 nDaughterRecursive += getNDaughtersRecursive(dDaug);
293 }
294
295 return nDaughterRecursive;
296}
297
299{
300 int maxDepth = 0;
301 for (int i = 0; i < d->getNDaughters(); i++) {
302 int tmp_depth = 1;
303 countMaxDepthOfNest(d->getDaughter(i), tmp_depth);
304
305 if (tmp_depth > maxDepth)
306 maxDepth = tmp_depth;
307 }
308
309 depth += maxDepth;
310}
311
313{
314 auto newParticle = buildParticleFromDecayTree(decay, dd);
315 int status = MCMatching::getMCErrors(newParticle);
316 return (status == MCMatching::c_Correct);
317}
318
319
321{
322 const int nDaughters = dd->getNDaughters();
323 const vector<DecayTree<MCParticle>*> decayDaughters = decay->getDaughters();
324
325 // sanity check
326 if ((int)decayDaughters.size() != nDaughters) {
327 B2ERROR("MCDecayFinderModule::buildParticleFromDecayTree Inconsistency on the number of daughters between DecayTree and DecayDescriptor");
328 return nullptr;
329 }
330
331 // build particle from head of DecayTree
332 MCParticle* mcp = decay->getObj();
333 Particle* newParticle = m_particles.appendNew(mcp);
334 newParticle->addRelationTo(mcp);
335
336 int property = dd->getProperty();
337 property |= dd->getMother()->getProperty();
338 newParticle->setProperty(property);
339
340 // if nDaughters is 0 but mcp is not FSP, all primary daughters are appended for mcmatching
341 if (nDaughters == 0 and not MCMatching::isFSP(mcp->getPDG())) {
342 for (auto mcDaughter : mcp->getDaughters()) {
343 if (mcDaughter->hasStatus(MCParticle::c_PrimaryParticle) or
344 (abs(mcp->getPDG()) == Const::Lambda.getPDGCode())) { // Lambda's daughters are not primary but it is not FSP at mcmatching
345
346 auto partDaughter = createParticleRecursively(mcDaughter, true); // for mcmatching non-primary should be omitted
347 newParticle->appendDaughter(partDaughter, false);
348 }
349 }
350 }
351
352
353 // Daughters of DecayTree were filled in ascending order of depth of nest.
354 std::vector<std::pair<int, int>> daughtersDepthMapD; // first: -1*depth, second:iDD
355 for (int iDD = 0; iDD < nDaughters; iDD++) {
356 auto daugD = dd->getDaughter(iDD);
357
358 int depth = 1; // to be incremented
359 countMaxDepthOfNest(daugD, depth);
360 daughtersDepthMapD.push_back({-1 * depth, iDD});
361 }
362 std::sort(daughtersDepthMapD.begin(), daughtersDepthMapD.end());
363
364 for (int iDD = 0; iDD < nDaughters; iDD++) {
365
366 int index_decayDaughter = 0;
367 for (auto pair : daughtersDepthMapD) {
368 if (pair.second == iDD) break;
369 index_decayDaughter++;
370 }
371
372 Particle* partDaughter = buildParticleFromDecayTree(decayDaughters[index_decayDaughter], dd->getDaughter(iDD));
373
374 int daughterProperty = dd->getDaughter(iDD)->getMother()->getProperty();
375 newParticle->appendDaughter(partDaughter, false, daughterProperty);
376 }
377
378 return newParticle;
379}
380
382{
383 Particle* newParticle = m_particles.appendNew(mcp);
384 newParticle->addRelationTo(mcp);
385
386 for (auto mcDaughter : mcp->getDaughters()) {
387 if (mcDaughter->hasStatus(MCParticle::c_PrimaryParticle) or not skipNonPrimaryDaughters
388 or abs(mcp->getPDG()) == Const::Lambda.getPDGCode()) { // Lambda's daughters are not primary but it is not FSP at mcmatching
389 auto partDaughter = createParticleRecursively(mcDaughter, skipNonPrimaryDaughters);
390 newParticle->appendDaughter(partDaughter, false);
391 }
392 }
393
394 return newParticle;
395}
396
398{
399 for (auto& existingParticle : *m_outputList) {
400 if (existingParticle.isCopyOf(newParticle))
401 return;
402 }
403
404 m_outputList->addParticle(newParticle);
405}
static const ParticleType Lambda
Lambda particle.
Definition Const.h:679
static const ParticleType Kshort
K^0_S particle.
Definition Const.h:677
EStoreFlags
Flags describing behaviours of objects etc.
Definition DataStore.h:69
@ c_WriteOut
Object/array should be saved by output modules.
Definition DataStore.h:70
@ c_DontWriteOut
Object/array should be NOT saved by output modules.
Definition DataStore.h:71
@ c_Event
Different object in each event, all objects/arrays are invalidated after event() function has been ca...
Definition DataStore.h:59
int getProperty() const
return property of the particle.
The DecayDescriptor stores information about a decay tree or parts of a decay tree.
const DecayDescriptor * getDaughter(int i) const
return i-th daughter (0 based index).
int getNDaughters() const
return number of direct daughters.
int getProperty() const
return property of the particle.
const DecayDescriptorParticle * getMother() const
return mother.
This is a helper class for the MCDecayFinderModule.
Definition DecayTree.h:20
Particle * createParticleRecursively(const MCParticle *mcp, bool skipNonPrimaryDaughters)
Create a Particle from the given MCParticle appending all daughters of the MCParticle.
bool m_isSelfConjugatedParticle
Is the particle list for a self-conjugated particle.
std::string m_antiListName
Name of output anti-particle list.
DecayTree< MCParticle > * match(const MCParticle *mcp, const DecayDescriptor *d, bool isCC, int &arrayIndex)
Search for MCParticles matching the given DecayString.
Particle * buildParticleFromDecayTree(const DecayTree< MCParticle > *decay, const DecayDescriptor *dd)
Build a Particle from the given DecayTree and DecayDescriptor.
int getNDaughtersRecursive(const DecayDescriptor *d)
Recursively get number of daughters of given DecayDescriptor.
virtual void initialize() override
Initialises the module.
bool performMCMatching(const DecayTree< MCParticle > *decay, const DecayDescriptor *dd)
Perform the MCMatching on the Particle built from the given DecayTree and DecayDescriptor.
virtual void event() override
Method called for each event.
StoreArray< MCParticle > m_mcparticles
StoreArray of MCParticles.
std::string m_listName
Name of output particle list.
StoreArray< Particle > m_particles
StoreArray of Particles.
StoreObjPtr< ParticleList > m_outputList
output particle list
std::string m_strDecay
Decay string to build the decay descriptor.
void appendParticles(const MCParticle *gen, std::vector< const MCParticle * > &children)
Recursively gather all MC daughters of gen.
void addUniqueParticleToList(Particle *newParticle)
Add a new Particle into the output ParticleList if it does not exist already.
bool m_appendAllDaughters
if true, all daughters are appended
bool m_skipNonPrimaryDaughters
toggle skip of secondary MC daughters
StoreObjPtr< ParticleList > m_antiOutputList
output anti-particle list
DecayDescriptor m_decaydescriptor
Decay descriptor of decays to look for.
bool m_writeOut
toggle output particle list btw.
void countMaxDepthOfNest(const DecayDescriptor *d, int &depth)
Count the max depth of nest from the given DecayDescriptor.
StoreObjPtr< ParticleExtraInfoMap > m_extraInfoMap
object pointer to ParticleExtraInfoMaps
A Class to store the Monte Carlo particle information.
Definition MCParticle.h:32
@ c_PrimaryParticle
bit 0: Particle is primary particle.
Definition MCParticle.h:47
std::vector< Belle2::MCParticle * > getDaughters() const
Get vector of all daughter particles, empty vector if none.
Definition MCParticle.cc:52
int getPDG() const
Return PDG code of particle.
Definition MCParticle.h:101
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
Module()
Constructor.
Definition Module.cc:30
@ 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 store reconstructed particles.
Definition Particle.h:76
void setProperty(const int properties)
sets m_properties
Definition Particle.h:385
void appendDaughter(const Particle *daughter, const bool updateType=true, const int daughterProperty=c_Ordinary)
Appends index of daughter to daughters index array.
Definition Particle.cc:707
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
int getArrayIndex() const
Returns this object's array index (in StoreArray), or -1 if not found.
T * getObj() const
Return the decaying object itself, e.g.
Definition DecayTree.h:65
std::vector< DecayTree< T > * > getDaughters() const
Return list of decay daughters.
Definition DecayTree.h:58
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
bool hasAntiParticle(int pdgCode)
Checks if the particle with given pdg code has an anti-particle or not.
Definition EvtPDLUtil.cc:12
std::string antiParticleListName(const std::string &listName)
Returns name of anti-particle-list corresponding to listName.
Abstract base class for different kinds of events.
STL namespace.
@ c_Correct
This Particle and all its daughters are perfectly reconstructed.
Definition MCMatching.h:34
static bool isFSP(int pdg)
Returns true if given PDG code indicates a FSP.
static int getMCErrors(const Particle *particle, const MCParticle *mcParticle=nullptr)
Returns quality indicator of the match as a bit pattern where the individual bits indicate the the ty...