From 28aa6a135b534e7cc3409990cd8b1862721e39c8 Mon Sep 17 00:00:00 2001
From: Davide Fazzini <davide.fazzini@cern.ch>
Date: Thu, 25 Feb 2021 08:55:37 +0000
Subject: [PATCH] Fixed formatting

patch generated by https://gitlab.cern.ch/lhcb/DaVinci/-/jobs/12217080
---
 Phys/DaVinci/options/DVconfig-Default.yaml    |  15 +-
 .../options/DaVinci-UserAlgs-Example.py       |  40 ++++
 Phys/DaVinci/options/DaVinciDB-Example.yaml   |   2 +-
 Phys/DaVinci/options/jobOptions-Example.py    |   2 +-
 Phys/DaVinci/options/jobOptions-Example.yaml  |   2 +-
 .../python/DaVinci/ConfigurationUpgrade.py    | 108 ++++++++--
 Phys/DaVinci/python/DaVinci/algorithms.py     | 173 +++++++++++-----
 Phys/DaVinci/python/DaVinci/configOptions.py  | 187 +++++++++++-------
 Phys/DaVinci/python/DaVinci/configurations.py | 135 +++++++++++--
 Phys/DaVinci/python/DaVinci/optionChecker.py  | 107 ++++++++++
 Phys/DaVinci/python/DaVinci/utilities.py      |  88 +++++++++
 11 files changed, 697 insertions(+), 162 deletions(-)
 create mode 100644 Phys/DaVinci/options/DaVinci-UserAlgs-Example.py
 create mode 100644 Phys/DaVinci/python/DaVinci/optionChecker.py
 create mode 100644 Phys/DaVinci/python/DaVinci/utilities.py

diff --git a/Phys/DaVinci/options/DVconfig-Default.yaml b/Phys/DaVinci/options/DVconfig-Default.yaml
index 838039323..403cfcb9f 100644
--- a/Phys/DaVinci/options/DVconfig-Default.yaml
+++ b/Phys/DaVinci/options/DVconfig-Default.yaml
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2020-2021 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".   #
@@ -61,8 +61,8 @@ WriteFSR:
    text: '""" Flags whether to write out an FSR """'
    value: True
 MergeGenFSR: 
-   text: '""" Flags whether to merge the generatore level FSRs """'
-   value: False
+   text: '""" Flags whether to merge the generator-level FSRs """'
+   value: True
 RootCompressionLevel: 
    text: '""" ROOT Compression level for ntuples """'
    value: 'LZMA:6'
@@ -74,9 +74,12 @@ IgnoreDQFlags:
 MainOptions: 
    text: '""" Main option file to execute """'
    value: ''
-UserAlgorithms: 
-   text: '""" User algorithms to run. """'
-   value: []
+UserAlgorithmFile:
+   text: '""" User algorithms file to be processed without .py extension. """'
+   value: '$DAVINCIROOT/options/DaVinci-UserAlgs-Example'
+UserAlgorithm: 
+   text: '""" Main user algorithms function to run. """'
+   value: 'myfunc'
 RedoMCLinks: 
    text: '""" On some stripped DST one needs to redo the Track<->MC link table. Set to true if problems with association. """'
    value: False
diff --git a/Phys/DaVinci/options/DaVinci-UserAlgs-Example.py b/Phys/DaVinci/options/DaVinci-UserAlgs-Example.py
new file mode 100644
index 000000000..4bab731b3
--- /dev/null
+++ b/Phys/DaVinci/options/DaVinci-UserAlgs-Example.py
@@ -0,0 +1,40 @@
+###############################################################################
+# (c) Copyright 2021 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.                                       #
+#                                                                             #
+###############################################################################
+"""
+ Simple DaVinci example for defining user algorithms
+"""
+__author__ = "Davide Fazzini <davide.fazzini@cern.ch>"
+
+##############################################################################################
+##### SETTINGS
+##############################################################################################
+
+from GaudiConfig2 import Configurables as C
+from PhysConf.Selections import AutomaticData
+
+
+def myfunc():
+    sel = AutomaticData('/Phys/BetaSBd2JpsiKstarDetachedLine/Particles')
+    inputTuple = sel.outputLocation()
+
+    # make a tuple
+    dtt = C.DecayTreeTuple("Bd2JpsiKstar")
+    dtt.Inputs = [inputTuple]
+    dtt.ToolList = ['TupleToolKinematic']
+    dtt.Decay = "[B0 -> ^(J/psi(1S) -> ^mu+ ^mu-) ^(K*(892)0 -> ^K+ ^pi-)]CC"
+
+    evtTuple = C.EventTuple()
+    evtTuple.ToolList += ["TupleToolEventInfo"]
+
+    #listAlg = [evtTuple, dtt]
+    listAlg = []
+    return listAlg
diff --git a/Phys/DaVinci/options/DaVinciDB-Example.yaml b/Phys/DaVinci/options/DaVinciDB-Example.yaml
index 4f360ba5a..85bd9e4d7 100644
--- a/Phys/DaVinci/options/DaVinciDB-Example.yaml
+++ b/Phys/DaVinci/options/DaVinciDB-Example.yaml
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2020-2021 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".   #
diff --git a/Phys/DaVinci/options/jobOptions-Example.py b/Phys/DaVinci/options/jobOptions-Example.py
index 6d4291779..3d32fecd1 100644
--- a/Phys/DaVinci/options/jobOptions-Example.py
+++ b/Phys/DaVinci/options/jobOptions-Example.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2020-2021 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".   #
diff --git a/Phys/DaVinci/options/jobOptions-Example.yaml b/Phys/DaVinci/options/jobOptions-Example.yaml
index aaf8e6812..0c7b6afa6 100644
--- a/Phys/DaVinci/options/jobOptions-Example.yaml
+++ b/Phys/DaVinci/options/jobOptions-Example.yaml
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2020-2021 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".   #
diff --git a/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py b/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py
index a0a95bdec..8d3e81f08 100644
--- a/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py
+++ b/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py
@@ -11,26 +11,93 @@
 """
 High level configuration tools for DaVinci.
 """
+
 from GaudiConfig2 import Configurables as C
 from GaudiConfig2 import mergeConfigs
 
-from DaVinci.configOptions import initialize_slots, check_options, set_file_options, set_job_options
-from DaVinci.algorithms import define_input, define_monitors, define_log, define_root_files, define_services
-from DaVinci.algorithms import define_dv_init_algo, setupAlgorithms
+from DaVinci.configOptions import initialize_slots, check_options, set_file_options, set_job_options, set_args_options
+from DaVinci.algorithms import (define_input, define_monitors, define_log,
+                                define_root_files, define_services,
+                                define_dv_init_algo, setup_algorithms,
+                                define_genfsr_writer)
 from DaVinci.configurations import configure_app_mgr
+from DaVinci.utilities import dump_call, dump_configuration
+
+import click
+#import os
+#os.environ["LC_ALL"] = "en_GB.utf8"
+
+
+@click.option(
+    "--key",
+    default="Upgrade_Bd2KstarMuMu_ldst",
+    help="Key name related to the input files listed in the TestFileDB")
+@click.option(
+    "--dbfile",
+    default="$DAVINCIROOT/options/DaVinciDB-Example.yaml",
+    help="YAML file database containing input file infos")
+@click.option(
+    "--optfile",
+    default="$DAVINCIROOT/options/jobOptions-Example.yaml",
+    help="YAML option file containing the job infos")
+@click.command(
+    context_settings=dict(
+        ignore_unknown_options=True,
+        allow_extra_args=True,
+    ))
+@click.pass_context
+def cli(ctx, key, dbfile, optfile):
+    pass
+
+
+def mc(key="Upgrade_Bd2KstarMuMu_ldst",
+       dbfile="$DAVINCIROOT/options/DaVinciDB-Example.yaml",
+       optfile="$DAVINCIROOT/options/jobOptions-Example.yaml"):
+    """
+    DaVinci function for running job with simulated samples
+    """
+    #dump_call(ctx, key, dbfile, optfile)
+    kwargs = ""
+    #if (len(ctx.args) > 1):
+    #    kwargs = ctx.args
+
+    return main(key, dbfile, optfile, True, kwargs)
+
+
+@click.option(
+    "--key",
+    default="Upgrade_Bd2KstarMuMu_ldst",
+    help="Key name related to the input files listed in the TestFileDB")
+@click.option(
+    "--dbfile",
+    default="$DAVINCIROOT/options/DaVinciDB-Example.yaml",
+    help="YAML file database containing input file infos")
+@click.option(
+    "--optfile",
+    default="$DAVINCIROOT/options/jobOptions-Example.yaml",
+    help="YAML option file containing the job infos")
+@click.command(
+    context_settings=dict(
+        ignore_unknown_options=True,
+        allow_extra_args=True,
+    ))
+@click.pass_context
+def data(ctx, key, dbfile, optfile):
+    """
+    DaVinci function for running job with data samples
+    """
+    raise ValueError(
+        'Data file with upgrade conditions are not yet available. Please use :mc function instead.'
+    )
+    dump_call(ctx, key, dbfile, optfile)
+    kwargs = ""
+    if (len(ctx.args) > 1):
+        kwargs = ctx.args
+    return main(key, dbfile, optfile, False, kwargs)
 
 
-def mc(key='Upgrade_genFSR_ldst', dbFile='$DAVINCIROOT/options/DaVinciDB-Example.yaml', jobOptFile='$DAVINCIROOT/options/jobOptions-Example.yaml'):
-    return main(key, dbFile, jobOptFile, True)
-
-
-def data(key='', dbFile='', jobOptFile=''):
-    raise ValueError('Data file with upgrade conditions are not yet available. Please use :mc function instead.')
-    return main(key, dbFile, jobOptFile, False)
-
-
-def main(key, dbFile, jobOptFile, isMC):
-    """ 
+def main(key, dbFile, jobOptFile, isMC, kwargs):
+    """
     DaVinci application main configuration.
     """
     slots = {}
@@ -41,9 +108,12 @@ def main(key, dbFile, jobOptFile, isMC):
 
     set_file_options(slots, key, dbFile, isMC)
     set_job_options(slots, jobOptFile, key, dbFile)
+    if kwargs:
+        set_args_options(slots, kwargs, key, dbFile)
 
-    check_options(slots)
+    dump_configuration(slots)
 
+    check_options(slots)
     define_input(slots)
 
     config = []
@@ -51,16 +121,18 @@ def main(key, dbFile, jobOptFile, isMC):
     config.append(appMgr)
 
     define_dv_init_algo(config, slots)
-    
+
     define_monitors(config, slots)
 
-    setupAlgorithms(config, slots)
+    setup_algorithms(config, slots)
 
     define_services(config, slots)
 
     define_root_files(config, slots)
 
-    configure_app_mgr(appMgr, slots, config)
+    define_genfsr_writer(config, slots)
+
+    configure_app_mgr(config, slots, appMgr)
 
     config = mergeConfigs(config)
     return config
diff --git a/Phys/DaVinci/python/DaVinci/algorithms.py b/Phys/DaVinci/python/DaVinci/algorithms.py
index 6c0009ebb..180056e65 100644
--- a/Phys/DaVinci/python/DaVinci/algorithms.py
+++ b/Phys/DaVinci/python/DaVinci/algorithms.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020-20201 CERN for the benefit of the LHCb Collaboration     #
+# (c) Copyright 2020-2021 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".   #
@@ -11,7 +11,8 @@
 
 from GaudiConfig2 import Configurables as C
 from DaVinci.configOptions import get_slot_value, set_slot_value
-from DaVinci.configurations import * 
+from DaVinci.optionChecker import DVOptionError, DVRuntimeError
+from DaVinci.configurations import *
 
 
 def define_dv_init_algo(config, slots):
@@ -19,7 +20,7 @@ def define_dv_init_algo(config, slots):
     Define the DaVinci initialisation algorithm.
     """
     dvInit = C.DaVinciInit("DaVinciInitAlg")
-    configure_init(dvInit, slots)
+    configure_init(slots, dvInit)
     config.append(dvInit)
 
 
@@ -28,44 +29,99 @@ def define_input(slots):
     Define the input files via the IOHelper.
     """
     from GaudiConf import IOHelper
-    
+
     input = get_slot_value(slots, "Input")
-    
-    if ( len(input) > 0 ):    
+
+    if (len(input) > 0):
         persistency = None
         inputType = get_slot_value(slots, "InputType").upper()
-        if inputType == "MDF" : persistency = "MDF" 
+        if inputType == "MDF": persistency = "MDF"
         # Support connection strings and lists of files
-        input = IOHelper(persistency, persistency).convertConnectionStrings(input, "I")
+        input = IOHelper(persistency, persistency).convertConnectionStrings(
+            input, "I")
         # Clear selector to maintain the same behaviour
-        IOHelper(persistency, persistency).inputFiles(input, clear = True)
+        IOHelper(persistency, persistency).inputFiles(input, clear=True)
     else:
         log = get_slot_value(slots, "Log")
-        log.warning("No input files available along with the selected key. Check the related DaVinciDB.")
+        log.warning(
+            "No input files available along with the selected key. Check the related DaVinciDB."
+        )
 
 
 def define_monitors(config, slots):
     """
-    Define the monitors - AuditorSvc, EventSelector, SequencerTimerTool, TimingAuditor.
+    Define the monitors:
+
+    - AuditorSvc
+    - EventSelector
+    - SequencerTimerTool
+    - TimingAuditor
+    - EventClock.
     """
     auditor = C.AuditorSvc()
     timer = C.TimingAuditor()
     sequencer = C.SequencerTimerTool()
-    configure_timing(auditor, timer, sequencer)
-    
+    configure_timing(slots, auditor, sequencer, timer)
+
     evtSel = C.EventSelector()
-    configure_event_selector(evtSel, slots)
-    
-    config.extend([auditor, timer, sequencer, evtSel])
+    configure_event_selector(slots, evtSel)
 
+    evtTime = C.EventClockSvc(EventTimeDecoder="OdinTimeDecoder")
+    config.extend([auditor, timer, sequencer, evtSel])  #, evtTime])
 
-def setupAlgorithms(config, slots):
+
+def setup_algorithms(config, slots):
     """
     Set DaVinci algorithms
     """
-    algorithms = [C.GaudiHistoAlgorithm("SimpleHistos",
-                                        HistoPrint=True,
-                                        OutputLevel=get_slot_value(slots, "OutputLevel"))]
+    import importlib, sys, os
+    stdAlgorithms = [
+        C.GaudiHistoAlgorithm(
+            "SimpleHistos",
+            HistoPrint=True,
+            OutputLevel=get_slot_value(slots, "OutputLevel"))
+    ]
+
+    fileAlg = get_slot_value(slots, 'UserAlgorithmFile')
+    funcName = get_slot_value(slots, 'UserAlgorithm')
+
+    modulePath = fileAlg.rsplit('/', 1)[0]
+    moduleName = fileAlg.rsplit('/', 1)[1]
+
+    modulePath = os.path.expandvars(modulePath)
+    _, ext = os.path.splitext(moduleName)
+    if ext == ".py":
+        moduleName = moduleName.rsplit('.', 1)[0]
+
+    sys.path.append(modulePath)
+    log = get_slot_value(slots, "Log")
+
+    if moduleName and funcName:
+        try:
+            module = importlib.import_module(moduleName)
+        except:
+            raise DVOptionError(
+                "UserAlgorithmFile",
+                "Importing user algorithms failed. Check if the user python module %s defined in %s exists!"
+                % (moduleName, modulePath))
+            algorithms = stdAlgorithms
+        else:
+            try:
+                userAlgorithms = eval("module.%s()" % funcName)
+                log.info("User algorithm %s.%s imported successfully!" %
+                         (moduleName, funcName))
+                algorithms = stdAlgorithms + userAlgorithms
+            except:
+                raise DVRuntimeError(
+                    "UserAlgorithmFile", "UserAlgorithm",
+                    "Run time error when calling the user algorithm. User algorithm %s can not be imported!"
+                    % funcName)
+                algorithms = stdAlgorithms
+    else:
+        log.warning(
+            "DV option file or main function not defined. No user algorithms will be used."
+        )
+        algorithms = stdAlgorithms
 
     config.extend(algorithms)
 
@@ -73,33 +129,49 @@ def setupAlgorithms(config, slots):
 def define_services(config, slots):
     """
     Define standard services:
-    
-    - DataOnDemandSvc
-    - DetDataSvc
+
+    - AlgContextSvc
+    - EvtDataSvc
     - EvtPersistencySvc
+    - FileRecordDataSvc
     - IODataManager
     - LoKiSvc
     - MultiFileCatalog
     - RootCnvSvc
     - ToolSvc
+    - XmlCnvSvc
+    - XmlParserSvc
     """
-    lokiSvc = C.LoKiSvc()
-    dataSvc = C.DataOnDemandSvc()
-    configure_printouts(lokiSvc, dataSvc, slots)
- 
+
+    evtSvc = C.EvtDataSvc()
     evtPers = C.EvtPersistencySvc("EventPersistencySvc")
-    configure_event_persistency(evtPers)
-
-    extSvc = [lokiSvc,
-              dataSvc,
-              evtPers,
-              C.ToolSvc(),
-              C.DetDataSvc("DetectorDataSvc"),
-              C.Gaudi.MultiFileCatalog("FileCatalog"),
-              C.Gaudi.IODataManager("IODataManager"),
-              C.Gaudi.RootCnvSvc("FileRecordCnvSvc"),
-          ]
- 
+    configure_event(evtSvc, evtPers)
+
+    algSvc = C.AlgContextSvc()
+    algSvc.BypassIncidents = True
+
+    rootSvc = C.Gaudi.RootCnvSvc("FileRecordCnvSvc")
+    fileDataSvc = C.FileRecordDataSvc()
+    configure_file_record_data(rootSvc, fileDataSvc)
+
+    xmlSvc = C.XmlCnvSvc()
+    xmlParser = C.XmlParserSvc()
+    configure_xml(xmlSvc, xmlParser)
+
+    extSvc = [
+        evtSvc,
+        evtPers,
+        algSvc,
+        fileDataSvc,
+        rootSvc,
+        xmlSvc,
+        C.LoKiSvc(),
+        C.ToolSvc(),
+        C.Gaudi.Monitoring.MessageSvcSink(),
+        C.Gaudi.MultiFileCatalog("FileCatalog"),
+        C.Gaudi.IODataManager("IODataManager"),
+    ]
+
     config.extend(extSvc)
 
 
@@ -113,16 +185,18 @@ def define_root_files(config, slots):
     from GaudiKernel.Configurable import ConfigurableGeneric as RFileCnv
 
     if get_slot_value(slots, "HistogramFile"):
-        histoSvc = C.HistogramPersistencySvc(OutputFile = get_slot_value(slots, "HistogramFile"))
+        histoSvc = C.HistogramPersistencySvc(
+            OutputFile=get_slot_value(slots, "HistogramFile"))
         config.append(histoSvc)
 
     if get_slot_value(slots, "TupleFile"):
         ntSvc = C.NTupleSvc()
-        configure_options_root_ntuples(ntSvc, slots)
+        configure_options_root_ntuples(slots, ntSvc)
         config.append(ntSvc)
 
     # Set the compression level for the ROOT tuple file
-    RFileCnv("RFileCnv").GlobalCompression = get_slot_value(slots, "RootCompressionLevel")
+    RFileCnv("RFileCnv").GlobalCompression = get_slot_value(
+        slots, "RootCompressionLevel")
 
 
 def define_log(slots):
@@ -131,6 +205,17 @@ def define_log(slots):
     """
     from AnalysisPython.Logger import getLogger
 
-    log = getLogger ("DaVinci")
-    log.info("Applying DaVinci configuration")
+    log = getLogger("DaVinci")
     set_slot_value(slots, "Log", log)
+
+
+def define_genfsr_writer(config, slots):
+    """
+    Define Generator FSR writer
+    """
+    if get_slot_value(slots, "MergeGenFSR"):
+        config.append(C.GenFSRMerge())
+    config.append(
+        C.RecordStream(
+            "FSROutputStreamDstWriter",
+            OutputLevel=get_slot_value(slots, "OutputLevel")))
diff --git a/Phys/DaVinci/python/DaVinci/configOptions.py b/Phys/DaVinci/python/DaVinci/configOptions.py
index 24a7fd669..bb0a7a9b8 100644
--- a/Phys/DaVinci/python/DaVinci/configOptions.py
+++ b/Phys/DaVinci/python/DaVinci/configOptions.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2020-2021 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".   #
@@ -8,39 +8,49 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 ###############################################################################
-
 """
 Set and retrieve the value of a specific slot property.
 """
 
-import os
+import os, difflib
+from DaVinci.optionChecker import DVOptionError, DVRuntimeError, option_checker
 
 
 def get_slot_value(slots, name):
-    return slots[name]
+    if if_slot_exists(slots, name):
+        return slots[name]
+    else:
+        return None
 
 
 def set_slot_value(slots, name, value):
-    if name in slots:
+    if if_slot_exists(slots, name):
         slots[name] = value
+
+
+def if_slot_exists(slots, name):
+    if name in slots:
+        return True
     else:
-        import difflib
-        raise ValueError("Unknown qualifier called %s. Please check the DaVinci database or the job option file!\n "
-                         "List of possible valid qualifiers: %s" %(name, difflib.get_close_matches(name, slots)))
+        raise DVOptionError(
+            name,
+            "Unknown qualifier called %s. Please check the DaVinci database or the job option file!\nList of possible valid qualifiers: %s"
+            % (name, difflib.get_close_matches(name, slots)))
+        return False
 
 
-def get_default_value(myKey):
+def get_default_value(inputKey):
     """
     Get the default value for a specific property.
     """
     import yaml
-    
+
     optsDefaultName = "$DAVINCIROOT/options/DVconfig-Default.yaml"
     with open(os.path.expandvars(optsDefaultName)) as optsDefault:
         config = yaml.safe_load(optsDefault)
 
     for key, args in config.items():
-        if key == myKey:
+        if key == inputKey:
             for name, value in args.items():
                 if name == 'value':
                     return value
@@ -65,7 +75,7 @@ def initialize_slots(slots, propDct):
                 propDct[key] = value
 
 
-def set_file_options(slots, myKey, dbName, isMC):
+def set_file_options(slots, inputKey, dbName, isMC):
     """
     Set the dataset properties required by the user.
     """
@@ -74,12 +84,12 @@ def set_file_options(slots, myKey, dbName, isMC):
         dataDV = yaml.safe_load(dbFile)
 
     idxFile = -1
-    if ":" in myKey:
-        idxFile = int(myKey.split(":")[1])
-        myKey = myKey.split(":")[0]
-    
+    if ":" in inputKey:
+        idxFile = int(inputKey.split(":")[1])
+        inputKey = inputKey.split(":")[0]
+
     for key, config in dataDV.items():
-        if key == myKey:
+        if key == inputKey:
             for item, obj in config.items():
                 if item == 'qualifiers':
                     for prop, value in obj.items():
@@ -89,52 +99,76 @@ def set_file_options(slots, myKey, dbName, isMC):
                         if idxFile < len(obj):
                             set_slot_value(slots, "Input", [obj[idxFile]])
                         else:
-                            raise ValueError('Index file requested exceeds the number of available files related to the given key!')
+                            raise ValueError(
+                                'Index file exceeds the number of available files related to the given key!'
+                            )
                     else:
                         set_slot_value(slots, "Input", obj)
 
 
-def set_job_options(slots, configName, myKey, dbName):
+def set_job_options(slots, configName, inputKey, dbName):
     """
     Set the job properties required by the user.
     """
     log = get_slot_value(slots, "Log")
-    if configName == '': 
+    if configName == '':
         log.warning('No jobOption file selected, the default values are used.')
     else:
-        dataOptions = list_data_options(myKey.split(":")[0], dbName)
-        
+        dataOptions = list_data_options(inputKey.split(":")[0], dbName)
+
         with open(os.path.expandvars(configName)) as config_file:
-            """
-            if configName.endswith('.json'):
-                import json
-                config = json.load(config_file)
-            elif configName.endswith('.yaml') or configName.endswith('.yml'):
-            """
             _, ext = os.path.splitext(configName)
             if ext in (".yaml", ".yml", ".json"):
                 import yaml
                 config = yaml.safe_load(config_file)
-            elif configName.endswith('.py'):
+            elif ext == '.py':
                 import ast
                 config = ast.literal_eval(config_file.read())
             else:
-                raise ValueError('JobOption file extension not known! Please use only a .py, .json or .yaml (.yml) file!')
-                
+                raise ValueError(
+                    'JobOption file extension not known! Please use only a .py, .json or .yaml (.yml) file!'
+                )
+
             for key, value in config.items():
-                if key in dataOptions:
-                    if get_slot_value(slots, "OverwriteDataOptions"):
-                        log.warning('Default value for option %s is taken from DaVinciDB but a new value is found in option file. '
-                                    'Since "OverwriteDataOptions" is active the new value is used. Make sure this is the configuration you want to use for this job.' %key)
-                        set_slot_value(slots, key, value)
-                    else:
-                        log.warning('Default value for option %s is taken from DaVinciDB but a new value is found in option file. '
-                                    'Since "OverwriteDataOptions" is not active the default value is used. Make sure this is the configuration you want to use for this job.' %key)
-                else:
+                if is_option_settable(slots, key, dataOptions):
                     set_slot_value(slots, key, value)
 
 
-def list_data_options(myKey, dbName):
+def set_args_options(slots, kwargs, inputKey, dbName):
+    """
+    Set the extra arguments required by the user.
+    """
+    dataOptions = list_data_options(inputKey.split(":")[0], dbName)
+    for i in range(1, len(kwargs), 2):
+        key = kwargs[i][2:]
+        value = kwargs[i + 1]
+
+        if is_option_settable(slots, key, dataOptions):
+            set_slot_value(slots, key, value)
+
+
+def is_option_settable(slots, key, dataOptions):
+    """
+    Check if the current option 'key' can be set corretly
+    """
+    log = get_slot_value(slots, "Log")
+    if key in dataOptions:
+        if get_slot_value(slots, "OverwriteDataOptions"):
+            log.info(
+                "New value found for the option %s in the job option file. 'OverwriteDataOptions' is active so the default value will be overwritten."
+                % key)
+            return True
+        else:
+            raise DVRuntimeError(
+                key, "OverwriteDataOptions",
+                "Default value for option %s is taken from DaVinciDB.\nA new value is found in the job option file but 'OverwriteDataOptions' is not active so the default value can't be overwritten!"
+                % key)
+            return False
+    else:
+        return True
+
+
+def list_data_options(inputKey, dbName):
     """
     List of the properties that are set automatically given a dataset.
     """
@@ -145,7 +179,7 @@ def list_data_options(myKey, dbName):
 
     optionList = []
     for key, config in dataDV.items():
-        if key == myKey:
+        if key == inputKey:
             for item, obj in config.items():
                 if item == 'qualifiers':
                     for prop, value in obj.items():
@@ -158,36 +192,45 @@ def check_options(slots):
     """
     Check the options. Applies changes if needed.
     """
-    log = get_slot_value(slots, "Log")
-    known_datatypes = ["Upgrade"]
-    known_default_detectors = ['VP', 'UT', 'FT', 'Rich1Pmt', 'Rich2Pmt', 'Ecal', 'Hcal', 'Muon', 'Magnet', 'Tr' ]
-
-    dataType = get_slot_value(slots,"DataType")
-    if (not dataType):
-        raise TypeError( "You must set DataType" )
-    if dataType not in known_datatypes:
-        raise TypeError( "Invalid DataType '%s'" %dataType )
+    dataType = get_slot_value(slots, "DataType")
+    option_checker("DataType", dataType)
 
     inputType = get_slot_value(slots, "InputType").upper()
-    if inputType not in [ "MDF", "DST", "DIGI", "RDST", "MDST", "SDST", "XDST", "LDST" ]:
-        raise TypeError( "Invalid inputType '%s'"%inputType )
-
-    if get_slot_value(slots, "Simulation") and not inputType in ( "MDF" , "DIGI" , "MDST" ):
-        redo = get_slot_value(slots, "RedoMCLinks")
-        if (inputType == "RDST")  and (redo):
-            log.warning("Re-doing MC links not possible for RDST")
-            set_slot_value(slots, "RedoMCLinks", False)
-
-    if get_slot_value(slots, "Simulation") and get_slot_value(slots,'Lumi'):
-        log.warning('Lumi not valid for Simulation. Setting Lumi = False')
-        set_slot_value(slots, 'Lumi', False)
-
-    ## for simulation, it is very important to specify proper DB-tags:
-    if get_slot_value(slots, 'Simulation'):
-        if not get_slot_value(slots,'DDDBtag'):
-            log.warning("``DDDBtag''   is not specified for simulated data")
+    option_checker("InputType", inputType)
+
+    redoLink = get_slot_value(slots, "RedoMCLinks")
+    isMC = get_slot_value(slots, "Simulation")
+
+    if not isMC:
+        if redoLink:
+            raise DVRuntimeError(
+                "RedoMCLinks", "Simulation",
+                "Re-doing MC links not possible for data! Set RedoMCLinks = False."
+            )
+
+        if get_slot_value(slots, "MergeGenFSR"):
+            raise DVRuntimeError(
+                "MergeGenFSR", "Simulation",
+                "GenFSR are not available in real data so MergeGenFSR cannot be run! Set MergeGenFSR = False."
+            )
+    else:
+        if redoLink and inputType("MDF", "DIGI", "MDST", "RDST"):
+            raise DVRuntimeError(
+                "RedoMCLinks", "InputType",
+                "Re-doing MC links not possible for %s input type! Set RedoMCLinks = False."
+                % inputType)
+
+        if get_slot_value(slots, "Lumi"):
+            raise DVRuntimeError(
+                "Lumi", "Simulation",
+                "Lumi not valid for Simulation! Set Lumi = False.")
+
+        ## for simulation, it is very important to specify proper DB-tags:
+        if not get_slot_value(slots, 'DDDBtag'):
+            raise DVOptionError(
+                "DDDBtag",
+                "``DDDBtag''   is not specified for simulated data!", "")
         if not get_slot_value(slots, 'CondDBtag'):
-            log.warning("``CondDBtag'' is not specified for simulated data")
-
-    if get_slot_value(slots, "MergeGenFSR") and not get_slot_value(slots, "Simulation"):
-        raise TypeError("Cannot MergeGenFSR on real data");
+            raise DVOptionError(
+                "CondDBtag",
+                "``CondDBtag'' is not specified for simulated data!", "")
diff --git a/Phys/DaVinci/python/DaVinci/configurations.py b/Phys/DaVinci/python/DaVinci/configurations.py
index 7048daa80..a1b3572b7 100644
--- a/Phys/DaVinci/python/DaVinci/configurations.py
+++ b/Phys/DaVinci/python/DaVinci/configurations.py
@@ -15,7 +15,7 @@ Define configurations for applications, algorithms and external services.
 from DaVinci.configOptions import get_slot_value, get_default_value
 
 
-def configure_app_mgr(appMgr, slots, config):
+def configure_app_mgr(config, slots, appMgr):
     """
     Configuration of the main application.
     """
@@ -28,6 +28,8 @@ def configure_app_mgr(appMgr, slots, config):
     appMgr.HistogramPersistency = 'ROOT'
     appMgr.AuditAlgorithms = True
 
+    propagate_properties_to_lhcb(config, slots)
+
     appMgr.ExtSvc = [
         c for c in config if c.__component_type__ == 'Service'
         and c.name not in ['ApplicationMgr']
@@ -38,24 +40,29 @@ def configure_app_mgr(appMgr, slots, config):
     ]
 
 
-def configure_init(dvInit, slots):
+def configure_init(slots, dvInit):
     """
     Configuration of initialisation algorithm.
     """
     dvInit.Increment = get_slot_value(slots, "PrintFreq")
 
 
-def configure_timing(auditor, timer, sequencer):
+################################################################################
+# Define configurations for external services
+#
+
+
+def configure_timing(slots, auditor, sequencer, timer):
     """
     Configuration of timing auditors.
     """
     auditor.Auditors = ['ChronoAuditor', 'TimingAuditor']
-    sequencer.OutputLevel = 4
+    sequencer.OutputLevel = get_slot_value(slots, "OutputLevel")
 
 
-def configure_options_root_ntuples(svc, slots):
+def configure_options_root_ntuples(slots, svc):
     """
-    Configuration of output .root ntuples
+    Configuration of output .root ntuples.
     """
     for _line in svc.Output:
         if 0 <= _line.find('FILE1'):
@@ -65,26 +72,20 @@ def configure_options_root_ntuples(svc, slots):
     tupleFile = get_slot_value(slots, "TupleFile")
     tupleStr = "FILE1 DATAFILE='%s' TYP='ROOT' OPT='NEW'" % tupleFile
     svc.Output += [tupleStr]
-    svc.OutputLevel = 1
+    svc.OutputLevel = get_slot_value(slots, "OutputLevel")
 
 
-def configure_event_persistency(svc):
+def configure_event(evtSvc, svc):
     """
-    Configuration of the event persistency service
+    Configuration of the event services.
     """
+    evtSvc.EnableFaultHandler = True
+    evtSvc.ForceLeaves = True
+    evtSvc.RootCLID = 1
     svc.CnvServices.append("Gaudi::RootCnvSvc/RootCnvSvc")
 
 
-def configure_printouts(lokiSvc, dataSvc, slots):
-    """
-    Configuration of the printout services.
-    """
-    verbosePrint = get_slot_value(slots, "VerboseMessages")
-    lokiSvc.Welcome = verbosePrint
-    dataSvc.Dump = verbosePrint
-
-
-def configure_event_selector(svc, slots):
+def configure_event_selector(slots, svc):
     """
     Configuration of the event selector.
     """
@@ -94,3 +95,99 @@ def configure_event_selector(svc, slots):
         log = get_slot_value(slots, "Log")
         log.warning("Print frequency cannot be 0. Set to %f." % printFreq)
     svc.PrintFreq = printFreq
+
+
+#def configure_detector(detSvc, detPers):
+#    """
+#    Configure Detector services.
+#    """
+#    detSvc.DetDbLocation = "git:/lhcb.xml"
+#    detSvc.DetDbRootName = "dd"
+#    detSvc.DetStorageType = 7
+#    detSvc.UsePersistency = True
+
+#    detPers.CnvServices = ["XmlCnvSvc/XmlCnvSvc"]
+
+
+def configure_file_record_data(rootSvc, fileDataSvc):
+    """
+    Configure File Record Data service.
+    """
+    rootSvc.CacheBranches = []
+    rootSvc.EnableIncident = True
+    rootSvc.VetoBranches = ["*"]
+
+    fileDataSvc.EnableFaultHandler = True
+    fileDataSvc.ForceLeaves = True
+    fileDataSvc.PersistencySvc = "PersistencySvc/FileRecordPersistencySvc"
+    fileDataSvc.RootCLID = 1
+
+
+def configure_xml(xmlSvc, xmlParser):
+    """
+    Configure xml services.
+    """
+    xmlSvc.AllowGenericConversion = True
+
+    xmlParser.CacheBehavior = 3
+    xmlParser.EntityResolver = "EntityResolverDispatcher/EntityResolverDispatcher"
+    xmlParser.MaxDocNbInCache = 15
+
+
+################################################################################
+# Define configurations for MC algorithms
+#
+
+
+def configure_MC_algorithms(slots, dataSvc):
+    """
+    Configure DaVinciAssociators and do MC unpacking.
+    """
+    from CaloKernel.ConfUtils import getAlgo
+
+    root = ""
+    if get_slot_value(slots, "InputType") == "MDST" and get_slot_value(
+            slots, "RootInTES"):
+        root = get_slot_value(slots, "RootInTES")
+    else:
+        root = "/Event"
+
+    configure_MC_unpacking(slots, dataSvc, root)
+
+
+def configure_MC_unpacking(slots, dataSvc, root):
+    """
+    Configure the unpacking of MC particles.
+    """
+    import os
+
+    log = get_slot_value(slots, "Log")
+    mcRoot = os.path.join(root, "MC")
+    log.info("Will unpack MC objects to {0}".format(mcRoot))
+
+    particlesOutput = os.path.join(mcRoot, "Particles")
+    verticesOutput = os.path.join(mcRoot, "Vertices")
+
+    samba = C.UnpackMCParticle("UnpackMCParticle", RootInTES=root)
+    dataSvc.NodeMap[mcRoot] = "DataObject"
+    #dataSvc.AlgMap[particlesOutput] = C.UnpackMCParticle("UnpackMCParticle", RootInTES=root)
+    #dataSvc.AlgMap[verticesOutput] = C.UnpackMCVertex("UnpackMCVertex", RootInTES=root)
+
+
+def propagate_properties_to_lhcb(config, slots):
+    """
+    Define and configure LHCbApp with the relevant DV properties.
+    """
+    from Configurables import LHCbApp
+
+    lhcbApp = LHCbApp()
+    properties = [
+        "EvtMax", "SkipEvents", "DataType", "CondDBtag", "DDDBtag",
+        "DQFLAGStag", "Simulation", "IgnoreDQFlags"
+    ]
+
+    for prop in properties:
+        if get_slot_value(slots, prop):
+            lhcbApp.setProp(prop, get_slot_value(slots, prop))
+
+    #config.append(lhcbApp)
diff --git a/Phys/DaVinci/python/DaVinci/optionChecker.py b/Phys/DaVinci/python/DaVinci/optionChecker.py
new file mode 100644
index 000000000..89dbb4f18
--- /dev/null
+++ b/Phys/DaVinci/python/DaVinci/optionChecker.py
@@ -0,0 +1,107 @@
+###############################################################################
+# (c) Copyright 2020-2021 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.                                       #
+###############################################################################
+"""
+Define value and run-time errors used in DaVinci application.
+"""
+
+
+class DVOptionError(ValueError):
+    """
+    DaVinci exception class for invalid option configurations.
+    """
+
+    def __init__(self, name, value):
+        """
+        Constructor.
+        """
+        self.message = "invalid DaVinci %s option value specified: %s" % (
+            name, value)
+
+    def __str__(self):
+        """
+        String representation.
+        """
+        return (set_color("red") + self.message + set_color("plain"))
+
+
+class DVRuntimeError(RuntimeError):
+    """
+    DaVinci exception class for inconsistent option configurations.
+    """
+
+    def __init__(self, option1, option2, value):
+        """
+        Constructor.
+        """
+        self.message = "Option %s and %s are inconsistent! \n %s" % (
+            option1, option2, value)
+
+    def __str__(self):
+        """
+        String representation.
+        """
+        return (set_color("red") + self.message + set_color("plain"))
+
+
+def option_checker(name, value):
+    """
+    Check the correctness of a DaVinci configurable option.
+    """
+    try:
+        allowedValues = get_allowed_option_values()
+        if value not in allowedValues[name]:
+            print_allowed_option_values(allowedValues, name)
+            raise DVOptionError(name, value)
+    except KeyError:
+        print set_color(
+            "red"
+        ) + "You are trying to check the unknown option %s!" % name + set_color(
+            "plain")
+        print_allowed_option_values()
+
+
+def get_allowed_option_values():
+    allowedValues = {
+        "DataType": ["Upgrade"],
+        "Detectors": [
+            'VP', 'UT', 'FT', 'Rich1Pmt', 'Rich2Pmt', 'Ecal', 'Hcal', 'Muon',
+            'Magnet', 'Tr'
+        ],
+        "InputType":
+        ["MDF", "DST", "DIGI", "RDST", "MDST", "SDST", "XDST", "LDST"]
+    }
+
+    return allowedValues
+
+
+def print_allowed_option_values(allowedValues, name=None):
+    """
+    Print the allowed values for the DaVinci option configurations.
+    """
+    if name == None:
+        print set_color(
+            "green") + "Known job option configurations and allowed values:"
+        for name, values in allowedValues.iteritems():
+            print "%s \t : %s" % (name, value), set_color("plain")
+    else:
+        print set_color(
+            "green") + "Allowed values for DaVinci option %s:" % name
+        print allowedValues[name], set_color("plain")
+
+
+def set_color(key):
+    colors = {
+        "plain": "\x1b[00m",
+        "red": "\x1b[01;31m",
+        "green": "\x1b[01;32m"
+    }
+
+    return colors[key]
diff --git a/Phys/DaVinci/python/DaVinci/utilities.py b/Phys/DaVinci/python/DaVinci/utilities.py
new file mode 100644
index 000000000..a0e4e06c2
--- /dev/null
+++ b/Phys/DaVinci/python/DaVinci/utilities.py
@@ -0,0 +1,88 @@
+###############################################################################
+# (c) Copyright 2020-2021 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.                                       #
+###############################################################################
+
+from GaudiConfig2 import Configurables as C
+from DaVinci.configOptions import get_slot_value
+import os, click
+
+
+def dump_call(ctx, key, dbfile, optfile):
+    indentStr = "# "
+    click.echo("%sDaVinci: running using the following arguments:" % indentStr)
+    click.echo("%s         - key:     {key}" % indentStr)
+    click.echo("%s         - dbfile:  {dbfile}" % indentStr)
+    click.echo("%s         - optfile: {optfile}" % indentStr)
+
+    if len(ctx.args) > 1:
+        for i in range(1, len(ctx.args), 2):
+            click.echo("%s         - %s: %s" % (indentStr, ctx.args[i][2:],
+                                                ctx.args[i + 1]))
+
+
+def dump_configuration(slots):
+    """
+    Dump the DaVinci Configuration.
+    """
+    indentStr = "# "
+    appName = "DaVinci"
+    preLen = 10
+    headerWidth = 100
+
+    log = get_slot_value(slots, "Log")
+
+    header = "Applying %s configuration" % appName
+    dump = indentStr + header + os.linesep
+
+    title = "User %s/%s" % (appName, appName)
+    postLen = get_post_length(title, preLen, headerWidth)
+
+    dump += "%s%s %s %s" % (indentStr, preLen * '*', title,
+                            postLen * '*') + os.linesep
+    dump += get_prop_infos(slots, indentStr)
+
+    footer = "(End of User %s/%s)" % (appName, appName)
+    postLen = get_post_length(footer, preLen, headerWidth)
+    dump += "%s%s %s %s" % (indentStr, preLen * '-', footer, postLen * '-')
+    log.info(dump)
+
+
+def get_prop_infos(slots, indentStr):
+    """
+    Dump the DV configuration with the name and value of each property.
+    """
+    nameWidth = get_name_width(slots)
+    config = ""
+
+    for prop in slots:
+        prefix = "%s|-%s" % (indentStr, prop.ljust(nameWidth))
+        value = get_slot_value(slots, prop)
+        line = "%s = %s" % (prefix, repr(value))
+        config += line + os.linesep
+    return config
+
+
+def get_name_width(slots):
+    """
+    Get the maximum length of the property names.
+    """
+    nameWidth = 0
+    for slot in slots:
+        nameWidth = max(nameWidth, len(slot))
+    return nameWidth
+
+
+def get_post_length(name, preLen, width):
+    """
+    Get the number of remaining blank spaces after 'name' given the specific width.
+    """
+    postLen = width - preLen - len(name)
+    postLen = max(preLen, postLen)
+    return postLen
-- 
GitLab