231 pi0veto: bool = False):
232 """
233 Appends a new decay channel to the Particle object.
234 @param daughters is a list of pdg particle names e.g. ['pi+','K-']
235 @param mvaConfig multivariate analysis configuration
236 @param preCutConfig pre cut configuration object
237 @param pi0veto if true, additional pi0veto variables are added to the MVA configuration
238 """
239
240 daughters = [d + ':generic' if ':' not in d else d for d in daughters]
241
242 mvaConfig = copy.deepcopy(self.mvaConfig if mvaConfig is None else mvaConfig)
243
244 preCutConfig = copy.deepcopy(self.preCutConfig if preCutConfig is None else preCutConfig)
245
246 if mvaConfig is not None and mvaConfig.target != self.mvaConfig.target:
247 basf2.B2FATAL(
248 f'Particle {self.identifier} has common target {self.mvaConfig.target}, while channel '
249 f'{" ".join(daughters)} has {mvaConfig.target}. Each particle must have exactly one target!')
250
251
252 mvaVars = []
253 for v in mvaConfig.variables:
254 if v.count('{') == 0:
255 mvaVars.append(v)
256 continue
257 matches = re.findall(r'\{\s*\d*\s*\.\.\s*\d*\s*\}', v)
258 if len(matches) == 0 and v.count('{}') == 0:
259 mvaVars.append(v)
260 elif v.count('{}') > 0 and len(matches) > 0:
261 basf2.B2FATAL(f'Variable {v} contains both '+'{}'+f' and {matches}. Only one is allowed!')
262 elif len(matches) > 0:
263 ranges = []
264 skip = False
265 for match in matches:
266 tempRange = match[1:-1].split('..')
267 if tempRange[0] == '':
268 tempRange[0] = 0
269 else:
270 tempRange[0] = int(tempRange[0])
271 if tempRange[0] >= len(daughters):
272 basf2.B2DEBUG(11, f'Variable {v} contains index {tempRange[0]} which is more than daughters, skipping!')
273 skip = True
274 break
275 if tempRange[1] == '':
276 tempRange[1] = len(daughters)
277 else:
278 tempRange[1] = int(tempRange[1])
279 if tempRange[1] > len(daughters):
280 basf2.B2DEBUG(11, f'Variable {v} contains index {tempRange[1]} which is more than daughters, skipping!')
281 skip = True
282 break
283 ranges.append(tempRange)
284 if skip:
285 continue
286 if len(ranges) == 1:
287 mvaVars += [v.replace(matches[0], str(c)) for c in range(ranges[0][0], ranges[0][1])]
288 else:
289 for match in matches:
290 v = v.replace(match, '{}')
291 mvaVars += [v.format(*c) for c in itertools.product(*[range(r[0], r[1]) for r in ranges])]
292 elif v.count('{}') <= len(daughters):
293 mvaVars += [v.format(*c) for c in itertools.combinations(list(range(0, len(daughters))), v.count('{}'))]
294 elif v.count('{}') > len(daughters):
295 basf2.B2DEBUG(11, f'Variable {v} contains more brackets than daughters, which is why it will be ignored!')
296 continue
297 else:
298 basf2.B2FATAL(f'Something went wrong with variable {v}!')
299 mvaConfig = mvaConfig._replace(variables=mvaVars)
300
301 decayModeID = len(self.channels)
302 self.channels.append(DecayChannel(name=self.identifier + '_' + str(decayModeID),
303 label=removeJPsiSlash(self.identifier + ' ==> ' + ' '.join(daughters)),
304 decayString=self.identifier + '_' + str(decayModeID) + ' -> ' + ' '.join(daughters),
305 daughters=daughters,
306 mvaConfig=mvaConfig,
307 preCutConfig=preCutConfig,
308 decayModeID=decayModeID,
309 pi0veto=pi0veto))
310 return self