Source code for Gaussino.Generation
###############################################################################
# (c) Copyright 2021 CERN for the benefit of the LHCb and FCC Collaborations  #
#                                                                             #
# This software is distributed under the terms of the Apache License          #
# version 2 (Apache-2.0), 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.                                       #
###############################################################################
"""
High level and utility functions to set up the Generation step in Gaussino
"""
from Gaudi.Configuration import ConfigurableUser, Configurable, ApplicationMgr
from GaudiKernel import SystemOfUnits
from Gaudi.Configuration import log
from Gaussino.GenUtils import configure_rnd_init, configure_gen_monitor
from Gaussino.GenUtils import configure_hepmc_writer
[docs]class GenPhase(ConfigurableUser):
    """Configurable for the Generation phase in Gaussino. Does not implement
    a self.__apply_configuration__ itself. Instead, all member functions are
    explicitly called during the configuration of Gaussino()
    :var BeamMomentum: default: ``3.5 * SystemOfUnits.TeV``
    :vartype BeamMomentum: float, optional
    :var BeamHCrossingAngle: default: ``-0.520 * SystemOfUnits.mrad``
    :vartype BeamHCrossingAngle: float, optional
    :var BeamVCrossingAngle: default: ``0.0``
    :vartype BeamVCrossingAngle: float, optional
    :var BeamEmittance: default: ``0.0037 * SystemOfUnits.mm``
    :vartype BeamEmittance: float, optional
    :var BeamBetaStar: default: ``3.1 * SystemOfUnits.m``
    :vartype BeamBetaStar: float, optional
    :var BeamLineAngles: default:
        ``[-0.075 * SystemOfUnits.mrad, 0.035 * SystemOfUnits.mrad]``
    :vartype BeamLineAngles: list, optional
    :var InteractionPosition: default:
        ``[0.459 * SystemOfUnits.mm, -0.015 * SystemOfUnits.mm,
        0.5 * SystemOfUnits.mm]``
    :vartype InteractionPosition: list, optional
    :var BunchRMS: default: ``82.03 * SystemOfUnits.mm``
    :vartype BunchRMS: float, optional
    :var Luminosity: default:
        ``0.247 * (10 ** 30) / (SystemOfUnits.cm2 * SystemOfUnits.s)``
    :vartype Luminosity: float, optional
    :var TotalCrossSection: default: ``91.1 * SystemOfUnits.millibarn``
    :vartype TotalCrossSection: float, optional
    :var B2Momentum: default: ``3.5 * SystemOfUnits.TeV``
    :vartype B2Momentum: float, optional
    :var B1Particle: default: ``'p'``
    :vartype B1Particle: str, optional
    :var B2Particle: default: ``'p'``
    :vartype B2Particle: str, optional
    :var EvtMax: default: ``-1``
    :vartype EvtMax: int, optional
    :var WriteHepMC: default: ``False``
    :vartype WriteHepMC: bool, optional
    :var GenMonitor: default: ``False``
    :vartype GenMonitor: bool, optional
    :var ParticleGun: default: ``False``
    :vartype ParticleGun: bool, optional
    :var ParticleGunUseDefault: default: ``False``
    :vartype ParticleGunUseDefault: bool, optional
    :var Production_kwargs: default: ``{}``
    :vartype Production_kwargs: dict, optional
    :var ConvertEDM: default: ``False``
    :vartype ConvertEDM: bool, optional
    :var SampleGenerationTool: default: ``'SignalPlain'``
    :vartype SampleGenerationTool: str, optional
    :var SampleGenerationToolOpts: default: ``{}``
    :vartype SampleGenerationToolOpts: dict, optional
    :var PileUpTool: default: ``'FixedLuminosityWithSvc'``
    :vartype PileUpTool: str, optional
    :var ProductionTool: default: ``'Pythia8Production'``
    :vartype ProductionTool: str, optional
    :var ProductionToolOpts: default: ``{}``
    :vartype ProductionToolOpts: dict, optional
    :var DecayTool: default: ``''``
    :vartype DecayTool: str, optional
    :var CutTool: default: ``''``
    :vartype CutTool: str, optional
    :var CutToolOpts: default: ``{}``
    :vartype CutToolOpts: dict, optional
    :var FullGenEventTool: default: ``''``
    :vartype FullGenEventTool: str, optional
    :var FullGenEventToolOpts: default: ``{}``
    :vartype FullGenEventToolOpts: dict, optional
    """
    __slots__ = {
        "BeamMomentum":
        3.5 * SystemOfUnits.TeV,  # NOQA
        "BeamHCrossingAngle":
        -0.520 * SystemOfUnits.mrad,  # NOQA
        "BeamVCrossingAngle":
        0.0,  # NOQA
        "BeamEmittance":
        0.0037 * SystemOfUnits.mm,  # NOQA
        "BeamBetaStar":
        3.1 * SystemOfUnits.m,  # NOQA
        "BeamLineAngles":
        [-0.075 * SystemOfUnits.mrad, 0.035 * SystemOfUnits.mrad],  # NOQA
        "InteractionPosition": [
            0.459 * SystemOfUnits.mm, -0.015 * SystemOfUnits.mm,
            0.5 * SystemOfUnits.mm
        ],  # NOQA
        "BunchRMS":
        82.03 * SystemOfUnits.mm,  # NOQA
        "Luminosity":
        0.247 * (10**30) / (SystemOfUnits.cm2 * SystemOfUnits.s),  # NOQA
        "TotalCrossSection":
        91.1 * SystemOfUnits.millibarn,  # NOQA
        "B2Momentum":
        3.5 * SystemOfUnits.TeV,  # NOQA
        "B1Particle":
        'p',  # NOQA
        "B2Particle":
        'p',  # NOQA
        "EvtMax":
        -1,  # NOQA
        "WriteHepMC":
        False,  # NOQA
        "GenMonitor":
        False,  # NOQA
        "ParticleGun":
        False,  # NOQA
        "ParticleGunUseDefault":
        True,  # NOQA
        "Production_kwargs": {},  # NOQA
        "ConvertEDM":
        False,  # NOQA
        "SampleGenerationTool":
        'SignalPlain',  # NOQA
        "SampleGenerationToolOpts": {},  # NOQA
        "PileUpTool":
        'FixedLuminosityWithSvc',  # NOQA
        "ProductionTool":
        'Pythia8Production',  # NOQA
        "ProductionToolOpts": {},
        "DecayTool":
        '',  # NOQA
        "CutTool":
        '',  # NOQA
        "CutToolOpts": {},  # NOQA
        "FullGenEventCutTool":
        '',  # NOQA
        "FullGenEventCutToolOpts": {}  # NOQA
    }
    def __init__(self, name=Configurable.DefaultName, **kwargs):
        kwargs["name"] = name
        super(GenPhase, self).__init__(*(), **kwargs)
[docs]    def setOtherProp(self, other, name):
        """Set the given property in another configurable object
        :param other: The other configurable to set the property for
        :param name:  The property name
        """
        self.propagateProperty(name, other)
[docs]    def setOtherProps(self, other, names):
        """ Set the given properties in another configurable object
        :param other: The other configurable to set the property for
        :param names: The property names
        """
        self.propagateProperties(names, other)
[docs]    def configure_generation(self, seq):
        """ Configuration method for the generation other than
        a particle gun.
        :param seq: list of algorithms
        """
        # Algorithm that produces the actual HepMC by talking to stuff
        SampleGenerationTool = self.getProp('SampleGenerationTool')
        ProductionTool = self.getProp('ProductionTool')
        DecayTool = self.getProp('DecayTool')
        CutTool = self.getProp('CutTool')
        FullGenEventCutTool = self.getProp('FullGenEventCutTool')
        PileUpTool = self.getProp('PileUpTool')
        from Gaussino.Utilities import beaminfoService
        from Gaussino.Utilities import get_set_configurable
        beaminfoService()
        from Configurables import Gaussino
        if Gaussino().getProp("ReDecay"):
            from Configurables import ReDecayGeneration
            gen_alg = ReDecayGeneration()
        else:
            from Configurables import Generation
            gen_alg = Generation()
        sgt = get_set_configurable(gen_alg, 'SampleGenerationTool',
                                   SampleGenerationTool)
        sgt_opts = self.getProp('SampleGenerationToolOpts')
        for n, v in sgt_opts.items():
            sgt.setProp(n, v)
        try:
            sgt.DecayTool = DecayTool
        except:
            pass
        try:
            if CutTool != '':
                ct = get_set_configurable(sgt, 'CutTool', CutTool)
                ct_opts = self.getProp('CutToolOpts')
                for n, v in ct_opts.items():
                    ct.setProp(n, v)
            else:
                sgt.CutTool = ''
        except Exception as e:
            log.error('Could not configure CutTool', e)
        if FullGenEventCutTool != '':
            ct = get_set_configurable(gen_alg, 'FullGenEventCutTool',
                                      FullGenEventCutTool)
            ct_opts = self.getProp('FullGenEventCutToolOpts')
            for n, v in ct_opts.items():
                ct.setProp(n, v)
        else:
            gen_alg.FullGenEventCutTool = ''
        prod = get_set_configurable(sgt, 'ProductionTool', ProductionTool)
        if ProductionTool in ["Pythia8Production", "Pythia8ProductionMT"]:
            # For now keep it only for Pythia, but potentially in future we
            # want to do this for all possible production tools
            prot_opts = self.getProp('ProductionToolOpts')
            for n, v in prot_opts.items():
                prod.setProp(n, v)
            prod.BeamToolName = 'CollidingBeamsWithSvc'
        if ProductionTool == "Pythia8ProductionMT":
            from Configurables import Gaussino
            prod.NThreads = Gaussino().ThreadPoolSize
        gen_alg.PileUpTool = PileUpTool
        gen_alg.VertexSmearingTool = 'BeamSpotSmearVertexWithSvc'
        gen_alg.DecayTool = DecayTool
        seq += [gen_alg]
        # Now do it all again for the signal part
        if Gaussino().getProp("ReDecay"):
            from Configurables import ReDecaySignalGeneration
            siggen_alg = ReDecaySignalGeneration()
            siggen_alg.HepMCEventLocation = 'Gen/SignalDecayTree'
            siggen_alg.GenCollisionLocation = 'Gen/SignalCollisions'
            siggen_alg.GenHeaderOutputLocation = 'Gen/SignalGenHeader'
            seq += [siggen_alg]
            sgt = get_set_configurable(siggen_alg, 'SampleGenerationTool',
                                       'SignalPlain')
            sgt.RevertWhenBackward = False  # Don't invert in the redecay part
            siggen_alg.GenFSRLocation = ""
            sgt.GenFSRLocation = ""
            sgt_opts = self.getProp('SampleGenerationToolOpts')
            if 'SignalPIDList' in sgt_opts:
                sgt.setProp('SignalPIDList', sgt_opts['SignalPIDList'])
            else:
                # FIXME: First only support signal like org tool
                log.error("Original sample generation tool not of signal type")
            try:
                sgt.DecayTool = DecayTool
            except:
                pass
            try:
                if CutTool != '':
                    ct = get_set_configurable(sgt, 'CutTool', CutTool)
                    ct_opts = self.getProp('CutToolOpts')
                    for n, v in ct_opts.items():
                        ct.setProp(n, v)
                else:
                    sgt.CutTool = ''
            except Exception as e:
                log.error('Could not configure CutTool', e)
            if FullGenEventCutTool != '':
                ct = get_set_configurable(siggen_alg, 'FullGenEventCutTool',
                                          FullGenEventCutTool)
                ct_opts = self.getProp('FullGenEventCutToolOpts')
                for n, v in ct_opts.items():
                    ct.setProp(n, v)
            else:
                siggen_alg.FullGenEventCutTool = ''
            prod = get_set_configurable(sgt, 'ProductionTool',
                                        'ReDecayProduction')
            siggen_alg.PileUpTool = 'ReDecayPileUp'
            siggen_alg.VertexSmearingTool = ''
            siggen_alg.DecayTool = DecayTool
[docs]    def configure_pgun(self, seq):
        """Simple utility function to create and configure an instance of particle
        gun
        :param seq: list of algorithms
        """
        from GaudiKernel.SystemOfUnits import GeV, rad
        from Configurables import ParticleGun
        pgun = ParticleGun("ParticleGun")
        if self.getProp('ParticleGunUseDefault'):
            pgun.EventType = 53210205
            from Configurables import MomentumRange
            pgun.addTool(MomentumRange, name="MomentumRange")
            pgun.ParticleGunTool = "MomentumRange"
            from Configurables import FlatNParticles
            pgun.addTool(FlatNParticles, name="FlatNParticles")
            pgun.NumberOfParticlesTool = "FlatNParticles"
            pgun.FlatNParticles.MinNParticles = 1
            pgun.FlatNParticles.MaxNParticles = 1
            pgun.MomentumRange.PdgCodes = [-2112]
            pgun.MomentumRange.MomentumMin = 2.0 * GeV
            pgun.MomentumRange.MomentumMax = 100.0 * GeV
            pgun.MomentumRange.ThetaMin = 0.015 * rad
            pgun.MomentumRange.ThetaMax = 0.300 * rad
        seq += [pgun]
[docs]    def configure_phase(self):  # NOQA
        """ Main configuration method for the generation phase. """
        EvtMax = self.getProp('EvtMax')
        if EvtMax <= 0:
            raise RuntimeError(
                "Generating events but selected '%s' events." % EvtMax)  # NOQA
        seq = []
        if self.getProp('ParticleGun'):
            self.configure_pgun(seq)
        else:
            self.configure_generation(seq)
        # Algorithm to initialise the random seeds and make a GenHeader
        rnd_init = configure_rnd_init()
        seq += [rnd_init]
        if self.getProp('GenMonitor'):
            gen_moni = configure_gen_monitor()
            seq += [gen_moni]
        if self.getProp('WriteHepMC'):
            seq += [configure_hepmc_writer()]
        # seq.Members += [GenerationToSimulation(), CheckMCStructure()]
        ApplicationMgr().TopAlg += seq
[docs]    def configure_genonly(self):
        """ Method that is used when only the generation phase
        is used.
        """
        seq = []
        from Configurables import Gaussino
        if Gaussino().getProp('ReDecay'):
            from Configurables import ReDecaySkipSimAlg
            alg = ReDecaySkipSimAlg()
        else:
            from Configurables import SkipSimAlg
            alg = SkipSimAlg()
        from Gaussino.Utilities import get_set_configurable
        tool = get_set_configurable(alg, 'HepMCConverter')
        try:
            tool.CheckParticle = False
        except:
            pass
        seq += [alg]
        ApplicationMgr().TopAlg += seq
    @staticmethod
    def eventType():
        from Configurables import Generation
        evtType = ''
        if Generation("Generation").isPropertySet("EventType"):
            evtType = str(Generation("Generation").EventType)
        return evtType