From bcb85aff01228d0afe2205939a69775a23418413 Mon Sep 17 00:00:00 2001
From: Chris Burr <christopher.burr@cern.ch>
Date: Fri, 3 Jun 2022 22:11:46 +0200
Subject: [PATCH] Initial lbexec support

---
 ...-tupling-AllFunctors.py => AllFunctors.py} | 18 ++++-------
 .../tupling/example-tupling-DTF-filtered.py   |  3 +-
 .../tupling/option_davinci_configFuntuple.py  |  2 +-
 .../option_davinci_tupling_array_taggers.py   |  3 +-
 ...option_davinci_tupling_from_collections.py |  3 +-
 .../option_davinci_tupling_from_hlt2.py       |  3 +-
 ...tion_davinci_tupling_from_hlt2_gaudirun.py |  3 +-
 ...option_davinci_tupling_from_passthrough.py |  3 +-
 .../option_davinci_tupling_from_spruce.py     |  5 ++--
 .../option_davinci_tupling_from_spruce_mc.py  |  3 +-
 .../tupling.qms/test_davinci_tupling_All.qmt  | 28 ++++++++++++-----
 DaVinciSys/tests/test_davinci_script.py       |  4 +--
 .../tests/options/option_davinci_filters.py   |  4 +--
 .../options/option_davinci_funtuple_array.py  |  2 +-
 .../options/option_davinci_recVertices.py     |  2 +-
 .../tests/options/option_davinci_sprucing.py  |  3 +-
 .../python/tutorial0_basic_DVjob.py           |  3 +-
 .../python/tutorial1_functors_specialfield.py |  3 +-
 DaVinciTutorials/python/tutorial2_LoKi.py     |  3 +-
 .../python/tutorial3_ThOrfunctors.py          |  3 +-
 .../python/tutorial4_trigger_eventinfo.py     |  3 +-
 DaVinciTutorials/python/tutorial5_MCTruth.py  |  3 +-
 .../python/tutorial6_DecayTreeFit.py          |  3 +-
 .../python/tutorial7_multiple_sel_lines.py    |  6 ++--
 Phys/DaVinci/python/DaVinci/Configuration.py  | 20 +++++++------
 Phys/DaVinci/python/DaVinci/LbExec.py         | 30 +++++++++++++++++++
 Phys/DaVinci/python/DaVinci/__init__.py       |  7 ++---
 Phys/DaVinci/python/DaVinci/algorithms.py     | 19 ++++++------
 Phys/DaVinci/python/DaVinci/config.py         |  9 ++++++
 Phys/DaVinci/python/DaVinci/configOptions.py  |  4 ++-
 Phys/DaVinci/tests/config/test_algorithms.py  | 12 ++++----
 31 files changed, 140 insertions(+), 77 deletions(-)
 rename DaVinciExamples/python/DaVinciExamples/tupling/{example-tupling-AllFunctors.py => AllFunctors.py} (94%)
 create mode 100644 Phys/DaVinci/python/DaVinci/LbExec.py

diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-AllFunctors.py b/DaVinciExamples/python/DaVinciExamples/tupling/AllFunctors.py
similarity index 94%
rename from DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-AllFunctors.py
rename to DaVinciExamples/python/DaVinciExamples/tupling/AllFunctors.py
index 6c62d8476..f4b2db28d 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-AllFunctors.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/AllFunctors.py
@@ -10,9 +10,6 @@
 ###############################################################################
 """
 Example of a DaVinci job filling all available functors. This is obviously a stress test and not realistic.
-
- This example is meant to be run with
-    $ ./run davinci --inputfiledb Spruce_all_lines_dst Phys/DaVinci/options/DaVinciDB-Example.yaml --joboptfile DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.yaml --user_algorithms  DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-AllFunctors:alg_config --evt_max 100 |& cat | tee log
 """
 __author__ = "P. Koppenburg"
 __date__ = "2021-11-23"
@@ -27,7 +24,7 @@ from DecayTreeFitter import DTFAlg
 from DaVinci.truth_matching import configured_MCTruthAndBkgCatAlg
 from PyConf.Algorithms import PrintDecayTree
 
-from DaVinci import options
+from DaVinci import make_config
 
 #
 # Definition of strucing line
@@ -39,9 +36,6 @@ _basic = 'basic'
 _composite = 'composite'
 _toplevel = 'toplevel'
 
-options.ntuple_file = "DV_example_allFunctors_ntp.root"
-options.histo_file = "DV_example_allFunctors_his.root"
-
 
 def all_variables(pvs, DTFR, mctruth, ptype):
     """
@@ -255,7 +249,7 @@ def event_variables(PVs, ODIN, decreports):
     return evt_vars
 
 
-def alg_config():
+def alg_config(options):
     """
     Algorithm configuration function called from the comad line
     """
@@ -312,7 +306,8 @@ def alg_config():
     #
     # Sprucing filter
     #
-    my_filter = add_filter("HDRFilter_B0DsK", f"HLT_PASS('{bd2dsk_line}')")
+    my_filter = add_filter(options, "HDRFilter_B0DsK",
+                           f"HLT_PASS('{bd2dsk_line}')")
 
     #
     # FunTuple
@@ -329,6 +324,5 @@ def alg_config():
     #
     # Algorithms to be run
     #
-    return {
-        "UserAlgs": [PrintDecayTree(Input=bd2dsk_data), my_filter, my_tuple]
-    }, []
+    return make_config(
+        options, [PrintDecayTree(Input=bd2dsk_data), my_filter, my_tuple])
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-DTF-filtered.py b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-DTF-filtered.py
index cd6109fa8..7df6c01a3 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-DTF-filtered.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-DTF-filtered.py
@@ -21,6 +21,7 @@ Example of a typical DaVinci job:
 
 import Functors as F
 from Gaudi.Configuration import INFO
+from DaVinci import options
 from DaVinci.algorithms import filter_on, add_filter
 from DecayTreeFitter import DTFAlg
 from FunTuple import FunctorCollection
@@ -59,7 +60,7 @@ variables_ds = FunctorCollection({
 #associate FunctorCollection to field (branch) name
 variables = {'ALL': variables_all, 'Ds': variables_ds}
 
-filter_data = add_filter("SpruceFilter", f"HLT_PASS('{spruce_line}')")
+filter_data = add_filter(options, "SpruceFilter", f"HLT_PASS('{spruce_line}')")
 
 #Configure Funtuple algorithm
 tuple_data = Funtuple(
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_configFuntuple.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_configFuntuple.py
index a20137d76..889dd4505 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_configFuntuple.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_configFuntuple.py
@@ -66,6 +66,6 @@ def main():
     }
 
     tools = []
-    algs = configured_FunTuple({"B0Dspi": config})
+    algs = configured_FunTuple(options, {"B0Dspi": config})
 
     return algs, tools
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
index 2baf3e4a3..8b609646a 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_array_taggers.py
@@ -81,7 +81,8 @@ tuple_B0DsK = Funtuple(
     variables=variables,
     inputs=bd2dsk_data)
 
-filter_B0DsK = add_filter("HDRFilter_B0DsK", f"HLT_PASS('{bd2dsk_line}')")
+filter_B0DsK = add_filter(options, "HDRFilter_B0DsK",
+                          f"HLT_PASS('{bd2dsk_line}')")
 
 options.annsvc_config = 'root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/spruce_all_lines_realtime_newPacking.tck.json'
 options.histo_file = 'DV-example-tagger-his.root'
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_collections.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_collections.py
index d5f5156e4..2f8b14eb1 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_collections.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_collections.py
@@ -84,6 +84,7 @@ my_tuple = Funtuple(
 
 
 def main():
-    my_filter = add_filter("HDRFilter_D0Kpi", f"HLT_PASS('{line_name}')")
+    my_filter = add_filter(options, "HDRFilter_D0Kpi",
+                           f"HLT_PASS('{line_name}')")
 
     return {"UserAlgs": [my_filter, my_tuple]}, []
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2.py
index d426b375e..e7ffca8fc 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2.py
@@ -70,7 +70,8 @@ def main():
     line_name = 'Hlt2CharmD0ToKmPipLine'
     d02kpi_data = force_location(f"/Event/HLT2/{line_name}/Particles")
 
-    my_filter = add_filter("HDRFilter_D0Kpi", f"HLT_PASS('{line_name}')")
+    my_filter = add_filter(options, "HDRFilter_D0Kpi",
+                           f"HLT_PASS('{line_name}')")
 
     #get configured "MCTruthAndBkgCatAlg" algorithm for HLT2 output
     mctruth = configured_MCTruthAndBkgCatAlg(
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py
index c18f7cad0..fa285391d 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py
@@ -53,7 +53,8 @@ def main():
     line_name = 'Hlt2CharmD0ToKmPipLine'
     d02kpi_data = force_location(f"/Event/HLT2/{line_name}/Particles")
 
-    my_filter = add_filter("HDRFilter_D0Kpi", f"HLT_PASS('{line_name}')")
+    my_filter = add_filter(options, "HDRFilter_D0Kpi",
+                           f"HLT_PASS('{line_name}')")
     my_tuple = Funtuple(
         name="Tuple",
         tuple_name="DecayTree",
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_passthrough.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_passthrough.py
index 787740868..195c6a09a 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_passthrough.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_passthrough.py
@@ -14,6 +14,7 @@ Test for checking the correct processing of Hlt2 .dst file where packed reco obj
 from FunTuple import FunTuple_Particles as Funtuple
 from FunTuple.functorcollections import Kinematics
 from PyConf.components import force_location
+from DaVinci import options
 from DaVinci.algorithms import add_filter
 
 bs2jpsiphi_line = "Hlt2BsToJpsiPhi_JPsi2MuMu_PhiToKK_Line"
@@ -33,7 +34,7 @@ variables = {
 
 
 def main():
-    filter_bs = add_filter("HDRFilter_Bs2JpsiPhi",
+    filter_bs = add_filter(options, "HDRFilter_Bs2JpsiPhi",
                            f"HLT_PASS('{bs2jpsiphi_line}')")
 
     tuple_bs = Funtuple(
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.py
index 4bd35aca0..5bace68ef 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.py
@@ -91,8 +91,9 @@ def main():
     options.ntuple_file = "DV_example_sprucing_ntp.root"
     options.histo_file = "DV_example_sprucing_his.root"
 
-    filter_B0DsK = add_filter("HDRFilter_B0DsK", f"HLT_PASS('{line_B0DsK}')")
-    filter_B0Dspi = add_filter("HDRFilter_B0Dspi",
+    filter_B0DsK = add_filter(options, "HDRFilter_B0DsK",
+                              f"HLT_PASS('{line_B0DsK}')")
+    filter_B0Dspi = add_filter(options, "HDRFilter_B0Dspi",
                                f"HLT_PASS('{line_B0Dspi}')")
 
     tools = []
diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce_mc.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce_mc.py
index 001ba01be..b02c50413 100644
--- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce_mc.py
+++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce_mc.py
@@ -12,6 +12,7 @@ from FunTuple import FunctorCollection
 import Functors as F
 from FunTuple import FunTuple_Particles as Funtuple
 from FunTuple.functorcollections import Kinematics
+from DaVinci import options
 from DaVinci.truth_matching import configured_MCTruthAndBkgCatAlg
 from DaVinci.algorithms import add_filter
 from PyConf.components import force_location
@@ -37,7 +38,7 @@ def main():
     line_name = 'Spruce_Test_line'
     B_data = force_location(f"/Event/Spruce/{line_name}/Particles")
 
-    my_filter = add_filter("HDRFilter_B", f"HLT_PASS('{line_name}')")
+    my_filter = add_filter(options, "HDRFilter_B", f"HLT_PASS('{line_name}')")
 
     #get configured "MCTruthAndBkgCatAlg" algorithm for HLT2 output
     mctruth = configured_MCTruthAndBkgCatAlg(inputs=B_data)
diff --git a/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_All.qmt b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_All.qmt
index 80a9a0224..f92761987 100755
--- a/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_All.qmt
+++ b/DaVinciExamples/tests/qmtest/tupling.qms/test_davinci_tupling_All.qmt
@@ -22,16 +22,28 @@
 #######################################################
 -->
 <extension class="GaudiTest.GaudiExeTest" kind="test">
-  <argument name="program"><text>davinci</text></argument>
+  <argument name="program"><text>lbexec</text></argument>
+  <!-- Minimum bias dst processed using topo {2,3} hlt2 lines and all sprucing lines -->
+  <argument name="extra_options_yaml"><text>
+    input_files:
+      - root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/spruce_all_lines_realtimereco_newPacking.dst'
+    annsvc_config: 'root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/spruce_all_lines_realtime_newPacking.tck.json'
+    data_type: Upgrade
+    input_type: ROOT
+    simulation: true
+    conddb_tag: sim-20171127-vc-md100
+    dddb_tag: dddb-20171126
+    histo_file: 'DV_example_allFunctors_his.root'
+    input_raw_format: 0.3
+    lumi: false
+    ntuple_file: 'DV_example_allFunctors_ntp.root'
+    print_freq: 1
+    process: 'Spruce'
+    stream: 'default'
+  </text></argument>
   <argument name="timeout"><integer>1000</integer></argument>
   <argument name="args"><set>
-  <text>--inputfiledb</text>
-  <text>Spruce_all_lines_dst</text>
-  <text>$DAVINCIROOT/options/DaVinciDB-Example.yaml</text>
-  <text>--joboptfile</text>
-  <text>../../python/DaVinciExamples/tupling/option_davinci_tupling_from_spruce.yaml</text>
-  <text>--user_algorithms</text>
-  <text>../../python/DaVinciExamples/tupling/example-tupling-AllFunctors:alg_config</text>
+  <text>DaVinciExamples.tupling.AllFunctors:alg_config</text>
   </set></argument>
 <argument name="validator"><text>
 findReferenceBlock("""B0DsK_Tuple                         SUCCESS Booked 1 N-Tuples and 0 Event Tag Collections"""
diff --git a/DaVinciSys/tests/test_davinci_script.py b/DaVinciSys/tests/test_davinci_script.py
index 5555feab6..4aa0c1e22 100644
--- a/DaVinciSys/tests/test_davinci_script.py
+++ b/DaVinciSys/tests/test_davinci_script.py
@@ -140,7 +140,7 @@ def test_create_template_interactively_changing_defaults(pytestconfig):
     input_mock.side_effect = list_of_args
 
     def test_prompt_values():
-        with patch('builtins.input', input_mock):
+        with patch('click.termui.visible_prompt_func', input_mock):
             create_jobopt_template(DIR / "test_jobopt_template_new.yaml", True)
 
     capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
@@ -180,7 +180,7 @@ def test_create_template_interactively_with_defaults(pytestconfig):
     input_mock.side_effect = list_of_args
 
     def test_prompt_values():
-        with patch('builtins.input', input_mock):
+        with patch('click.termui.visible_prompt_func', input_mock):
             create_jobopt_template(DIR / "test_jobopt_template.yaml", True)
 
     capmanager = pytestconfig.pluginmanager.getplugin('capturemanager')
diff --git a/DaVinciTests/tests/options/option_davinci_filters.py b/DaVinciTests/tests/options/option_davinci_filters.py
index ae4c97d18..88963dd8a 100644
--- a/DaVinciTests/tests/options/option_davinci_filters.py
+++ b/DaVinciTests/tests/options/option_davinci_filters.py
@@ -23,10 +23,10 @@ def main():
     options.histo_file = "histo_filter.root"
 
     filter_B0DsK = add_filter(
-        "HDRFilter_B0DsK",
+        options, "HDRFilter_B0DsK",
         "HLT_PASS('SpruceB2OC_BdToDsmK_DsmToHHH_FEST_LineDecision')")
     filter_B0Dspi = add_filter(
-        "HDRFilter_B0Dspi",
+        options, "HDRFilter_B0Dspi",
         "HLT_PASS('SpruceB2OC_BdToDsmPi_DsmToHHH_LineDecision')")
 
     tools = []
diff --git a/DaVinciTests/tests/options/option_davinci_funtuple_array.py b/DaVinciTests/tests/options/option_davinci_funtuple_array.py
index 4bba260d5..8b4d86068 100644
--- a/DaVinciTests/tests/options/option_davinci_funtuple_array.py
+++ b/DaVinciTests/tests/options/option_davinci_funtuple_array.py
@@ -68,7 +68,7 @@ tuple_B0DsK = Funtuple(
     variables=variables,
     inputs=bd2dsk_data)
 
-filter_B0DsK = add_filter("HDRFilter_B0DsK",
+filter_B0DsK = add_filter(options, "HDRFilter_B0DsK",
                           f"HLT_PASS('{bd2dsk_line}Decision')")
 
 
diff --git a/DaVinciTests/tests/options/option_davinci_recVertices.py b/DaVinciTests/tests/options/option_davinci_recVertices.py
index a749abb9b..f8e8a9abf 100644
--- a/DaVinciTests/tests/options/option_davinci_recVertices.py
+++ b/DaVinciTests/tests/options/option_davinci_recVertices.py
@@ -43,7 +43,7 @@ def alg_config():
         'B0': variables_pvs,
     }
 
-    my_filter = add_filter("HDRFilter_B0DsK",
+    my_filter = add_filter(options, "HDRFilter_B0DsK",
                            f"HLT_PASS('{bd2dsk_line}Decision')")
     my_tuple = Funtuple(
         name="B0DsK_Tuple",
diff --git a/DaVinciTests/tests/options/option_davinci_sprucing.py b/DaVinciTests/tests/options/option_davinci_sprucing.py
index 53e8ab6c1..adf012aa6 100644
--- a/DaVinciTests/tests/options/option_davinci_sprucing.py
+++ b/DaVinciTests/tests/options/option_davinci_sprucing.py
@@ -15,6 +15,7 @@ from FunTuple import FunctorCollection
 from FunTuple import FunTuple_Particles as Funtuple
 from FunTuple.functorcollections import Kinematics
 from DaVinci.algorithms import add_filter
+from DaVinci import options
 from PyConf.components import force_location
 
 bd2dsk_line = force_location(
@@ -63,7 +64,7 @@ tuple_B0DsK = Funtuple(
 def main():
 
     filter_B0DsK = add_filter(
-        "HDRFilter_B0DsK",
+        options, "HDRFilter_B0DsK",
         "HLT_PASS('SpruceB2OC_BdToDsmK_DsmToHHH_FEST_LineDecision')")
 
     tools = []
diff --git a/DaVinciTutorials/python/tutorial0_basic_DVjob.py b/DaVinciTutorials/python/tutorial0_basic_DVjob.py
index f52e8899e..9b2d5f08f 100644
--- a/DaVinciTutorials/python/tutorial0_basic_DVjob.py
+++ b/DaVinciTutorials/python/tutorial0_basic_DVjob.py
@@ -42,7 +42,8 @@ input_data = force_location(f"/Event/HLT2/{turbo_line}/Particles")
 # Side step this issue with a filter, where:
 # - 1st argument is a user defined name.
 # - 2nd argument is the line decision (simply append "Decision" to your HLT2 line name (or inspect hlt2_starterkit.tck.json))
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 # Defining an algorithm. The alorithm here prints the decaytree
 pdt = PrintDecayTree(name="PrintBsToJpsiPhi", Input=input_data)
diff --git a/DaVinciTutorials/python/tutorial1_functors_specialfield.py b/DaVinciTutorials/python/tutorial1_functors_specialfield.py
index 03c6ee32c..ce0bf1aa0 100644
--- a/DaVinciTutorials/python/tutorial1_functors_specialfield.py
+++ b/DaVinciTutorials/python/tutorial1_functors_specialfield.py
@@ -78,7 +78,8 @@ turbo_line = "Hlt2BsToJpsiPhi_JPsi2MuMu_PhiToKK_Line"
 input_data = force_location(f"/Event/HLT2/{turbo_line}/Particles")
 
 #Define a filter (see previous example for explaination)
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial2_LoKi.py b/DaVinciTutorials/python/tutorial2_LoKi.py
index 0601c9cf5..8adcbafa7 100644
--- a/DaVinciTutorials/python/tutorial2_LoKi.py
+++ b/DaVinciTutorials/python/tutorial2_LoKi.py
@@ -78,7 +78,8 @@ turbo_line = "Hlt2BsToJpsiPhi_JPsi2MuMu_PhiToKK_Line"
 input_data = force_location(f"/Event/HLT2/{turbo_line}/Particles")
 
 #Add a filter
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial3_ThOrfunctors.py b/DaVinciTutorials/python/tutorial3_ThOrfunctors.py
index 422050472..57493831e 100644
--- a/DaVinciTutorials/python/tutorial3_ThOrfunctors.py
+++ b/DaVinciTutorials/python/tutorial3_ThOrfunctors.py
@@ -103,7 +103,8 @@ turbo_line = "Hlt2BsToJpsiPhi_JPsi2MuMu_PhiToKK_Line"
 input_data = force_location(f"/Event/HLT2/{turbo_line}/Particles")
 
 #Add a filter
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial4_trigger_eventinfo.py b/DaVinciTutorials/python/tutorial4_trigger_eventinfo.py
index bf7c8357c..44b2c01ea 100644
--- a/DaVinciTutorials/python/tutorial4_trigger_eventinfo.py
+++ b/DaVinciTutorials/python/tutorial4_trigger_eventinfo.py
@@ -104,7 +104,8 @@ variables = {"ALL": kin}
 input_data = force_location(f"/Event/HLT2/{turbo_line}/Particles")
 
 #Add a filter
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial5_MCTruth.py b/DaVinciTutorials/python/tutorial5_MCTruth.py
index 0bb78981a..00f72146d 100644
--- a/DaVinciTutorials/python/tutorial5_MCTruth.py
+++ b/DaVinciTutorials/python/tutorial5_MCTruth.py
@@ -98,7 +98,8 @@ bkg_cat = FC({"BKGCAT": F.BKGCAT(Relations=mctruth.BkgCatTable)})
 variables = {"ALL": kin + mckin + mchierarchy + bkg_cat, "Kp": extra_info}
 
 #Add a filter
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial6_DecayTreeFit.py b/DaVinciTutorials/python/tutorial6_DecayTreeFit.py
index 3662dda5a..ccad8ab55 100644
--- a/DaVinciTutorials/python/tutorial6_DecayTreeFit.py
+++ b/DaVinciTutorials/python/tutorial6_DecayTreeFit.py
@@ -106,7 +106,8 @@ pv_coll += FC({
 variables = {"ALL": kin + dtf_kin, "Bs": pv_coll}
 
 #Add a filter (See Example7)
-my_filter = add_filter("HDRFilter_SeeNoEvil", f"HLT_PASS('{turbo_line}')")
+my_filter = add_filter(options, "HDRFilter_SeeNoEvil",
+                       f"HLT_PASS('{turbo_line}')")
 
 #Define instance of FunTuple
 mytuple = Funtuple(
diff --git a/DaVinciTutorials/python/tutorial7_multiple_sel_lines.py b/DaVinciTutorials/python/tutorial7_multiple_sel_lines.py
index d000c66b7..8e40e91af 100644
--- a/DaVinciTutorials/python/tutorial7_multiple_sel_lines.py
+++ b/DaVinciTutorials/python/tutorial7_multiple_sel_lines.py
@@ -47,7 +47,8 @@ variables = {"ALL": Kinematics()}
 #Load data from dst onto a TES
 turbo_line1 = "Hlt2BsToJpsiPhi_JPsi2MuMu_PhiToKK_Line"
 input_data1 = force_location(f"/Event/HLT2/{turbo_line1}/Particles")
-my_filter1 = add_filter("HDRFilter_SeeNoEvil1", f"HLT_PASS('{turbo_line1}')")
+my_filter1 = add_filter(options, "HDRFilter_SeeNoEvil1",
+                        f"HLT_PASS('{turbo_line1}')")
 mytuple1 = Funtuple(
     "TDirectoryName1",
     "TTreeName1",
@@ -58,7 +59,8 @@ mytuple1 = Funtuple(
 # If running over several sprucing lines (e.g. for calibration) one can define multiple instances of FunTuple
 turbo_line2 = "Hlt2BsToJpsiPhi_JPsi2ee_PhiToKK_Line"
 input_data2 = force_location(f"/Event/HLT2/{turbo_line2}/Particles")
-my_filter2 = add_filter("HDRFilter_SeeNoEvil2", f"HLT_PASS('{turbo_line2}')")
+my_filter2 = add_filter(options, "HDRFilter_SeeNoEvil2",
+                        f"HLT_PASS('{turbo_line2}')")
 mytuple2 = Funtuple(
     "TDirectoryName2",
     "TTreeName2",
diff --git a/Phys/DaVinci/python/DaVinci/Configuration.py b/Phys/DaVinci/python/DaVinci/Configuration.py
index 8732e0b08..d2f299015 100644
--- a/Phys/DaVinci/python/DaVinci/Configuration.py
+++ b/Phys/DaVinci/python/DaVinci/Configuration.py
@@ -21,7 +21,7 @@ from DaVinci.configOptions import (
 from DaVinci.algorithms import (setup_algorithms, define_fsr_writer,
                                 apply_filters_and_unpacking,
                                 expand_input_files)
-from DaVinci.config import davinci_control_flow, prepare_davinci_nodes
+from DaVinci.config import davinci_control_flow, prepare_davinci_nodes, DVAppOptions
 
 
 def run_davinci_app(fileDB_key="",
@@ -60,7 +60,7 @@ def run_davinci_app(fileDB_key="",
     from DaVinci import options
 
     # Workaround ConfigurableUser limitation: options.<name> cannot be called if a value is not
-    # explicitely assigned and the related property is not set.
+    # explicitly assigned and the related property is not set.
     # Initialization in DVAppOptions class seems to be not sufficient.
     set_properties(options)
 
@@ -101,25 +101,27 @@ def add_davinci_configurables(options, user_algorithms, public_tools):
     Returns:
         ComponentConfig instance, a dict of configured Gaudi and DaVinci Configurable instances and user algorithms.
     """
-    dvMainFlow = {}
-    fsrAlgs = {}
+    if not public_tools:
+        public_tools = []
 
-    if options.main_options:
+    if isinstance(options, DVAppOptions) and options.main_options:
         importOptions(options.main_options)
 
     # For xgen files we need to unpack only mc particles and vertices
-    # Needed until unpacking will become functinal
+    # Needed until unpacking will become functional
     unpack_only_mc = options.input_type == "XGEN"
 
-    expand_input_files(options)
+    if isinstance(options, DVAppOptions):
+        expand_input_files(options)
     check_options(options)
     config = configure_input(options)
 
     if options.annsvc_config:
         config.update(reading.set_hltAnn_svc(options.annsvc_config))
-    dvMainFlow.update(
-        apply_filters_and_unpacking(options, user_algorithms, unpack_only_mc))
+    dvMainFlow = apply_filters_and_unpacking(options, user_algorithms,
+                                             unpack_only_mc)
 
+    fsrAlgs = {}
     if options.write_fsr:
         if options.simulation:
             fsrAlgs.update({"GenFSR": define_fsr_writer(options)})
diff --git a/Phys/DaVinci/python/DaVinci/LbExec.py b/Phys/DaVinci/python/DaVinci/LbExec.py
new file mode 100644
index 000000000..4fa0ba2fb
--- /dev/null
+++ b/Phys/DaVinci/python/DaVinci/LbExec.py
@@ -0,0 +1,30 @@
+###############################################################################
+# (c) Copyright 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.                                       #
+###############################################################################
+from enum import Enum
+from typing import Optional
+
+from GaudiConf.LbExec import Options as DefaultOptions
+
+
+class ProcessTypes(str, Enum):
+    Turbo = "Turbo"
+    Hlt2 = "Hlt2"
+    Spruce = "Spruce"
+
+
+class Options(DefaultOptions):
+    annsvc_config: str
+    process: ProcessTypes
+    stream: str
+    lumi: bool = False
+    evt_pre_filters: Optional[str] = None
+    enable_unpack: bool = True
+    write_fsr: bool = False
diff --git a/Phys/DaVinci/python/DaVinci/__init__.py b/Phys/DaVinci/python/DaVinci/__init__.py
index 2427da62d..d6ca40e25 100644
--- a/Phys/DaVinci/python/DaVinci/__init__.py
+++ b/Phys/DaVinci/python/DaVinci/__init__.py
@@ -8,8 +8,7 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 ###############################################################################
+from .config import options, run_davinci, DVNode, make_config
+from .LbExec import Options
 
-from __future__ import absolute_import
-from .config import options, run_davinci, DVNode
-
-__all__ = ('options', 'run_davinci', 'DVNode')
+__all__ = ('options', 'run_davinci', 'DVNode', 'Options', 'make_config')
diff --git a/Phys/DaVinci/python/DaVinci/algorithms.py b/Phys/DaVinci/python/DaVinci/algorithms.py
index 431c5d95c..de1e8821b 100644
--- a/Phys/DaVinci/python/DaVinci/algorithms.py
+++ b/Phys/DaVinci/python/DaVinci/algorithms.py
@@ -8,7 +8,7 @@
 # granted to it by virtue of its status as an Intergovernmental Organization  #
 # or submit itself to any jurisdiction.                                       #
 ###############################################################################
-
+import re
 import os, sys, importlib
 from PyConf.Algorithms import (
     FilterDecays,
@@ -23,6 +23,7 @@ from PyConf.application import (
     make_odin,
 )
 from DaVinci.algorithms_pyconf import make_dvalgorithm
+from DaVinci.config import DVAppOptions
 from PyConf.components import force_location
 
 
@@ -79,20 +80,18 @@ def set_filter(name, code, dec_reports):
     return algFilter
 
 
-def add_filter(name, code):
+def add_filter(options, name, code):
     """
     Adding an event pre-filter using a code defined by the user.
 
     Args:
+        options (class Options): lbexec provided options object
         name (str): filter's name.
         code (str): filter's code.
 
     Returns:
         Filter with name and code defined by the user.
     """
-    import re
-    from DaVinci import options
-
     #if code ends with HLT_PASS then check that the lines inside
     # HLT_PASS contain suffix decision
     if code.startswith("HLT_PASS("):
@@ -145,7 +144,7 @@ def apply_filters_and_unpacking(options, algs_dict, unpack_only_mc):
             evt_pre_filters = []
             code_filters = options.evt_pre_filters
             for title, code in code_filters.items():
-                evt_filter = add_filter(title, code)
+                evt_filter = add_filter(options, title, code)
                 evt_pre_filters.append(evt_filter)
             algs_list += evt_pre_filters
         if options.enable_unpack:
@@ -272,7 +271,8 @@ def unpack_locations(options, unpack_only_mc):
     process = options.process
     stream = options.stream
     reading_algs = []
-    set_properties(options)
+    if isinstance(options, DVAppOptions):
+        set_properties(options)
 
     if process == "Spruce":
         TES_ROOT = '/Event/Spruce'
@@ -363,7 +363,7 @@ def get_hlt_reports(options, source=''):
     return dec_reports
 
 
-def configured_FunTuple(config):
+def configured_FunTuple(options, config):
     """
     Function for the FunTuple configuration and instantiation of the related HDR filter.
 
@@ -394,7 +394,7 @@ def configured_FunTuple(config):
                 filter_name += "_%d" % i
                 i = i + 1
 
-            tupleFilter = add_filter(filter_name, line)
+            tupleFilter = add_filter(options, filter_name, line)
             dictAlgs[key].append(tupleFilter)
 
         funTuple = Funtuple(
@@ -419,7 +419,6 @@ def get_odin(dv_options):
     Returns:
         odin_loc: Location of the LHCb::ODIN
     """
-
     if dv_options.process == 'Hlt2':
         stream = ""
     else:
diff --git a/Phys/DaVinci/python/DaVinci/config.py b/Phys/DaVinci/python/DaVinci/config.py
index 40944d48d..f9d2f67a9 100644
--- a/Phys/DaVinci/python/DaVinci/config.py
+++ b/Phys/DaVinci/python/DaVinci/config.py
@@ -152,3 +152,12 @@ def run_davinci(options, user_algs, public_tools=[]):
     config.update(configure(options, top_dv_node, public_tools=public_tools))
 
     return config
+
+
+def make_config(options, user_algorithms, *, public_tools=None):
+    from DaVinci.Configuration import add_davinci_configurables
+
+    if isinstance(user_algorithms, list):
+        user_algorithms = {"default": user_algorithms}
+
+    return add_davinci_configurables(options, user_algorithms, public_tools)
diff --git a/Phys/DaVinci/python/DaVinci/configOptions.py b/Phys/DaVinci/python/DaVinci/configOptions.py
index 4f3c95e70..6c3121d90 100644
--- a/Phys/DaVinci/python/DaVinci/configOptions.py
+++ b/Phys/DaVinci/python/DaVinci/configOptions.py
@@ -15,6 +15,7 @@ Set and retrieve the value of a specific option property.
 import os
 from DaVinci.optionChecker import DVOptionError, DVRuntimeError
 from DaVinci.optionChecker import option_checker, log_click
+from DaVinci.application import DVAppOptions
 
 
 def set_option_value(options, name, value):
@@ -206,7 +207,8 @@ def check_options(options):
 
     inputType = (options.input_type).upper()
     option_checker("input_type", inputType)
-    if inputType != "MDF":
+    # Avoid running this check if we're using lbexec
+    if isinstance(options, DVAppOptions) and inputType != "MDF":
         set_option_value(options, "input_type", "ROOT")
 
     flagMC = options.simulation
diff --git a/Phys/DaVinci/tests/config/test_algorithms.py b/Phys/DaVinci/tests/config/test_algorithms.py
index ea4d5a360..b51475d47 100644
--- a/Phys/DaVinci/tests/config/test_algorithms.py
+++ b/Phys/DaVinci/tests/config/test_algorithms.py
@@ -46,9 +46,6 @@ def main():
 
     os.remove(f"{filename}.py")
 
-    # Restore default value avoid failure in the next tests
-    options.user_algorithms = ""
-
 
 def test_no_user_algorithms():
     """
@@ -161,7 +158,8 @@ def test_add_hlt2_filter():
     """
     options.process = "Hlt2"
     options.stream = "default"
-    test_filter = add_filter("test_filter", "HLT_PASS('Hlt2TESTLineDecision')")
+    test_filter = add_filter(options, "test_filter",
+                             "HLT_PASS('Hlt2TESTLineDecision')")
     assert "HDRFilter" in test_filter.fullname
 
 
@@ -171,7 +169,7 @@ def test_add_spruce_filter():
     """
     options.process = "Spruce"
     options.stream = "default"
-    test_filter = add_filter("test_filter",
+    test_filter = add_filter(options, "test_filter",
                              "HLT_PASS('SpruceTESTLineDecision')")
     assert "HDRFilter" in test_filter.fullname
 
@@ -181,7 +179,7 @@ def test_add_void_filter():
     Check if DaVinci is able to implement correcty a Void filter
     if 'HLT_PASS' string is not found in the filter code."
     """
-    test_filter = add_filter("test_filter", "VOIDTEST_Filter")
+    test_filter = add_filter(options, "test_filter", "VOIDTEST_Filter")
     assert "VoidFilter" in test_filter.fullname
 
 
@@ -249,7 +247,7 @@ def test_configured_funtuple():
         }
     }
 
-    test_dict = configured_FunTuple(config)
+    test_dict = configured_FunTuple(options, config)
     assert any("FunTupleBase_Particles/Tuple_TestTuple" in alg.fullname
                for alg in test_dict["TestTuple"])
 
-- 
GitLab