319 def reconstruct(self) -> pybasf2.Path:
320 """
321 Returns pybasf2.Path which reconstructs the particles and does the vertex fitting if necessary
322 """
323 path = basf2.create_path()
324
325 for particle in self.particles:
326 for channel in particle.channels:
327
329 channel.daughters[0].split(
':')[0]) ==
pdg.from_name(particle.name)):
330 ma.cutAndCopyList(channel.name, channel.daughters[0], channel.preCutConfig.userCut, writeOut=True, path=path)
331 v2EI = basf2.register_module('VariablesToExtraInfo')
332 v2EI.set_name(f'VariablesToExtraInfo_{channel.name}')
333 v2EI.param('particleList', channel.name)
334 v2EI.param('variables', {f'constant({channel.decayModeID})': 'decayModeID'})
335
336 v2EI.set_log_level(basf2.logging.log_level.ERROR)
337 path.add_module(v2EI)
338 else:
339 ma.reconstructDecay(channel.decayString, channel.preCutConfig.userCut, channel.decayModeID,
340 writeOut=True, path=path)
341
342
343
344 if channel.extraPathSpec is not None:
345 spec = channel.extraPathSpec
346 mod = importlib.import_module(spec["module"])
347 registerFunc = getattr(mod, spec["function"])
348 registerFunc(path, channel.name, *spec["args"], **spec["kwargs"])
349
350 if self.config.monitor:
351 if "tag" in (channel.name).lower():
352 ma.matchTagTruth(channel.name, path=path)
353 else:
354 ma.matchMCTruth(channel.name, path=path)
355 bc_variable = channel.preCutConfig.bestCandidateVariable
356 if self.config.monitor == 'simple':
357 hist_variables = [channel.mvaConfig.target, 'extraInfo(decayModeID)']
358 hist_variables_2d = [(channel.mvaConfig.target, 'extraInfo(decayModeID)')]
359 else:
360 hist_variables = [bc_variable, 'mcErrors', 'mcParticleStatus',
361 channel.mvaConfig.target] + list(channel.mvaConfig.spectators.keys())
362 hist_variables_2d = [(bc_variable, channel.mvaConfig.target),
363 (bc_variable, 'mcErrors'),
364 (bc_variable, 'mcParticleStatus')]
365 for specVar in channel.mvaConfig.spectators:
366 hist_variables_2d.append((bc_variable, specVar))
367 hist_variables_2d.append((channel.mvaConfig.target, specVar))
368 filename = os.path.join(self.config.monitoring_path, 'Monitor_PreReconstruction_BeforeRanking.root')
369 ma.variablesToHistogram(
370 channel.name,
371 variables=config.variables2binnings(hist_variables),
372 variables_2d=config.variables2binnings_2d(hist_variables_2d),
373 filename=filename,
374 ignoreCommandLineOverride=True,
375 directory=f'{channel.label}',
376 path=path)
377
378 if channel.preCutConfig.bestCandidateMode == 'lowest':
379 ma.rankByLowest(channel.name,
380 channel.preCutConfig.bestCandidateVariable,
381 channel.preCutConfig.bestCandidateCut,
382 'preCut_rank',
383 path=path)
384 elif channel.preCutConfig.bestCandidateMode == 'highest':
385 ma.rankByHighest(channel.name,
386 channel.preCutConfig.bestCandidateVariable,
387 channel.preCutConfig.bestCandidateCut,
388 'preCut_rank',
389 path=path)
390 else:
391 raise RuntimeError(f'Unknown bestCandidateMode {repr(channel.preCutConfig.bestCandidateMode)}')
392
393 if 'gamma' in channel.decayString and channel.pi0veto:
394 ma.buildRestOfEvent(channel.name, path=path)
395 Ddaughter_roe_path = basf2.Path()
396 deadEndPath = basf2.Path()
397 ma.signalSideParticleFilter(channel.name, '', Ddaughter_roe_path, deadEndPath)
398 ma.fillParticleList('gamma:roe', 'isInRestOfEvent == 1', path=Ddaughter_roe_path)
399
400 matches = list(re.finditer('gamma', channel.decayString))
401 pi0lists = []
402 for igamma in range(len(matches)):
403 start, end = matches[igamma-1].span()
404 tempString = f'{channel.decayString[:start]}^gamma{channel.decayString[end:]}'
405 ma.fillSignalSideParticleList(f'gamma:sig_{igamma}', tempString, path=Ddaughter_roe_path)
406 ma.reconstructDecay(f'pi0:veto_{igamma} -> gamma:sig_{igamma} gamma:roe', '', path=Ddaughter_roe_path)
407 pi0lists.append(f'pi0:veto_{igamma}')
408 ma.copyLists('pi0:veto', pi0lists, writeOut=False, path=Ddaughter_roe_path)
409 ma.rankByLowest('pi0:veto', 'abs(dM)', 1, path=Ddaughter_roe_path)
410 ma.matchMCTruth('pi0:veto', path=Ddaughter_roe_path)
411 ma.variableToSignalSideExtraInfo(
412 'pi0:veto',
413 {
414 'InvM': 'pi0vetoMass',
415 'formula((daughter(0,E)-daughter(1,E))/(daughter(0,E)+daughter(1,E)))': 'pi0vetoEneAsy',
416 'cosHelicityAngleMomentum': 'pi0vetoCosHelMom',
417 },
418 path=Ddaughter_roe_path
419 )
420 path.for_each('RestOfEvent', 'RestOfEvents', Ddaughter_roe_path)
421
422 if self.config.monitor:
423 filename = os.path.join(self.config.monitoring_path, 'Monitor_PreReconstruction_AfterRanking.root')
424 if self.config.monitor != 'simple':
425 hist_variables += ['extraInfo(preCut_rank)']
426 hist_variables_2d += [('extraInfo(preCut_rank)', channel.mvaConfig.target),
427 ('extraInfo(preCut_rank)', 'mcErrors'),
428 ('extraInfo(preCut_rank)', 'mcParticleStatus')]
429 for specVar in channel.mvaConfig.spectators:
430 hist_variables_2d.append(('extraInfo(preCut_rank)', specVar))
431 ma.variablesToHistogram(
432 channel.name,
433 variables=config.variables2binnings(hist_variables),
434 variables_2d=config.variables2binnings_2d(hist_variables_2d),
435 filename=filename,
436 ignoreCommandLineOverride=True,
437 directory=f'{channel.label}',
438 path=path)
439
440
441 elif self.config.training:
442 if "tag" in (channel.name).lower():
443 ma.matchTagTruth(channel.name, path=path)
444 else:
445 ma.matchMCTruth(channel.name, path=path)
446
448 pvfit = basf2.register_module('ParticleVertexFitter')
449 pvfit.set_name(f'ParticleVertexFitter_{channel.name}')
450 pvfit.param('listName', channel.name)
451 pvfit.param('confidenceLevel', channel.preCutConfig.vertexCut)
452 pvfit.param('vertexFitter', 'KFit')
453 pvfit.param('fitType', 'vertex')
454 pvfit.set_log_level(basf2.logging.log_level.ERROR)
455 path.add_module(pvfit)
456 elif re.findall(r"[\w']+", channel.decayString).count('pi0') > 1 and particle.name != 'pi0':
457 basf2.B2INFO(f"Ignoring vertex fit for {channel.name} because multiple pi0 are not supported yet.")
458 elif len(channel.daughters) > 1:
459 pvfit = basf2.register_module('ParticleVertexFitter')
460 pvfit.set_name(f'ParticleVertexFitter_{channel.name}')
461 pvfit.param('listName', channel.name)
462 pvfit.param('confidenceLevel', channel.preCutConfig.vertexCut)
463 pvfit.param('vertexFitter', 'KFit')
464 if particle.name in ['pi0']:
465 pvfit.param('fitType', 'mass')
466 else:
467 pvfit.param('fitType', 'vertex')
468 pvfit.set_log_level(basf2.logging.log_level.ERROR)
469 path.add_module(pvfit)
470
471 if self.config.monitor:
472 if self.config.monitor == 'simple':
473 hist_variables = [channel.mvaConfig.target, 'extraInfo(decayModeID)']
474 hist_variables_2d = [(channel.mvaConfig.target, 'extraInfo(decayModeID)')]
475 else:
476 hist_variables = ['chiProb', 'mcErrors', 'mcParticleStatus',
477 channel.mvaConfig.target] + list(channel.mvaConfig.spectators.keys())
478 hist_variables_2d = [('chiProb', channel.mvaConfig.target),
479 ('chiProb', 'mcErrors'),
480 ('chiProb', 'mcParticleStatus')]
481 for specVar in channel.mvaConfig.spectators:
482 hist_variables_2d.append(('chiProb', specVar))
483 hist_variables_2d.append((channel.mvaConfig.target, specVar))
484 filename = os.path.join(self.config.monitoring_path, 'Monitor_PreReconstruction_AfterVertex.root')
485 ma.variablesToHistogram(
486 channel.name,
487 variables=config.variables2binnings(hist_variables),
488 variables_2d=config.variables2binnings_2d(hist_variables_2d),
489 filename=filename,
490 ignoreCommandLineOverride=True,
491 directory=f'{channel.label}',
492 path=path)
493
494 return path
495
496