From 10bd5d43e7a0ff9212ffb24e3f7a9dd5c177e82b Mon Sep 17 00:00:00 2001 From: Dominic Kempf <dominic.kempf@iwr.uni-heidelberg.de> Date: Tue, 20 Jun 2017 13:42:31 +0200 Subject: [PATCH] Introduce subpackage driver in pdelab --- .../dune/perftool/pdelab/driver/__init__.py | 1745 +++++++++++++++-- 1 file changed, 1626 insertions(+), 119 deletions(-) diff --git a/python/dune/perftool/pdelab/driver/__init__.py b/python/dune/perftool/pdelab/driver/__init__.py index b5efebf3..48fa4574 100644 --- a/python/dune/perftool/pdelab/driver/__init__.py +++ b/python/dune/perftool/pdelab/driver/__init__.py @@ -43,30 +43,6 @@ def set_driver_data(formdatas, data): _driver_data['data'] = data -def get_dimension(): - return _driver_data['form'].ufl_cell().geometric_dimension() - - -def get_cell(): - return _driver_data['form'].ufl_cell().cellname() - - -def get_test_element(): - return _driver_data['form'].arguments()[0].ufl_element() - - -def get_trial_element(): - return _driver_data['form'].coefficients()[0].ufl_element() - - -def get_formdata(): - return _driver_data['formdata'] - - -def get_mass_formdata(): - return _driver_data["mass_formdata"] - - def is_stationary(): return 'mass_form' not in _driver_data @@ -78,8 +54,29 @@ def form_name_suffix(name, formdata): return name + '_' + form_name -def get_object(name): - return _driver_data['data'].object_by_name.get(name, None) +def has_constraints(element): + from ufl import MixedElement, VectorElement + if isinstance(element, MixedElement) and not isinstance(element, VectorElement): + return any(has_constraints(el) for el in element.sub_elements()) + return hasattr(element, "dirichlet_constraints") + + +# Those symbol generators that depend on the meta data we hacked into +# the ufl.FiniteElement should use fem_metadata_dependent_symbol instead of symbol. +def get_constraints_metadata(element): + """ + Normally, we suppress the constraints information on a finite element. + However, we might want to use it explicitly in those case where it matters + This function does the job! + """ + dc = getattr(element, 'dirichlet_constraints', None) + de = getattr(element, 'dirichlet_expression', None) + return (element, (dc, de)) + + +fem_metadata_dependent_symbol = generator_factory(item_tags=("symbol",), + cache_key_generator=get_constraints_metadata, + ) def mass_form_index(formdatas, data): @@ -91,10 +88,8 @@ def mass_form_index(formdatas, data): continue -def is_linear(form=None): - '''Test if form is linear in trial function''' - if form is None: - form = get_formdata().original_form +def is_linear(form): + '''Test if form is linear in test function''' from ufl import derivative from ufl.algorithms import expand_derivatives jacform = expand_derivatives(derivative(form, form.coefficients()[0])) @@ -109,26 +104,20 @@ def isLagrange(fem): return fem._short_name is 'CG' -def isSimplical(cell): - # Cells can be identified through strings *or* ufl objects - if not isinstance(cell, str): - cell = cell.cellname() - return cell in ["vertex", "interval", "triangle", "tetrahedron"] +def isSimplical(fem): + return any(fem._cell._cellname in x for x in ["triangle", "tetrahedron"]) -def isQuadrilateral(cell): - # Cells can be identified through strings *or* ufl objects - if not isinstance(cell, str): - cell = cell.cellname() - return cell in ["vertex", "interval", "quadrilateral", "hexahedron"] +def isQuadrilateral(fem): + return any(fem._cell._cellname in x for x in ["vertex", "interval", "quadrilateral", "hexahedron"]) def isPk(fem): - return isLagrange(fem) and isSimplical(fem.cell()) + return isLagrange(fem) and isSimplical(fem) def isQk(fem): - return isLagrange(fem) and isQuadrilateral(fem.cell()) + return isLagrange(fem) and isQuadrilateral(fem) def isDG(fem): @@ -159,72 +148,1000 @@ def FEM_name_mangling(fem): raise NotImplementedError("FEM NAME MANGLING") -def _flatten_list(l): - if isinstance(l, (tuple, list)): - for i in l: - for ni in _flatten_list(i): - yield ni +def name_inifile(): + # TODO pass some other option here. + return "argv[1]" + + +@preamble +def parse_initree(varname): + include_file("dune/common/parametertree.hh", filetag="driver") + include_file("dune/common/parametertreeparser.hh", filetag="driver") + filename = name_inifile() + return ["Dune::ParameterTree initree;", "Dune::ParameterTreeParser::readINITree({}, {});".format(filename, varname)] + + +def name_initree(): + parse_initree("initree") + # TODO we can get some other ini file here. + return "initree" + + +@preamble +def define_dimension(name): + return "static const int {} = {};".format(name, _driver_data['form'].ufl_cell().geometric_dimension()) + + +def name_dimension(): + define_dimension("dim") + return "dim" + + +@preamble +def typedef_grid(name): + dim = name_dimension() + if any(_driver_data['form'].ufl_cell().cellname() in x for x in ["vertex", "interval", "quadrilateral", "hexahedron"]): + # For Yasp Grids the jacobi of the transformation is diagonal and constant on each cell + from dune.perftool.options import set_option + set_option('diagonal_transformation_matrix', True) + set_option('constant_transformation_matrix', True) + + range_type = type_range() + gridt = "Dune::YaspGrid<{}, Dune::EquidistantCoordinates<{},dim>>".format(dim, range_type) + include_file("dune/grid/yaspgrid.hh", filetag="driver") else: - yield l + if any(_driver_data['form'].ufl_cell().cellname() in x for x in ["triangle", "tetrahedron"]): + # gridt = "Dune::UGGrid<{}>".format(dim) + # include_file("dune/grid/uggrid.hh", filetag="driver") + gridt = "Dune::ALUGrid<{}, {}, Dune::simplex, Dune::conforming>".format(dim, dim) + include_file("dune/alugrid/grid.hh", filetag="driver") + else: + raise PerftoolCodegenError("Cant match your geometry with a DUNE grid. Please report bug.") + return "using {} = {};".format(name, gridt) -def _unroll_list_tensors(expr): - from ufl.classes import ListTensor - if isinstance(expr, ListTensor): - for op in expr.ufl_operands: - yield op +def type_grid(): + typedef_grid("Grid") + return "Grid" + + +@preamble +def define_grid(name): + include_file("dune/testtools/gridconstruction.hh", filetag="driver") + ini = name_initree() + _type = type_grid() + return ["IniGridFactory<{}> factory({});".format(_type, ini), + "std::shared_ptr<{}> grid = factory.getGrid();".format(_type)] + + +def name_grid(): + define_grid("grid") + return "grid" + + +@preamble +def typedef_leafview(name): + grid = type_grid() + return "using {} = {}::LeafGridView;".format(name, grid) + + +def type_leafview(): + typedef_leafview("GV") + return "GV" + + +@preamble +def define_leafview(name): + _type = type_leafview() + grid = name_grid() + return "{} {} = {}->leafGridView();".format(_type, name, grid) + + +def name_leafview(): + define_leafview("gv") + return "gv" + + +@preamble +def typedef_vtkwriter(name): + include_file("dune/grid/io/file/vtk/subsamplingvtkwriter.hh", filetag="driver") + gv = type_leafview() + return "using {} = Dune::SubsamplingVTKWriter<{}>;".format(name, gv) + + +def type_vtkwriter(): + typedef_vtkwriter("VTKWriter") + return "VTKWriter" + + +@preamble +def define_subsamplinglevel(name): + ini = name_initree() + return "int {} = {}.get<int>(\"vtk.subsamplinglevel\", 0);".format(name, ini) + + +def name_subsamplinglevel(): + define_subsamplinglevel("sublevel") + return "sublevel" + + +@preamble +def define_vtkwriter(name): + _type = type_vtkwriter() + gv = name_leafview() + subsamp = name_subsamplinglevel() + return "{} {}({}, {});".format(_type, name, gv, subsamp) + + +def name_vtkwriter(): + define_vtkwriter("vtkwriter") + return "vtkwriter" + + +@preamble +def typedef_domainfield(name): + gridt = type_grid() + return "using {} = {}::ctype;".format(name, gridt) + + +def type_domainfield(): + typedef_domainfield("DF") + return "DF" + + +@preamble +def typedef_range(name): + if get_option('opcounter'): + setup_timer() + return "using {} = oc::OpCounter<double>;".format(name) else: - yield expr + return "using {} = double;".format(name) -def unroll_list_tensors(data): - for expr in data: - for e in _unroll_list_tensors(expr): - yield e +def type_range(): + typedef_range("RangeType") + return "RangeType" -def preprocess_leaf_data(element, data): - data = get_object(data) - from ufl import MixedElement +@preamble +def typedef_fem(expr, name): + gv = type_leafview() + df = type_domainfield() + r = type_range() + dim = name_dimension() + if isPk(expr): + include_file("dune/pdelab/finiteelementmap/pkfem.hh", filetag="driver") + return "using {} = Dune::PDELab::PkLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, gv, df, r, expr._degree) + if isQk(expr): + include_file("dune/pdelab/finiteelementmap/qkfem.hh", filetag="driver") + return "using {} = Dune::PDELab::QkLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, gv, df, r, expr._degree) + if isDG(expr): + if isQuadrilateral(expr): + include_file("dune/pdelab/finiteelementmap/qkdg.hh", filetag="driver") + # TODO allow switching the basis here! + return "using {} = Dune::PDELab::QkDGLocalFiniteElementMap<{}, {}, {}, {}>;".format(name, df, r, expr._degree, dim) + if isSimplical(expr): + include_file("dune/pdelab/finiteelementmap/opbfem.hh", filetag="driver") + return "using {} = Dune::PDELab::OPBLocalFiniteElementMap<{}, {}, {}, {}, Dune::GeometryType::simplex>;".format(name, df, r, expr._degree, dim) + raise NotImplementedError("Geometry type not known in code generation") + + raise NotImplementedError("FEM not implemented in dune-perftool") + + +def type_fem(expr): + name = "{}_FEM".format(FEM_name_mangling(expr).upper()) + typedef_fem(expr, name) + return name + + +@preamble +def define_fem(expr, name): + femtype = type_fem(expr) + if isDG(expr): + return "{} {};".format(femtype, name) + else: + gv = name_leafview() + return "{} {}({});".format(femtype, name, gv) + + +def name_fem(expr): + name = "{}_fem".format(FEM_name_mangling(expr).lower()) + define_fem(expr, name) + return name + + +@preamble +def define_blocksize(name, expr): + assert isDG(expr) + assert isQuadrilateral(expr) + dimension = name_dimension() + degree = expr._degree + return "static const int {} = Dune::QkStuff::QkSize<{}, {}>::value;".format(name, degree, dimension) + + +def name_blocksize(expr): + name = "blocksize" + define_blocksize(name, expr) + return name + + +@preamble +def typedef_vectorbackend(name, expr): + include_file("dune/pdelab/backend/istl.hh", filetag="driver") + if get_option("fastdg"): + blocksize = name_blocksize(expr) + return "using {} = Dune::PDELab::istl::VectorBackend<Dune::PDELab::istl::Blocking::fixed, {}>;".format(name, blocksize) + else: + return "using {} = Dune::PDELab::istl::VectorBackend<Dune::PDELab::istl::Blocking::none, 1>;".format(name) + + +def type_vectorbackend(expr): + name = "VectorBackend" + typedef_vectorbackend(name, expr) + return name + + +def type_orderingtag(): + return "Dune::PDELab::LexicographicOrderingTag" + + +@preamble +def typedef_dirichlet_constraintsassembler(name): + include_file("dune/pdelab/constraints/conforming.hh", filetag="driver") + return "using {} = Dune::PDELab::ConformingDirichletConstraints;".format(name) + + +@preamble +def typedef_no_constraintsassembler(name): + return "using {} = Dune::PDELab::NoConstraints;".format(name) + + +def type_constraintsassembler(dirichlet): + if dirichlet[1]: + typedef_dirichlet_constraintsassembler("DirichletConstraintsAssember") + return "DirichletConstraintsAssember" + else: + typedef_no_constraintsassembler("NoConstraintsAssembler") + return "NoConstraintsAssembler" + + +@preamble +def typedef_constraintscontainer(element, name): + gfs = type_gfs(element) + r = type_range() + return "using {} = {}::ConstraintsContainer<{}>::Type;".format(name, gfs, r) + + +def type_constraintscontainer(element): + name = "{}_cc".format(FEM_name_mangling(element)).upper() + typedef_constraintscontainer(element, name) + return name + + +@preamble +def define_constraintscontainer(expr, name): + cctype = type_constraintscontainer(expr) + return ["{} {};".format(cctype, name), "{}.clear();".format(name)] + + +# TODO this does not yet fully support having same elements with different constraints! +@fem_metadata_dependent_symbol +def name_constraintscontainer(expr): + name = "{}{}_cc".format(FEM_name_mangling(expr), "_dirichlet" if has_constraints(expr) else "").lower() + define_constraintscontainer(expr, name) + return name + + +@preamble +def define_intersection_lambda(expression, name): + from dune.perftool.ufl.execution import Expression + from ufl.classes import Expr + if expression is None: + return "auto {} = [&](const auto& x){{ return 0.0; }};".format(name) + elif isinstance(expression, Expression): + if expression.is_global: + return "auto {} = [&](const auto& x){{ {} }};".format(name, expression.c_expr[0]) + else: + return "auto {} = [&](const auto& is, const auto& x){{ {} }};".format(name, expression.c_expr[0]) + elif isinstance(expression, Expr): + # Set up a visitor + with global_context(integral_type="exterior_facet", formdata=_driver_data["formdata"], driver=True): + from dune.perftool.ufl.visitor import UFL2LoopyVisitor + from dune.perftool.pdelab import PDELabInterface + visitor = UFL2LoopyVisitor(PDELabInterface(), "exterior_facet", {}) + from pymbolic.mapper.c_code import CCodeMapper + ccm = CCodeMapper() + expr = visitor(expression) + return "auto {} = [&](const auto& x){{ return {}; }};".format(name, ccm(expr)) + + raise ValueError("Expression not understood") + + +def name_bctype_lambda(name, dirichlet): + name = name + "_lambda" + define_intersection_lambda(dirichlet, name) + return name + + +@preamble +def define_bctype_function(dirichlet, name): + gv = name_leafview() + bctype_lambda = name_bctype_lambda(name, dirichlet) + include_file('dune/pdelab/function/callableadapter.hh', filetag='driver') + return "auto {} = Dune::PDELab::makeBoundaryConditionFromCallable({}, {});".format(name, + gv, + bctype_lambda, + ) + + +@preamble +def define_powergfs_constraints(name, child, dim): + include_file('dune/pdelab/constraints/common/constraintsparameters.hh', filetag='driver') + return "Dune::PDELab::PowerConstraintsParameters<decltype({}), {}> {}({});".format(child, dim, name, child) + + +@preamble +def define_compositegfs_constraints(name, *children): + include_file('dune/pdelab/constraints/common/constraintsparameters.hh', filetag='driver') + return "Dune::PDELab::CompositeConstraintsParameters<{}> {}({});".format(', '.join('decltype({})'.format(c) for c in children), + name, + ', '.join(c for c in children) + ) + + +@cached +def name_bctype_function(expr): + from ufl import MixedElement, VectorElement, TensorElement + if isinstance(expr, (VectorElement, TensorElement)): + element, (dirichlet, _) = get_constraints_metadata(expr) + # get the correct name from the corresponding UFL file + from dune.perftool.generation import get_global_context_value + name = get_global_context_value("data").object_names.get(id(dirichlet), "everywhere") + define_bctype_function(dirichlet, name) + pgfs_name = '{}_{}'.format(name, expr.num_sub_elements()) + define_powergfs_constraints(pgfs_name, name, expr.num_sub_elements()) + return pgfs_name + if isinstance(expr, MixedElement): + children = tuple(name_bctype_function(el) for el in expr.sub_elements()) + name = '_'.join(children) + define_compositegfs_constraints(name, *children) + return name + + # So, this seems to be a leaf! + element, (dirichlet, _) = get_constraints_metadata(expr) + + # get the correct name from the corresponding UFL file + from dune.perftool.generation import get_global_context_value + name = get_global_context_value("data").object_names.get(id(dirichlet), "everywhere") + + define_bctype_function(dirichlet, name) + return name + + +@preamble +def assemble_constraints(name, expr): + bctype_function = name_bctype_function(expr) + gfs = name_gfs(expr) + return "Dune::PDELab::constraints({}, {}, {});".format(bctype_function, + gfs, + name, + ) + + +def name_assembled_constraints(expr): + name = name_constraintscontainer(expr) + if has_constraints(expr): + assemble_constraints(name, expr) + return name + + +@preamble +def typedef_gfs(element, dirichlet, name): + vb = type_vectorbackend(element) + from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement, TensorElement + if isinstance(element, FiniteElement): + gv = type_leafview() + fem = type_fem(element) + cass = type_constraintsassembler(dirichlet) + return "using {} = Dune::PDELab::GridFunctionSpace<{}, {}, {}, {}>;".format(name, gv, fem, cass, vb) + if isinstance(element, (VectorElement, TensorElement)): + include_file("dune/pdelab/gridfunctionspace/vectorgridfunctionspace.hh", filetag="driver") + gv = type_leafview() + fem = type_fem(element.sub_elements()[0]) + # TODO implement symmetry here + dim = element.num_sub_elements() + cass = type_constraintsassembler(dirichlet) + return "using {} = Dune::PDELab::VectorGridFunctionSpace<{}, {}, {}, {}, {}, {}>;".format(name, gv, fem, dim, vb, vb, cass) if isinstance(element, MixedElement): - # data is None -> use 0 default - if data is None: - data = (0,) * element.value_size() + ot = type_orderingtag() + args = ", ".join(type_gfs(e) for e in element.sub_elements()) + return "using {} = Dune::PDELab::CompositeGridFunctionSpace<{}, {}, {}>;".format(name, vb, ot, args) + if isinstance(element, EnrichedElement): + raise NotImplementedError("Dune does not support enriched elements!") + if isinstance(element, RestrictedElement): + raise NotImplementedError("Dune does not support restricted elements!") + + +@fem_metadata_dependent_symbol +def type_gfs(expr): + element, dirichlet = get_constraints_metadata(expr) + name = "{}{}_gfs".format(FEM_name_mangling(element), "_dirichlet" if dirichlet else "").upper() + typedef_gfs(element, dirichlet, name) + return name + + +@preamble +def define_gfs(element, dirichlet, name): + gfstype = type_gfs(element) + from ufl import FiniteElement, MixedElement, VectorElement, EnrichedElement, RestrictedElement, TensorElement + if isinstance(element, FiniteElement): + gv = name_leafview() + fem = name_fem(element) + return "{} {}({}, {});".format(gfstype, name, gv, fem) + if isinstance(element, (VectorElement, TensorElement)): + gv = name_leafview() + fem = name_fem(element.sub_elements()[0]) + return "{} {}({}, {});".format(gfstype, name, gv, fem) + if isinstance(element, MixedElement): + args = ", ".join(name_gfs(childgfs) for childgfs in element.sub_elements()) + return "{} {}({});".format(gfstype, name, args) + raise NotImplementedError("Dune does not support this type of finite element!") + + +@fem_metadata_dependent_symbol +def name_gfs(expr): + element, dirichlet = get_constraints_metadata(expr) + name = "{}{}_gfs".format(FEM_name_mangling(element), "_dirichlet" if dirichlet else "").lower() + define_gfs(element, dirichlet, name) + return name + + +@preamble +def define_dofestimate(name): + # Provide a worstcase estimate for the number of entries per row based on the given gridfunction space and cell geometry + if isQuadrilateral(_driver_data['form'].coefficients()[0].ufl_element()): + geo_factor = "4" + else: + geo_factor = "6" + gfs = name_gfs(_driver_data['form'].coefficients()[0].ufl_element()) + ini = name_initree() + + # Assure that gfs in initialized + formdata = _driver_data['formdata'] + x = name_vector(formdata) + define_vector(x, formdata) + + return ["int generic_dof_estimate = {} * {}.maxLocalSize();".format(geo_factor, gfs), + "int {} = {}.get<int>(\"istl.number_of_nnz\", generic_dof_estimate);".format(name, ini)] + + +def name_dofestimate(): + define_dofestimate("dofestimate") + return "dofestimate" + + +@preamble +def typedef_matrixbackend(name): + include_file("dune/pdelab/backend/istl.hh", filetag="driver") + return "using {} = Dune::PDELab::istl::BCRSMatrixBackend<>;".format(name) + + +def type_matrixbackend(): + typedef_matrixbackend("MatrixBackend") + return "MatrixBackend" + + +@preamble +def define_matrixbackend(name): + mbtype = type_matrixbackend() + dof = name_dofestimate() + return "{} {}({});".format(mbtype, name, dof) + + +def name_matrixbackend(): + define_matrixbackend("mb") + return "mb" + + +def type_parameters(formdata): + from dune.perftool.generation import get_global_context_value + data = get_global_context_value("data") + from dune.perftool.pdelab.parameter import parameterclass_basename + name = parameterclass_basename(formdata, data) + return name + + +@preamble +def define_parameters(name, formdata): + partype = type_parameters(formdata) + return "{} {};".format(partype, name) - # Flatten nested lists - data = tuple(i for i in _flatten_list(data)) - # Expand any list tensors - data = tuple(i for i in unroll_list_tensors(data)) +def name_parameters(formdata): + name = form_name_suffix("params", formdata) + define_parameters(name, formdata) + return name + + +@preamble +def typedef_localoperator(name, formdata): + # No Parameter class here, yet + # params = type_parameters() + # return "using {} = LocalOperator<{}>;".format(name, params) + ugfs = type_gfs(_driver_data['form'].coefficients()[0].ufl_element()) + vgfs = type_gfs(_driver_data['form'].arguments()[0].ufl_element()) + from dune.perftool.generation import get_global_context_value + data = get_global_context_value("data") + filename = get_option("operator_file") + include_file(filename, filetag="driver") + from dune.perftool.pdelab.localoperator import localoperator_basename + lopname = localoperator_basename(formdata, data) + range_type = type_range() + return "using {} = {}<{}, {}, {}>;".format(name, lopname, ugfs, vgfs, range_type) + + +def type_localoperator(formdata): + name = form_name_suffix("LOP", formdata).upper() + typedef_localoperator(name, formdata) + return name + + +@preamble +def define_localoperator(name, formdata): + loptype = type_localoperator(formdata) + ini = name_initree() + params = name_parameters(formdata) + return "{} {}({}, {});".format(loptype, name, ini, params) - assert len(data) == element.value_size() - return data + +def name_localoperator(formdata): + name = form_name_suffix("lop", formdata) + define_localoperator(name, formdata) + return name + + +@preamble +def typedef_gridoperator(name, formdata): + ugfs = type_gfs(_driver_data['form'].coefficients()[0].ufl_element()) + vgfs = type_gfs(_driver_data['form'].arguments()[0].ufl_element()) + lop = type_localoperator(formdata) + ucc = type_constraintscontainer(_driver_data['form'].coefficients()[0].ufl_element()) + vcc = type_constraintscontainer(_driver_data['form'].arguments()[0].ufl_element()) + mb = type_matrixbackend() + df = type_domainfield() + r = type_range() + if get_option("fastdg"): + if not get_option("sumfact"): + raise PerftoolCodegenError("FastDGGridOperator is only implemented for sumfactorization.") + include_file("dune/pdelab/gridoperator/fastdg.hh", filetag="driver") + return "using {} = Dune::PDELab::FastDGGridOperator<{}, {}, {}, {}, {}, {}, {}, {}, {}>;".format(name, ugfs, vgfs, lop, mb, df, r, r, ucc, vcc) else: - # Do not return lists for non-MixedElement - if not isinstance(data, (tuple, list)): - return (data,) + include_file("dune/pdelab/gridoperator/gridoperator.hh", filetag="driver") + return "using {} = Dune::PDELab::GridOperator<{}, {}, {}, {}, {}, {}, {}, {}, {}>;".format(name, ugfs, vgfs, lop, mb, df, r, r, ucc, vcc) + + +def type_gridoperator(formdata): + name = form_name_suffix("GO", formdata).upper() + typedef_gridoperator(name, formdata) + return name + + +@preamble +def define_gridoperator(name, formdata): + gotype = type_gridoperator(formdata) + # TODO use formdata insteat of _driver_data object + ugfs = name_gfs(_driver_data['form'].coefficients()[0].ufl_element()) + ucc = name_assembled_constraints(_driver_data['form'].coefficients()[0].ufl_element()) + vgfs = name_gfs(_driver_data['form'].arguments()[0].ufl_element()) + vcc = name_assembled_constraints(_driver_data['form'].arguments()[0].ufl_element()) + lop = name_localoperator(formdata) + mb = name_matrixbackend() + return ["{} {}({}, {}, {}, {}, {}, {});".format(gotype, name, ugfs, ucc, vgfs, vcc, lop, mb), + "std::cout << \"gfs with \" << {}.size() << \" dofs generated \"<< std::endl;".format(ugfs), + "std::cout << \"cc with \" << {}.size() << \" dofs generated \"<< std::endl;".format(ucc)] + + +def name_gridoperator(formdata): + name = form_name_suffix("go", formdata) + define_gridoperator(name, formdata) + return name + + +@preamble +def typedef_vector(name, formdata): + go_type = type_gridoperator(formdata) + return "using {} = {}::Traits::Domain;".format(name, go_type) + + +def type_vector(formdata): + name = form_name_suffix("V", formdata).upper() + typedef_vector(name, formdata) + return name + + +@preamble +def define_vector(name, formdata): + vtype = type_vector(formdata) + gfs = name_gfs(_driver_data['form'].coefficients()[0].ufl_element()) + return ["{} {}({});".format(vtype, name, gfs), "{} = 0.0;".format(name)] + + +@preamble +def define_boundary_lambda(boundary, name): + from dune.perftool.ufl.execution import Expression + from ufl.classes import Expr + if boundary is None: + return "auto {} = [&](const auto& x){{ return 0.0; }};".format(name) + elif isinstance(boundary, Expression): + if boundary.is_global: + return "auto {} = [&](const auto& x){{ {} }};".format(name, boundary.c_expr[0]) else: - assert len(data) == 1 - return data + return "auto {} = [&](const auto& e, const auto& x){{ {} }};".format(name, boundary.c_expr[0]) + elif isinstance(boundary, Expr): + # Set up a visitor + with global_context(integral_type="exterior_facet", formdata=_driver_data["formdata"], driver=True): + from dune.perftool.ufl.visitor import UFL2LoopyVisitor + from dune.perftool.pdelab import PDELabInterface + visitor = UFL2LoopyVisitor(PDELabInterface(), "exterior_facet", {}) + from dune.perftool.loopy.target import numpy_to_cpp_dtype + return "auto {} = [&](const auto& x){{ return ({}){}; }};".format(name, + numpy_to_cpp_dtype("float64"), + visitor(boundary)) + raise ValueError("Expression not understood") -def name_inifile(): - # TODO pass some other option here. - return "argv[1]" + +def name_boundary_lambda(boundary, name): + define_boundary_lambda(boundary, name + "lambda") + return name + "lambda" @preamble -def parse_initree(varname): - include_file("dune/common/parametertree.hh", filetag="driver") - include_file("dune/common/parametertreeparser.hh", filetag="driver") - filename = name_inifile() - return ["Dune::ParameterTree initree;", "Dune::ParameterTreeParser::readINITree({}, {});".format(filename, varname)] +def define_boundary_function(boundary, name): + gv = name_leafview() + lambdaname = name_boundary_lambda(boundary, name) + include_file('dune/pdelab/function/callableadapter.hh', filetag='driver') + if is_stationary(): + return "auto {} = Dune::PDELab::makeGridFunctionFromCallable({}, {});".format(name, + gv, + lambdaname, + ) + else: + params = name_parameters(_driver_data['formdata']) + return "auto {} = Dune::PDELab::makeInstationaryGridFunctionFromCallable({}, {}, {});".format(name, + gv, + lambdaname, + params) -def name_initree(): - parse_initree("initree") - # TODO we can get some other ini file here. - return "initree" +@preamble +def define_compositegfs_parameterfunction(name, *children): + return "Dune::PDELab::CompositeGridFunction<{}> {}({});".format(', '.join('decltype({})'.format(c) for c in children), + name, + ', '.join(children)) + + +def expression_splitter(expr, length): + if expr is None: + return (None,) * length + else: + from dune.perftool.ufl.execution import Expression, split_expression + from ufl.classes import ListTensor + if isinstance(expr, Expression): + return split_expression(expr) + elif isinstance(expr, ListTensor): + return expr.ufl_operands + + raise TypeError("No idea how to split {} in components".format(type(expr))) + + +@cached +def _name_boundary_function(element, boundary, name=None): + from ufl import MixedElement, VectorElement, TensorElement + if isinstance(element, (VectorElement, TensorElement)): + from dune.perftool.generation import get_global_context_value + basename = get_global_context_value("data").object_names.get(id(boundary), "veczero") + children = tuple(_name_boundary_function(el, exp, basename + '_' + str(i)) for el, exp, i in zip(element.sub_elements(), expression_splitter(boundary, element.num_sub_elements()), range(element.num_sub_elements()))) + define_compositegfs_parameterfunction(basename, *children) + return basename + if isinstance(element, MixedElement): + children = tuple(name_boundary_function(el) for el in element.sub_elements()) + name = '_'.join(children) + define_compositegfs_parameterfunction(name, *children) + return name + + from dune.perftool.generation import get_global_context_value + if name is None: + name = get_global_context_value("data").object_names.get(id(boundary), "zero") + + define_boundary_function(boundary, name) + return name + + +def name_boundary_function(expr): + # This is a scalar leaf of the ansatz function tree + element, (_, boundary) = get_constraints_metadata(expr) + + return _name_boundary_function(element, boundary) + + +@cached +def name_solution_function(tree_path=()): + from dune.perftool.generation import get_global_context_value + expr = get_global_context_value("data").object_by_name[get_option("exact_solution_expression")] + + from dune.perftool.ufl.execution import Expression, split_expression + from ufl.classes import ListTensor + for i in tree_path: + if isinstance(expr, Expression): + expr = split_expression(expr)[int(i)] + elif isinstance(expr, tuple): + expr = expr[int(i)] + elif isinstance(expr, ListTensor): + expr = expr.ufl_operands[int(i)] + else: + raise TypeError("No idea how to split {}".format(type(expr))) + + from dune.perftool.generation import get_global_context_value + try: + name = get_global_context_value("data").object_names[id(expr)] + except KeyError: + from dune.perftool.generation import get_counter + name = 'generic_{}'.format(get_counter('__generic')) + + define_boundary_function(expr, name) + + return name + + +@preamble +def interpolate_vector(name, formdata): + define_vector(name, formdata) + element = _driver_data['form'].coefficients()[0].ufl_element() + bf = name_boundary_function(element) + gfs = name_gfs(element) + return "Dune::PDELab::interpolate({}, {}, {});".format(bf, + gfs, + name, + ) + + +@preamble +def interpolate_solution_expression(name): + formdata = _driver_data['formdata'] + define_vector(name, formdata) + element = _driver_data['form'].coefficients()[0].ufl_element() + sol = name_solution_function() + gfs = name_gfs(element) + return "Dune::PDELab::interpolate({}, {}, {});".format(sol, + gfs, + name, + ) + + +def maybe_interpolate_vector(name, formdata): + element = _driver_data['form'].coefficients()[0].ufl_element() + if has_constraints(element): + interpolate_vector(name, formdata) + else: + define_vector(name, formdata) + + +def name_vector(formdata): + name = form_name_suffix("x", formdata) + maybe_interpolate_vector(name, formdata) + return name + + +def name_vector_from_solution_expression(): + interpolate_solution_expression("solution") + return "solution" + + +@preamble +def typedef_linearsolver(name): + include_file("dune/pdelab/backend/istl.hh", filetag="driver") + return "using {} = Dune::PDELab::ISTLBackend_SEQ_UMFPack;".format(name) + + +def type_linearsolver(): + typedef_linearsolver("LinearSolver") + return "LinearSolver" + + +@preamble +def define_linearsolver(name): + lstype = type_linearsolver() + return "{} {}(false);".format(lstype, name) + + +def name_linearsolver(): + define_linearsolver("ls") + return "ls" + + +@preamble +def define_reduction(name): + ini = name_initree() + return "double {} = {}.get<double>(\"reduction\", 1e-12);".format(name, ini) + + +def name_reduction(): + define_reduction("reduction") + return "reduction" + + +@preamble +def typedef_stationarylinearproblemsolver(name): + include_file("dune/pdelab/stationary/linearproblem.hh", filetag="driver") + gotype = type_gridoperator(_driver_data['formdata']) + lstype = type_linearsolver() + xtype = type_vector(_driver_data['formdata']) + return "using {} = Dune::PDELab::StationaryLinearProblemSolver<{}, {}, {}>;".format(name, gotype, lstype, xtype) + + +def type_stationarylinearproblemsolver(): + typedef_stationarylinearproblemsolver("SLP") + return "SLP" + + +@preamble +def define_stationarylinearproblemsolver(name): + slptype = type_stationarylinearproblemsolver() + formdata = _driver_data['formdata'] + go = name_gridoperator(formdata) + ls = name_linearsolver() + x = name_vector(formdata) + red = name_reduction() + return "{} {}({}, {}, {}, {});".format(slptype, name, go, ls, x, red) + + +def name_stationarylinearproblemsolver(): + define_stationarylinearproblemsolver("slp") + return "slp" + + +@preamble +def typedef_stationarynonlinearproblemsolver(name, go_type): + include_file("dune/pdelab/newton/newton.hh", filetag="driver") + ls_type = type_linearsolver() + x_type = type_vector(_driver_data['formdata']) + return "using {} = Dune::PDELab::Newton<{}, {}, {}>;".format(name, go_type, ls_type, x_type) + + +def type_stationarynonlinearproblemssolver(go_type): + typedef_stationarynonlinearproblemsolver("SNP", go_type) + return "SNP" + + +@preamble +def define_stationarynonlinearproblemsolver(name, go_type, go): + snptype = type_stationarynonlinearproblemssolver(go_type) + x = name_vector(_driver_data['formdata']) + ls = name_linearsolver() + return "{} {}({}, {}, {});".format(snptype, name, go, x, ls) + + +def name_stationarynonlinearproblemsolver(go_type, go): + define_stationarynonlinearproblemsolver("snp", go_type, go) + return "snp" + + +@preamble +def typedef_timesteppingmethod(name): + r_type = type_range() + explicit = get_option('explicit_time_stepping') + if explicit: + return "using {} = Dune::PDELab::ExplicitEulerParameter<{}>;".format(name, r_type) + else: + return "using {} = Dune::PDELab::OneStepThetaParameter<{}>;".format(name, r_type) + + +def type_timesteppingmethod(): + typedef_timesteppingmethod("TSM") + return "TSM" + + +@preamble +def define_timesteppingmethod(name): + tsm_type = type_timesteppingmethod() + explicit = get_option('explicit_time_stepping') + if explicit: + return "{} {};".format(tsm_type, name) + else: + return "{} {}(1.0);".format(tsm_type, name) + + +def name_timesteppingmethod(): + define_timesteppingmethod("tsm") + return "tsm" + + +@preamble +def typedef_instationarygridoperator(name): + include_file("dune/pdelab/gridoperator/onestep.hh", filetag="driver") + go_type = type_gridoperator(_driver_data['formdata']) + mass_go_type = type_gridoperator(_driver_data['mass_formdata']) + explicit = get_option('explicit_time_stepping') + if explicit: + return "using {} = Dune::PDELab::OneStepGridOperator<{},{},false>;".format(name, go_type, mass_go_type) + else: + return "using {} = Dune::PDELab::OneStepGridOperator<{},{}>;".format(name, go_type, mass_go_type) + + +def type_instationarygridoperator(): + typedef_instationarygridoperator("IGO") + return "IGO" + + +@preamble +def define_instationarygridoperator(name): + igo_type = type_instationarygridoperator() + go = name_gridoperator(_driver_data['formdata']) + mass_go = name_gridoperator(_driver_data['mass_formdata']) + return "{} {}({}, {});".format(igo_type, name, go, mass_go) + + +def name_instationarygridoperator(): + define_instationarygridoperator("igo") + return "igo" + + +@preamble +def typedef_onestepmethod(name): + r_type = type_range() + igo_type = type_instationarygridoperator() + snp_type = type_stationarynonlinearproblemssolver(igo_type) + vector_type = type_vector(_driver_data['formdata']) + return "using {} = Dune::PDELab::OneStepMethod<{}, {}, {}, {}, {}>;".format(name, r_type, igo_type, snp_type, vector_type, vector_type) + + +def type_onestepmethod(): + typedef_onestepmethod("OSM") + return "OSM" + + +@preamble +def define_onestepmethod(name): + ilptype = type_onestepmethod() + tsm = name_timesteppingmethod() + igo_type = type_instationarygridoperator() + igo = name_instationarygridoperator() + snp = name_stationarynonlinearproblemsolver(igo_type, igo) + return "{} {}({},{},{});".format(ilptype, name, tsm, igo, snp) + + +def name_onestepmethod(): + define_onestepmethod("osm") + return "osm" + + +@preamble +def typedef_explicitonestepmethod(name): + r_type = type_range() + igo_type = type_instationarygridoperator() + ls_type = type_linearsolver() + vector_type = type_vector(_driver_data['formdata']) + return "using {} = Dune::PDELab::ExplicitOneStepMethod<{}, {}, {}, {}>;".format(name, r_type, igo_type, ls_type, vector_type) + + +def type_explicitonestepmethod(): + typedef_explicitonestepmethod("EOSM") + return "EOSM" + + +@preamble +def define_explicitonestepmethod(name): + eosm_type = type_explicitonestepmethod() + tsm = name_timesteppingmethod() + igo = name_instationarygridoperator() + ls = name_linearsolver() + return "{} {}({}, {}, {});".format(eosm_type, name, tsm, igo, ls) + + +def name_explicitonestepmethod(): + define_explicitonestepmethod("eosm") + return "eosm" @preamble @@ -239,34 +1156,638 @@ def name_mpihelper(): return name -def _blockstructured_degree_helper(element, op): - element._degree = op(element._degree, get_option("number_of_blocks")) +@preamble +def define_timing_stream(name): + include_file('fstream', filetag='driver', system=True) + include_file('sstream', filetag='driver', system=True) + include_file('sys/types.h', filetag='driver', system=True) + include_file('unistd.h', filetag='driver', system=True) + + return ["std::stringstream ss;", + "ss << \"{}/timings-rank-\" << {}.rank() << \"-pid-\" << getpid() << \".csv\";".format(get_option('project_basedir'), name_mpihelper()), + "std::ofstream {};".format(name), + "{}.open(ss.str(), std::ios_base::app);".format(name), + ] + + +@preamble +def dump_dof_numbers(stream): + ident = name_timing_identifier() + return "{} << {} << \" dofs dofs \" << {}.size() << std::endl;".format(stream, + ident, + name_gfs(_driver_data['form'].coefficients()[0].ufl_element())) + + +def name_timing_stream(): + name = "timestream" + define_timing_stream(name) + dump_dof_numbers(name) + return name + + +@preamble +def dune_solve(): + # Test if form is linear in ansatzfunction + linear = is_linear(_driver_data['formdata'].original_form) + + # Test wether we want to do matrix free operator evaluation + matrix_free = get_option('matrix_free') + + # Get right solve command + if linear and matrix_free: + formdata = _driver_data['formdata'] + go = name_gridoperator(formdata) + x = name_vector(formdata) + include_file("dune/perftool/matrixfree.hh", filetag="driver") + solve = "solveMatrixFree({},{});".format(go, x) + elif linear and not matrix_free: + slp = name_stationarylinearproblemsolver() + solve = "{}.apply();".format(slp) + elif not linear and matrix_free: + # TODO copy of linear case and obviously broken, used to generate something ;) + formdata = _driver_data['formdata'] + go = name_gridoperator(formdata) + x = name_vector(formdata) + include_file("dune/perftool/matrixfree.hh", filetag="driver") + solve = "solveNonlinearMatrixFree({},{});".format(go, x) + elif not linear and not matrix_free: + go_type = type_gridoperator(_driver_data['formdata']) + go = name_gridoperator(_driver_data['formdata']) + snp = name_stationarynonlinearproblemsolver(go_type, go) + solve = "{}.apply();".format(snp) + + if get_option('instrumentation_level') >= 2: + setup_timer() + from dune.perftool.generation import post_include + post_include("HP_DECLARE_TIMER(solve);", filetag="driver") + + # Print times after solving + from dune.perftool.generation import get_global_context_value + formdatas = get_global_context_value("formdatas") + print_times = [] + for formdata in formdatas: + lop_name = name_localoperator(formdata) + timestream = name_timing_stream() + print_times.append("{}.dump_timers({}, {}, true);".format(lop_name, timestream, name_timing_identifier())) + + solve = ["HP_TIMER_START(solve);", + "{}".format(solve), + "HP_TIMER_STOP(solve);", + "DUMP_TIMER(solve, {}, true);".format(timestream), + ] + if get_option('instrumentation_level') >= 3: + solve.extend(print_times) + + return solve + + +@preamble +def define_vtkfile(name): + ini = name_initree() + include_file("string", filetag="driver") + return "std::string {} = {}.get<std::string>(\"wrapper.vtkcompare.name\", \"output\");".format(name, ini) + + +def name_vtkfile(): + define_vtkfile("vtkfile") + return "vtkfile" + + +@preamble +def compare_dofs(): + v = name_vector(_driver_data['formdata']) + solution = name_vector_from_solution_expression() + fail = name_test_fail_variable() + return ["", + "// Maximum norm of difference between dof vectors of the", + "// numerical solution and the interpolation of the exact solution", + "using Dune::PDELab::Backend::native;", + "double maxerror(0.0);", + "for (std::size_t i=0; i<native({}).size(); ++i)".format(v), + " if (std::abs(native({})[i]-native({})[i]) > maxerror)".format(v, solution), + " maxerror = std::abs(native({})[i]-native({})[i]);".format(v, solution), + "std::cout << std::endl << \"Maxerror: \" << maxerror << std::endl;", + "if (maxerror>{})".format(get_option("compare_dofs")), + " {} = true;".format(fail)] + + +def type_discrete_grid_function(gfs): + return "DGF_{}".format(gfs.upper()) + + +@preamble +def define_discrete_grid_function(gfs, vector_name, dgf_name): + dgf_type = type_discrete_grid_function(gfs) + return ["using {} = Dune::PDELab::DiscreteGridFunction<decltype({}),decltype({})>;".format(dgf_type, gfs, vector_name), + "{} {}({},{});".format(dgf_type, dgf_name, gfs, vector_name)] + + +def name_discrete_grid_function(gfs, vector_name): + dgf_name = gfs + "_dgf" + define_discrete_grid_function(gfs, vector_name, dgf_name) + return dgf_name + + +def type_subgfs(element, tree_path): + include_file('dune/pdelab/gridfunctionspace/subspace.hh', filetag='driver') + gfs = type_gfs(element) + return "Dune::PDELab::GridFunctionSubSpace<{}, Dune::TypeTree::TreePath<{}> >".format(gfs, ', '.join(tree_path)) + + +@preamble +def define_subgfs(name, element, tree_path): + t = type_subgfs(element, tree_path) + gfs = name_gfs(element) + + return '{} {}({});'.format(t, name, gfs) + + +def name_subgfs(element, tree_path): + gfs = name_gfs(element) + if len(tree_path) == 0: + return gfs + else: + name = "subgfs_{}_{}".format(gfs, '_'.join(tree_path)) + define_subgfs(name, element, tree_path) + return name + + +@preamble +def typedef_difference_squared_adapter(name, tree_path): + element = _driver_data['form'].coefficients()[0].ufl_element() + formdata = _driver_data['formdata'] + + solution_function = name_solution_function(tree_path) + gfs = name_subgfs(element, tree_path) + vector = name_vector(formdata) + dgf = name_discrete_grid_function(gfs, vector) + + return 'using {} = Dune::PDELab::DifferenceSquaredAdapter<decltype({}), decltype({})>;'.format(name, solution_function, dgf) + + +def type_difference_squared_adapter(tree_path): + t = 'DifferenceSquared_{}'.format('_'.join(tree_path)) + typedef_difference_squared_adapter(t, tree_path) + return t + + +@preamble +def define_difference_squared_adapter(name, tree_path): + element = _driver_data['form'].coefficients()[0].ufl_element() + formdata = _driver_data['formdata'] + + t = type_difference_squared_adapter(tree_path) + solution_function = name_solution_function(tree_path) + gfs = name_subgfs(element, tree_path) + vector = name_vector(formdata) + dgf = name_discrete_grid_function(gfs, vector) + + return '{} {}({}, {});'.format(t, name, solution_function, dgf) + + +def name_difference_squared_adapter(tree_path): + name = 'differencesquared_{}'.format('_'.join(tree_path)) + define_difference_squared_adapter(name, tree_path) + return name + + +@preamble +def _accumulate_L2_squared(tree_path): + dsa = name_difference_squared_adapter(tree_path) + t_dsa = type_difference_squared_adapter(tree_path) + accum_error = name_accumulated_L2_error() + + include_file("dune/pdelab/gridfunctionspace/gridfunctionadapter.hh", filetag="driver") + include_file("dune/pdelab/common/functionutilities.hh", filetag="driver") + + return ["{", + " // L2 error squared of difference between numerical", + " // solution and the interpolation of exact solution", + " // only subgfs with treepath: {}".format(', '.join(tree_path)), + " typename {}::Traits::RangeType err(0.0);".format(t_dsa), + " Dune::PDELab::integrateGridFunction({}, err, 10);".format(dsa), + " {} += err;".format(accum_error), + " std::cout << \"Accumlated L2 Error for tree path {}: \" << err << std::endl;".format(tree_path), + "}"] + + +def accumulate_L2_squared(tree_path=()): + element = _driver_data['form'].coefficients()[0].ufl_element() + from ufl.functionview import select_subelement + from ufl.classes import MultiIndex, FixedIndex + element = select_subelement(element, MultiIndex(tuple(FixedIndex(int(i)) for i in tree_path))) + + from ufl import MixedElement + if isinstance(element, MixedElement): + for i, subel in enumerate(element.sub_elements()): + accumulate_L2_squared(tree_path + (str(i),)) + else: + _accumulate_L2_squared(tree_path) + + +@preamble +def define_accumulated_L2_error(name): + return "double {}(0.0);".format(name) + + +def name_accumulated_L2_error(): + name = 'l2error' + define_accumulated_L2_error(name) + return name + + +@preamble +def compare_L2_squared(): + accumulate_L2_squared() + + accum_error = name_accumulated_L2_error() + fail = name_test_fail_variable() + return ["std::cout << \"l2errorsquared: \" << {} << std::endl;".format(accum_error), + "if (std::isnan({0}) or std::abs({0})>{1})".format(accum_error, get_option("compare_l2errorsquared")), + " {} = true;".format(fail)] + + +@preamble +def define_test_fail_variable(name): + return 'bool {}(false);'.format(name) + + +def name_test_fail_variable(): + name = "testfail" + define_test_fail_variable(name) + return name + + +@cached +def setup_timer(): + # Necessary includes and defines + from dune.perftool.generation import pre_include + + # TODO check that we are using YASP? + if get_option('opcounter'): + pre_include("#define ENABLE_COUNTER", filetag="driver") + pre_include("#define ENABLE_HP_TIMERS", filetag="driver") + include_file("dune/perftool/common/timer.hh", filetag="driver") + + +@preamble +def define_timing_identifier(name): + ini = name_initree() + return "auto {} = {}.get<std::string>(\"identifier\", std::string(argv[0]));".format(name, ini) + + +def name_timing_identifier(): + name = "ident" + define_timing_identifier(name) + return name + + +@preamble +def evaluate_residual_timer(): + formdata = _driver_data['formdata'] + n_go = name_gridoperator(formdata) + v = name_vector(formdata) + t_v = type_vector(formdata) + setup_timer() + + if get_option('instrumentation_level') >= 2: + # Write back times + from dune.perftool.generation import post_include + post_include("HP_DECLARE_TIMER(residual_evaluation);", filetag="driver") + timestream = name_timing_stream() + print_times = [] + + from dune.perftool.generation import get_global_context_value + formdatas = get_global_context_value("formdatas") + for formdata in formdatas: + lop_name = name_localoperator(formdata) + if get_option('instrumentation_level') >= 3: + print_times.append("{}.dump_timers({}, {}, true);".format(lop_name, timestream, name_timing_identifier())) + + if get_option('instrumentation_level') >= 2: + evaluation = ["HP_TIMER_START(residual_evaluation);", + "{}.residual({}, r);".format(n_go, v), + "HP_TIMER_STOP(residual_evaluation);", + "DUMP_TIMER(residual_evaluation, {}, true);".format(timestream)] + evaluation.extend(print_times) + else: + evaluation = ["{}.residual({}, r);".format(n_go, v)] + + evaluation = ["{} r({});".format(t_v, v), "r=0.0;"] + evaluation + + return evaluation + + +@preamble +def apply_jacobian_timer(): + # Set the matrix_free option to True! + from dune.perftool.options import set_option + set_option("matrix_free", True) + + formdata = _driver_data['formdata'] + n_go = name_gridoperator(formdata) + v = name_vector(formdata) + t_v = type_vector(formdata) + setup_timer() + + if get_option('instrumentation_level') >= 2: + # Write back times + from dune.perftool.generation import post_include + post_include("HP_DECLARE_TIMER(apply_jacobian);", filetag="driver") + timestream = name_timing_stream() + print_times = [] + + from dune.perftool.generation import get_global_context_value + formdatas = get_global_context_value("formdatas") + for formdata in formdatas: + lop_name = name_localoperator(formdata) + if get_option('instrumentation_level') >= 3: + print_times.append("{}.dump_timers({}, {}, true);".format(lop_name, timestream, name_timing_identifier())) + + if get_option('instrumentation_level') >= 2: + evaluation = ["HP_TIMER_START(apply_jacobian);", + "{}.jacobian_apply({}, j);".format(n_go, v), + "HP_TIMER_STOP(apply_jacobian);", + "DUMP_TIMER(apply_jacobian, {}, true);".format(timestream)] + evaluation.extend(print_times) + else: + evaluation = ["{}.jacobian_apply({}, j);".format(n_go, v)] + + evaluation = ["{} j({});".format(t_v, v), "j=0.0;"] + evaluation + + return evaluation + + +@preamble +def assemble_matrix_timer(): + formdata = _driver_data['formdata'] + t_go = type_gridoperator(formdata) + n_go = name_gridoperator(formdata) + v = name_vector(formdata) + t_v = type_vector(formdata) + setup_timer() + + if get_option('instrumentation_level') >= 2: + # Write back times + from dune.perftool.generation import post_include + post_include("HP_DECLARE_TIMER(matrix_assembly);", filetag="driver") + timestream = name_timing_stream() + print_times = [] + + from dune.perftool.generation import get_global_context_value + formdatas = get_global_context_value("formdatas") + for formdata in formdatas: + lop_name = name_localoperator(formdata) + if get_option('instrumentation_level') >= 3: + print_times.append("{}.dump_timers({}, {}, true);".format(lop_name, timestream, name_timing_identifier())) + + if get_option('instrumentation_level') >= 2: + assembly = ["HP_TIMER_START(matrix_assembly);", + "{}.jacobian({},m);".format(n_go, v), + "HP_TIMER_STOP(matrix_assembly);", + "DUMP_TIMER(matrix_assembly, {}, true);".format(timestream)] + assembly.extend(print_times) + else: + assembly = ["{}.jacobian({},m);".format(n_go, v)] + + assembly = ["using M = typename {}::Traits::Jacobian;".format(t_go), + "M m({});".format(n_go)] + assembly + + return assembly + + +@preamble +def print_residual(): + ini = name_initree() + formdata = _driver_data['formdata'] + n_go = name_gridoperator(formdata) + v = name_vector(formdata) + t_v = type_vector(formdata) + include_file("random", system=True, filetag="driver") + + return ["if ({}.get<bool>(\"printresidual\", false)) {{".format(ini), + " using Dune::PDELab::Backend::native;", + " {} r({});".format(t_v, v), + " // Setup random input", + " std::size_t seed = 0;", + " auto rng = std::mt19937_64(seed);", + " auto dist = std::uniform_real_distribution<>(-1., 1.);", + " for (auto& v : {})".format(v), + " v = dist(rng);", + " r=0.0;", + " {}.residual({}, r);".format(n_go, v), + " Dune::printvector(std::cout, native(r), \"residual vector\", \"row\");", + "}"] + + +@preamble +def print_matrix(): + formdata = _driver_data['formdata'] + ini = name_initree() + t_go = type_gridoperator(formdata) + n_go = name_gridoperator(formdata) + v = name_vector(formdata) + t_v = type_vector(formdata) + + return ["if ({}.get<bool>(\"printmatrix\", false)) {{".format(ini), + " using M = typename {}::Traits::Jacobian;".format(t_go), + " M m({});".format(n_go), + " {}.jacobian({},m);".format(n_go, v), + " using Dune::PDELab::Backend::native;", + " Dune::printmatrix(std::cout, native(m),\"global stiffness matrix\",\"row\",9,1);", + "}"] + + +@preamble +def define_gfs_name(element, prefix=()): from ufl import MixedElement, VectorElement, TensorElement - if isinstance(element, VectorElement) or isinstance(element, TensorElement): - _blockstructured_degree_helper(element.sub_elements()[0], op) - elif isinstance(element, MixedElement): - for e in element.sub_elements(): - _blockstructured_degree_helper(e, op) + if isinstance(element, (VectorElement, TensorElement)): + gfs = name_gfs(element) + return "{}.name(\"data_{}\");".format(gfs, "_".join(str(p) for p in prefix)) + if isinstance(element, MixedElement): + for (i, el) in enumerate(element.sub_elements()): + define_gfs_name(el, prefix + (i,)) + return "" + # This is a scalar leaf + gfs = name_gfs(element) + return "{}.name(\"data_{}\");".format(gfs, "_".join(str(p) for p in prefix)) + +def type_predicate(): + include_file("dune/perftool/vtkpredicate.hh", filetag="driver") + return "CuttingPredicate" -def set_blockstructured_degree(): - from operator import mul - _blockstructured_degree_helper(_driver_data["form"].coefficients()[0].ufl_element(), mul) + +@preamble +def define_predicate(name): + t = type_predicate() + return "{} {};".format(t, name) -def unset_blockstructured_degree(): - from operator import floordiv - _blockstructured_degree_helper(_driver_data["form"].coefficients()[0].ufl_element(), floordiv) +def name_predicate(): + define_predicate("predicate") + return "predicate" + + +@preamble +def vtkoutput(): + element = _driver_data['form'].coefficients()[0].ufl_element() + define_gfs_name(element) + include_file("dune/pdelab/gridfunctionspace/vtk.hh", filetag="driver") + vtkwriter = name_vtkwriter() + gfs = name_gfs(element) + vec = name_vector(_driver_data['formdata']) + vtkfile = name_vtkfile() + predicate = name_predicate() + dune_solve() + print_residual() + print_matrix() + + if get_option("exact_solution_expression"): + if get_option("compare_dofs"): + compare_dofs() + if get_option("compare_l2errorsquared"): + compare_L2_squared() + + return ["Dune::PDELab::addSolutionToVTKWriter({}, {}, {}, Dune::PDELab::vtk::defaultNameScheme(), {});".format(vtkwriter, gfs, vec, predicate), + "{}.write({}, Dune::VTK::ascii);".format(vtkwriter, vtkfile)] + + +@preamble +def define_time(name): + return "double {} = 0.0;".format(name) + + +def name_time(): + define_time("time") + return "time" + + +@preamble +def typedef_vtk_sequence_writer(name): + include_file("dune/grid/io/file/vtk/vtksequencewriter.hh", filetag="driver") + gv_type = type_leafview() + return "using {} = Dune::VTKSequenceWriter<{}>;".format(name, gv_type) + + +def type_vtk_sequence_writer(): + typedef_vtk_sequence_writer("VTKSW") + return "VTKSW" + + +@preamble +def define_vtk_sequence_writer(name): + vtksw_type = type_vtk_sequence_writer() + vtkw_type = type_vtkwriter() + vtkw = name_vtkwriter() + vtkfile = name_vtkfile() + return "{} {}(std::make_shared<{}>({}), {});".format(vtksw_type, name, vtkw_type, vtkw, vtkfile) + + +def name_vtk_sequence_writer(): + define_vtk_sequence_writer("vtkSequenceWriter") + return "vtkSequenceWriter" + + +@preamble +def visualize_initial_condition(): + include_file("dune/pdelab/gridfunctionspace/vtk.hh", filetag="driver") + vtkwriter = name_vtk_sequence_writer() + element = _driver_data['form'].coefficients()[0].ufl_element() + define_gfs_name(element) + gfs = name_gfs(element) + vector = name_vector(_driver_data['formdata']) + predicate = name_predicate() + time = name_time() + return ["Dune::PDELab::addSolutionToVTKWriter({}, {}, {}, Dune::PDELab::vtk::defaultNameScheme(), {});".format(vtkwriter, gfs, vector, predicate), + "{}.write({}, Dune::VTK::appendedraw);".format(vtkwriter, time)] + + +@preamble +def time_loop(): + ini = name_initree() + formdata = _driver_data['formdata'] + params = name_parameters(formdata) + time = name_time() + expr = _driver_data['form'].coefficients()[0].ufl_element() + bctype = name_bctype_function(expr) + gfs = name_gfs(expr) + cc = name_constraintscontainer(expr) + vector_type = type_vector(formdata) + vector = name_vector(formdata) + + # Choose between explicit and implicit time stepping + explicit = get_option('explicit_time_stepping') + if explicit: + osm = name_explicitonestepmethod() + apply_call = "{}.apply(time, dt, {}, {}new);".format(osm, vector, vector) + else: + boundary = name_boundary_function(expr) + osm = name_onestepmethod() + apply_call = "{}.apply(time, dt, {}, {}, {}new);".format(osm, vector, boundary, vector) + + # Setup visualization + visualize_initial_condition() + vtk_sequence_writer = name_vtk_sequence_writer() + + return ["", + "double T = {}.get<double>(\"instat.T\", 1.0);".format(ini), + "double dt = {}.get<double>(\"instat.dt\", 0.1);".format(ini), + "while (time<T-1e-8){", + " // Assemble constraints for new time step", + " {}.setTime({}+dt);".format(params, time), + " Dune::PDELab::constraints({}, {}, {});".format(bctype, gfs, cc), + "", + " // Do time step", + " {} {}new({});".format(vector_type, vector, vector), + " {}".format(apply_call), + "", + " // Accept new time step", + " {} = {}new;".format(vector, vector), + " time += dt;", + "", + " // Output to VTK File", + " {}.write({}, Dune::VTK::appendedraw);".format(vtk_sequence_writer, time), + "}", + ""] + + +def solve_instationary(): + # Test if form is linear in ansatzfunction + linear = is_linear(_driver_data['formdata'].original_form) + + # Test wether we want to do matrix free operator evaluation + matrix_free = get_option('matrix_free') + + # Create time loop + if linear and matrix_free: + assert False + elif linear and not matrix_free: + time_loop() + if not linear and matrix_free: + assert False + elif not linear and not matrix_free: + assert False + + print_residual() + print_matrix() + if get_option("exact_solution_expression"): + if get_option("compare_dofs"): + compare_dofs() + if get_option("compare_l2errorsquared"): + compare_L2_squared() + + +@preamble +def return_statement(): + fail = name_test_fail_variable() + return "return {};".format(fail) def generate_driver(formdatas, data): # The driver module uses a global dictionary for storing necessary data set_driver_data(formdatas, data) - # TODO find a better solution ... - if get_option("blockstructured"): - set_blockstructured_degree() - # Entrypoint for driver generation if get_option("opcounter") or get_option("time_opcounter"): if get_option("time_opcounter"): @@ -275,29 +1796,19 @@ def generate_driver(formdatas, data): ["vertex", "interval", "quadrilateral", "hexahedron"])) # In case of operator conunting we only assemble the matrix and evaluate the residual # assemble_matrix_timer() - from dune.perftool.pdelab.driver.timings import apply_jacobian_timer, evaluate_residual_timer evaluate_residual_timer() apply_jacobian_timer() elif is_stationary(): - from dune.perftool.pdelab.driver.solve import dune_solve - vec = dune_solve() - from dune.perftool.pdelab.driver.vtk import vtkoutput + # We could also use solve if we are not interested in visualization vtkoutput() else: - from dune.perftool.pdelab.driver.instationary import solve_instationary solve_instationary() - from dune.perftool.pdelab.driver.error import compare_L2_squared - if get_option("compare_l2errorsquared"): - compare_L2_squared() - # Make sure that timestream is declared before retrieving chache items if get_option("instrumentation_level") >= 1: - from dune.perftool.pdelab.driver.timings import setup_timer, name_timing_stream setup_timer() timestream = name_timing_stream() - from dune.perftool.pdelab.driver.error import return_statement return_statement() from dune.perftool.generation import retrieve_cache_items @@ -324,7 +1835,3 @@ def generate_driver(formdatas, data): # Reset the caching data structure from dune.perftool.generation import delete_cache_items delete_cache_items() - - # TODO find a better solution ... - if get_option("blockstructured"): - unset_blockstructured_degree() -- GitLab