###############################################################################
# (c) Copyright 2000-2022 CERN for the benefit of the LHCb Collaboration #
# #
# This software is distributed under the terms of the GNU General Public #
# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". #
# #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization #
# or submit itself to any jurisdiction. #
###############################################################################
__author__ = "Gloria Corti, Dominik Muller, and Michal Mazurek"
__email__ = "lhcb-simulation@cern.ch"
from Configurables import ApplicationMgr
from Gaudi.Configuration import Configurable, log
from GaudiConf.SimConf import SimConf
from Gauss.Generation import GaussGeneration
from Gauss.Geometry import GaussGeometry
from Gauss.Simulation import GaussSimulation
from Gauss.Visualization import GaussVisualization
from Gaussino.Configuration import Gaussino
# Configurables (do NOT use 'from Configurables' here)
from Gaussino.Utilities import GaussinoConfigurable
from LHCbAlgs.Configuration import LHCbApp
[docs]
class Gauss(GaussinoConfigurable):
__used_configurables__ = [
LHCbApp,
SimConf,
GaussGeneration,
GaussSimulation,
GaussGeometry,
GaussVisualization,
Gaussino,
]
__slots__ = {
"Histograms": "DEFAULT",
"EvtMax": -1,
"Phases": ["Generator", "Simulation"],
"EnableHive": True,
"ThreadPoolSize": 1,
"EventSlots": 1,
"FirstTimingEvent": 1,
"DatasetName": "Gauss",
"OutputType": "SIM",
"ReDecay": False,
"Debug": False,
"SpilloverPaths": [],
"EnablePack": True,
"DataType": "",
"WriteFSR": False,
"MergeGenFSR": False,
"FirstEventNumber": 1,
"RunNumber": 1,
}
# options to be directly propagated to Gaussino
GAUSSINO_OPTIONS = [
"Histograms",
"EvtMax",
"Phases",
"EnableHive",
"ThreadPoolSize",
"EventSlots",
"FirstTimingEvent",
"DatasetName",
"OutputType",
"ReDecay",
"Debug",
"FirstEventNumber",
"RunNumber",
]
# options to be directly propagated to LHCbApp
LHCBAPP_OPTIONS = [
# TODO: investigate this!
# I think this should be passed only to Gaussino
"EvtMax",
"DataType",
]
SIMCONF_OPTIONS = [
"SpilloverPaths",
"EnablePack",
"Phases",
"DataType",
]
Run1DataTypes = ["2009", "2010", "2011", "2012", "2013"]
Run2DataTypes = ["2015", "2016", "2017", "2018"]
Run3DataTypes = ["2022", "2023", "2024", "2025", "Run3"]
Run4DataTypes = ["Run4"]
Run5DataTypes = ["Run5"]
DataTypes = [
*Run1DataTypes,
*Run2DataTypes,
*Run3DataTypes,
*Run4DataTypes,
*Run5DataTypes,
]
KNOWN_OUTPUTS = ["NONE", "GEN", "XGEN", "RGEN", "SIM", "XSIM"]
def __init__(self, name=Configurable.DefaultName, _enabled=True, **kwargs):
# -> the following makes sure that one does not instantiate Gaussino before
# Gauss, otherwise the later in the code fix will be ill-formed
if "Gaussino" in Configurable.allConfigurables:
msg = "Gauss configurable must be instantiated before Gaussino"
log.error(msg)
raise RuntimeError(msg)
# -> this makes the Gaussino configurable LHCbApp-dependent
# -> explanation: in some cases different configurables have to be instantiated
# with the same name (e.g. HiveWhiteBoard and EventDataSvc);
# -> this makes sure that IOHelper via LHCbApp will not set the defaults before
# Gaussino.__apply_configuration__ takes place
Gaussino.__used_configurables__.append(LHCbApp)
# -> end of the fix
super(Gauss, self).__init__(name=name, _enabled=_enabled, **kwargs)
# Apply the configuration
[docs]
def __apply_configuration__(self):
self._check_options_compatibility()
self._propagate_gaussino()
self._propagate_lhcbapp()
packing = self._set_packing()
self._propagate_sim_conf(packing)
self._propagate_simulation()
self._set_evtgen()
self._define_output(prerequisites=[packing])
for conf in self.__used_configurables__:
conf()
[docs]
def _check_options_compatibility(self):
if not self.getProp("DataType"):
msg = "Must have a DataType."
log.error(msg)
raise ValueError(msg)
if self.getProp("DataType") not in self.DataTypes:
msg = (
f"Unknown DataType '{self.getProp('DataType')}'. "
f"Must be one of: '{self.DataTypes}'"
)
log.error(msg)
raise ValueError(msg)
for conf, props in Configurable.allConfigurables.items():
if conf.startswith("Gaussino") and props._enabled:
msg = (
"When using Gauss, you must set "
"Gaussino's properties through "
"the corresponding configurable "
"in Gauss. Setting Gaussino's options "
"directly has been disabled."
)
log.error(msg)
raise RuntimeError(msg)
[docs]
def _propagate_gaussino(self):
self.propagateProperties(self.GAUSSINO_OPTIONS, Gaussino())
Gaussino().ConvertEDM = True
# FIXME: temporary! we have to wait for an official way of getting ParticleTable.txt
# in the meantime, we use the internal ParticleTable.txt in Gaussino
# see: https://gitlab.cern.ch/lhcb/LHCb/-/merge_requests/3510#note_5530125
#
# Gaussino().ParticleTable = 'git:///param/ParticleTable.txt'
[docs]
def _propagate_lhcbapp(self):
self.propagateProperties(self.LHCBAPP_OPTIONS, LHCbApp())
LHCbApp(
Simulation=True,
# do not turn hive mode with LHCbApp
# this is now done in Gaussino() in _setup_hive()
EnableHive=False,
)
[docs]
def _set_packing(self):
from Configurables import GaudiSequencer
return GaudiSequencer("PackingSeq", Sequential=True, IgnoreFilterPassed=True)
[docs]
def _propagate_sim_conf(self, packing):
# Propagate properties to SimConf
SimConf().setProp("Writer", "GaussTape")
self.propagateProperties(self.SIMCONF_OPTIONS, SimConf())
# Empty the list of detectors, it is later populated when configuring
# the subdetectors
SimConf().Detectors = []
# if we have post-sim filters, we only want to write if the filter is
# passed
# if self.getProp("PostSimFilters"):
# OutputStream("GaussTape").RequireAlgs.append("PostSimFilterSeq")
# Don't want SIM data unpacking enabled in DoD service
SimConf().EnableUnpack = False
SimConf().SaveHepMC = False
phases = self.getProp("Phases")
if "GenToMCTree" in phases or "Simulation" in phases:
for so in self.defineCrossingList():
SimConf().PackingSequencers[so] = packing
[docs]
def defineCrossingList(self):
crossingList = [""]
spillOverList = self.getProp("SpilloverPaths")
while "" in spillOverList:
spillOverList.remove("")
crossingList += spillOverList
return crossingList
[docs]
def _outStream(self, filename, winstance, writeFSR):
winstance.Output = "DATAFILE='" + filename + "' SVC='RootCnvSvc' OPT='REC'"
algs = [winstance]
if writeFSR:
# ignore name when name is type
writer = writer.getFullName()
if writer.split("/")[0] == writer.split("/")[-1]:
writer = writer.split("/")[0]
FSRWriter = GaudiConfigurables.RecordStream(
"FSR" + writer.replace("/", "").replace("::", "").replace("__", ""),
ItemList=["/FileRecords#999"],
EvtDataSvc="FileRecordDataSvc",
EvtConversionSvc="FileRecordPersistencySvc",
Output="DATAFILE='" + filename + "' SVC='FileRecordCnvSvc' OPT='REC'",
)
algs.append(FSRWriter)
if ApplicationMgr().OutStream is None or len(ApplicationMgr().OutStream) == 0:
ApplicationMgr().OutStream = []
ApplicationMgr().OutStream.extend(algs)
[docs]
def _define_output(self, prerequisites):
"""
Set up output stream according to phase processed,
the spill-over slots and the type of output
"""
output = self.getProp("OutputType").upper()
if output == "NONE":
log.warning("No event data output produced")
return
simWriter = SimConf().writer()
# define default file extensions depending on the phase that has been
# run
fileDefaultExtension = ".gen"
fileAllowedExtension = [fileDefaultExtension]
if "GenToMCTree" in self.getProp("Phases"):
fileDefaultExtension = ".xgen"
fileAllowedExtension = [fileDefaultExtension, ".rgen"]
elif "Simulation" in self.getProp("Phases"):
fileDefaultExtension = ".sim"
fileAllowedExtension = [fileDefaultExtension, ".xsim"]
# choose the file extension from the one selected compatibly with the
# phase run
if output not in self.KNOWN_OUTPUTS:
log.warning(
"OutputType not supported. "
f"Use default for chosen phases: {fileDefaultExtension}"
)
fileExtension = "." + output.lower()
if fileExtension not in fileAllowedExtension:
fileExtension = fileDefaultExtension
log.warning(
"OutputType not supported "
f"for this phase. Use default: {fileExtension}"
)
# set saving or not of HepMC depending on chosen file extension
if SimConf().isPropertySet("SaveHepMC"):
log.warning("SimConf().SaveHepMC will " "be ignored. Value set by Gauss()")
saveHepMC = False
if fileExtension in [".gen", ".xgen", ".xsim"]:
saveHepMC = True
SimConf().setProp("SaveHepMC", saveHepMC)
outputFile = Gaussino()._get_output_name() + fileExtension
# Merge genFSRs
if self.getProp("WriteFSR"):
seqGenFSR = GaudiSequencer("GenFSRSeq")
ApplicationMgr().TopAlg += [seqGenFSR]
if self.getProp("MergeGenFSR"):
seqGenFSR.Members += ["GenFSRMerge"]
self._outStream(outputFile, simWriter, self.getProp("WriteFSR"))
from Configurables import FileCatalog
if not FileCatalog().isPropertySet("Catalogs"):
FileCatalog().Catalogs = ["xmlcatalog_file:NewCatalog.xml"]
edm_algos = Gaussino.edm_algorithms(self.getProp("ReDecay"))
members = edm_algos + prerequisites + [SimConf().writer()]
from Configurables import GaudiSequencer
output_seq = GaudiSequencer(
"OutputSeq",
Members=members,
Sequential=True,
)
ApplicationMgr().TopAlg.append(output_seq)
[docs]
def _propagate_simulation(self):
if "Simulation" not in self.getProp("Phases"):
GaussSimulation.only_generation_phase = True
GaussGeometry.only_generation_phase = True
return
run1or2 = self.getProp("DataType") in self.Run1DataTypes + self.Run2DataTypes
GaussGeometry.run1or2 = run1or2
GaussGeometry.run4or5 = (
self.getProp("DataType") in self.Run4DataTypes + self.Run5DataTypes
)
GaussGeometry.datatype = self.getProp("DataType")
GaussSimulation.run1or2 = run1or2
[docs]
def _set_evtgen(self):
from Configurables import EvtGenDecay
EvtGenDecay().DecayFile = "$DECFILESROOT/dkfiles/DECAY.DEC"