From f914e0c0bafc0922ab77a4347462a15b62d2efa7 Mon Sep 17 00:00:00 2001
From: Markus Scheidgen <markus.scheidgen@gmail.com>
Date: Tue, 13 Aug 2019 12:04:30 +0200
Subject: [PATCH] Adapted implementatino for nomad-fairdi.

---
 .gitignore                                    |  58 +-----
 parser/parser-qbox/setup_paths.py             |   6 -
 .../parser-qbox => qboxparser}/QboxCommon.py  |   1 -
 .../QboxInputParser.py                        |  47 +++--
 .../parser-qbox => qboxparser}/QboxParser.py  | 173 ++++++++++--------
 .../QboxXMLParser.py                          |   5 +-
 qboxparser/__init__.py                        |   1 +
 setup.py                                      |  33 ++++
 .../eu/nomad_lab/parsers/QboxParser.scala     |  65 -------
 .../eu/nomad_lab/parsers/QboxParserSpec.scala |  29 ---
 10 files changed, 158 insertions(+), 260 deletions(-)
 delete mode 100644 parser/parser-qbox/setup_paths.py
 rename {parser/parser-qbox => qboxparser}/QboxCommon.py (97%)
 rename {parser/parser-qbox => qboxparser}/QboxInputParser.py (94%)
 rename {parser/parser-qbox => qboxparser}/QboxParser.py (87%)
 rename {parser/parser-qbox => qboxparser}/QboxXMLParser.py (98%)
 create mode 100644 qboxparser/__init__.py
 create mode 100644 setup.py
 delete mode 100644 src/main/scala/eu/nomad_lab/parsers/QboxParser.scala
 delete mode 100644 src/test/scala/eu/nomad_lab/parsers/QboxParserSpec.scala

diff --git a/.gitignore b/.gitignore
index 1df2c2bb..efbcf02a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,55 +1,5 @@
-# use glob syntax.
-syntax: glob
-*.ser
-*.class
-*~
-*.bak
-#*.off
-*.old
+# python
+__pycache__
+.mypy_cache
 *.pyc
-*.bk
-*.swp
-*.annotate
-.DS_Store
-
-# logging files
-detailed.log
-
-# eclipse conf file
-.settings
-.classpath
-.project
-.manager
-.scala_dependencies
-
-# idea
-.idea
-*.iml
-
-# building
-target
-build
-null
-tmp*
-temp*
-dist
-test-output
-build.log
-
-# other scm
-.svn
-.CVS
-.hg*
-
-# switch to regexp syntax.
-#  syntax: regexp
-#  ^\.pc/
-
-#SHITTY output not in target directory
-build.log
-
-#emacs TAGS
-TAGS
-
-lib/
-env/
+*.egg-info/
\ No newline at end of file
diff --git a/parser/parser-qbox/setup_paths.py b/parser/parser-qbox/setup_paths.py
deleted file mode 100644
index e0a0fb00..00000000
--- a/parser/parser-qbox/setup_paths.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import sys, os, os.path
-baseDir = os.path.dirname(os.path.abspath(__file__))
-commonDir = os.path.normpath(os.path.join(baseDir,"../../../../python-common/common/python"))
-
-if not commonDir in sys.path:
-    sys.path.insert(0, commonDir)
diff --git a/parser/parser-qbox/QboxCommon.py b/qboxparser/QboxCommon.py
similarity index 97%
rename from parser/parser-qbox/QboxCommon.py
rename to qboxparser/QboxCommon.py
index 80ed478f..5997a07d 100644
--- a/parser/parser-qbox/QboxCommon.py
+++ b/qboxparser/QboxCommon.py
@@ -1,4 +1,3 @@
-import setup_paths
 import numpy as np
 from nomadcore.local_meta_info import loadJsonFile, InfoKindEl
 from nomadcore.unit_conversion.unit_conversion import convert_unit
diff --git a/parser/parser-qbox/QboxInputParser.py b/qboxparser/QboxInputParser.py
similarity index 94%
rename from parser/parser-qbox/QboxInputParser.py
rename to qboxparser/QboxInputParser.py
index 0fb28bc3..d5732763 100644
--- a/parser/parser-qbox/QboxInputParser.py
+++ b/qboxparser/QboxInputParser.py
@@ -1,5 +1,4 @@
 from builtins import object
-import setup_paths
 import numpy as np
 import nomadcore.ActivateLogging
 from nomadcore.caching_backend import CachingLevel
@@ -16,7 +15,7 @@ import logging, os, re, sys
 ############################################################
 ###############[1] transfer PARSER CONTEXT #################
 ############################################################
-logger = logging.getLogger("nomad.qboxInputParser") 
+logger = logging.getLogger("nomad.qboxInputParser")
 
 class QboxInputParserContext(object):
 
@@ -32,7 +31,7 @@ class QboxInputParserContext(object):
         """
         self.secMethodIndex = None
         self.secSystemDescriptionIndex = None
-       
+
         self.singleConfCalcs = []
 
 
@@ -74,20 +73,20 @@ class QboxInputParserContext(object):
     def onClose_x_qbox_section_functionals(self, backend, gIndex, section):
         functional_list = section["x_qbox_functional_name"]
 
-        if not functional_list: # default is LDA in qbox 
+        if not functional_list: # default is LDA in qbox
            functional = "LDA"
         else :
-           functional = functional_list[-1] # use the xc appeared the last time 
+           functional = functional_list[-1] # use the xc appeared the last time
+
 
- 
         if functional:
             functionalMap = {
-                "LDA": ["LDA_X", "LDA_C_PZ"], 
-                "VMN": ["LDA_X", "LDA_C_VWN"], 
-                "PBE": ["GGA_X_PBE","GGA_C_PBE"], 
-                "PBE0": ["GGA_X_PBE","GGA_C_PBE"], 
-                "B3LYP": ["HYB_GGA_XC_B3LYP5"]  
-     #need to be extended to add alpha_PBE0 :coefficient of Hartree-Fock exchange in the PBE0 xc functional 
+                "LDA": ["LDA_X", "LDA_C_PZ"],
+                "VMN": ["LDA_X", "LDA_C_VWN"],
+                "PBE": ["GGA_X_PBE","GGA_C_PBE"],
+                "PBE0": ["GGA_X_PBE","GGA_C_PBE"],
+                "B3LYP": ["HYB_GGA_XC_B3LYP5"]
+     #need to be extended to add alpha_PBE0 :coefficient of Hartree-Fock exchange in the PBE0 xc functional
             }
             # Push the functional string into the backend
             nomadNames = functionalMap.get(functional)
@@ -103,7 +102,7 @@ class QboxInputParserContext(object):
 
     ###################################################################
     # (3.4) onClose for geometry and force (section_system)
-    # todo: maybe we can move the force to onClose_section_single_configuration_calculation in the future. 
+    # todo: maybe we can move the force to onClose_section_single_configuration_calculation in the future.
     ###################################################################
     def onOpen_section_method(self, backend, gIndex, section):
         # keep track of the latest method section
@@ -148,7 +147,7 @@ class QboxInputParserContext(object):
         if unit_cell:
            backend.addArrayValues('simulation_cell', np.asarray(unit_cell))
            backend.addArrayValues("configuration_periodic_dimensions", np.ones(3, dtype=bool))
-  
+
 
 
 
@@ -163,7 +162,7 @@ class QboxInputParserContext(object):
 
 
 
-                
+
 
 #############################################################
 #################[2] MAIN PARSER STARTS HERE  ###############
@@ -178,7 +177,7 @@ def build_QboxInputFileSimpleMatcher():
     which allows nice formating of nested SimpleMatchers in python.
 
     Returns:
-       SimpleMatcher that parses input file of qbox. 
+       SimpleMatcher that parses input file of qbox.
     """
 
 
@@ -204,18 +203,18 @@ def build_QboxInputFileSimpleMatcher():
             SM(r"\s*set\s+atoms_dyn\s+(?P<x_qbox_atoms_dyn>[A-Za-z0-9]+)\s*"),
             SM(r"\s*set\s+cell_dyn\s+(?P<x_qbox_cell_dyn>[A-Za-z0-9]+)\s*"),
 
-        #--------set xc--------- 
+        #--------set xc---------
             SM(name = "qboxXC",
               startReStr = r"\s*set\s+xc\s+(?P<x_qbox_functional_name>[A-Za-z0-9]+)\s*",
               sections = ["x_qbox_section_functionals"]
-               ), 
+               ),
 
         #-------set efield---------
             SM (r"\s*set\s+e_field\s*(?P<x_qbox_efield_x>[-+0-9.]+)\s+(?P<x_qbox_efield_y>[-+0-9.]+)\s+(?P<x_qbox_efield_z>[-+0-9.]+)\s*",repeats = True)
           #???both this version adn qbox_section_efield version could not give mather for efield, need to check.
         ])
- 
-      
+
+
 
 
 
@@ -231,7 +230,7 @@ def build_QboxInputFileSimpleMatcher():
         subMatchers = [
 
         #=============================================================================
-        #  read OUPUT file *.r, the method part comes from INPUT file *.i,  so we 
+        #  read OUPUT file *.r, the method part comes from INPUT file *.i,  so we
         #  do not need to parser INPUT file, the OUTPUT file contains all information
         #=============================================================================
         SM (name = 'NewRun',
@@ -243,11 +242,11 @@ def build_QboxInputFileSimpleMatcher():
             fixedStartValues={'program_name': 'qbox', 'program_basis_set_type': 'plane waves'},
             sections = ['section_run'],
             subMatchers = [
- 
+
              #-----------input  method---------------------
              calculationMethodSubMatcher
 
-           ]) # CLOSING SM NewRun  
+           ]) # CLOSING SM NewRun
 
 
         ]) # END Root
@@ -259,7 +258,7 @@ def get_cachingLevelForMetaName(metaInfoEnv):
         metaInfoEnv: metadata which is an object of the class InfoKindEnv in nomadcore.local_meta_info.py.
 
     Returns:
-        Dictionary with metaname as key and caching level as value. 
+        Dictionary with metaname as key and caching level as value.
     """
     # manually adjust caching of metadata
     cachingLevelForMetaName = {
diff --git a/parser/parser-qbox/QboxParser.py b/qboxparser/QboxParser.py
similarity index 87%
rename from parser/parser-qbox/QboxParser.py
rename to qboxparser/QboxParser.py
index be995ead..fe4edc73 100644
--- a/parser/parser-qbox/QboxParser.py
+++ b/qboxparser/QboxParser.py
@@ -1,14 +1,15 @@
 from builtins import object
-import setup_paths
 import numpy as np
 import nomadcore.ActivateLogging
 from nomadcore.caching_backend import CachingLevel
 from nomadcore.simple_parser import AncillaryParser, mainFunction
 from nomadcore.simple_parser import SimpleMatcher as SM
-from QboxCommon import get_metaInfo
-import QboxXMLParser
+from .QboxCommon import get_metaInfo
+from . import QboxXMLParser
 import logging, os, re, sys
 
+import nomad_meta_info
+
 ############################################################
 # This is the parser for the main file of qbox (for the output file *.r) .
 ############################################################
@@ -17,7 +18,7 @@ import logging, os, re, sys
 ############################################################
 ###############[1] transfer PARSER CONTEXT #################
 ############################################################
-logger = logging.getLogger("nomad.qboxParser") 
+logger = logging.getLogger("nomad.qboxParser")
 
 class QboxParserContext(object):
 
@@ -33,7 +34,7 @@ class QboxParserContext(object):
         """
         self.secMethodIndex = None
         self.secSystemDescriptionIndex = None
-       
+
         self.singleConfCalcs = []
 
 
@@ -78,9 +79,9 @@ class QboxParserContext(object):
 
         x_qbox_loading_xml_file_list = section['x_qbox_loading_xml_file']
 
-        xml_file = x_qbox_loading_xml_file_list[-1]        
- 
-        if xml_file is not None: 
+        xml_file = x_qbox_loading_xml_file_list[-1]
+
+        if xml_file is not None:
            logger.warning("This output showed this calculation need to load xml file, so we need this xml file ('%s') to read geometry information" % os.path.normpath(xml_file) )
            fName = os.path.normpath(xml_file)
 
@@ -94,7 +95,7 @@ class QboxParserContext(object):
            try:
                 with open(fName) as fxml:
                      xmlParser.parseFile(fxml)
-          
+
            except IOError:
                 logger.warning("Could not find xml file in directory '%s'. " % os.path.dirname(os.path.abspath(self.fName)))
 
@@ -102,20 +103,20 @@ class QboxParserContext(object):
     def onClose_x_qbox_section_functionals(self, backend, gIndex, section):
         functional_list = section["x_qbox_functional_name"]
 
-        if not functional_list: # default is LDA in qbox 
+        if not functional_list: # default is LDA in qbox
            functional = "LDA"
         else :
-           functional = functional_list[-1] # use the xc appeared the last time 
+           functional = functional_list[-1] # use the xc appeared the last time
+
 
- 
         if functional:
             functionalMap = {
-                "LDA": ["LDA_X", "LDA_C_PZ"], 
-                "VMN": ["LDA_X", "LDA_C_VWN"], 
-                "PBE": ["GGA_X_PBE","GGA_C_PBE"], 
-                "PBE0": ["GGA_X_PBE","GGA_C_PBE"], 
-                "B3LYP": ["HYB_GGA_XC_B3LYP5"]  
-     #need to be extended to add alpha_PBE0 :coefficient of Hartree-Fock exchange in the PBE0 xc functional 
+                "LDA": ["LDA_X", "LDA_C_PZ"],
+                "VMN": ["LDA_X", "LDA_C_VWN"],
+                "PBE": ["GGA_X_PBE","GGA_C_PBE"],
+                "PBE0": ["GGA_X_PBE","GGA_C_PBE"],
+                "B3LYP": ["HYB_GGA_XC_B3LYP5"]
+     #need to be extended to add alpha_PBE0 :coefficient of Hartree-Fock exchange in the PBE0 xc functional
             }
             # Push the functional string into the backend
             nomadNames = functionalMap.get(functional)
@@ -132,7 +133,7 @@ class QboxParserContext(object):
 
 
     # #################################################################
-    # # (3.1) onClose for OUTPUT SCF (section_scf_iteration) 
+    # # (3.1) onClose for OUTPUT SCF (section_scf_iteration)
     # #################################################################
     # # Storing the total energy of each SCF iteration in an array
     # def onClose_section_scf_iteration(self, backend, gIndex, section):
@@ -140,15 +141,15 @@ class QboxParserContext(object):
     #     # get cached values for energy_total_scf_iteration
     #     ev = section['energy_total_scf_iteration']
     #     self.scfIterNr = len(ev)
-    # 
+    #
     #     #self.energy_total_scf_iteration_list.append(ev)
     #     #backend.addArrayValues('energy_total_scf_iteration_list', np.asarray(ev))
     #     #backend.addValue('number_of_scf_iterations', self.scfIterNr)
     #     #-----???shanghui want to know why can not add them.
- 
+
 
     #################################################################
-    # (3.2) onClose for OUTPUT eigenvalues (section_eigenvalues) 
+    # (3.2) onClose for OUTPUT eigenvalues (section_eigenvalues)
     #################################################################
     #def onClose_section_eigenvalues(self, backend, gIndex, section):
     #    """Trigger called when _section_eigenvalues is closed.
@@ -158,22 +159,22 @@ class QboxParserContext(object):
     #    evs =  []
 
     #    ev = section['qbox_eigenvalue_eigenvalue']
-    #    if ev is not None: 
+    #    if ev is not None:
     #       occ = section['qbox_eigenvalue_occupation']
-    #       occs.append(occ) 
+    #       occs.append(occ)
     #       evs.append(ev)
 
     #    self.eigenvalues_occupation = []
     #    self.eigenvalues_values = []
 
-    #    #self.eigenvalues_kpoints = [] 
+    #    #self.eigenvalues_kpoints = []
     #    self.eigenvalues_occupation.append(occs)
     #    self.eigenvalues_values.append(evs)
 
 
     ###################################################################
     # (3.4) onClose for geometry and force (section_system)
-    # todo: maybe we can move the force to onClose_section_single_configuration_calculation in the future. 
+    # todo: maybe we can move the force to onClose_section_single_configuration_calculation in the future.
     ###################################################################
     def onOpen_section_method(self, backend, gIndex, section):
         # keep track of the latest method section
@@ -227,7 +228,7 @@ class QboxParserContext(object):
         if unit_cell:
            backend.addArrayValues('simulation_cell', np.asarray(unit_cell))
            backend.addArrayValues("configuration_periodic_dimensions", np.ones(3, dtype=bool))
-  
+
 
 
 
@@ -251,10 +252,10 @@ class QboxParserContext(object):
            backend.addArrayValues('stress_tensor', np.transpose(np.asarray(x_qbox_stress_tensor)))
 
 
-   
 
 
-                
+
+
 
 #############################################################
 #################[2] MAIN PARSER STARTS HERE  ###############
@@ -269,19 +270,19 @@ def build_QboxMainFileSimpleMatcher():
     which allows nice formating of nested SimpleMatchers in python.
 
     Returns:
-       SimpleMatcher that parses main file of qbox. 
+       SimpleMatcher that parses main file of qbox.
     """
 
 
 
     #####################################################################
     # (1) submatcher for header
-    #note: we add x_qbox_section_functionals here because we want to add 
-    #      a default LDA here even 'set xc' is not shown in *.i file. 
+    #note: we add x_qbox_section_functionals here because we want to add
+    #      a default LDA here even 'set xc' is not shown in *.i file.
     #####################################################################
     headerSubMatcher = SM(name = 'ProgramHeader',
                   startReStr = r"\s*I qbox\s+(?P<program_version>[0-9.]+)",
-                  sections = ["x_qbox_section_functionals"],
+                #   sections = ["x_qbox_section_functionals"],
                   subMatchers = [
                      SM(r"\s*<nodename>\s+(?P<x_qbox_nodename>[a-zA-Z0-9.-]+)\s+</nodename>")
                                   ])
@@ -308,11 +309,11 @@ def build_QboxMainFileSimpleMatcher():
         subMatchers = [
 
            #-----load from xml file-----
-           # when using 'load *.xml' in *.i file, qbox will read geometry information from xml file, 
-           # here we call a QboxXMLParser to read the geometry information out. 
+           # when using 'load *.xml' in *.i file, qbox will read geometry information from xml file,
+           # here we call a QboxXMLParser to read the geometry information out.
            SM(name = "qboxXMLfile",
              startReStr = r"\s*LoadCmd",
-             forwardMatch = True, #use this or not like qboxXC 
+             forwardMatch = True, #use this or not like qboxXC
              sections = ["x_qbox_section_xml_file"],
              subMatchers = [
              SM(r"\s*LoadCmd:\s*loading from\s+(?P<x_qbox_loading_xml_file>[A-Za-z0-9./-_]+)"),
@@ -327,17 +328,17 @@ def build_QboxMainFileSimpleMatcher():
             SM(r"\s*\[qbox\]\s+<cmd>\s*set\s+atoms_dyn\s+(?P<x_qbox_atoms_dyn>[A-Za-z0-9]+)\s*</cmd>"),
             SM(r"\s*\[qbox\]\s+<cmd>\s*set\s+cell_dyn\s+(?P<x_qbox_cell_dyn>[A-Za-z0-9]+)\s*</cmd>"),
 
-        #--------set xc--------- 
+        #--------set xc---------
             SM(name = "qboxXC",
               startReStr = r"\s*\[qbox\]\s+<cmd>\s*set\s+xc\s+(?P<x_qbox_functional_name>[A-Za-z0-9]+)\s*</cmd>",
               sections = ["x_qbox_section_functionals"]
-               ), 
+               ),
 
         #-------set efield---------
             SM (r"\s*\[qbox\]\s*\[qbox\]\s*<cmd>\s*set\s+e_field\s*(?P<x_qbox_efield_x>[-+0-9.]+)\s+(?P<x_qbox_efield_y>[-+0-9.]+)\s+(?P<x_qbox_efield_z>[-+0-9.]+)\s*</cmd>",repeats = True)
           #???both this version adn qbox_section_efield version could not give mather for efield, need to check.
         ])
- 
+
     ####################################################################
     # (3.1) submatcher for OUPUT SCF, this Dmol parser, we need to change to qbox later
     ####################################################################
@@ -348,18 +349,18 @@ def build_QboxMainFileSimpleMatcher():
 #            SM(r"\s*Ef\s+(?P<energy_total_scf_iteration__hartree>[-+0-9.eEdD]+)\s+(?P<dmol3_binding_energy_scf_iteration__hartree>[-+0-9.eEdD]+)\s+(?P<dmol3_convergence_scf_iteration>[-+0-9.eEdD]+)\s+(?P<dmol3_time_scf_iteration>[0-9.eEdD]+)\s+(?P<dmol3_number_scf_iteration>[0-9]+)\s*",
 #               sections = ['section_scf_iteration'],
 #               repeats = True)
-# 
-#        ]) 
-      
+#
+#        ])
+
     ####################################################################
     # (3.2) submatcher for OUPUT eigenvalues,  this Dmol parser, we need to change to qbox later
-    ####################################################################                        
-#   eigenvalueSubMatcher = SM(name = 'Eigenvalues',                                             
-#       startReStr = r"\s*state\s+eigenvalue\s+occupation\s*",                                  
-#       sections = ['section_eigenvalues'],                                                     
-#       subMatchers = [                                                                         
+    ####################################################################
+#   eigenvalueSubMatcher = SM(name = 'Eigenvalues',
+#       startReStr = r"\s*state\s+eigenvalue\s+occupation\s*",
+#       sections = ['section_eigenvalues'],
+#       subMatchers = [
 #           SM(r"\s*[0-9]+\s+[+-]\s+[0-9]+\s+[A-Za-z]+\s+[-+0-9.eEdD]+\s+(?P<dmol3_eigenvalue_e igenvalue__eV>[-+0-9.eEdD]+)\s+(?P<dmol3_eigenvalue_occupation>[0-9.eEdD]+)", repeats = True)
-#       ]) 
+#       ])
 #
 
     ####################################################################
@@ -368,9 +369,9 @@ def build_QboxMainFileSimpleMatcher():
     totalenergySubMatcher = SM(name = 'Totalenergy',
         startReStr = r"\s*<ekin>",
         subMatchers = [
-            SM(r"\s*<etotal>\s+(?P<energy_total__hartree>[-+0-9.eEdD]+)\s+</etotal>",repeats = True ) 
-        ]) 
-    
+            SM(r"\s*<etotal>\s+(?P<energy_total__hartree>[-+0-9.eEdD]+)\s+</etotal>",repeats = True )
+        ])
+
 
     #####################################################################
     # (3.4) submatcher for OUTPUT relaxation_geometry(section_system)
@@ -439,7 +440,7 @@ def build_QboxMainFileSimpleMatcher():
         subMatchers = [
 
         #=============================================================================
-        #  read OUPUT file *.r, the method part comes from INPUT file *.i,  so we 
+        #  read OUPUT file *.r, the method part comes from INPUT file *.i,  so we
         #  do not need to parser INPUT file, the OUTPUT file contains all information
         #=============================================================================
         SM (name = 'NewRun',
@@ -451,15 +452,15 @@ def build_QboxMainFileSimpleMatcher():
             fixedStartValues={'program_name': 'qbox', 'program_basis_set_type': 'plane waves'},
             sections = ['section_run'],
             subMatchers = [
-             #-----------(1)output: header--------------------- 
+             #-----------(1)output: header---------------------
              headerSubMatcher,
- 
+
              #-----------(2)output: method---------------------
              calculationMethodSubMatcher,
              #-----------(2.2) efield----------------------
              # efieldSubMatcher,
 
-             #-----------(3)output: single configuration------- 
+             #-----------(3)output: single configuration-------
              SM(name = "single configuration matcher",
                 startReStr = r"\s*<iteration count*",
                 #endReStr = r"\s*</iteration>",
@@ -479,13 +480,13 @@ def build_QboxMainFileSimpleMatcher():
                     ]
                 ),
 
-             #-----------(4)output: properties------- 
+             #-----------(4)output: properties-------
              #-----------(4.1) MLWF----------------------
               MLWFSubMatcher,
              #-----------(4.3) dipole----------------------
               dipoleSubMatcher
 
-           ]) # CLOSING SM NewRun  
+           ]) # CLOSING SM NewRun
 
 
         ]) # END Root
@@ -497,7 +498,7 @@ def get_cachingLevelForMetaName(metaInfoEnv):
         metaInfoEnv: metadata which is an object of the class InfoKindEnv in nomadcore.local_meta_info.py.
 
     Returns:
-        Dictionary with metaname as key and caching level as value. 
+        Dictionary with metaname as key and caching level as value.
     """
     # manually adjust caching of metadata
     cachingLevelForMetaName = {
@@ -513,29 +514,45 @@ def get_cachingLevelForMetaName(metaInfoEnv):
     return cachingLevelForMetaName
 
 
+# get main file description
+QboxMainFileSimpleMatcher = build_QboxMainFileSimpleMatcher()
+# loading metadata from nomad-meta-info/meta_info/nomad_meta_info/qbox.nomadmetainfo.json
+metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(nomad_meta_info.__file__)), "qbox.nomadmetainfo.json"))
+metaInfoEnv = get_metaInfo(metaInfoPath)
+# set parser info
+parserInfo = {'name':'qbox-parser', 'version': '1.0'}
+# get caching level for metadata
+cachingLevelForMetaName = get_cachingLevelForMetaName(metaInfoEnv)
 
 
-def main():
-    """Main function.
 
-    Set up everything for the parsing of the qbox main file and run the parsing.
-    """
-    # get main file description
-    QboxMainFileSimpleMatcher = build_QboxMainFileSimpleMatcher()
-    # loading metadata from nomad-meta-info/meta_info/nomad_meta_info/qbox.nomadmetainfo.json
-    metaInfoPath = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../../nomad-meta-info/meta_info/nomad_meta_info/qbox.nomadmetainfo.json"))
-    metaInfoEnv = get_metaInfo(metaInfoPath)
-    # set parser info
-    parserInfo = {'name':'qbox-parser', 'version': '1.0'}
-    # get caching level for metadata
-    cachingLevelForMetaName = get_cachingLevelForMetaName(metaInfoEnv)
-    # start parsing
-    mainFunction(mainFileDescription = QboxMainFileSimpleMatcher,
-                 metaInfoEnv = metaInfoEnv,
-                 parserInfo = parserInfo,
-                 cachingLevelForMetaName = cachingLevelForMetaName,
-                 superContext = QboxParserContext())
+class QboxParser():
+    """ A proper class envolop for running this parser from within python. """
+    def __init__(self, backend, **kwargs):
+        self.backend_factory = backend
+
+    def parse(self, mainfile):
+        from unittest.mock import patch
+        logging.debug('qbox parser started')
+        logging.getLogger('nomadcore').setLevel(logging.WARNING)
+        backend = self.backend_factory(metaInfoEnv)
+        with patch.object(sys, 'argv', ['<exe>', '--uri', 'nmd://uri', mainfile]):
+            mainFunction(
+                mainFileDescription=QboxMainFileSimpleMatcher,
+                metaInfoEnv=metaInfoEnv,
+                parserInfo=parserInfo,
+                cachingLevelForMetaName=cachingLevelForMetaName,
+                superContext=QboxParserContext(),
+                superBackend=backend)
+
+        return backend
+
 
 if __name__ == "__main__":
-    main()
+    mainFunction(
+        mainFileDescription = QboxMainFileSimpleMatcher,
+        metaInfoEnv = metaInfoEnv,
+        parserInfo = parserInfo,
+        cachingLevelForMetaName = cachingLevelForMetaName,
+        superContext = QboxParserContext())
 
diff --git a/parser/parser-qbox/QboxXMLParser.py b/qboxparser/QboxXMLParser.py
similarity index 98%
rename from parser/parser-qbox/QboxXMLParser.py
rename to qboxparser/QboxXMLParser.py
index 570f4051..1a4b923d 100644
--- a/parser/parser-qbox/QboxXMLParser.py
+++ b/qboxparser/QboxXMLParser.py
@@ -1,18 +1,17 @@
 from builtins import object
-import setup_paths
 import numpy as np
 import nomadcore.ActivateLogging
 from nomadcore.caching_backend import CachingLevel
 from nomadcore.simple_parser import mainFunction
 from nomadcore.simple_parser import SimpleMatcher as SM
-from QboxCommon import get_metaInfo
+from .QboxCommon import get_metaInfo
 import logging, os, re, sys
 
 
 
 #################################################################################
 # This is the parser for the *.xml file of qbox to read the geometry information.
-# Only needed when 'load *.xml' is used in the *.r file. 
+# Only needed when 'load *.xml' is used in the *.r file.
 ###################################################################################
 
 logger = logging.getLogger("nomad.QboxXMLParser")
diff --git a/qboxparser/__init__.py b/qboxparser/__init__.py
new file mode 100644
index 00000000..f1c8d3f8
--- /dev/null
+++ b/qboxparser/__init__.py
@@ -0,0 +1 @@
+from qboxparser.QboxParser import QboxParser
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..f4eac5a7
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,33 @@
+# Copyright 2016-2018 Fawzi Mohamed, Lauri Himanen, Danio Brambila, Ankit Kariryaa, Henning Glawe
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+from setuptools import setup, find_packages
+
+
+def main():
+    setup(
+        name='qboxparser',
+        version='0.1',
+        description='NOMAD parser implementation for qbox.',
+        author='Fawzi Mohamed, Honghui Shang',
+        license='APACHE 2.0',
+        packages=find_packages(),
+        install_requires=[
+            'nomadcore'
+        ],
+    )
+
+
+if __name__ == '__main__':
+    main()
diff --git a/src/main/scala/eu/nomad_lab/parsers/QboxParser.scala b/src/main/scala/eu/nomad_lab/parsers/QboxParser.scala
deleted file mode 100644
index b934dd05..00000000
--- a/src/main/scala/eu/nomad_lab/parsers/QboxParser.scala
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-   Copyright 2016-2017 The NOMAD Developers Group
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
- */
-package eu.nomad_lab.parsers
-
-import eu.{ nomad_lab => lab }
-import eu.nomad_lab.DefaultPythonInterpreter
-import org.{ json4s => jn }
-import scala.collection.breakOut
-
-object QboxParser extends SimpleExternalParserGenerator(
-  name = "QboxParser",
-  parserInfo = jn.JObject(
-    ("name" -> jn.JString("QboxParser")) ::
-      ("parserId" -> jn.JString("QboxParser" + lab.QboxVersionInfo.version)) ::
-      ("versionInfo" -> jn.JObject(
-        ("nomadCoreVersion" -> jn.JObject(lab.NomadCoreVersionInfo.toMap.map {
-          case (k, v) => k -> jn.JString(v.toString)
-        }(breakOut): List[(String, jn.JString)])) ::
-          (lab.QboxVersionInfo.toMap.map {
-            case (key, value) =>
-              (key -> jn.JString(value.toString))
-          }(breakOut): List[(String, jn.JString)])
-      )) :: Nil
-  ),
-  mainFileTypes = Seq("application/xml"),
-  mainFileRe = """<\?xml version="1.0" encoding="UTF-8"\?>\s*
-\s*<fpmd:simulation xmlns:fpmd="http://www.quantum-simulation.org/ns/fpmd/fpmd-1.0">\s*
-\s*
-\s*=+\s*
-\s*I\s*qbox\s*(?<version>[-_.0-9A-Za-z]+).*I
-(?:\s*I.+I
-)*\s*I\s+http://qboxcode.org\s*I
-(?:\s*I.+I
-)*\s*=+\s*
-""".r,
-  cmd = Seq(DefaultPythonInterpreter.pythonExe(), "${envDir}/parsers/qbox/parser/parser-qbox/QboxParser.py",
-    "--uri", "${mainFileUri}", "${mainFilePath}"),
-  resList = Seq(
-    "parser-qbox/QboxParser.py",
-    "parser-qbox/QboxCommon.py",
-    "parser-qbox/QboxXMLParser.py",
-    "parser-qbox/setup_paths.py",
-    "nomad_meta_info/public.nomadmetainfo.json",
-    "nomad_meta_info/common.nomadmetainfo.json",
-    "nomad_meta_info/meta_types.nomadmetainfo.json",
-    "nomad_meta_info/qbox.nomadmetainfo.json"
-  ) ++ DefaultPythonInterpreter.commonFiles(),
-  dirMap = Map(
-    "parser-qbox" -> "parsers/qbox/parser/parser-qbox",
-    "nomad_meta_info" -> "nomad-meta-info/meta_info/nomad_meta_info"
-  ) ++ DefaultPythonInterpreter.commonDirMapping()
-)
diff --git a/src/test/scala/eu/nomad_lab/parsers/QboxParserSpec.scala b/src/test/scala/eu/nomad_lab/parsers/QboxParserSpec.scala
deleted file mode 100644
index 200e53e9..00000000
--- a/src/test/scala/eu/nomad_lab/parsers/QboxParserSpec.scala
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-   Copyright 2016-2017 The NOMAD Developers Group
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
- */
-package eu.nomad_lab.parsers
-
-import org.specs2.mutable.Specification
-
-object QboxParserSpec extends Specification {
-  "QboxParserTest" >> {
-    "test with json-events" >> {
-      ParserRun.parse(QboxParser, "parsers/qbox/test/examples/01_h2ogs.r", "json-events") must_== ParseResult.ParseSuccess
-    }
-    "test with json" >> {
-      ParserRun.parse(QboxParser, "parsers/qbox/test/examples/01_h2ogs.r", "json") must_== ParseResult.ParseSuccess
-    }
-  }
-}
-- 
GitLab