From 87a7a367ed04dc6d010d5e02792764c2be768f65 Mon Sep 17 00:00:00 2001
From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de>
Date: Mon, 29 Jan 2018 11:29:36 +0100
Subject: [PATCH] Introduce separate PerftoolFormOptionsArray

---
 python/dune/perftool/options.py | 77 +++++++++++++++++++++++----------
 1 file changed, 53 insertions(+), 24 deletions(-)

diff --git a/python/dune/perftool/options.py b/python/dune/perftool/options.py
index bc96b7d7..8ed41f79 100644
--- a/python/dune/perftool/options.py
+++ b/python/dune/perftool/options.py
@@ -25,10 +25,10 @@ class PerftoolOption(ImmutableRecord):
                                  )
 
 
-class PerftoolOptionsArray(ImmutableRecord):
+class PerftoolGlobalOptionsArray(ImmutableRecord):
     """ A collection of form compiler arguments """
     def __init__(self, **kwargs):
-        opts = {k: v.default for k, v in PerftoolOptionsArray.__dict__.items() if isinstance(v, PerftoolOption)}
+        opts = {k: v.default for k, v in PerftoolGlobalOptionsArray.__dict__.items() if isinstance(v, PerftoolOption)}
         opts.update(**kwargs)
         ImmutableRecord.__init__(self, **opts)
 
@@ -49,13 +49,24 @@ class PerftoolOptionsArray(ImmutableRecord):
     yaspgrid_offset = PerftoolOption(default=False, helpstr="Set to true if you want a yasp grid where the lower left corner is not in the origin.")
     precision_bits = PerftoolOption(default=64, helpstr="The number of bits for the floating point type")
     overlapping = PerftoolOption(default=False, helpstr="Use an overlapping solver and constraints. You still need to make sure to construct a grid with overlap! The parallel option will be set automatically.")
+    operators = PerftoolOption(default="operator", helpstr="A comma separated list of operators, each name will be interpreted as a subsection name within the formcompiler section")
 
     # Arguments that are mainly to be set by logic depending on other options
     max_vector_width = PerftoolOption(default=256, helpstr=None)
     parallel = PerftoolOption(default=False, helpstr="Mark that this program should be run in parallel. If set to true the c++ code will check that there are more than 1 MPI-ranks involved and the error computation will use communication.")
 
-    # Form specific options
+    #TODO: should be form specific
     operator_file = PerftoolOption(helpstr="The filename for the generated local operator header")
+
+
+class PerftoolFormOptionsArray(ImmutableRecord):
+    """ A collection of form-specific form compiler arguments """
+    def __init__(self, **kwargs):
+        opts = {k: v.default for k, v in PerftoolFormOptionsArray.__dict__.items() if isinstance(v, PerftoolOption)}
+        opts.update(**kwargs)
+        ImmutableRecord.__init__(self, **opts)
+
+    # Form specific options
     numerical_jacobian = PerftoolOption(default=False, helpstr="use numerical jacobians (only makes sense, if uflpdelab for some reason fails to generate analytic jacobians)")
     matrix_free = PerftoolOption(default=False, helpstr="Use iterative solver with matrix free jacobian application")
     print_transformations = PerftoolOption(default=False, helpstr="print out dot files after ufl tree transformations")
@@ -82,19 +93,20 @@ class PerftoolOptionsArray(ImmutableRecord):
 
 
 # Until more sophisticated logic is needed, we keep the actual option data in this module
-_options = PerftoolOptionsArray()
+_global_options = PerftoolGlobalOptionsArray()
+_form_options = {}
 
 
 def initialize_options():
     """ Initialize the options from the command line """
-    global _options
-    _options = update_options_from_commandline(_options)
-    _options = update_options_from_inifile(_options)
+    global _global_options
+    _global_options = update_options_from_commandline(_global_options)
+    _global_options = update_options_from_inifile(_global_options)
 
 
 def update_options_from_commandline(opt):
     """ Return an options array object with updated values from the commandline """
-    assert isinstance(opt, PerftoolOptionsArray)
+    assert isinstance(opt, PerftoolGlobalOptionsArray)
     parser = ArgumentParser(description="Compile UFL files to PDELab C++ code",
                             epilog="Please report bugs to dominic.kempf@iwr.uni-heidelberg.de",
                             )
@@ -110,32 +122,44 @@ def update_options_from_commandline(opt):
 def update_options_from_inifile(opt):
     """ Return an options array object with updated values from an inifile """
     if opt.ini_file:
-        def _fix_types(k, v):
-            if hasattr(type(opt), k) and getattr(type(opt), k).type is bool:
-                return bool(eval(v))
-            if hasattr(type(opt), k):
-                return getattr(type(opt), k).type(v)
-            return v
-        ini = parse_ini_file(opt.ini_file).get("formcompiler", {})
-        ini = {k: _fix_types(k, v) for k, v in ini.items()}
-        opt = opt.copy(**ini)
+        def parse_ini(section):
+            def _fix_types(k, v):
+                if hasattr(type(opt), k) and getattr(type(opt), k).type is bool:
+                    return bool(eval(v))
+                if hasattr(type(opt), k):
+                    return getattr(type(opt), k).type(v)
+                return v
+            ini = parse_ini_file(opt.ini_file).get(section, {})
+            return {k: _fix_types(k, v) for k, v in ini.items()}
+
+        opt = opt.copy(**parse_ini("formcompiler"))
+
+        # Also parse form-specific options
+        for form in opt.operators.split(","):
+            _form_options[form] = PerftoolFormOptionsArray(**parse_ini("formcompiler.{}".format(form)))
+
     return opt
 
 
 @memoize
-def process_options(opt):
+def process_global_options(opt):
     """ Make sure that the options have been fully processed """
     opt = expand_architecture_options(opt)
 
+    if opt.overlapping:
+        opt = opt.copy(parallel=True)
+
+    return opt
+
+
+@memoize
+def process_form_options(opt):
     if opt.sumfact:
         opt = opt.copy(unroll_dimension_loops=True)
 
     if opt.numerical_jacobian:
         opt = opt.copy(generate_jacobians=False)
 
-    if opt.overlapping:
-        opt = opt.copy(parallel=True)
-
     return opt
 
 
@@ -157,12 +181,17 @@ def set_option(key, value):
     overwritten.  Form compiler arguments will always be set before
     any other options.
     """
-    global _options
-    _options = process_options(_options).copy(**{key: value})
+    global _global_options
+    _global_options = process_options(_global_options).copy(**{key: value})
 
 
 def get_option(key):
-    return getattr(process_options(_options), key)
+    processed_global_opts = process_global_options(_global_options)
+    if hasattr(processed_global_opts, key):
+        return getattr(processed_global_opts, key)
+    else:
+        processed_form_opts = process_form_options(_form_options["operator"])
+        return getattr(processed_form_opts, key)
 
 
 def option_switch(opt):
-- 
GitLab