diff --git a/python/dune/perftool/ufl/visitor.py b/python/dune/perftool/ufl/visitor.py index 3c4562c96dc1532461f46d53cd6ef8cc9edd7e8f..cbede6c30fa7d6754b58b57b662ba126a1dec6b5 100644 --- a/python/dune/perftool/ufl/visitor.py +++ b/python/dune/perftool/ufl/visitor.py @@ -343,6 +343,22 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker): def min_value(self, o): return self._minmax_impl(min, "min", tuple(self.call(op) for op in o.ufl_operands)) + def math_function(self, o): + # MathFunction is a base class for unary functions. We use this to provide + # custom functions. Such a custom functions inherits from it and defines the + # following methods: + # * visit: This function is called from here to delegate the visiting process + # to the user code. The only argument is this visitor instance. + # * derivative: It is called from UFL AD code to determine the derivative. + # Upstream documentation indicates that FEniCS allows the same + # (ab)use of the MathFunction node. + # Note that if the __init__ method of your function differs from MathFunction, + # you also need to implement the method _ufl_expr_reconstruct_ + if hasattr(o, "visit"): + return o.visit(self) + else: + raise NotImplementedError("Function {} is not known to dune-perftool.".format(o._name)) + # # Handler for conditionals, use pymbolic base implementation # diff --git a/test/poisson/CMakeLists.txt b/test/poisson/CMakeLists.txt index 68c635bc79eef0818780ca09d36c808c1eb01954..a05cbe8c3650c9e33f8f5c4f1bbce696b2df9ba8 100644 --- a/test/poisson/CMakeLists.txt +++ b/test/poisson/CMakeLists.txt @@ -67,6 +67,12 @@ dune_add_formcompiler_system_test(UFLFILE poisson_dg_tensor.ufl INIFILE poisson_dg_tensor.mini ) +# 12. Poisson Test Case with a custom function +dune_add_formcompiler_system_test(UFLFILE poisson_customfunction.ufl + BASENAME poisson_customfunction + INIFILE poisson_customfunction.mini + ) + # the reference vtk file add_executable(poisson_dg_ref reference_main.cc) set_target_properties(poisson_dg_ref PROPERTIES EXCLUDE_FROM_ALL 1) diff --git a/test/poisson/poisson_customfunction.mini b/test/poisson/poisson_customfunction.mini new file mode 100644 index 0000000000000000000000000000000000000000..c45e50fabd2b2b7743a0a6d94a3eb88e6926800f --- /dev/null +++ b/test/poisson/poisson_customfunction.mini @@ -0,0 +1,18 @@ +__name = poisson_customfunction_{__exec_suffix} +__exec_suffix = numdiff, symdiff | expand num + +lowerleft = 0.0 0.0 +upperright = 1.0 1.0 +elements = 32 32 +elementType = simplical + +[wrapper.vtkcompare] +name = {__name} +reference = poisson_ref +extension = vtu + +[formcompiler] +compare_l2errorsquared = 1e-7 + +[formcompiler.r] +numerical_jacobian = 1, 0 | expand num diff --git a/test/poisson/poisson_customfunction.ufl b/test/poisson/poisson_customfunction.ufl new file mode 100644 index 0000000000000000000000000000000000000000..026e069d697e8bfe05e8393abaf1118169f884a3 --- /dev/null +++ b/test/poisson/poisson_customfunction.ufl @@ -0,0 +1,33 @@ +import ufl + +cell = triangle + +x = SpatialCoordinate(cell) + +class SquareFct(ufl.classes.MathFunction): + def __init__(self, arg): + ufl.classes.MathFunction.__init__(self, 'square', arg) + + def _ufl_expr_reconstruct_(self, *operands): + return SquareFct(*operands) + + def derivative(self): + return 2 * self.ufl_operands[0] + + def visit(self, visitor): + op = visitor.call(self.ufl_operands[0]) + return op * op + + +c = SquareFct(0.5-x[0]) + SquareFct(0.5-x[1]) +g = exp(-1.*c) +f = 4*(1.-c)*g + +V = FiniteElement("CG", cell, 1) +u = TrialFunction(V) +v = TestFunction(V) + +r = (inner(grad(u), grad(v)) - f*v)*dx +exact_solution = g +interpolate_expression = g +is_dirichlet = 1 \ No newline at end of file