Skip to content
Snippets Groups Projects
Commit 5ee12763 authored by René Heß's avatar René Heß
Browse files

Split up kernel generation

parent 108821ff
No related branches found
No related tags found
No related merge requests found
...@@ -3,6 +3,8 @@ from os.path import splitext ...@@ -3,6 +3,8 @@ from os.path import splitext
import logging import logging
import numpy as np
from dune.perftool.options import (get_form_option, from dune.perftool.options import (get_form_option,
get_option, get_option,
option_switch) option_switch)
...@@ -707,81 +709,7 @@ def cgen_class_from_cache(tag, members=[]): ...@@ -707,81 +709,7 @@ def cgen_class_from_cache(tag, members=[]):
return Class(basename, base_classes=base_classes, members=[constructor] + members + pm + decls, tparam_decls=tparams) return Class(basename, base_classes=base_classes, members=[constructor] + members + pm + decls, tparam_decls=tparams)
def generate_localoperator_kernels(operator): def local_operator_default_settings(operator, form):
logger = logging.getLogger(__name__)
data = get_global_context_value("data")
original_form = data.object_by_name[get_form_option("form")]
from dune.perftool.ufl.preprocess import preprocess_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
# Jacobian of the adjoint form
jacform = derivative(original_form, original_form.coefficients()[0])
adjoint_jacform = adjoint(jacform)
# Derivative of objective function w.r.t. state
objective = data.object_by_name[get_form_option("objective_function")]
objective_jacobian = derivative(objective, objective.coefficients()[0])
# Replace coefficient belonging to ansatz function with new coefficient
element = objective.coefficients()[0].ufl_element()
coeff = Coefficient(element, count=3)
objective_jacobian = replace(objective_jacobian, {objective.coefficients()[0]: coeff})
if len(adjoint_jacform.coefficients()) > 0:
adjoint_jacform = replace(adjoint_jacform, {adjoint_jacform.coefficients()[0]: coeff})
# Residual of the adjoint form
adjoint_form = action(adjoint_jacform, original_form.coefficients()[0])
adjoint_form = adjoint_form + objective_jacobian
# Update form and original_form
original_form = adjoint_form
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, replace
from ufl.classes import Coefficient
control_form = diff(original_form, control)
# element = control_form.coefficients()[0].ufl_element()
# coeff = Coefficient(element, count=3)
# control_form = replace(control_form, {control_form.coefficients()[0]: coeff})
original_form = control_form
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
delete_cache_items()
# Manage includes and base classes that we always need # Manage includes and base classes that we always need
include_file('dune/pdelab/gridfunctionspace/gridfunctionspace.hh', filetag="operatorfile") include_file('dune/pdelab/gridfunctionspace/gridfunctionspace.hh', filetag="operatorfile")
include_file('dune/pdelab/localoperator/idefault.hh', filetag="operatorfile") include_file('dune/pdelab/localoperator/idefault.hh', filetag="operatorfile")
...@@ -825,10 +753,9 @@ def generate_localoperator_kernels(operator): ...@@ -825,10 +753,9 @@ def generate_localoperator_kernels(operator):
base_class('Dune::PDELab::InstationaryLocalOperatorDefaultMethods<{}>' base_class('Dune::PDELab::InstationaryLocalOperatorDefaultMethods<{}>'
.format(rf), classtag="operator") .format(rf), classtag="operator")
# Have a data structure collect the generated kernels
operator_kernels = {}
logger.info("generate_localoperator_kernels: create residual methods") def generate_residual_kernels(form):
logger = logging.getLogger(__name__)
with global_context(form_type='residual'): with global_context(form_type='residual'):
# Generate the necessary residual methods # Generate the necessary residual methods
for measure in set(i.integral_type() for i in form.integrals()): for measure in set(i.integral_type() for i in form.integrals()):
...@@ -883,63 +810,181 @@ def generate_localoperator_kernels(operator): ...@@ -883,63 +810,181 @@ def generate_localoperator_kernels(operator):
classtag="operator", classtag="operator",
) )
operator_kernels[(measure, 'residual')] = kernel return {(measure, 'residual'): kernel}
# Generate the necessary jacobian methods
if not get_form_option("numerical_jacobian"):
logger.info("generate_localoperator_kernels: create jacobian methods")
from ufl import derivative
jacform = derivative(original_form, original_form.coefficients()[0])
from dune.perftool.ufl.preprocess import preprocess_form def generate_jacobian_kernels(form, original_form):
jacform = preprocess_form(jacform).preprocessed_form logger = logging.getLogger(__name__)
with global_context(form_type="jacobian"): from ufl import derivative
if get_form_option("generate_jacobians"): jacform = derivative(original_form, original_form.coefficients()[0])
for measure in set(i.integral_type() for i in jacform.integrals()):
logger.info("generate_localoperator_kernels: measure {}".format(measure)) from dune.perftool.ufl.preprocess import preprocess_form
with global_context(integral_type=measure): jacform = preprocess_form(jacform).preprocessed_form
with global_context(kernel=assembler_routine_name()):
kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jacform.integrals_by_type(measure))] operator_kernels = {}
operator_kernels[(measure, 'jacobian')] = kernel with global_context(form_type="jacobian"):
if get_form_option("generate_jacobians"):
for measure in set(i.integral_type() for i in jacform.integrals()):
logger.info("generate_localoperator_kernels: measure {}".format(measure))
with global_context(integral_type=measure):
from dune.perftool.pdelab.signatures import assembler_routine_name
with global_context(kernel=assembler_routine_name()):
kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jacform.integrals_by_type(measure))]
operator_kernels[(measure, 'jacobian')] = kernel
# Generate dummy functions for those kernels, that vanished in the differentiation process
# We *could* solve this problem by using lambda_* terms but we do not really want that, so
# we use empty jacobian assembly methods instead
alpha_measures = set(i.integral_type() for i in form.integrals())
jacobian_measures = set(i.integral_type() for i in jacform.integrals())
for it in alpha_measures - jacobian_measures:
with global_context(integral_type=it):
from dune.perftool.pdelab.signatures import assembly_routine_signature
operator_kernels[(it, 'jacobian')] = [LoopyKernelMethod(assembly_routine_signature(), kernel=None)]
# Jacobian apply methods for matrix-free computations
if get_form_option("matrix_free"):
# The apply vector has reserved index 1 so we directly use Coefficient class from ufl
from ufl import Coefficient
apply_coefficient = Coefficient(form.coefficients()[0].ufl_element(), 1)
# Create application of jacobian on vector
from ufl import action
jac_apply_form = action(jacform, apply_coefficient)
# Create kernel for jacobian application
with global_context(form_type="jacobian_apply"):
for measure in set(i.integral_type() for i in jac_apply_form.integrals()):
with global_context(integral_type=measure):
with global_context(kernel=assembler_routine_name()):
kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jac_apply_form.integrals_by_type(measure))]
operator_kernels[(measure, 'jacobian_apply')] = kernel
# Generate dummy functions for those kernels, that vanished in the differentiation process # Generate dummy functions for those kernels, that vanished in the differentiation process
# We *could* solve this problem by using lambda_* terms but we do not really want that, so # We *could* solve this problem by using lambda_* terms but we do not really want that, so
# we use empty jacobian assembly methods instead # we use empty jacobian assembly methods instead
alpha_measures = set(i.integral_type() for i in form.integrals()) alpha_measures = set(i.integral_type() for i in form.integrals())
jacobian_measures = set(i.integral_type() for i in jacform.integrals()) jacobian_apply_measures = set(i.integral_type() for i in jac_apply_form.integrals())
for it in alpha_measures - jacobian_measures: for it in alpha_measures - jacobian_apply_measures:
with global_context(integral_type=it): with global_context(integral_type=it):
from dune.perftool.pdelab.signatures import assembly_routine_signature from dune.perftool.pdelab.signatures import assembly_routine_signature
operator_kernels[(it, 'jacobian')] = [LoopyKernelMethod(assembly_routine_signature(), kernel=None)] operator_kernels[(it, 'jacobian_apply')] = [LoopyKernelMethod(assembly_routine_signature(), kernel=None)]
# Jacobian apply methods for matrix-free computations return operator_kernels
if get_form_option("matrix_free"):
# The apply vector has reserved index 1 so we directly use Coefficient class from ufl
from ufl import Coefficient def generate_localoperator_kernels(operator):
apply_coefficient = Coefficient(form.coefficients()[0].ufl_element(), 1) logger = logging.getLogger(__name__)
# Create application of jacobian on vector data = get_global_context_value("data")
from ufl import action original_form = data.object_by_name[get_form_option("form")]
jac_apply_form = action(jacform, apply_coefficient) from dune.perftool.ufl.preprocess import preprocess_form
# Create kernel for jacobian application if get_form_option("adjoint"):
with global_context(form_type="jacobian_apply"): # Generate adjoint operator
for measure in set(i.integral_type() for i in jac_apply_form.integrals()): #
with global_context(integral_type=measure): # The jacobian of the adjoint form is just the jacobian of the
with global_context(kernel=assembler_routine_name()): # original form with test and ansazt function swapped. A a
kernel = [k for k in get_backend(interface="generate_kernels_per_integral")(jac_apply_form.integrals_by_type(measure))] # linear form you have to subtract the derivative of the
operator_kernels[(measure, 'jacobian_apply')] = kernel # objective function w.r.t the ansatz function to get the
# final residual formulation of the adjoint.
# Generate dummy functions for those kernels, that vanished in the differentiation process #
# We *could* solve this problem by using lambda_* terms but we do not really want that, so # TODO: This might only be true for linear problems. Adjust
# we use empty jacobian assembly methods instead # documentation as knowledge improves ;)
alpha_measures = set(i.integral_type() for i in form.integrals()) assert get_form_option("objective_function") is not None
jacobian_apply_measures = set(i.integral_type() for i in jac_apply_form.integrals()) assert get_form_option("control") is False
for it in alpha_measures - jacobian_apply_measures:
with global_context(integral_type=it): from ufl import derivative, adjoint, action, replace
from dune.perftool.pdelab.signatures import assembly_routine_signature from ufl.classes import Coefficient
operator_kernels[(it, 'jacobian_apply')] = [LoopyKernelMethod(assembly_routine_signature(), kernel=None)]
# Jacobian of the adjoint form
jacform = derivative(original_form, original_form.coefficients()[0])
adjoint_jacform = adjoint(jacform)
# Derivative of objective function w.r.t. state
objective = data.object_by_name[get_form_option("objective_function")]
objective_jacobian = derivative(objective, objective.coefficients()[0])
# Replace coefficient belonging to ansatz function with new coefficient
element = objective.coefficients()[0].ufl_element()
coeff = Coefficient(element, count=3)
objective_jacobian = replace(objective_jacobian, {objective.coefficients()[0]: coeff})
if len(adjoint_jacform.coefficients()) > 0:
adjoint_jacform = replace(adjoint_jacform, {adjoint_jacform.coefficients()[0]: coeff})
# Residual of the adjoint form
adjoint_form = action(adjoint_jacform, original_form.coefficients()[0])
adjoint_form = adjoint_form + objective_jacobian
# Update form and original_form
original_form = adjoint_form
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.
assert get_form_option("control_variable") is not None
controls = [data.object_by_name[ctrl.strip()] for ctrl in get_form_option("control_variable").split(",")]
assert len(controls) == 1
# We need to transform numpy ints to python native ints
def _unravel(flat_index, shape):
multi_index = np.unravel_index(flat_index, shape)
multi_index = tuple(int(i) for i in multi_index)
return multi_index
forms = []
for control in controls:
shape = control.ufl_shape
flat_length = np.prod(shape)
for i in range(flat_length):
c = control[_unravel(i, shape)]
control_form = diff(original_form, control)
forms.append(preprocess_form(control_form).preprocessed_form)
# Used to create local operator default settings
form = preprocess_form(original_form).preprocessed_form
# control = data.object_by_name[get_form_option("control_variable")]
# assert control.ufl_shape is ()
# from ufl import diff, replace
# from ufl.classes import Coefficient
# control_form = diff(original_form, control)
# # element = control_form.coefficients()[0].ufl_element()
# # coeff = Coefficient(element, count=3)
# # control_form = replace(control_form, {control_form.coefficients()[0]: coeff})
# original_form = control_form
# 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
delete_cache_items()
# Have a data structure collect the generated kernels
operator_kernels = {}
# Generate things needed for all local operator files
local_operator_default_settings(operator, form)
if get_form_option("control"):
logger.info("generate_localoperator_kernels: create methods for control operator")
operator_kernels.update(generate_control_kernels(forms))
else:
logger.info("generate_localoperator_kernels: create residual methods")
operator_kernels.update(generate_residual_kernels(form))
# Generate the necessary jacobian methods
if not get_form_option("numerical_jacobian"):
logger.info("generate_localoperator_kernels: create jacobian methods")
operator_kernels.update(generate_jacobian_kernels(form, original_form))
# Return the set of generated kernels # Return the set of generated kernels
return operator_kernels return operator_kernels
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment