From 04e5a5309983658152e0d0b193a742524b50d290 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20He=C3=9F?= <rene.hess@iwr.uni-heidelberg.de>
Date: Fri, 2 Feb 2018 17:40:55 +0100
Subject: [PATCH] Generate adjoint operators (only scalar controls!)

---
 python/dune/perftool/options.py              |  7 ++-
 python/dune/perftool/pdelab/geometry.py      |  7 ++-
 python/dune/perftool/pdelab/localoperator.py | 55 ++++++++++++++++++--
 python/dune/perftool/ufl/visitor.py          |  7 +++
 4 files changed, 69 insertions(+), 7 deletions(-)

diff --git a/python/dune/perftool/options.py b/python/dune/perftool/options.py
index 067d6da6..50417725 100644
--- a/python/dune/perftool/options.py
+++ b/python/dune/perftool/options.py
@@ -91,7 +91,10 @@ class PerftoolFormOptionsArray(ImmutableRecord):
     precompute_quadrature_info = PerftoolOption(default=True, helpstr="compute quadrature points and weights in the constructor of the local operator")
     blockstructured = PerftoolOption(default=False, helpstr="Use block structure")
     number_of_blocks = PerftoolOption(default=1, helpstr="Number of sub blocks in one direction")
-
+    adjoint = PerftoolOption(default=False, helpstr="Generate adjoint operator")
+    control = PerftoolOption(default=False, helpstr="Generate operator of derivative w.r.t. the control variable")
+    objective_function = PerftoolOption(default=None, helpstr="Name of form representing the objective function in UFL file")
+    control_variable = PerftoolOption(default=None, helpstr="Name of control variable in UFL file")
 
 # Until more sophisticated logic is needed, we keep the actual option data in this module
 _global_options = PerftoolGlobalOptionsArray()
@@ -164,7 +167,7 @@ def process_form_options(opt, form):
         opt = opt.copy(form=form)
 
     if opt.classname is None:
-        opt = opt.copy(classname="{}Operator".format(opt.form))
+        opt = opt.copy(classname="{}Operator".format(form))
 
     if opt.filename is None:
         opt = opt.copy(filename="{}_{}_file.hh".format(get_option("target_name"), opt.classname))
diff --git a/python/dune/perftool/pdelab/geometry.py b/python/dune/perftool/pdelab/geometry.py
index 6c400cc4..d5360273 100644
--- a/python/dune/perftool/pdelab/geometry.py
+++ b/python/dune/perftool/pdelab/geometry.py
@@ -231,8 +231,11 @@ def to_cell_coordinates(local, restriction):
 
 
 def world_dimension():
-    from dune.perftool.pdelab.driver import get_dimension
-    return get_dimension()
+    data = get_global_context_value("data")
+    form = data.object_by_name[get_form_option("form")]
+    from dune.perftool.ufl.preprocess import preprocess_form
+    form = preprocess_form(form).preprocessed_form
+    return form.ufl_cell().geometric_dimension()
 
 
 def intersection_dimension():
diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py
index 4873920b..cc8aa4e1 100644
--- a/python/dune/perftool/pdelab/localoperator.py
+++ b/python/dune/perftool/pdelab/localoperator.py
@@ -712,9 +712,58 @@ def generate_localoperator_kernels(operator):
 
     data = get_global_context_value("data")
     original_form = data.object_by_name[get_form_option("form")]
-
     from dune.perftool.ufl.preprocess import preprocess_form
-    form = preprocess_form(original_form).preprocessed_form
+
+    if get_form_option("adjoint"):
+        # Generate adjoint operator
+        #
+        # The jacobian of the adjoint form is just the jacobian of the
+        # original form with test and ansazt function swapped. A a
+        # linear form you have to subtract the derivative of the
+        # objective function w.r.t the ansatz function to get the
+        # final residual formulation of the adjoint.
+        #
+        # TODO: This might only be true for linear problems. Adjust
+        # documentation as knowledge improves ;)
+        assert get_form_option("objective_function") is not None
+        assert get_form_option("control") is False
+
+        from ufl import derivative, adjoint, action, replace
+        from ufl.classes import Coefficient
+
+        jacform = derivative(original_form, original_form.coefficients()[0])
+        adjoint_jacform = adjoint(jacform)
+        adjoint_form = action(adjoint_jacform, original_form.coefficients()[0])
+
+        objective = data.object_by_name[get_form_option("objective_function")]
+        jacobian_objective = derivative(objective, objective.coefficients()[0])
+
+        element = objective.coefficients()[0].ufl_element()
+        coeff = Coefficient(element)
+        jacobian_objective = replace(jacobian_objective, {objective.coefficients()[0]: coeff})
+
+        adjoint_form = adjoint_form + jacobian_objective
+
+        form = preprocess_form(adjoint_form).preprocessed_form
+
+    elif get_form_option("control"):
+        # Generate control operator
+        #
+        # This is the normal form derived w.r.t. the control
+        # variable. Right now this is olny implemented for scalar
+        # controls.
+        assert get_form_option("control_variable") is not None
+
+        control = data.object_by_name[get_form_option("control_variable")]
+        assert control.ufl_shape is ()
+
+        from ufl import diff
+        control_form = diff(original_form, control)
+        form = preprocess_form(control_form).preprocessed_form
+
+    else:
+        form = preprocess_form(original_form).preprocessed_form
+
 
     # Reset the generation cache
     from dune.perftool.generation import delete_cache_items
@@ -749,7 +798,7 @@ def generate_localoperator_kernels(operator):
 
     # Set some options!
     from dune.perftool.pdelab.driver import isQuadrilateral
-    if isQuadrilateral(form.coefficients()[0].ufl_element().cell()):
+    if isQuadrilateral(form.arguments()[0].ufl_element().cell()):
         from dune.perftool.options import set_form_option
         # For Yasp Grids the jacobian of the transformation is diagonal and constant on each cell
         set_form_option('diagonal_transformation_matrix', True)
diff --git a/python/dune/perftool/ufl/visitor.py b/python/dune/perftool/ufl/visitor.py
index aa9d7714..e208de92 100644
--- a/python/dune/perftool/ufl/visitor.py
+++ b/python/dune/perftool/ufl/visitor.py
@@ -169,6 +169,13 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker):
 
             return self.interface.pymbolic_gridfunction(o, restriction, self.grad)
 
+
+    def variable(self, o):
+        # Right now only scalar varibables are supported
+        assert o.ufl_shape is ()
+        return o.expression().value()
+
+
     #
     # Handlers for all indexing related stuff
     #
-- 
GitLab