diff --git a/dune/perftool/matrixfree.hh b/dune/perftool/matrixfree.hh index 3ffa62f1a9fbba76168e55ce0c8dbfdb43301dd7..4ab94377e2aa7d8790360042c89a9423bf4a6352 100644 --- a/dune/perftool/matrixfree.hh +++ b/dune/perftool/matrixfree.hh @@ -3,12 +3,14 @@ #include <iostream> -#include <dune/common/timer.hh> -#include <dune/common/parametertree.hh> +// #include <dune/common/timer.hh> +// #include <dune/common/parametertree.hh> -#include <dune/pdelab/backend/interface.hh> -#include <dune/pdelab/constraints/common/constraints.hh> -#include <dune/pdelab/backend/solver.hh> +// #include <dune/pdelab/backend/interface.hh> +// #include <dune/pdelab/constraints/common/constraints.hh> +// #include <dune/pdelab/backend/solver.hh> + +#include <dune/pdelab/backend/istl/seqistlsolverbackend.hh> namespace Dune{ namespace PDELab{ @@ -18,7 +20,7 @@ namespace Dune{ typedef Dune::PDELab::OnTheFlyOperator<V,V,GO> ISTLOnTheFlyOperator; ISTLOnTheFlyOperator opb(go); Dune::Richardson<V,V> richardson(1.0); - Dune::CGSolver<V> solverb(opb,richardson,1E-10,5000,2); + Dune::BiCGSTABSolver<V> solverb(opb,richardson,1E-10,5000,2); Dune::InverseOperatorResult stat; // evaluate residual w.r.t initial guess using TrialGridFunctionSpace = typename GO::Traits::TrialGridFunctionSpace; diff --git a/python/dune/perftool/loopy/transformer.py b/python/dune/perftool/loopy/transformer.py index 54938c547e06ccf743fdcac29594cd1eed7396da..3cad73b4eb16076948c536db2951ea486916c4b2 100644 --- a/python/dune/perftool/loopy/transformer.py +++ b/python/dune/perftool/loopy/transformer.py @@ -50,12 +50,13 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp # Initialize the local function spaces that we might need for this term # We therefore gather a list of modified trial functions too. from dune.perftool.ufl.modified_terminals import extract_modified_arguments - test_ma = extract_modified_arguments(o) - trial_ma = extract_modified_arguments(o, trialfunction=True, testfunction=False) + test_ma = extract_modified_arguments(o,) + trial_ma = extract_modified_arguments(o, testfunction=False, trialfunction=True) + apply_ma = extract_modified_arguments(o, testfunction=False, applyfunction=True) # All test and trial functions on boundary integrals are technically restricted import itertools - for ma in itertools.chain(test_ma, trial_ma): + for ma in itertools.chain(test_ma, trial_ma, apply_ma): if self.measure == 'exterior_facet': ma.restriction = Restriction.NEGATIVE @@ -209,21 +210,29 @@ class UFL2LoopyVisitor(ModifiedTerminalTracker, UFL2PymbolicMapper, GeometryMapp return Subscript(Variable(name_testfunction(leaf_element, restriction)), (Variable(iname),)) def coefficient(self, o): - # If this is a trialfunction, we do something entirely different - if o.count() == 0: + # Do something different for trial function and coefficients from jacobian apply + if o.count() == 0 or o.count() == 1: # Correct the restriction on boundary integrals restriction = self.restriction if self.measure == 'exterior_facet': restriction = Restriction.NEGATIVE if self.grad: - from dune.perftool.pdelab.argument import name_trialfunction_gradient - return Variable(name_trialfunction_gradient(o.ufl_element(), restriction, self.component)) + if o.count() == 0: + from dune.perftool.pdelab.argument import name_trialfunction_gradient + return Variable(name_trialfunction_gradient(o.ufl_element(), restriction, self.component)) + else: + from dune.perftool.pdelab.argument import name_apply_function_gradient + return Variable(name_apply_function_gradient(o.ufl_element(), restriction, self.component)) else: - from dune.perftool.pdelab.argument import name_trialfunction - return Variable(name_trialfunction(o.ufl_element(), restriction, self.component)) - - # so this is a parameter function + if o.count() == 0: + from dune.perftool.pdelab.argument import name_trialfunction + return Variable(name_trialfunction(o.ufl_element(), restriction, self.component)) + else: + from dune.perftool.pdelab.argument import name_apply_function + return Variable(name_apply_function(o.ufl_element(), restriction, self.component)) + + # Check if this is a parameter function else: # We expect all coefficients to be of type Expression or VectorExpression! from dune.perftool.ufl.execution import Expression, VectorExpression diff --git a/python/dune/perftool/pdelab/argument.py b/python/dune/perftool/pdelab/argument.py index 6ff8444d27df79f4822629de12d15310422448e6..818075addc1ac1e04f95191007361e6c2517991f 100644 --- a/python/dune/perftool/pdelab/argument.py +++ b/python/dune/perftool/pdelab/argument.py @@ -47,6 +47,22 @@ def name_trialfunction(element, restriction, component): return name +@symbol +def name_apply_function_gradient(element, restriction, component): + rawname = "gradz_func" + "_".join(str(c) for c in component) + name = restricted_name(rawname, restriction) + evaluate_trialfunction_gradient(element, name, restriction, component) + return name + + +@symbol +def name_apply_function(element, restriction, component): + rawname = "z_func" + "_".join(str(c) for c in component) + name = restricted_name(rawname, restriction) + evaluate_trialfunction(element, name, restriction, component) + return name + + @symbol def name_testfunctionspace(restriction): return restricted_name("lfsv", restriction) @@ -73,6 +89,12 @@ def name_coefficientcontainer(restriction): return name +@symbol +def name_applycontainer(restriction): + name = restricted_name("z", restriction) + return name + + @pymbolic_expr def pymbolic_coefficient(lfs, index, restriction): # TODO introduce a proper type for local function spaces! @@ -101,7 +123,8 @@ def name_argumentspace(ma): if ma.argexpr.number() == 1: return name_trialfunctionspace(ma.restriction) if isinstance(ma.argexpr, Coefficient): - assert ma.argexpr.count() == 0 + # Index 0 is reserved for trialfunction, index 1 is reserved for jacobian apply function + assert ma.argexpr.count() < 2 return name_trialfunctionspace(ma.restriction) # We should never encounter an argument other than 0 or 1 assert False @@ -121,7 +144,6 @@ def name_jacobian(restriction1, restriction2): # Restrictions may only differ if NONE if (restriction1 == Restriction.NONE) or (restriction2 == Restriction.NONE): assert restriction1 == restriction2 - return restricted_name(restricted_name("jac", restriction1), restriction2) @@ -143,7 +165,7 @@ def type_residual(): def name_accumulation_variable(restrictions=(Restriction.NONE,)): from dune.perftool.generation import get_global_context_value ft = get_global_context_value("form_type") - if ft == 'residual': + if ft == 'residual' or ft == 'jacobian_apply': return name_residual(*restrictions) if ft == 'jacobian': return name_jacobian(*restrictions) @@ -153,7 +175,7 @@ def name_accumulation_variable(restrictions=(Restriction.NONE,)): def type_accumulation_variable(): from dune.perftool.generation import get_global_context_value ft = get_global_context_value("form_type") - if ft == 'residual': + if ft == 'residual' or ft == 'jacobian_apply': return type_residual() if ft == 'jacobian': return type_jacobian() diff --git a/python/dune/perftool/pdelab/localoperator.py b/python/dune/perftool/pdelab/localoperator.py index 3b89f76e513eb8b1a26c2e82eafcd7fccb1ebef9..9b2ad5fa68a1f6f2b5b3733d342c95012e0737cd 100644 --- a/python/dune/perftool/pdelab/localoperator.py +++ b/python/dune/perftool/pdelab/localoperator.py @@ -36,7 +36,8 @@ def lop_template_gfs(ma): if ma.argexpr.number() == 1: return lop_template_ansatz_gfs() if isinstance(ma.argexpr, Coefficient): - assert ma.argexpr.count() == 0 + # Index 0 is reserved for trialfunction, index 1 is reserved for jacobian apply function + assert ma.argexpr.count() < 2 return lop_template_ansatz_gfs() assert False @@ -150,6 +151,17 @@ def assembly_routine_signature(): from dune.perftool.pdelab.signatures import jacobian_skeleton_signature return jacobian_skeleton_signature() + if form_type == 'jacobian_apply': + if integral_type == 'cell': + from dune.perftool.pdelab.signatures import jacobian_apply_volume_signature + return jacobian_apply_volume_signature() + if integral_type == 'exterior_facet': + from dune.perftool.pdelab.signatures import jacobian_apply_boundary_signature + return jacobian_apply_boundary_signature() + if integral_type == 'interior_facet': + from dune.perftool.pdelab.signatures import jacobian_apply_skeleton_signature + return jacobian_apply_skeleton_signature() + assert False @@ -365,7 +377,27 @@ def generate_localoperator_kernels(formdata, data): for it in alpha_measures - jacobian_measures: operator_kernels[(it, 'jacobian')] = None - # TODO: JacobianApply for matrix-free computations. + # Jacobian apply methods for matrix-free computations + if get_option("matrix_free"): + from dune.perftool.ufl.execution import ApplyCoefficient + apply_coefficient = ApplyCoefficient(form.coefficients()[0].ufl_element(),1) + from ufl import action + jac_apply_form = action(expand_derivatives(derivative(form, form.coefficients()[0])),apply_coefficient) + + 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): + kernel = generate_kernel(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 + # 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_apply_measures = set(i.integral_type() for i in jac_apply_form.integrals()) + for it in alpha_measures - jacobian_apply_measures: + operator_kernels[(it, 'jacobian_apply')] = None + # Return the set of generated kernels return operator_kernels diff --git a/python/dune/perftool/pdelab/signatures.py b/python/dune/perftool/pdelab/signatures.py index b0e02593ba8975387c243ea999edec99fe183310..45745524d9063c8783dc704ff62e357981d5eca5 100644 --- a/python/dune/perftool/pdelab/signatures.py +++ b/python/dune/perftool/pdelab/signatures.py @@ -9,6 +9,7 @@ from dune.perftool.pdelab.argument import (name_accumulation_variable, type_accumulation_variable, name_coefficientcontainer, type_coefficientcontainer, + name_applycontainer, name_testfunctionspace, type_testfunctionspace, name_trialfunctionspace, @@ -236,3 +237,114 @@ def jacobian_skeleton_signature(): av_nn, ) ] + + +@symbol +def jacobian_apply_volume_signature(): + geot = type_geometry_wrapper() + geo = name_geometry_wrapper() + lfsut = type_trialfunctionspace() + lfsu = name_trialfunctionspace(Restriction.NONE) + lfsvt = type_testfunctionspace() + lfsv = name_testfunctionspace(Restriction.NONE) + cct = type_coefficientcontainer() + cc = name_coefficientcontainer(Restriction.NONE) + avt = type_accumulation_variable() + av = name_accumulation_variable((Restriction.NONE,)) + return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot, + lfsut, + cct, + lfsvt, + avt, + ), + 'void jacobian_apply_volume(const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}) const'.format( + geot, + geo, + lfsut, + lfsu, + cct, + cc, + lfsvt, + lfsv, + avt, + av, + ) + ] + + +@symbol +def jacobian_apply_boundary_signature(): + geot = type_geometry_wrapper() + geo = name_geometry_wrapper() + lfsut = type_trialfunctionspace() + lfsu = name_trialfunctionspace(Restriction.NEGATIVE) + lfsvt = type_testfunctionspace() + lfsv = name_testfunctionspace(Restriction.NEGATIVE) + cct = type_coefficientcontainer() + cc = name_coefficientcontainer(Restriction.NEGATIVE) + avt = type_accumulation_variable() + av = name_accumulation_variable((Restriction.NEGATIVE,)) + return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot, + lfsut, + cct, + lfsvt, + avt, + ), + 'void jacobian_apply_boundary(const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}) const'.format( + geot, + geo, + lfsut, + lfsu, + cct, + cc, + lfsvt, + lfsv, + avt, + av, + ) + ] + + +@symbol +def jacobian_apply_skeleton_signature(): + geot = type_geometry_wrapper() + geo = name_geometry_wrapper() + lfsut = type_trialfunctionspace() + lfsu_s = name_trialfunctionspace(Restriction.NEGATIVE) + lfsu_n = name_trialfunctionspace(Restriction.POSITIVE) + lfsvt = type_testfunctionspace() + lfsv_s = name_testfunctionspace(Restriction.NEGATIVE) + lfsv_n = name_testfunctionspace(Restriction.POSITIVE) + cct = type_coefficientcontainer() + cc_s = name_coefficientcontainer(Restriction.NEGATIVE) + cc_n = name_coefficientcontainer(Restriction.POSITIVE) + avt = type_accumulation_variable() + av_s = name_accumulation_variable((Restriction.NEGATIVE,)) + av_n = name_accumulation_variable((Restriction.POSITIVE,)) + return ['template<typename {}, typename {}, typename {}, typename {}, typename {}>'.format(geot, + lfsut, + cct, + lfsvt, + avt, + ), + 'void jacobian_apply_skeleton(const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, const {}& {}, {}& {}, {}& {}) const'.format( + geot, + geo, + lfsut, + lfsu_s, + cct, + cc_s, + lfsvt, + lfsv_s, + lfsut, + lfsu_n, + cct, + cc_n, + lfsvt, + lfsv_n, + avt, + av_s, + avt, + av_n, + ) + ] diff --git a/python/dune/perftool/ufl/execution.py b/python/dune/perftool/ufl/execution.py index 8018e8593cbc3cffb304a6aea1fa62f8423846ff..d420edf2e66904c49f51282366a52e0682b99a45 100644 --- a/python/dune/perftool/ufl/execution.py +++ b/python/dune/perftool/ufl/execution.py @@ -26,13 +26,22 @@ class TrialFunction(ufl.Coefficient): ufl.Coefficient.__init__(self, element, count=0) +class ApplyCoefficient(ufl.Coefficient): + """ A coefficient that always takes the reserved index 1 """ + def __init__(self, element, count=None): + if count and count is not 1: + raise ValueError("The jacobian apply coefficient must be of index 1 in uflpdelab") + ufl.Coefficient.__init__(self, element, count=1) + class Coefficient(ufl.Coefficient): """ A coefficient that honors the reserved index 0. """ def __init__(self, element, count=None): if count and count is 0: raise ValueError("The coefficient of index 0 is reserved for the trial function in uflpdelab") - if not count and ufl.Coefficient._globalcount is 0: - count = 1 + if count and count is 1: + raise ValueError("The coefficient of index 1 is reserved for the jacobian apply vector in uflpdelab") + if not count and ufl.Coefficient._globalcount < 2: + count = 2 ufl.Coefficient.__init__(self, element, count) diff --git a/python/dune/perftool/ufl/modified_terminals.py b/python/dune/perftool/ufl/modified_terminals.py index 27543551b6b218c299e4b82502a4e00c2cf1af47..88eed9589636b1fcbd40e163b7889bcc98b1dc05 100644 --- a/python/dune/perftool/ufl/modified_terminals.py +++ b/python/dune/perftool/ufl/modified_terminals.py @@ -103,10 +103,11 @@ class ModifiedArgumentDescriptor(MultiFunction): class _ModifiedArgumentExtractor(MultiFunction): """ A multifunction that extracts and returns the set of modified arguments """ - def __call__(self, o, argnumber=None, testfunction=True, trialfunction=False): + def __call__(self, o, argnumber=None, testfunction=True, trialfunction=False, applyfunction=False): self.argnumber = argnumber self.trialfunction = trialfunction self.testfunction = testfunction + self.applyfunction = applyfunction self.modified_arguments = set() ret = self.call(o) if ret: @@ -140,6 +141,9 @@ class _ModifiedArgumentExtractor(MultiFunction): if self.trialfunction: if o.count() == 0: return o + if self.applyfunction: + if o.count() == 1: + return o call = MultiFunction.__call__ diff --git a/test/poisson/CMakeLists.txt b/test/poisson/CMakeLists.txt index 3f95382ff6ec91b8f3fd8f3c1f61d231a7ea02b4..c096c63a9da4dfa8a3bc70e50a8d599b55a5ee38 100644 --- a/test/poisson/CMakeLists.txt +++ b/test/poisson/CMakeLists.txt @@ -88,7 +88,7 @@ dune_add_system_test(TARGET poisson_dg_neumann_symdiff INIFILE poisson_dg_neumann_symdiff.mini ) -# 9. Poisson Test Case: source f, bc g + numerical differentiation +# 9. Poisson Test Case: matrix free, numeric differentiation add_generated_executable(UFLFILE poisson.ufl TARGET poisson_numdiff_matrix_free FORM_COMPILER_ARGS --numerical-jacobian --matrix-free @@ -99,6 +99,38 @@ dune_add_system_test(TARGET poisson_numdiff_matrix_free SCRIPT dune_vtkcompare.py ) +# 10. Poisson Test Case: matrix free, symbolic differentiation +add_generated_executable(UFLFILE poisson.ufl + TARGET poisson_symdiff_matrix_free + FORM_COMPILER_ARGS --matrix-free + ) + +dune_add_system_test(TARGET poisson_symdiff_matrix_free + INIFILE poisson_symdiff_matrix_free.mini + SCRIPT dune_vtkcompare.py + ) + +# 11. Poisson Test Case: dg, matrix free, numeric differentiation +add_generated_executable(UFLFILE poisson_dg.ufl + TARGET poisson_dg_numdiff_matrix_free + FORM_COMPILER_ARGS --numerical-jacobian --matrix-free + ) + +dune_add_system_test(TARGET poisson_dg_numdiff_matrix_free + INIFILE poisson_dg_numdiff_matrix_free.mini + SCRIPT dune_vtkcompare.py + ) + +# 12. Poisson Test Case: dg, matrix free, symbolic differentiation +add_generated_executable(UFLFILE poisson_dg.ufl + TARGET poisson_dg_symdiff_matrix_free + FORM_COMPILER_ARGS --matrix-free + ) + +dune_add_system_test(TARGET poisson_dg_symdiff_matrix_free + INIFILE poisson_dg_symdiff_matrix_free.mini + SCRIPT dune_vtkcompare.py + ) # Add the reference code with the PDELab localoperator that produced # the reference vtk file diff --git a/test/poisson/poisson_dg_numdiff_matrix_free.mini b/test/poisson/poisson_dg_numdiff_matrix_free.mini new file mode 100644 index 0000000000000000000000000000000000000000..b45d4a8654e11107f8bbf111db0f996461085e2a --- /dev/null +++ b/test/poisson/poisson_dg_numdiff_matrix_free.mini @@ -0,0 +1,11 @@ +__name = poisson_dg_numdiff_matrix_free + +lowerleft = 0.0 0.0 +upperright = 1.0 1.0 +elements = 32 32 +elementType = simplical + +[wrapper.vtkcompare] +name = {__name} +reference = poisson_dg_ref +extension = vtu diff --git a/test/poisson/poisson_dg_symdiff_matrix_free.mini b/test/poisson/poisson_dg_symdiff_matrix_free.mini new file mode 100644 index 0000000000000000000000000000000000000000..c3fcaf7fcf1576850a8d1a99f4a1d8b07d0ddcd8 --- /dev/null +++ b/test/poisson/poisson_dg_symdiff_matrix_free.mini @@ -0,0 +1,11 @@ +__name = poisson_dg_symdiff_matrix_free + +lowerleft = 0.0 0.0 +upperright = 1.0 1.0 +elements = 32 32 +elementType = simplical + +[wrapper.vtkcompare] +name = {__name} +reference = poisson_dg_ref +extension = vtu diff --git a/test/poisson/poisson_symdiff_matrix_free.mini b/test/poisson/poisson_symdiff_matrix_free.mini new file mode 100644 index 0000000000000000000000000000000000000000..8d288ef3358fdda09706b80874e444d0ff5c8b2c --- /dev/null +++ b/test/poisson/poisson_symdiff_matrix_free.mini @@ -0,0 +1,11 @@ +__name = poisson_symdiff_matrix_free + +lowerleft = 0.0 0.0 +upperright = 1.0 1.0 +elements = 32 32 +elementType = simplical + +[wrapper.vtkcompare] +name = {__name} +reference = poisson_ref +extension = vtu