From 52b33cdad3ea2d3bdc25dd96876c4a3afe4f654f Mon Sep 17 00:00:00 2001
From: Johannes Schumann <johannes.schumann@fau.de>
Date: Wed, 27 May 2020 20:40:36 +0200
Subject: [PATCH] Restructure of jobcard object

---
 km3buu/ctrl.py    |  43 ++++++++++------
 km3buu/jobcard.py | 129 ++++++++++++++++++----------------------------
 2 files changed, 76 insertions(+), 96 deletions(-)

diff --git a/km3buu/ctrl.py b/km3buu/ctrl.py
index f6adbca..bab283d 100644
--- a/km3buu/ctrl.py
+++ b/km3buu/ctrl.py
@@ -20,13 +20,12 @@ from thepipe.logger import get_logger
 
 from . import IMAGE_NAME
 from .config import Config
-from .jobcard import Jobcard
+from .jobcard import Jobcard, read_jobcard
 from .environment import is_singularity_version_greater, MIN_SINGULARITY_VERSION
 
 log = get_logger(basename(__file__))
 
-if not is_singularity_version_greater(
-        MIN_SINGULARITY_VERSION):  # pragma: no cover
+if not is_singularity_version_greater(MIN_SINGULARITY_VERSION):  # pragma: no cover
     log.error("Singularity version lower than %s" % MIN_SINGULARITY_VERSION)
     raise OSError("Singularity version below %s" % MIN_SINGULARITY_VERSION)
 
@@ -45,7 +44,7 @@ $CONTAINER_GIBUU_EXEC < {1};
 """
 
 
-def run_jobcard(jobcard, outdir):
+def run_jobcard(jobcard, outdir, fluxfile=None):
     """
     Method for run
 
@@ -56,28 +55,40 @@ def run_jobcard(jobcard, outdir):
         of a jobcard object or a path to a jobcard
     outdir: str 
         The path to the directory the output should be written to.
+    fluxfile: str
+        Filepath of the custom flux file if initNeutrino/nuExp = 99
     """
-    script_dir = TemporaryDirectory()
+    input_dir = TemporaryDirectory()
     outdir = abspath(outdir)
     log.info("Create temporary file for jobcard")
-    jobcard_fpath = join(script_dir.name, "tmp.job")
+    jobcard_fpath = join(input_dir.name, "tmp.job")
+
+    if isinstance(jobcard, str) and isfile(jobcard):
+        jobcard = read_jobcard(jobcard)
+
+    if "initneutrino" in jobcard and jobcard["initneutrino"]["nuexp"] == 99:
+        if fluxfile is None or not isfile(fluxfile):
+            raise IOError("Fluxfile not found!")
+        tmp_fluxfile = join(input_dir.name, basename(fluxfile))
+        os.system("cp %s %s" % (fluxfile, tmp_fluxfile))
+        jobcard["initneutrino"]["filenameflux"] = tmp_fluxfile
     if isinstance(jobcard, Jobcard):
-        with open(jobcard_fpath, 'w') as f:
+        with open(jobcard_fpath, "w") as f:
             f.write(str(jobcard))
-    elif isfile(jobcard):
-        os.system("cp %s %s" % (jobcard, jobcard_fpath))
     else:
         log.error("No valid jobcard reference given: %s" % jobcard)
     log.info("Create temporary file for associated runscript")
-    script_fpath = join(script_dir.name, "run.sh")
-    with open(script_fpath, 'w') as f:
+    script_fpath = join(input_dir.name, "run.sh")
+    with open(script_fpath, "w") as f:
         ctnt = GIBUU_SHELL.format(outdir, jobcard_fpath)
         f.write(ctnt)
-    output = Client.execute(Config().gibuu_image_path,
-                            ['/bin/sh', script_fpath],
-                            bind=[outdir, script_dir.name],
-                            return_result=True)
-    msg = output['message']
+    output = Client.execute(
+        Config().gibuu_image_path,
+        ["/bin/sh", script_fpath],
+        bind=[outdir, input_dir.name],
+        return_result=True,
+    )
+    msg = output["message"]
     if isinstance(msg, str):
         log.info("GiBUU output:\n %s" % msg)
     else:
diff --git a/km3buu/jobcard.py b/km3buu/jobcard.py
index 6962c9b..3f0efaf 100644
--- a/km3buu/jobcard.py
+++ b/km3buu/jobcard.py
@@ -13,6 +13,7 @@ __email__ = "jschumann@km3net.de"
 __status__ = "Development"
 
 import f90nml
+
 try:
     from StringIO import StringIO
 except ImportError:
@@ -38,11 +39,11 @@ XSECTIONMODE_LOOKUP = {
     "EXP_dSigmadCosTheta": 14,
     "EXP_dSigmadElepton": 15,
     "EXP_dSigmaMC": 16,
-    "EXP_dSigmadW": 17
+    "EXP_dSigmadW": 17,
 }
 
 
-class Jobcard(object):
+class Jobcard(f90nml.Namelist):
     """
     A object to manage GiBUU jobcard properties and format them
 
@@ -51,70 +52,40 @@ class Jobcard(object):
     input_path: str
         The input path pointing to the GiBUU lookup data which should be used
     """
-    def __init__(self, input_path=INPUT_PATH):
-        self.input_path = "%s" % input_path
-        self._nml = f90nml.Namelist()
-        self.set_property("input", "path_to_input", self.input_path)
-
-    def set_property(self, group, name, value):
-        """ Set a property to the jobcard
-
-        Parameters
-        ----------
-        group: str
-            The parameter group where the property is incorporated
-        name: str
-            The property name
-        value:
-            The property value
-        """
-        if group not in self._nml.keys():
-            self._nml[group] = {}
-        self._nml[group][name] = value
-
-    def remove_property(self, group, name):
-        del self._nml[group][name]
-        if len(self._nml[group]) == 0:
-            del self._nml[group]
 
-    def __str__(self):
-        stream = StringIO()
-        self._nml.write(stream)
-        return stream.getvalue()
+    def __init__(self, *args, **kwargs):
+        if "input_path" in kwargs:
+            self.input_path = "%s" % input_path
+            del kwargs["input_path"]
+        else:
+            self.input_path = INPUT_PATH
+        self.__getitem__("input")["path_to_input"] = self.input_path
+        super(Jobcard, self).__init__(*args, **kwargs)
 
     def __getitem__(self, key):
-        if isinstance(key, str):
-            if '/' in key:
-                k = key.split('/')
-                return self._nml[k[0]][k[1]]
-            else:
-                return self._nml[key]
-        elif isinstance(key, tuple) and len(key) == 2:
-            return self._nml[key[0]][key[1]]
-        else:
-            raise IndexError("Invalid access to Jobcard")
+        if not self.__contains__(key):
+            self.__setitem__(key, f90nml.Namelist())
+        return super(Jobcard, self).__getitem__(key)
+
+    def _clean_namelist(self):
+        for k, v in self.items():
+            if isinstance(v, f90nml.Namelist) and len(v) == 0:
+                self.__delitem__(k)
 
+    def __str__(self):
+        self._clean_namelist()
+        stream = StringIO()
+        self.write(stream)
+        return stream.getvalue()
 
-def read_jobcard(fpath):
-    """ Read a jobcard from file
 
-    Parameters
-    ----------
-    fpath: str
-        Filepath of the jobcard
-    """
-    jc = Jobcard()
-    jc._nml = f90nml.read(fpath)
-    return jc
+def read_jobcard(filepath):
+    return Jobcard(f90nml.read(filepath))
 
 
 def generate_neutrino_jobcard_template(
-    process,
-    flavour,
-    energy,
-    target,
-    write_events=False,
-    input_path=INPUT_PATH):  # pragma: no cover
+    process, flavour, energy, target, write_events=False, input_path=INPUT_PATH
+):  # pragma: no cover
     """
     Generate a jobcard for neutrino interaction
 
@@ -133,31 +104,29 @@ def generate_neutrino_jobcard_template(
     """
     jc = Jobcard(input_path)
     # NEUTRINO
-    jc.set_property("neutrino_induced", "process_ID",
-                    PROCESS_LOOKUP[process.lower()])
-    jc.set_property("neutrino_induced", "flavour_ID",
-                    FLAVOR_LOOKUP[flavour.lower()])
-    jc.set_property("neutrino_induced", "nuXsectionMode", 6)
-    jc.set_property("neutrino_induced", "includeDIS", True)
-    jc.set_property("neutrino_induced", "includeDELTA", True)
-    jc.set_property("neutrino_induced", "includeRES", True)
-    jc.set_property("neutrino_induced", "includeQE", True)
-    jc.set_property("neutrino_induced", "include1pi", True)
-    jc.set_property("neutrino_induced", "include2p2hQE", True)
-    jc.set_property("neutrino_induced", "include2pi", False)
-    jc.set_property("neutrino_induced", "include2p2hDelta", False)
-    jc.set_property("neutrino_inducted", "printAbsorptionXS", "T")
+    jc["neutrino_induced"]["process_ID"] = PROCESS_LOOKUP[process.lower()]
+    jc["neutrino_induced"]["flavour_ID"] = FLAVOR_LOOKUP[flavour.lower()]
+    jc["neutrino_induced"]["nuXsectionMode"] = 6
+    jc["neutrino_induced"]["includeDIS"] = True
+    jc["neutrino_induced"]["includeDELTA"] = True
+    jc["neutrino_induced"]["includeRES"] = True
+    jc["neutrino_induced"]["includeQE"] = True
+    jc["neutrino_induced"]["include1pi"] = True
+    jc["neutrino_induced"]["include2p2hQE"] = True
+    jc["neutrino_induced"]["include2pi"] = False
+    jc["neutrino_induced"]["include2p2hDelta"] = False
+    jc["neutrino_inducted"]["printAbsorptionXS"] = True
 
     # INPUT
-    jc.set_property("input", "numTimeSteps", 0)
-    jc.set_property("input", "eventtype", 5)
-    jc.set_property("input", "numEnsembles", 100000)
-    jc.set_property("input", "delta_T", 0.2)
-    jc.set_property("input", "localEnsemble", True)
-    jc.set_property("input", "num_runs_SameEnergy", 1)
+    jc["input"]["numTimeSteps"] = 0
+    jc["input"]["eventtype"] = 5
+    jc["input"]["numEnsembles"] = 100000
+    jc["input"]["delta_T"] = 0.2
+    jc["input"]["localEnsemble"] = True
+    jc["input"]["num_runs_SameEnergy"] = 1
     # TARGET
-    jc.set_property("target", "Z", target[0])
-    jc.set_property("target", "A", target[1])
+    jc["target"]["Z"] = target[0]
+    jc["target"]["A"] = target[1]
     # MISC
-    jc.set_property("neutrinoAnalysis", "outputEvents", write_events)
+    jc["neutrinoAnalysis"]["outputEvents"] = write_events
     return jc
-- 
GitLab