diff --git a/dune.module b/dune.module index 390ad16e23f809e35891886e8edf4c13e2b551ce..bd22ba1dd5e77e68e84713eeb8d7624f062f645a 100644 --- a/dune.module +++ b/dune.module @@ -6,4 +6,4 @@ Module: amdis Version: 0.1 Maintainer: simon.praetorius@tu-dresden.de Depends: dune-common (>= 2.6) dune-geometry (>= 2.6) dune-localfunctions (>= 2.6) dune-typetree (>= 2.6) dune-grid (>= 2.6) dune-functions (>= 2.6) -Suggests: dune-uggrid dune-alugrid dune-foamgrid +Suggests: dune-uggrid dune-alugrid dune-foamgrid dune-spgrid diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 72c586a1570c6253d0392577eacafc381ad7a70c..14ca4fe498081040f84f9bc1fdffb6deeac8802a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,17 @@ add_custom_target(examples) -set(projects2d "ellipt" "heat" "vecellipt" "stokes0" "stokes1" "stokes3" "navier_stokes" "convection_diffusion") +set(projects2d + "ellipt" + "heat" + "vecellipt" + "stokes0" + "stokes1" + "stokes3" + "navier_stokes" + "convection_diffusion" + "boundary" + "periodic" + "neumann") foreach(project ${projects2d}) add_executable(${project}.2d ${project}.cc) diff --git a/examples/boundary.cc b/examples/boundary.cc new file mode 100644 index 0000000000000000000000000000000000000000..c6f31b4b990a2e58321357bed1ebafedfb8e90f2 --- /dev/null +++ b/examples/boundary.cc @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <iostream> + +#if HAVE_DUNE_SPGRID +#include <dune/grid/spgrid.hh> +#endif + +#include <amdis/AMDiS.hpp> +#include <amdis/Integrate.hpp> +#include <amdis/ProblemStat.hpp> +#include <amdis/Operators.hpp> +#include <amdis/common/Literals.hpp> + +using namespace AMDiS; +using namespace Dune::Indices; + +// 1 component with polynomial degree 2 +using Param = YaspGridBasis<AMDIS_DIM, 2>; +using ElliptProblem = ProblemStat<Param>; + +template <class SetBoundary> +void run(SetBoundary setBoundary) +{ + ElliptProblem prob("ellipt"); + prob.initialize(INIT_ALL); + setBoundary(prob.boundaryManager()); + + auto opL = makeOperator(tag::gradtest_gradtrial{}, 1.0); + prob.addMatrixOperator(opL, 0, 0); + + auto f = [](auto const& x) { + double r2 = dot(x,x); + double ux = std::exp(-10.0 * r2); + return -(400.0 * r2 - 20.0 * x.size()) * ux; + }; + auto opForce = makeOperator(tag::test{}, f, 6); + prob.addVectorOperator(opForce, 0); + + // set boundary condition + auto g = [](auto const& x){ return std::exp(-10.0 * dot(x,x)); }; + prob.addDirichletBC(BoundaryType{1}, 0, 0, g); + + AdaptInfo adaptInfo("adapt"); + + prob.assemble(adaptInfo); + prob.solve(adaptInfo); + + double errorL2 = integrate(sqr(g - prob.solution(0)), prob.gridView(), 6); + msg("error_L2 = {}", errorL2); +} + +void run_periodic() +{ +#if HAVE_DUNE_SPGRID + Dune::SPCube<double,2> cube({0.0,0.0},{1.0,1.0}); + Dune::SPDomain<double,2> domain({cube}, Dune::SPTopology<2>(0b01)); + Dune::SPGrid<double,2> grid(domain, Dune::SPMultiIndex<2>({2,2})); + using Grid = Dune::SPGrid<double,2>; +#else + Dune::YaspGrid<2> grid({1.0,1.0},{2,2},std::bitset<2>("10"),0); + using Grid = Dune::YaspGrid<2>; +#endif + + using Traits = LagrangeBasis<typename Grid::LeafGridView, 2>; + ProblemStat<Traits> prob("ellipt", grid); + prob.initialize(INIT_ALL); + prob.boundaryManager().setBoxBoundary({-1,-1,1,1}); + + auto opL = makeOperator(tag::gradtest_gradtrial{}, 1.0); + prob.addMatrixOperator(opL, 0, 0); + + auto opForce = makeOperator(tag::test{}, 1.0, 6); + prob.addVectorOperator(opForce, 0); + + // set boundary condition + auto g = [](auto const& x) { + return std::sin(0.5*M_PI*x[1])*std::sin(2*M_PI * (0.25 + x[0])) + + std::cos(0.5*M_PI*x[1])*std::sin(2*M_PI * (-0.25 + x[0])); + }; + prob.addDirichletBC(BoundaryType{1}, 0, 0, g); + + typename ElliptProblem::WorldMatrix A{{1.0,0.0}, {0.0,1.0}}; + typename ElliptProblem::WorldVector b{1.0, 0.0}; + prob.addPeriodicBC(BoundaryType{-1}, A, b); + + AdaptInfo adaptInfo("adapt"); + + prob.assemble(adaptInfo); + prob.solve(adaptInfo); + prob.writeFiles(adaptInfo, true); +} + + +int main(int argc, char** argv) +{ + AMDiS::init(argc, argv); + + auto b = [](auto const& x){ return x[0] < 1.e-8 || x[1] < 1.e-8 || x[0] > 1.0-1.e-8 || x[1] > 1.0-1.e-8; }; + + auto setBoundary1 = [](auto& boundaryManager) + { + boundaryManager.setBoxBoundary({1,1,1,1}); + }; + run(setBoundary1); + + auto setBoundary2 = [b](auto& boundaryManager) + { + boundaryManager.setIndicator([b](auto const& x) { return b(x) ? 1 : 0; }); + }; + run(setBoundary2); + + auto setBoundary3 = [b](auto& boundaryManager) + { + boundaryManager.setPredicate(b, 1); + }; + run(setBoundary3); + + run_periodic(); + + AMDiS::finalize(); + return 0; +} diff --git a/examples/ellipt.cc b/examples/ellipt.cc index 61e679262b84473cd3f5e1cfb0f1fcf19e3bec7f..3a19575a81544a55a9df0bf0a81d7baa6ea1a43b 100644 --- a/examples/ellipt.cc +++ b/examples/ellipt.cc @@ -44,8 +44,7 @@ int main(int argc, char** argv) prob.addVectorOperator(opForce, 0); // set boundary condition - auto boundary = [](auto const& x){ return x[0] < 1.e-8 || x[1] < 1.e-8 || x[0] > 1.0-1.e-8 || x[1] > 1.0-1.e-8; }; - prob.addDirichletBC(boundary, 0, 0, g); + prob.addDirichletBC(BoundaryType{1}, 0, 0, g); AdaptInfo adaptInfo("adapt"); diff --git a/examples/init/boundary.dat.2d b/examples/init/boundary.dat.2d new file mode 100644 index 0000000000000000000000000000000000000000..1f284109be8700240d6856c65a04874f86fe2cf1 --- /dev/null +++ b/examples/init/boundary.dat.2d @@ -0,0 +1,15 @@ +elliptMesh->global refinements: 3 + +ellipt->mesh: elliptMesh + +ellipt->solver->name: bicgstab +ellipt->solver->max iteration: 10000 +ellipt->solver->relative tolerance: 1.e-10 +ellipt->solver->info: 10 +ellipt->solver->left precon: ilu +ellipt->solver->right precon: no +ellipt->solver->precon->name: ilu + +ellipt->output[0]->filename: boundary.2d +ellipt->output[0]->name: u +ellipt->output[0]->output directory: ./output diff --git a/examples/neumann.cc b/examples/neumann.cc new file mode 100644 index 0000000000000000000000000000000000000000..65ab4016564cf489f33d0cdedc269b6049737c6f --- /dev/null +++ b/examples/neumann.cc @@ -0,0 +1,90 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <iostream> + +#if HAVE_DUNE_SPGRID +#include <dune/grid/spgrid.hh> +#endif +#if HAVE_DUNE_ALUGRID +#include <dune/alugrid/grid.hh> +#endif +#include <dune/grid/uggrid.hh> + +#include <amdis/AMDiS.hpp> +#include <amdis/Integrate.hpp> +#include <amdis/ProblemStat.hpp> +#include <amdis/Operators.hpp> +#include <amdis/common/Literals.hpp> + +using namespace AMDiS; +using namespace Dune::Indices; + +template <class Grid> +void run(Grid& grid) +{ + grid.globalRefine(Grid::dimension == 2 ? 4 : 2); + + using Traits = LagrangeBasis<typename Grid::LeafGridView, 2>; + ProblemStat<Traits> prob("ellipt", grid); + prob.initialize(INIT_ALL); + prob.boundaryManager().setBoxBoundary({1,2,2,1}); + + auto opL = makeOperator(tag::gradtest_gradtrial{}, 1.0); + prob.addMatrixOperator(opL, 0, 0); + + // set dirichlet boundary condition + auto g = [](auto const& x) { return 0.0; }; + prob.addDirichletBC(BoundaryType{1}, 0, 0, g); + + // set neumann boundary condition + auto opNeumann = makeOperator(tag::test{}, 1.0); + prob.addVectorOperator(BoundaryType{2}, opNeumann, 0); + + AdaptInfo adaptInfo("adapt"); + + prob.assemble(adaptInfo); + prob.solve(adaptInfo); + prob.writeFiles(adaptInfo, true); +} + + +int main(int argc, char** argv) +{ + AMDiS::init(argc, argv); + + // 2d grids + + Dune::YaspGrid<2> grid0({1.0,1.0},{2,2}); + run(grid0); + +#if HAVE_DUNE_SPGRID + Dune::SPDomain<double,2> domain({0.0,0.0}, {1.0,1.0}); + Dune::SPGrid<double,2> grid1(domain, Dune::SPMultiIndex<2>({2,2})); + run(grid1); +#endif + +#if HAVE_DUNE_ALUGRID + using Grid2 = Dune::ALUGrid<2,2,Dune::simplex,Dune::conforming>; + using Factory2 = Dune::StructuredGridFactory<Grid2>; + auto grid2 = Factory2::createSimplexGrid({0.0,0.0}, {1.0,1.0}, + std::array<unsigned int,2>{2u,2u}); + run(*grid2); +#endif + +#if HAVE_DUNE_UGGRID + using Grid3 = Dune::UGGrid<2>; + using Factory3 = Dune::StructuredGridFactory<Grid3>; + auto grid3 = Factory3::createSimplexGrid({0.0,0.0}, {1.0,1.0}, + std::array<unsigned int,2>{2u,2u}); + run(*grid3); +#endif + + // 3d grids + + Dune::YaspGrid<3> grid4({1.0,1.0,1.0},{2,2,2}); + run(grid4); + + AMDiS::finalize(); + return 0; +} diff --git a/examples/periodic.cc b/examples/periodic.cc new file mode 100644 index 0000000000000000000000000000000000000000..f03e0a07637f8910c289b4c9f5f09883d6ff04cd --- /dev/null +++ b/examples/periodic.cc @@ -0,0 +1,97 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <iostream> + +#if HAVE_DUNE_SPGRID +#include <dune/grid/spgrid.hh> +#endif + +#include <amdis/AMDiS.hpp> +#include <amdis/Integrate.hpp> +#include <amdis/ProblemStat.hpp> +#include <amdis/Operators.hpp> +#include <amdis/common/Literals.hpp> + +using namespace AMDiS; + + +template <class Grid> +void print(Grid const& grid) +{ + auto const& indexSet = grid.leafIndexSet(); + std::cout << "vertices:\n"; + for (auto const& v : vertices(grid.leafGridView())) { + auto coord = v.geometry().corner(0); + std::cout << indexSet.index(v) << ": [" << coord[0] << ", " << coord[1] << "]\n"; + } + std::cout << "\n"; + + std::cout << "elements:\n"; + for (auto const& e : elements(grid.leafGridView())) { + std::cout << indexSet.index(e) << ": {" << indexSet.subIndex(e,0,2); + for (unsigned int i = 1; i < e.subEntities(2); ++i) { + std::cout << ", " << indexSet.subIndex(e,i,2); + } + std::cout << "}\n"; + } + std::cout << "\n"; + + std::cout << "boundary-intersections:\n"; + for (auto const& e : elements(grid.leafGridView())) { + for (auto const& inter : intersections(grid.leafGridView(), e)) { + if (!inter.boundary()) + continue; + + std::cout << inter.boundarySegmentIndex() << ": [" << inter.geometry().center() << "] {" << indexSet.index(inter.inside()); + if (inter.neighbor()) + std::cout << ", " << indexSet.index(inter.outside()); + std::cout << "}\n"; + } + } +} + +template <class Grid> +void run(Grid& grid) +{ + using Traits = LagrangeBasis<typename Grid::LeafGridView, 1>; + + ProblemStat<Traits> prob("ellipt", grid); + prob.initialize(INIT_ALL); + prob.boundaryManager().setBoxBoundary({-1,-1,1,1}); + + print(grid); + + using BC = PeriodicBC<FieldVector<double,2>, typename Traits::GlobalBasis::MultiIndex>; + BC periodicBC(prob.boundaryManagerPtr(),-1,{{{1.0,0.0}, {0.0,1.0}}, {1.0, 0.0}}); + + periodicBC.init(prob.globalBasis(), prob.globalBasis()); + std::cout << "periodicNodes:\n"; + for (std::size_t i = 0; i < periodicBC.periodicNodes().size(); ++i) + std::cout << i << ": " << periodicBC.periodicNodes()[i] << "\n"; + std::cout << "\n"; + + std::cout << "associations:\n"; + for (auto const& a : periodicBC.associations()) + std::cout << a.first << " -> " << a.second << "\n"; + std::cout << "\n"; +} + +int main(int argc, char** argv) +{ + AMDiS::init(argc, argv); + +#if HAVE_DUNE_SPGRID + Dune::SPCube<double,2> cube({0.0,0.0},{1.0,1.0}); + Dune::SPDomain<double,2> domain({cube}, Dune::SPTopology<2>(0b01)); + Dune::SPGrid<double,2> grid1(domain, Dune::SPMultiIndex<2>({2,2})); + run(grid1); +#endif + + // NOTE: 'periodic' YaspGrid not supported yet, but a simple YaspGrid can be made periodic + Dune::YaspGrid<2> grid2({1.0,1.0}, {2,2}); + run(grid2); + + AMDiS::finalize(); + return 0; +} diff --git a/src/amdis/Assembler.hpp b/src/amdis/Assembler.hpp index 7452ea985a1cf682ab9c008f25d4eda85da22d7a..9da5ea595c4bade6807d37b712def39adc0c0836 100644 --- a/src/amdis/Assembler.hpp +++ b/src/amdis/Assembler.hpp @@ -2,15 +2,30 @@ namespace AMDiS { - template <class GridView, class Element, class Operators, class ElementAssembler> + template <class ElementAssembler, class IntersectionAssembler, class BoundaryAssembler> + struct AssemblerTriple + { + ElementAssembler elementAssembler; + IntersectionAssembler intersectionAssembler; + BoundaryAssembler boundaryAssembler; + }; + + template <class EA, class IA, class BA> + AssemblerTriple<EA, IA, BA> makeAssemblerTriple(EA const& ea, IA const& ia, BA const& ba) + { + return {ea, ia, ba}; + } + + + template <class GridView, class Element, class Operators, class EA, class IA, class BA> void assembleOperators( GridView const& gridView, Element const& element, Operators& operators, - ElementAssembler const& localAssembler) + AssemblerTriple<EA,IA,BA> const& assemblerTriple) { // assemble element operators - localAssembler(element, operators.onElement()); + assemblerTriple.elementAssembler(element, operators.onElement()); // assemble intersection operators if (!operators.onIntersection().empty() @@ -18,11 +33,53 @@ namespace AMDiS { for (auto const& intersection : intersections(gridView, element)) { if (intersection.boundary()) - localAssembler(intersection, operators.onBoundary()); + assemblerTriple.boundaryAssembler(intersection, operators.onBoundary()); else - localAssembler(intersection, operators.onIntersection()); + assemblerTriple.intersectionAssembler(intersection, operators.onIntersection()); } } } + + template <class Node, class ElementVector> + auto makeVectorAssembler(Node const& node, ElementVector& elementVector) + { + return makeAssemblerTriple( + [&](auto const& element, auto& operators) { + for (auto op : operators) + op->assemble(element, node, elementVector); + }, + [&](auto const& is, auto& operators) { + for (auto op : operators) + op->assemble(is, node, elementVector); + }, + [&](auto const& bis, auto& operators) { + for (auto data : operators) { + if (data.bc.onBoundary(bis)) + data.op->assemble(bis, node, elementVector); + } + }); + } + + + template <class RowNode, class ColNode, class ElementMatrix> + auto makeMatrixAssembler(RowNode const& rowNode, ColNode const& colNode, ElementMatrix& elementMatrix) + { + return makeAssemblerTriple( + [&](auto const& element, auto& operators) { + for (auto op : operators) + op->assemble(element, rowNode, colNode, elementMatrix); + }, + [&](auto const& is, auto& operators) { + for (auto op : operators) + op->assemble(is, rowNode, colNode, elementMatrix); + }, + [&](auto const& bis, auto& operators) { + for (auto data : operators) { + if (data.bc.onBoundary(bis)) + data.op->assemble(bis, rowNode, colNode, elementMatrix); + } + }); + } + } // end namespace AMDiS diff --git a/src/amdis/Boundary.hpp b/src/amdis/Boundary.hpp index ee11eb4cb7781d425b87e6975675072752641037..881b5010cfabaed06810370387db14d18b855839 100644 --- a/src/amdis/Boundary.hpp +++ b/src/amdis/Boundary.hpp @@ -2,6 +2,6 @@ namespace AMDiS { - struct BoundaryType { int b; }; + using BoundaryType = int; } // end namespace AMDiS diff --git a/src/amdis/BoundaryCondition.hpp b/src/amdis/BoundaryCondition.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e3a231aad23b6a1244480d7309019133baf73d3d --- /dev/null +++ b/src/amdis/BoundaryCondition.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include <memory> + +#include <amdis/Boundary.hpp> +#include <amdis/BoundaryManager.hpp> + +namespace AMDiS +{ + class BoundaryCondition + { + public: + BoundaryCondition() = default; + BoundaryCondition(std::shared_ptr<BoundaryManagerBase const> const& boundaryManager, BoundaryType id) + : boundaryManager_(boundaryManager) + , id_(id) + {} + + /// Return true if intersection is on boundary with id + template <class Intersection> + bool onBoundary(Intersection const& is) const + { + return is.boundary() && (!boundaryManager_ || boundaryManager_->boundaryId(is) == id_); + } + + template <class RowBasis, class ColBasis> + void init(RowBasis const& rowBasis, ColBasis const& colBasis) { /* do nothing */ } + + template <class Matrix, class X, class B, class RN, class CN> + void fillBoundaryCondition(Matrix& A, X& x, B& b, RN const& rowNode, CN const& colNode) { /* do nothing */ } + + protected: + std::shared_ptr<BoundaryManagerBase const> boundaryManager_{nullptr}; + BoundaryType id_{0}; + }; + +} // end namespace AMDiS diff --git a/src/amdis/BoundaryManager.hpp b/src/amdis/BoundaryManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..375c334ae1cba89044f78358ca6b04560573a856 --- /dev/null +++ b/src/amdis/BoundaryManager.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include <memory> +#include <vector> + +#include <dune/common/hybridutilities.hh> +#include <dune/common/std/type_traits.hh> + +#include <amdis/Boundary.hpp> +#include <amdis/common/Concepts.hpp> + +namespace AMDiS +{ + class BoundaryManagerBase + { + public: + BoundaryManagerBase(std::size_t numBoundarySegments) + : boundaryIds_(numBoundarySegments, BoundaryType{1}) + {} + + /// Return the stored boundary id for the given intersection + template <class Intersection> + BoundaryType boundaryId(Intersection const& intersection) const + { + return boundaryIds_[intersection.boundarySegmentIndex()]; + } + + protected: + std::vector<BoundaryType> boundaryIds_; // maps a boundarySegementIndex to an ID + }; + + + /// Manage boundary ids of boundary segments in a grid + /** + * Manager for bounary IDs, that can be initialized from different sources + * - cube domains can be assigned box boundaries, by specifying left-right, + * front-back, and bottom-top ids + * - An indicator from global coodinates that returns an id. The indicator function + * is evaluated in the center points of intersections. + * - A predicate that return true/false for a boundary part and sets the given id + * - Read ids from the grid. Those may be initialized by some grid readers. Note: + * not all grids support `intersection.boundaryId()`, but e.g. AlbertaGrid and + * ALUGrid do support this. Can be read by DGF parser and AlbertaGrid constructor + * from macroFileName. + **/ + template <class G> + class BoundaryManager + : public BoundaryManagerBase + { + using Super = BoundaryManagerBase; + + public: + using Grid = G; + enum { dim = Grid::dimension }; + enum { dow = Grid::dimensionworld }; + + using Segment = typename Grid::LevelGridView::Intersection; + using Domain = typename Grid::template Codim<0>::Geometry::GlobalCoordinate; + + public: + /// Constructor. Stores a shared pointer to the grid and initializes all + /// boundary IDs to the value stored in the grid + BoundaryManager(std::shared_ptr<G> const& grid) + : Super(grid->numBoundarySegments()) + , grid_(grid) + { + setBoundaryId(); + } + + /// Set boundary ids [left,right, front,back, bottom,top] for cube domains + void setBoxBoundary(std::array<BoundaryType, 2*dow> const& ids) + { + auto gv = grid_->levelGridView(0); + for (auto const& e : elements(gv)) + { + for (auto const& segment : intersections(gv,e)) { + if (!segment.boundary()) + continue; + + auto n = segment.centerUnitOuterNormal(); + auto index = segment.boundarySegmentIndex(); + + for (int i = 0; i < dow; ++i) { + if (n[i] < -0.5) + boundaryIds_[index] = ids[2*i]; + else if (n[i] > 0.5) + boundaryIds_[index] = ids[2*i+1]; + } + } + } + } + + + /// Set indicator(center) for all boundary intersections + template <class Indicator, + REQUIRES(Concepts::Functor<Indicator, int(Domain)>) > + void setIndicator(Indicator const& indicator) + { + auto gv = grid_->levelGridView(0); + for (auto const& e : elements(gv)) + { + for (auto const& segment : intersections(gv,e)) { + if (!segment.boundary()) + continue; + + auto index = segment.boundarySegmentIndex(); + boundaryIds_[index] = indicator(segment.geometry().center()); + } + } + } + + + /// Set id for all boundary intersections with pred(center) == true + template <class Predicate, + REQUIRES(Concepts::Functor<Predicate, bool(Domain)>) > + void setPredicate(Predicate const& pred, BoundaryType id) + { + auto gv = grid_->levelGridView(0); + for (auto const& e : elements(gv)) + { + for (auto const& segment : intersections(gv,e)) { + if (!segment.boundary()) + continue; + + auto index = segment.boundarySegmentIndex(); + if (pred(segment.geometry().center())) + boundaryIds_[index] = id; + } + } + } + + + template <class I> + using HasBoundaryId = decltype(std::declval<I>().boundaryId()); + + /// Set boundary ids as stored in the grid, e.g. read by grid reader + void setBoundaryId() + { + if (!Dune::Std::is_detected<HasBoundaryId, Segment>::value) + return; + + auto gv = grid_->levelGridView(0); + for (auto const& e : elements(gv)) + { + for (auto const& segment : intersections(gv,e)) { + if (!segment.boundary()) + continue; + + auto index = segment.boundarySegmentIndex(); + Dune::Hybrid::ifElse(Dune::Std::is_detected<HasBoundaryId, Segment>{}, + [&](auto id) { boundaryIds_[index] = id(segment).boundaryId(); }); + } + } + } + + private: + std::shared_ptr<Grid> grid_; + using Super::boundaryIds_; + }; + +} // end namespace AMDiS diff --git a/src/amdis/DirichletBC.hpp b/src/amdis/DirichletBC.hpp index 82a12b18079d030f929963ac5dde39e2ef97a5ad..0d570e28f4852d9b6cba5b8e710223b10b86937f 100644 --- a/src/amdis/DirichletBC.hpp +++ b/src/amdis/DirichletBC.hpp @@ -2,18 +2,14 @@ #include <functional> #include <list> -#include <type_traits> +#include <memory> #include <vector> -#include <dune/common/hybridutilities.hh> -#include <dune/functions/functionspacebases/boundarydofs.hh> -#include <dune/functions/functionspacebases/interpolate.hh> -#include <dune/functions/functionspacebases/subspacebasis.hh> +#include <dune/common/std/optional.hh> #include <amdis/Boundary.hpp> -#include <amdis/Output.hpp> +#include <amdis/BoundaryCondition.hpp> #include <amdis/common/Concepts.hpp> -#include <amdis/common/ValueCategory.hpp> #include <amdis/utility/RangeType.hpp> #include <amdis/utility/TreeData.hpp> #include <amdis/utility/Visitor.hpp> @@ -37,82 +33,47 @@ namespace AMDiS **/ template <class Domain, class Range = double> class DirichletBC + : public BoundaryCondition { + using Super = BoundaryCondition; + public: + template <class BM, class Values, + REQUIRES(Concepts::Functor<Values, Range(Domain)>) > + DirichletBC(BM&& boundaryManager, BoundaryType id, Values&& values) + : Super(std::forward<BM>(boundaryManager), id) + , values_(std::forward<Values>(values)) + {} + template <class Predicate, class Values, - REQUIRES(Concepts::Functor<Predicate, bool(Domain)> && - Concepts::Functor<Values, Range(Domain)>) > + REQUIRES(Concepts::Functor<Predicate, bool(Domain)>), + REQUIRES(Concepts::Functor<Values, Range(Domain)>)> DirichletBC(Predicate&& predicate, Values&& values) : predicate_(std::forward<Predicate>(predicate)) , values_(std::forward<Values>(values)) {} - - /// Prepare the matrix, rhs and solution block for assembling dirichlet - /// boundary conditions, e.g. collect the corresponding indices of boundary - /// DOFS for later elimination in \ref finish. - template <class Matrix, class VectorX, class VectorB, class RowNode, class ColNode> - void init(Matrix& /*matrix*/, VectorX& /*solution*/, VectorB& rhs, - RowNode const& rowNode, ColNode const&) + template <class Intersection> + bool onBoundary(Intersection const& intersection) const { - if (!initialized_) { - forEachLeafNode_(rowNode, [&](auto const& /*node*/, auto const& tp) { - auto subBasis = Dune::Functions::subspaceBasis(rhs.basis(), cat(rowNode.treePath(),tp)); - this->initImpl(subBasis); - }); - initialized_ = true; - } + return predicate_ ? (*predicate_)(intersection.geometry().center()) + : Super::onBoundary(intersection); } + // fill \ref dirichletNodes_ with 1 or 0 whether DOF corresponds to the boundary or not. + template <class RowBasis, class ColBasis> + void init(RowBasis const& rowBasis, ColBasis const& colBasis); /// \brief Apply dirichlet BC to matrix and vector, i.e., add a unit-row /// to the matrix and optionally delete the corresponding matrix-column. template <class Matrix, class VectorX, class VectorB, class RowNode, class ColNode> - void finish(Matrix& matrix, VectorX& solution, VectorB& rhs, - RowNode const& rowNode, ColNode const& colNode) - { - test_exit_dbg(initialized_, "Boundary condition not initialized!"); - auto columns = matrix.applyDirichletBC(dirichletNodes_); - - Dune::Hybrid::ifElse(std::is_same<RangeType_t<RowNode>, Range>{}, - [&](auto id) { - auto subBasis = Dune::Functions::subspaceBasis(rhs.basis(), rowNode.treePath()); - Dune::Functions::interpolate(subBasis, rhs, values_, dirichletNodes_); - }); - - Dune::Hybrid::ifElse(std::is_same<RangeType_t<ColNode>, Range>{}, - [&](auto id) { - auto subBasis = Dune::Functions::subspaceBasis(solution.basis(), colNode.treePath()); - Dune::Functions::interpolate(subBasis, solution, values_, dirichletNodes_); - }); - - // subtract columns of dirichlet nodes from rhs - for (auto const& triplet : columns) - rhs[triplet.row] -= triplet.value * solution[triplet.col]; - - initialized_ = false; - } - - protected: - // fill \ref dirichletNodes_ with 1 or 0 whether DOF corresponds to the boundary or not. - template <class SubBasis> - void initImpl(SubBasis const& subBasis) - { - using Dune::Functions::forEachBoundaryDOF; - using LV = typename SubBasis::LocalView; - using I = typename SubBasis::GridView::Intersection; - dirichletNodes_.resize(subBasis.dimension()); - forEachBoundaryDOF(subBasis, [&](int localIndex, LV const& localView, I const& intersection) { - dirichletNodes_[localView.index(localIndex)] = predicate_(intersection.geometry().center()); - }); - } + void fillBoundaryCondition(Matrix& matrix, VectorX& solution, VectorB& rhs, + RowNode const& rowNode, ColNode const& colNode); private: - std::function<bool(Domain)> predicate_; + Dune::Std::optional<std::function<bool(Domain)>> predicate_; std::function<Range(Domain)> values_; - - bool initialized_ = false; - std::vector<char> dirichletNodes_; + std::vector<bool> dirichletNodes_; }; @@ -126,6 +87,8 @@ namespace AMDiS }; template <class RowBasis, class ColBasis> - using Constraints = MatrixData<RowBasis, ColBasis, DirichletData<RowBasis>::template type>; + using DirichletBCs = MatrixData<RowBasis, ColBasis, DirichletData<RowBasis>::template type>; } // end namespace AMDiS + +#include "DirichletBC.inc.hpp" diff --git a/src/amdis/DirichletBC.inc.hpp b/src/amdis/DirichletBC.inc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d3ab7385eacf67fa0676d8607cd02c5c0fad0efe --- /dev/null +++ b/src/amdis/DirichletBC.inc.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include <type_traits> + +#include <dune/common/hybridutilities.hh> +#include <dune/functions/functionspacebases/boundarydofs.hh> +#include <dune/functions/functionspacebases/interpolate.hh> +#include <dune/functions/functionspacebases/subspacebasis.hh> + +#include <amdis/Output.hpp> +#include <amdis/linear_algebra/Constraints.hpp> + +namespace AMDiS { + +template <class D, class R> + template <class RowBasis, class ColBasis> +void DirichletBC<D,R>:: +init(RowBasis const& rowBasis, ColBasis const& colBasis) +{ + using Dune::Functions::forEachBoundaryDOF; + using LV = typename ColBasis::LocalView; + using I = typename ColBasis::GridView::Intersection; + dirichletNodes_.resize(colBasis.dimension()); + forEachBoundaryDOF(colBasis, [&](int localIndex, LV const& localView, I const& intersection) { + dirichletNodes_[localView.index(localIndex)] = onBoundary(intersection); + }); +} + + +template <class D, class R> + template <class Matrix, class VectorX, class VectorB, class RowNode, class ColNode> +void DirichletBC<D,R>:: +fillBoundaryCondition(Matrix& matrix, VectorX& solution, VectorB& rhs, + RowNode const& rowNode, ColNode const& colNode) +{ + auto columns = Constraints<Matrix>::dirichletBC(matrix, dirichletNodes_); + + Dune::Hybrid::ifElse(std::is_same<RangeType_t<RowNode>, R>{}, + [&](auto id) { + auto subBasis = Dune::Functions::subspaceBasis(rhs.basis(), rowNode.treePath()); + Dune::Functions::interpolate(subBasis, rhs, values_, dirichletNodes_); + }); + + // subtract columns of dirichlet nodes from rhs + for (auto const& triplet : columns) + rhs[triplet.row] -= triplet.value * solution[triplet.col]; + + Dune::Hybrid::ifElse(std::is_same<RangeType_t<ColNode>, R>{}, + [&](auto id) { + auto subBasis = Dune::Functions::subspaceBasis(solution.basis(), colNode.treePath()); + Dune::Functions::interpolate(subBasis, solution, values_, dirichletNodes_); + }); +} + +} // end namespace AMDiS diff --git a/src/amdis/LinearAlgebra.hpp b/src/amdis/LinearAlgebra.hpp index 6184b2042b8dc444e9f368bc56262c8f9fb83f2b..aa920278aeb6156d4224030cfc4f745352e4cea3 100644 --- a/src/amdis/LinearAlgebra.hpp +++ b/src/amdis/LinearAlgebra.hpp @@ -2,16 +2,19 @@ #if HAVE_MTL +#include <amdis/linear_algebra/mtl/Constraints.hpp> #include <amdis/linear_algebra/mtl/DOFMatrix.hpp> #include <amdis/linear_algebra/mtl/DOFVector.hpp> #elif HAVE_EIGEN +#include <amdis/linear_algebra/eigen/Constraints.hpp> #include <amdis/linear_algebra/eigen/DOFMatrix.hpp> #include <amdis/linear_algebra/eigen/DOFVector.hpp> #else // ISTL +#include <amdis/linear_algebra/istl/Constraints.hpp> #include <amdis/linear_algebra/istl/DOFMatrix.hpp> #include <amdis/linear_algebra/istl/DOFVector.hpp> diff --git a/src/amdis/LocalAssemblerList.hpp b/src/amdis/LocalAssemblerList.hpp index b9e7fc694542bcdff421069de9050eedbd222729..a5e993d63d6f5e384b1f732555345c96909ae7b0 100644 --- a/src/amdis/LocalAssemblerList.hpp +++ b/src/amdis/LocalAssemblerList.hpp @@ -3,7 +3,7 @@ #include <list> #include <memory> -#include <amdis/Boundary.hpp> +#include <amdis/BoundaryCondition.hpp> #include <amdis/LocalAssemblerBase.hpp> #include <amdis/utility/TreeData.hpp> @@ -13,7 +13,11 @@ namespace AMDiS { template <class E> struct element_operator { using type = E; }; template <class I> struct intersection_operator { using type = I; }; - template <class I> struct boundary_operator { using type = I; BoundaryType id; }; + template <class I> struct boundary_operator : public BoundaryCondition + { + using type = I; + using BoundaryCondition::BoundaryCondition; + }; } template <class GridView> @@ -26,7 +30,7 @@ namespace AMDiS struct DataElement { std::shared_ptr<OperatorType> op; - BoundaryType b = {0}; + BoundaryCondition bc; }; /// Lists of \ref DataElement on the Element, BoundaryIntersction, and InteriorIntersections @@ -54,49 +58,49 @@ namespace AMDiS template <class Geo> void bind(Element const& elem, Geo const& geo) { - for (auto& scaled : element_) scaled.op->bind(elem,geo); - for (auto& scaled : boundary_) scaled.op->bind(elem,geo); - for (auto& scaled : intersection_) scaled.op->bind(elem,geo); + for (auto op : element_) op->bind(elem,geo); + for (auto op : intersection_) op->bind(elem,geo); + for (auto& data : boundary_) data.op->bind(elem,geo); } /// Unbind all operators from the element void unbind() { - for (auto& scaled : element_) scaled.op->unbind(); - for (auto& scaled : boundary_) scaled.op->unbind(); - for (auto& scaled : intersection_) scaled.op->unbind(); + for (auto op : element_) op->unbind(); + for (auto op : intersection_) op->unbind(); + for (auto& data : boundary_) data.op->unbind(); assembled_ = true; } - template <class... Args> - void push(tag::element_operator<Element>, Args&&... args) + template <class Op> + void push(tag::element_operator<Element>, Op&& op) { - element_.push_back({std::forward<Args>(args)...}); + element_.emplace_back(std::forward<Op>(op)); } - template <class... Args> - void push(tag::boundary_operator<Intersection> b, Args&&... args) + template <class Op> + void push(tag::intersection_operator<Intersection>, Op&& op) { - boundary_.push_back({std::forward<Args>(args)..., b.id}); + intersection_.emplace_back(std::forward<Op>(op)); } - template <class... Args> - void push(tag::intersection_operator<Intersection>, Args&&... args) + template <class Op> + void push(tag::boundary_operator<Intersection> b, Op&& op) { - intersection_.push_back({std::forward<Args>(args)...}); + boundary_.push_back({std::forward<Op>(op), b}); } auto& onElement() { return element_; } auto const& onElement() const { return element_; } - auto& onBoundary() { return boundary_; } - auto const& onBoundary() const { return boundary_; } - auto& onIntersection() { return intersection_; } auto const& onIntersection() const { return intersection_; } + auto& onBoundary() { return boundary_; } + auto const& onBoundary() const { return boundary_; } + private: /// The type of local operators associated with grid elements using ElementOperator = LocalAssemblerBase<Element, Nodes...>; @@ -105,14 +109,14 @@ namespace AMDiS using IntersectionOperator = LocalAssemblerBase<Intersection, Nodes...>; /// List of operators to be assembled on grid elements - std::vector<DataElement<ElementOperator>> element_; + std::vector<std::shared_ptr<ElementOperator>> element_; + + /// List of operators to be assembled on interior intersections + std::vector<std::shared_ptr<IntersectionOperator>> intersection_; /// List of operators to be assembled on boundary intersections std::vector<DataElement<IntersectionOperator>> boundary_; - /// List of operators to be assembled on interior intersections - std::vector<DataElement<IntersectionOperator>> intersection_; - /// if false, do reassemble of all operators bool assembled_ = false; @@ -133,9 +137,11 @@ namespace AMDiS template <class RowBasis, class ColBasis> - using MatrixOperators = MatrixData<RowBasis, ColBasis, OperatorLists<typename RowBasis::GridView>::template MatData>; + using MatrixOperators + = MatrixData<RowBasis, ColBasis, OperatorLists<typename RowBasis::GridView>::template MatData>; template <class GlobalBasis> - using VectorOperators = VectorData<GlobalBasis, OperatorLists<typename GlobalBasis::GridView>::template VecData>; + using VectorOperators + = VectorData<GlobalBasis, OperatorLists<typename GlobalBasis::GridView>::template VecData>; } // end namespace AMDiS diff --git a/src/amdis/LocalBasisCache.hpp b/src/amdis/LocalBasisCache.hpp index 22c904fee9f46133c8db5e5eca8896e813087726..cab8d27c58129855977e1319f3f9af3a33ddf4bb 100644 --- a/src/amdis/LocalBasisCache.hpp +++ b/src/amdis/LocalBasisCache.hpp @@ -95,8 +95,8 @@ namespace AMDiS {} // evaluate local basis-function at all quadrature points - template <class LocalContext> - ShapeValuesContainer const& evaluateFunctionAtQp(LocalContext const& context, QuadratureRule const& quad) const + template <class LocalContext, int d> + ShapeValuesContainer const& evaluateFunctionAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const { ValuesKey key{context.type().id(),quad.order(),quad.size()}; return ShapeValuesContainerCache::get(key, [&](ValuesKey const&) @@ -109,8 +109,8 @@ namespace AMDiS } // evaluate local basis-gradients at all quadrature points - template <class LocalContext> - ShapeGradientsContainer const& evaluateJacobianAtQp(LocalContext const& context, QuadratureRule const& quad) const + template <class LocalContext, int d> + ShapeGradientsContainer const& evaluateJacobianAtQp(LocalContext const& context, Dune::QuadratureRule<RangeFieldType,d> const& quad) const { GradientsKey key{context.type().id(),quad.order(),quad.size()}; return ShapeGradientsContainerCache::get(key, [&](GradientsKey const&) diff --git a/src/amdis/LocalOperator.hpp b/src/amdis/LocalOperator.hpp index 5083e070b321bdc5555cf32753ea6229ebb93b54..ef6bfcf46b8b1b1d7d02e11b403ba7714d94bbd4 100644 --- a/src/amdis/LocalOperator.hpp +++ b/src/amdis/LocalOperator.hpp @@ -4,6 +4,7 @@ #include <type_traits> #include <amdis/GridFunctions.hpp> +#include <amdis/Output.hpp> #include <amdis/common/Utility.hpp> #include <amdis/utility/FiniteElementType.hpp> diff --git a/src/amdis/PeriodicBC.hpp b/src/amdis/PeriodicBC.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5d10800a83c5343c4201ff1c1e6f672f9925ad98 --- /dev/null +++ b/src/amdis/PeriodicBC.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include <functional> +#include <list> +#include <map> +#include <type_traits> +#include <vector> + +#include <amdis/common/ConceptsBase.hpp> + +#include <amdis/Output.hpp> + +namespace AMDiS +{ + template <class ct, int dow> + struct FaceTransformation + { + using WorldMatrix = FieldMatrix<ct, dow, dow>; + using WorldVector = FieldVector<ct, dow>; + + WorldMatrix matrix_; // Note: We assume the matrix to be orthogonal, here + WorldVector shift_; + + WorldVector evaluate(WorldVector const& x) const + { + WorldVector y = shift_; + matrix_.umv(x, y); + return y; + } + + WorldVector evaluateInverse(WorldVector const& y) const + { + WorldVector ys = y - shift_; + WorldVector x; + matrix_.mtv(ys, x); + return x; + } + }; + + + /// Implements a periodic boundary condition + template <class D, class MI> + class PeriodicBC + : public BoundaryCondition + { + using Super = BoundaryCondition; + + public: + using Domain = D; + using MultiIndex = MI; + using FaceTrafo = FaceTransformation<typename D::field_type, D::dimension>; + + public: + template <class BM> + PeriodicBC(BM&& boundaryManager, BoundaryType id, FaceTrafo faceTrafo) + : Super(std::forward<BM>(boundaryManager), id) + , faceTrafo_(std::move(faceTrafo)) + {} + + template <class RowBasis, class ColBasis> + void init(RowBasis const& rowBasis, ColBasis const& colBasis); + + template <class Matrix, class X, class B, class RN, class CN> + void fillBoundaryCondition(Matrix& A, X& x, B& b, RN const& rowNode, CN const& colNode); + + auto const& associations() const + { + return associations_; + } + + auto const& periodicNodes() const + { + return periodicNodes_; + } + + protected: + template <class Basis> + void initAssociations(Basis const& basis); + + template <class Basis> + void initAssociations2(Basis const& basis); + + // Get the coordinates of the DOFs + template <class Node> + std::vector<Domain> coords(Node const& node, std::vector<std::size_t> const& localIndices) const; + + private: + FaceTrafo faceTrafo_; + + std::vector<bool> periodicNodes_; + std::map<std::size_t, std::size_t> associations_; + Dune::Std::optional<bool> hasConnectedIntersections_; + }; + + + template <class Basis> + struct PeriodicData + { + using WorldVector = typename Basis::GridView::template Codim<0>::Geometry::GlobalCoordinate; + + template <class RowNode, class ColNode> + using type = std::list< std::shared_ptr<PeriodicBC<WorldVector, typename Basis::MultiIndex>> >; + }; + + template <class RowBasis, class ColBasis> + using PeriodicBCs = MatrixData<RowBasis, ColBasis, PeriodicData<RowBasis>::template type>; + +} // end namespace AMDiS + +#include "PeriodicBC.inc.hpp" diff --git a/src/amdis/PeriodicBC.inc.hpp b/src/amdis/PeriodicBC.inc.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ea08e9ed52e0e1eb281fb5d62e6f3d573e19fbcf --- /dev/null +++ b/src/amdis/PeriodicBC.inc.hpp @@ -0,0 +1,248 @@ +#pragma once + +#include <limits> + +#include <dune/common/reservedvector.hh> +#include <dune/functions/common/functionfromcallable.hh> +#include <dune/functions/functionspacebases/interpolate.hh> +#include <dune/functions/functionspacebases/subentitydofs.hh> +#include <amdis/LinearAlgebra.hpp> + +namespace AMDiS { + +template <class D, class MI> + template <class Basis, class ColBasis> +void PeriodicBC<D,MI>:: +init(Basis const& basis, ColBasis const& /*colBasis*/) +{ + if (!bool(hasConnectedIntersections_)) { + hasConnectedIntersections_ = true; + const auto& gridView = basis.gridView().grid().levelGridView(0); + for (auto const& element : elements(gridView)) { + for (const auto& intersection : intersections(gridView, element)) { + if (!this->onBoundary(intersection)) + continue; + + if (!intersection.neighbor()) { + hasConnectedIntersections_ = false; + break; + } + } + } + } + + if (*hasConnectedIntersections_) + initAssociations(basis); + else + initAssociations2(basis); +} + + +template <class D, class MI> + template <class Basis> +void PeriodicBC<D,MI>:: +initAssociations(Basis const& basis) +{ + static const double tol = std::sqrt(std::numeric_limits<typename D::field_type>::epsilon()); + periodicNodes_.clear(); + periodicNodes_.resize(basis.dimension(), false); + + auto localView = basis.localView(); + auto seDOFs = subEntityDOFs(basis); + const auto& gridView = basis.gridView(); + for (auto const& element : elements(gridView)) { + if (!element.hasBoundaryIntersections()) + continue; + + localView.bind(element); + for (const auto& intersection : intersections(gridView, element)) { + if (!this->onBoundary(intersection)) + continue; + + test_exit(intersection.neighbor(), + "Neighbors of periodic intersection not assigned. Use a periodic Grid instead!"); + + // collect DOFs of inside element on intersection + localView.bind(intersection.inside()); + seDOFs.bind(localView, intersection.indexInInside(), 1); + std::vector<std::size_t> insideDOFs(seDOFs.begin(), seDOFs.end()); + std::vector<MI> insideGlobalDOFs(insideDOFs.size()); + std::transform(insideDOFs.begin(), insideDOFs.end(), insideGlobalDOFs.begin(), + [&](std::size_t localIndex) { return localView.index(localIndex); }); + auto insideCoords = coords(localView.tree(), insideDOFs); + + // collect DOFs of ouside element on intersection + localView.bind(intersection.outside()); + seDOFs.bind(localView, intersection.indexInOutside(), 1); + std::vector<std::size_t> outsideDOFs(seDOFs.begin(), seDOFs.end()); + auto outsideCoords = coords(localView.tree(), outsideDOFs); + + // compare mapped coords of DOFs + assert(insideDOFs.size() == outsideDOFs.size()); + for (std::size_t i = 0; i < insideCoords.size(); ++i) { + auto x = faceTrafo_.evaluate(insideCoords[i]); + for (std::size_t j = 0; j < outsideCoords.size(); ++j) { + auto const& y = outsideCoords[j]; + if ((x-y).two_norm() < tol) { + periodicNodes_[insideGlobalDOFs[i]] = true; + associations_[insideGlobalDOFs[i]] = localView.index(outsideDOFs[j]); + } + } + } + } + } +} + +namespace Impl +{ + template <class D> + class CompareTol + { + using ctype = typename D::field_type; + + public: + CompareTol(ctype tol) + : tol_(tol) + {} + + bool operator()(D const& lhs, D const& rhs) const + { + for (int i = 0; i < D::dimension; i++) { + if (std::abs(lhs[i] - rhs[i]) < tol_) + continue; + return lhs[i] < rhs[i]; + } + return false; + } + + private: + ctype tol_; + }; +} + + + +template <class D, class MI> + template <class Basis> +void PeriodicBC<D,MI>:: +initAssociations2(Basis const& basis) +{ + static const double tol = std::sqrt(std::numeric_limits<typename D::field_type>::epsilon()); + periodicNodes_.clear(); + periodicNodes_.resize(basis.dimension(), false); + + // Dune::ReservedVector<std::pair<EntitySeed,int>,2> + using EntitySeed = typename Basis::GridView::Grid::template Codim<0>::EntitySeed; + using CoordAssoc = std::map<D, std::vector<std::pair<EntitySeed,int>>, Impl::CompareTol<D>>; + CoordAssoc coordAssoc(Impl::CompareTol<D>{tol}); + + // generate periodic element pairs + const auto& gridView = basis.gridView(); + for (auto const& element : elements(gridView)) { + for (const auto& intersection : intersections(gridView, element)) { + if (!this->onBoundary(intersection)) + continue; + + auto x = intersection.geometry().center(); + auto seed = intersection.inside().seed(); + coordAssoc[x].push_back({seed,intersection.indexInInside()}); + coordAssoc[faceTrafo_.evaluate(x)].push_back({seed,intersection.indexInInside()}); + } + } + + auto localView = basis.localView(); + auto seDOFs = subEntityDOFs(basis); + for (auto const& assoc : coordAssoc) { + auto const& seeds = assoc.second; + if (seeds.size() != 2) + continue; + + // collect DOFs of inside element on intersection + auto el1 = gridView.grid().entity(seeds[0].first); + localView.bind(el1); + seDOFs.bind(localView, seeds[0].second, 1); + std::vector<std::size_t> insideDOFs(seDOFs.begin(), seDOFs.end()); + std::vector<MI> insideGlobalDOFs(insideDOFs.size()); + std::transform(insideDOFs.begin(), insideDOFs.end(), insideGlobalDOFs.begin(), + [&](std::size_t localIndex) { return localView.index(localIndex); }); + auto insideCoords = coords(localView.tree(), insideDOFs); + + // collect DOFs of ouside element on intersection + auto el2 = gridView.grid().entity(seeds[1].first); + localView.bind(el2); + seDOFs.bind(localView, seeds[1].second, 1); + std::vector<std::size_t> outsideDOFs(seDOFs.begin(), seDOFs.end()); + auto outsideCoords = coords(localView.tree(), outsideDOFs); + + // compare mapped coords of DOFs + assert(insideDOFs.size() == outsideDOFs.size()); + for (std::size_t i = 0; i < insideCoords.size(); ++i) { + auto x = faceTrafo_.evaluate(insideCoords[i]); + for (std::size_t j = 0; j < outsideCoords.size(); ++j) { + auto const& y = outsideCoords[j]; + if ((x-y).two_norm() < tol) { + periodicNodes_[insideGlobalDOFs[i]] = true; + associations_[insideGlobalDOFs[i]] = localView.index(outsideDOFs[j]); + } + } + } + } +} + + +template <class D, class MI> + template <class Node> +std::vector<D> PeriodicBC<D,MI>:: +coords(Node const& tree, std::vector<std::size_t> const& localIndices) const +{ + std::vector<D> dofCoords(localIndices.size()); + AMDiS::forEachLeafNode_(tree, [&](auto const& node, auto const& tp) + { + std::size_t size = node.finiteElement().size(); + auto geometry = node.element().geometry(); + + auto const& localInterpol = node.finiteElement().localInterpolation(); + using FiniteElement = std::decay_t<decltype(node.finiteElement())>; + using DomainType = typename FiniteElement::Traits::LocalBasisType::Traits::DomainType; + using RangeType = typename FiniteElement::Traits::LocalBasisType::Traits::RangeType; + + std::array<std::vector<typename RangeType::field_type>, D::dimension> coeffs; + for (int d = 0; d < D::dimension; ++d) { + auto evalCoord = [&](DomainType const& local) -> RangeType { return geometry.global(local)[d]; }; + using FFC = Dune::Functions::FunctionFromCallable<RangeType(DomainType), decltype(evalCoord)>; + localInterpol.interpolate(FFC(evalCoord), coeffs[d]); + } + + for (std::size_t j = 0; j < localIndices.size(); ++j) { + D x; + for (std::size_t i = 0; i < size; ++i) { + if (node.localIndex(i) == localIndices[j]) { + for (int d = 0; d < D::dimension; ++d) + x[d] = coeffs[d][i]; + break; + } + } + dofCoords[j] = std::move(x); + } + }); + + return dofCoords; +} + + +template <class D, class MI> + template <class Matrix, class VectorX, class VectorB, class RowNode, class ColNode> +void PeriodicBC<D,MI>:: +fillBoundaryCondition(Matrix& matrix, VectorX& solution, VectorB& rhs, + RowNode const& rowNode, ColNode const& colNode) +{ + Constraints<Matrix>::periodicBC(matrix, periodicNodes_, associations_); + + for (auto const& a : associations_) { + rhs[a.second] += rhs[a.first]; + solution[a.second] = solution[a.first]; + } + Dune::Functions::interpolate(rhs.basis(), rhs, [](auto const&) { return 0.0; }, periodicNodes_); +} + +} // end namespace AMDiS diff --git a/src/amdis/ProblemStat.hpp b/src/amdis/ProblemStat.hpp index 2ae9e342ba9a079c929229506a60845ba0fb9482..7d028f8256e8c6ca1b20c7029b86fd9421e31bd9 100644 --- a/src/amdis/ProblemStat.hpp +++ b/src/amdis/ProblemStat.hpp @@ -24,6 +24,7 @@ #include <amdis/LocalAssemblerList.hpp> #include <amdis/Marker.hpp> #include <amdis/Mesh.hpp> +#include <amdis/PeriodicBC.hpp> #include <amdis/ProblemStatBase.hpp> #include <amdis/ProblemStatTraits.hpp> #include <amdis/StandardProblemIteration.hpp> @@ -62,6 +63,7 @@ namespace AMDiS using Grid = typename GridView::Grid; using Element = typename GridView::template Codim<0>::Entity; using WorldVector = typename Element::Geometry::GlobalCoordinate; + using WorldMatrix = FieldMatrix<typename WorldVector::field_type, WorldVector::dimension, WorldVector::dimension>; /// Dimension of the grid static constexpr int dim = Grid::dimension; @@ -158,7 +160,7 @@ namespace AMDiS void addMatrixOperator(BoundaryType b, Operator const& op, RowTreePath row = {}, ColTreePath col = {}) { using I = typename GridView::Intersection; - systemMatrix_->addOperator(tag::boundary_operator<I>{b}, op, row, col); + systemMatrix_->addOperator(tag::boundary_operator<I>{boundaryManager_,b}, op, row, col); } /** @} */ @@ -207,7 +209,7 @@ namespace AMDiS void addVectorOperator(BoundaryType b, Operator const& op, TreePath path = {}) { using I = typename GridView::Intersection; - rhs_->addOperator(tag::boundary_operator<I>{b}, op, path); + rhs_->addOperator(tag::boundary_operator<I>{boundaryManager_,b}, op, path); } /** @} */ @@ -238,6 +240,15 @@ namespace AMDiS void addDirichletBC(Predicate const& predicate, RowTreePath row, ColTreePath col, Values const& values); + + template <class RowTreePath, class ColTreePath, class Values> + void addDirichletBC(BoundaryType id, + RowTreePath row, ColTreePath col, + Values const& values); + + /// Add a periodic boundary conditions to the system, by specifying a face transformation + /// y = A*x + b of coordinates. We assume, that A is orthonormal. + void addPeriodicBC(BoundaryType id, WorldMatrix const& A, WorldVector const& b); /** @} */ @@ -278,6 +289,12 @@ namespace AMDiS /// Return the gridView of the leaf-level GridView const& gridView() const { return globalBasis_->gridView(); } + /// Return the boundary manager to identify boundary segments + BoundaryManager<Grid>& boundaryManager() { return *boundaryManager_; } + BoundaryManager<Grid> const& boundaryManager() const { return *boundaryManager_; } + + auto const& boundaryManagerPtr() { return boundaryManager_; } + /// Return the \ref globalBasis_ GlobalBasis& globalBasis() { return *globalBasis_; } GlobalBasis const& globalBasis() const { return *globalBasis_; } @@ -375,8 +392,15 @@ namespace AMDiS } void adoptGrid(std::shared_ptr<Grid> const& grid) + { + adoptGrid(grid, std::make_shared<BoundaryManager<Grid>>(grid)); + } + + void adoptGrid(std::shared_ptr<Grid> const& grid, + std::shared_ptr<BoundaryManager<Grid>> const& boundaryManager) { grid_ = grid; + boundaryManager_ = boundaryManager; Parameters::get(name_ + "->mesh", gridName_); } @@ -416,6 +440,9 @@ namespace AMDiS /// Name of the grid std::string gridName_ = "mesh"; + /// Management of boundary conditions + std::shared_ptr<BoundaryManager<Grid>> boundaryManager_; + /// FE spaces of this problem. std::shared_ptr<GlobalBasis> globalBasis_; @@ -448,7 +475,8 @@ namespace AMDiS private: // some internal data-structures - Constraints<GlobalBasis, GlobalBasis> constraints_; + DirichletBCs<GlobalBasis, GlobalBasis> dirichletBCs_; + PeriodicBCs<GlobalBasis, GlobalBasis> periodicBCs_; }; diff --git a/src/amdis/ProblemStat.inc.hpp b/src/amdis/ProblemStat.inc.hpp index f7d4cb1129ad53b5234babeb03293d15980b3ace..f1ff89b05ec1e286bf1b8e07ecdc36af386c3547 100644 --- a/src/amdis/ProblemStat.inc.hpp +++ b/src/amdis/ProblemStat.inc.hpp @@ -37,7 +37,7 @@ void ProblemStat<Traits>::initialize( (adoptFlag.isSet(INIT_MESH) || adoptFlag.isSet(INIT_SYSTEM) || adoptFlag.isSet(INIT_FE_SPACE))) { - grid_ = adoptProblem->grid_; + adoptGrid(adoptProblem->grid_, adoptProblem->boundaryManager_); } } @@ -123,6 +123,7 @@ void ProblemStat<Traits>::createGrid() { Parameters::get(name_ + "->mesh", gridName_); grid_ = MeshCreator<Grid>::create(gridName_); + boundaryManager_ = std::make_shared<BoundaryManager<Grid>>(grid_); msg("Create grid:"); msg("#elements = {}" , grid_->size(0)); @@ -163,7 +164,8 @@ void ProblemStat<Traits>::createGlobalBasisImpl(std::false_type) template <class Traits> void ProblemStat<Traits>::initGlobalBasis(GlobalBasis const& globalBasis) { - constraints_.init(*globalBasis_, *globalBasis_); + dirichletBCs_.init(*globalBasis_, *globalBasis_); + periodicBCs_.init(*globalBasis_, *globalBasis_); } @@ -261,8 +263,38 @@ addDirichletBC(Predicate const& predicate, RowTreePath row, ColTreePath col, Val using Range = RangeType_t<decltype(i)>; using BcType = DirichletBC<WorldVector,Range>; auto bc = std::make_unique<BcType>(predicate, valueGridFct); - constraints_[i][j].push_back(std::move(bc)); - // TODO: make DirichletBC an abstract class and add specialization with gridfunction type + dirichletBCs_[i][j].push_back(std::move(bc)); +} + + +// Adds a Dirichlet boundary condition +template <class Traits> + template <class RowTreePath, class ColTreePath, class Values> +void ProblemStat<Traits>:: +addDirichletBC(BoundaryType id, RowTreePath row, ColTreePath col, Values const& values) +{ + auto localView = globalBasis_->localView(); + auto i = child(localView.tree(), makeTreePath(row)); + auto j = child(localView.tree(), makeTreePath(col)); + + auto valueGridFct = makeGridFunction(values, globalBasis_->gridView()); + + using Range = RangeType_t<decltype(i)>; + using BcType = DirichletBC<WorldVector,Range>; + auto bc = std::make_unique<BcType>(boundaryManager_, id, valueGridFct); + dirichletBCs_[i][j].push_back(std::move(bc)); +} + + +template <class Traits> +void ProblemStat<Traits>:: +addPeriodicBC(BoundaryType id, WorldMatrix const& matrix, WorldVector const& vector) +{ + auto localView = globalBasis_->localView(); + using BcType = PeriodicBC<WorldVector, typename GlobalBasis::MultiIndex>; + using FaceTrafo = FaceTransformation<typename WorldVector::field_type, WorldVector::dimension>; + auto bc = std::make_unique<BcType>(boundaryManager_, id, FaceTrafo{matrix, vector}); + periodicBCs_[localView.tree()][localView.tree()].push_back(std::move(bc)); } @@ -347,10 +379,14 @@ buildAfterAdapt(AdaptInfo& /*adaptInfo*/, Flag /*flag*/, bool asmMatrix, bool as rhs_->init(asmVector); auto localView = globalBasis_->localView(); - forEachNode_(localView.tree(), [&,this](auto const& rowNode, auto) { - forEachNode_(localView.tree(), [&,this](auto const& colNode, auto) { - for (auto bc : constraints_[rowNode][colNode]) - bc->init(*systemMatrix_, *solution_, *rhs_, rowNode, colNode); + forEachNode_(localView.tree(), [&,this](auto const& rowNode, auto rowTp) { + auto rowBasis = Dune::Functions::subspaceBasis(*globalBasis_, rowTp); + forEachNode_(localView.tree(), [&,this](auto const& colNode, auto colTp) { + auto colBasis = Dune::Functions::subspaceBasis(*globalBasis_, colTp); + for (auto bc : dirichletBCs_[rowNode][colNode]) + bc->init(rowBasis, colBasis); + for (auto bc : periodicBCs_[rowNode][colNode]) + bc->init(rowBasis, colBasis); }); }); msg("{} total DOFs", globalBasis_->dimension()); @@ -374,8 +410,10 @@ buildAfterAdapt(AdaptInfo& /*adaptInfo*/, Flag /*flag*/, bool asmMatrix, bool as forEachNode_(localView.tree(), [&,this](auto const& rowNode, auto) { forEachNode_(localView.tree(), [&,this](auto const& colNode, auto) { // finish boundary condition - for (auto bc : constraints_[rowNode][colNode]) - bc->finish(*systemMatrix_, *solution_, *rhs_, rowNode, colNode); + for (auto bc : dirichletBCs_[rowNode][colNode]) + bc->fillBoundaryCondition(*systemMatrix_, *solution_, *rhs_, rowNode, colNode); + for (auto bc : periodicBCs_[rowNode][colNode]) + bc->fillBoundaryCondition(*systemMatrix_, *solution_, *rhs_, rowNode, colNode); }); }); diff --git a/src/amdis/linear_algebra/CMakeLists.txt b/src/amdis/linear_algebra/CMakeLists.txt index a436f7713b38c10fe2b334a1bc2dcd8541b5cc43..7027481fbf5e2d205a98cf50c6df0b359fb1f492 100644 --- a/src/amdis/linear_algebra/CMakeLists.txt +++ b/src/amdis/linear_algebra/CMakeLists.txt @@ -1,5 +1,6 @@ install(FILES Common.hpp + Constraints.hpp DOFMatrixBase.hpp DOFMatrixBase.inc.hpp DOFVectorBase.hpp diff --git a/src/amdis/linear_algebra/Constraints.hpp b/src/amdis/linear_algebra/Constraints.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c774ff771eab520813a45402b5169d221dfc28eb --- /dev/null +++ b/src/amdis/linear_algebra/Constraints.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include <amdis/linear_algebra/DOFMatrixBase.hpp> + +namespace AMDiS +{ + template <class Matrix> + struct Constraints + { + template <class R, class C, class B, class BitVector> + static auto dirichletBC(DOFMatrixBase<R,C,B>& matrix, BitVector const& nodes, bool setDiagonal = true) + { + return Constraints<typename Matrix::BaseMatrix>::dirichletBC(matrix.matrix(), nodes, setDiagonal); + } + + template <class R, class C, class B, class BitVector, class Association> + static auto periodicBC(DOFMatrixBase<R,C,B>& matrix, BitVector const& left, Association const& association, + bool setDiagonal = true) + { + return Constraints<typename Matrix::BaseMatrix>::periodicBC(matrix.matrix(), left, association, setDiagonal); + } + }; + +} // end namespace AMDiS diff --git a/src/amdis/linear_algebra/DOFMatrixBase.inc.hpp b/src/amdis/linear_algebra/DOFMatrixBase.inc.hpp index 9f5e6971866d4684d61f5447f7070bc30396f544..adde13479ec5b705a466c4ef69fd1fccd20ee5f0 100644 --- a/src/amdis/linear_algebra/DOFMatrixBase.inc.hpp +++ b/src/amdis/linear_algebra/DOFMatrixBase.inc.hpp @@ -81,10 +81,7 @@ assemble(RowLocalView const& rowLocalView, ColLocalView const& colLocalView) auto& matOp = operators_[rowNode][colNode]; if (matOp) { matOp.bind(element, geometry); - assembleOperators(gv, element, matOp, [&](auto const& context, auto& operators) { - for (auto scaled : operators) - scaled.op->assemble(context, rowNode, colNode, elementMatrix_); - }); + assembleOperators(gv, element, matOp, makeMatrixAssembler(rowNode, colNode, elementMatrix_)); matOp.unbind(); } }); diff --git a/src/amdis/linear_algebra/DOFVectorBase.inc.hpp b/src/amdis/linear_algebra/DOFVectorBase.inc.hpp index 884fad1d4e8dd668b17b66b350a0b8121989818a..7de4dcde61482e36d7991b920014dad31e3b3161 100644 --- a/src/amdis/linear_algebra/DOFVectorBase.inc.hpp +++ b/src/amdis/linear_algebra/DOFVectorBase.inc.hpp @@ -71,10 +71,7 @@ assemble(LocalView const& localView) auto& rhsOp = operators_[node]; if (rhsOp) { rhsOp.bind(element, geometry); - assembleOperators(gv, element, rhsOp, [&](auto const& context, auto& operators) { - for (auto scaled : operators) - scaled.op->assemble(context, node, elementVector_); - }); + assembleOperators(gv, element, rhsOp, makeVectorAssembler(node, elementVector_)); rhsOp.unbind(); } }); diff --git a/src/amdis/linear_algebra/eigen/CMakeLists.txt b/src/amdis/linear_algebra/eigen/CMakeLists.txt index 41aacc5baefecd529dfada60665c42586a89aa27..8fbcf5e3f3627776a82ccb1800e3da71a85c1843 100644 --- a/src/amdis/linear_algebra/eigen/CMakeLists.txt +++ b/src/amdis/linear_algebra/eigen/CMakeLists.txt @@ -1,4 +1,5 @@ install(FILES + Constraints.hpp DirectRunner.hpp DOFMatrix.hpp DOFVector.hpp diff --git a/src/amdis/linear_algebra/eigen/Constraints.hpp b/src/amdis/linear_algebra/eigen/Constraints.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fec59492f940bbbdb5d3c0d08b482bd089ba64c8 --- /dev/null +++ b/src/amdis/linear_algebra/eigen/Constraints.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include <list> + +#include <Eigen/SparseCore> + +#include <amdis/common/Mpl.hpp> +#include <amdis/linear_algebra/Common.hpp> +#include <amdis/linear_algebra/Constraints.hpp> + +namespace AMDiS +{ + template <class T, int O> + struct Constraints<Eigen::SparseMatrix<T,O>> + { + using Matrix = Eigen::SparseMatrix<T,O>; + + template <class BitVector> + static std::vector<Triplet<T>> dirichletBC(Matrix& mat, BitVector const& nodes, bool setDiagonal = true) + { + clearDirichletRow(mat, nodes, setDiagonal, int_<O>); + return {}; + } + + template <class BitVector, class Associations> + static std::vector<Triplet<T>> periodicBC(Matrix& mat, BitVector const& left, Associations const& left2right, + bool setDiagonal = true) + { + error_exit("Not implemented"); + return {}; + } + + protected: + template <class BitVector> + static void clearDirichletRow(Matrix& mat, BitVector const& nodes, bool setDiagonal, int_t<Eigen::ColMajor>) + { + for (typename Matrix::Index c = 0; c < mat.outerSize(); ++c) { + for (typename Matrix::InnerIterator it(mat, c); it; ++it) { + if (nodes[it.row()]) { + it.valueRef() = (setDiagonal && it.row() == it.col() ? T(1) : T(0)); + break; + } + } + } + } + + template <class BitVector> + static void clearDirichletRow(Matrix& mat, BitVector const& nodes, bool setDiagonal, int_t<Eigen::RowMajor>) + { + for (typename Matrix::Index r = 0; r < mat.outerSize(); ++r) { + if (nodes[r]) { + for (typename Matrix::InnerIterator it(mat, r); it; ++it) { + it.valueRef() = (setDiagonal && it.row() == it.col() ? T(1) : T(0)); + } + } + } + } + }; + +} // end namespace AMDiS diff --git a/src/amdis/linear_algebra/eigen/DOFMatrix.hpp b/src/amdis/linear_algebra/eigen/DOFMatrix.hpp index 0ae8d5dfef7cad611824a2a8771ca50c4fda0b63..46920b20442500d9e650399ed402f8f47b0575a9 100644 --- a/src/amdis/linear_algebra/eigen/DOFMatrix.hpp +++ b/src/amdis/linear_algebra/eigen/DOFMatrix.hpp @@ -85,38 +85,6 @@ namespace AMDiS return matrix_.nonZeros(); } - /// \brief Deletes all rows with \p dirichletNodes != 0 and if \p apply is true, adds - /// a one on the diagonal of the matrix. - auto applyDirichletBC(std::vector<char> const& dirichletNodes) - { - for (std::size_t i = 0; i < dirichletNodes.size(); ++i) { - if (dirichletNodes[i] == 1) - clearDirichletRow(i, int_<Orientation>); - } - return std::list<Triplet<value_type>>{}; - } - - - protected: - void clearDirichletRow(size_type idx, int_t<Eigen::ColMajor>) - { - for (size_type k = 0; k < matrix_.outerSize(); ++k) { - for (typename BaseMatrix::InnerIterator it(matrix_, k); it; ++it) { - if (it.row() == idx) { - it.valueRef() = (it.row() == it.col() ? value_type(1) : value_type(0)); - break; - } - } - } - } - - void clearDirichletRow(size_type idx, int_t<Eigen::RowMajor>) - { - Eigen::SparseVector<ValueType> unit_vector(matrix_.cols()); - unit_vector.coeffRef(idx) = value_type(1); - matrix_.row(idx) = unit_vector; - } - private: /// The data-matrix BaseMatrix matrix_; diff --git a/src/amdis/linear_algebra/istl/CMakeLists.txt b/src/amdis/linear_algebra/istl/CMakeLists.txt index d84972a0beae1be790e9e972845953fa061f363e..f342ea029c11494a5c06cf177fe6262b51e7cd26 100644 --- a/src/amdis/linear_algebra/istl/CMakeLists.txt +++ b/src/amdis/linear_algebra/istl/CMakeLists.txt @@ -1,4 +1,5 @@ install(FILES + Constraints.hpp DirectRunner.hpp DOFMatrix.hpp DOFVector.hpp diff --git a/src/amdis/linear_algebra/istl/Constraints.hpp b/src/amdis/linear_algebra/istl/Constraints.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5a11d6ff7d1e47cccdb79063e6bd446ca23a08a1 --- /dev/null +++ b/src/amdis/linear_algebra/istl/Constraints.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include <list> + +#include <dune/istl/bcrsmatrix.hh> + +#include <amdis/linear_algebra/Common.hpp> +#include <amdis/linear_algebra/Constraints.hpp> + +namespace AMDiS +{ + template <class T, class A> + struct Constraints<Dune::BCRSMatrix<T,A>> + { + using Matrix = Dune::BCRSMatrix<T,A>; + + template <class BitVector> + static std::vector<Triplet<T>> dirichletBC(Matrix& mat, BitVector const& nodes, bool setDiagonal = true) + { + // loop over the matrix rows + for (std::size_t i = 0; i < mat.N(); ++i) { + if (nodes[i]) { + auto cIt = mat[i].begin(); + auto cEndIt = mat[i].end(); + // loop over nonzero matrix entries in current row + for (; cIt != cEndIt; ++cIt) + *cIt = (setDiagonal && i == cIt.index() ? T(1) : T(0)); + } + } + + return {}; + } + + template <class BitVector, class Associations> + static std::vector<Triplet<T>> periodicBC(Matrix& mat, BitVector const& left, Associations const& left2right, + bool setDiagonal = true) + { + error_exit("Not implemented"); + return {}; + } + }; + +} // end namespace AMDiS diff --git a/src/amdis/linear_algebra/istl/DOFMatrix.hpp b/src/amdis/linear_algebra/istl/DOFMatrix.hpp index 678ef0b58b2d970db5e3ac9b33c6e079d8dfc8e1..572d2bd9bd01a262cc85ec143bf5c96541834700 100644 --- a/src/amdis/linear_algebra/istl/DOFMatrix.hpp +++ b/src/amdis/linear_algebra/istl/DOFMatrix.hpp @@ -102,21 +102,6 @@ namespace AMDiS return matrix_.nonzeroes(); } - auto applyDirichletBC(std::vector<char> const& dirichletNodes) - { - // loop over the matrix rows - for (std::size_t i = 0; i < matrix_.N(); ++i) { - if (dirichletNodes[i]) { - auto cIt = matrix_[i].begin(); - auto cEndIt = matrix_[i].end(); - // loop over nonzero matrix entries in current row - for (; cIt != cEndIt; ++cIt) - *cIt = (i == cIt.index() ? 1 : 0); - } - } - return std::list<Triplet<value_type>>{}; - } - private: BaseMatrix matrix_; diff --git a/src/amdis/linear_algebra/mtl/CMakeLists.txt b/src/amdis/linear_algebra/mtl/CMakeLists.txt index e295f73f7767c0b95987825dd993f89720bc30f5..adcf1b591b2a56d86c1c884b5c1d03bc1e5af7a0 100644 --- a/src/amdis/linear_algebra/mtl/CMakeLists.txt +++ b/src/amdis/linear_algebra/mtl/CMakeLists.txt @@ -1,4 +1,5 @@ install(FILES + Constraints.hpp DOFMatrix.hpp DOFVector.hpp ITL_Preconditioner.hpp diff --git a/src/amdis/linear_algebra/mtl/Constraints.hpp b/src/amdis/linear_algebra/mtl/Constraints.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bd20520609e12d897ee2b149ed48cdb3c4e3b53c --- /dev/null +++ b/src/amdis/linear_algebra/mtl/Constraints.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include <list> + +#include <boost/numeric/mtl/matrix/compressed2D.hpp> +#include <boost/numeric/mtl/matrix/inserter.hpp> +#include <boost/numeric/mtl/utility/property_map.hpp> +#include <boost/numeric/mtl/utility/range_wrapper.hpp> + +#include <amdis/linear_algebra/Common.hpp> +#include <amdis/linear_algebra/Constraints.hpp> + +namespace AMDiS +{ + template <class T, class P> + struct Constraints<mtl::compressed2D<T,P>> + { + using Matrix = mtl::compressed2D<T,P>; + +#ifdef SYMMETRIC_DIRICHLET_BC + template <class BitVector> + static std::vector<Triplet<T>> dirichletBC(Matrix& mat, BitVector const& nodes, bool setDiagonal = true) + { + std::vector<Triplet<T>> columns; + if (setDiagonal) + columns.reserve(std::size_t(mat.nnz()/(0.84*num_rows(mat)))); + + // Define the property maps + auto row = mtl::mat::row_map(mat); + auto col = mtl::mat::col_map(mat); + auto value = mtl::mat::value_map(mat); + + // iterate over the matrix + std::size_t slotSize = 0; + for (auto r : mtl::rows_of(mat)) { // rows or columns + std::size_t rowSize = 0; + for (auto i : mtl::nz_of(r)) { // non-zeros within + ++rowSize; + if (nodes[row(i)]) { + // set identity row + value(i, T(0)); + } + else if (setDiagonal && nodes[col(i)]) { + columns.push_back({row(i), col(i), value(i)}); + value(i, T(0)); + } + } + slotSize = std::max(slotSize, rowSize); + } + + // set diagonal entry + if (setDiagonal) { + mtl::mat::inserter<Matrix, mtl::update_store<T> > ins(mat, slotSize); + for (std::size_t i = 0; i < nodes.size(); ++i) { + if (nodes[i]) + ins[i][i] = T(1); + } + } + + return columns; + } +#else + template <class BitVector> + static std::vector<Triplet<T>> dirichletBC(Matrix& mat, BitVector const& nodes, bool setDiagonal = true) + { + // Define the property maps + auto row = mtl::mat::row_map(mat); + auto col = mtl::mat::col_map(mat); + auto value = mtl::mat::value_map(mat); + + // iterate over the matrix + for (auto r : mtl::rows_of(mat)) { // rows of the matrix + if (nodes[r.value()]) { + for (auto i : mtl::nz_of(r)) { // non-zeros within + // set identity row + value(i, (setDiagonal && row(i) == col(i) ? T(1) : T(0)) ); + } + } + } + + return {}; + } +#endif + + + template <class Associations> + static std::size_t at(Associations const& m, std::size_t idx) + { + auto it = m.find(idx); + assert(it != m.end()); + return it->second; + } + +#ifdef SYMMETRIC_PERIODIC_BC + template <class BitVector, class Associations> + static std::vector<Triplet<T>> periodicBC(Matrix& mat, BitVector const& left, Associations const& left2right, + bool setDiagonal = true) + { + error_exit("Not implemented"); + } +#else + template <class BitVector, class Associations> + static std::vector<Triplet<T>> periodicBC(Matrix& mat, BitVector const& left, Associations const& left2right, + bool setDiagonal = true) + { + std::vector<Triplet<T>> rowValues; + rowValues.reserve(left2right.size()*std::size_t(mat.nnz()/(0.9*num_rows(mat)))); + + // Define the property maps + // auto row = mtl::mat::row_map(mat); + auto col = mtl::mat::col_map(mat); + auto value = mtl::mat::value_map(mat); + + // iterate over the matrix + std::size_t slotSize = 0; + for (auto r : mtl::rows_of(mat)) { + if (left[r.value()]) { + slotSize = std::max(slotSize, std::size_t(mat.nnz_local(r.value()))); + std::size_t right = at(left2right,r.value()); + + for (auto i : mtl::nz_of(r)) { + rowValues.push_back({right,col(i),value(i)}); + value(i, T(0)); + } + } + } + + mtl::mat::inserter<Matrix, mtl::update_plus<T> > ins(mat, 2*slotSize); + for (auto const& t : rowValues) + ins[t.row][t.col] += t.value; + + if (setDiagonal) { + for (std::size_t i = 0; i < size(left); ++i) { + if (left[i]) { + ins[i][i] = T(1); + ins[i][at(left2right,i)] = T(-1); + } + } + } + + return {}; + } +#endif + }; + +} // end namespace AMDiS diff --git a/src/amdis/linear_algebra/mtl/DOFMatrix.hpp b/src/amdis/linear_algebra/mtl/DOFMatrix.hpp index f1213b7e06ce5f68de39b9bf2d4c68c740667514..26ecdf75afe01b658465bc5086a0932ae78924c4 100644 --- a/src/amdis/linear_algebra/mtl/DOFMatrix.hpp +++ b/src/amdis/linear_algebra/mtl/DOFMatrix.hpp @@ -94,28 +94,6 @@ namespace AMDiS return matrix_.nnz(); } - /// \brief Deletes all rows with \p dirichletNodes != 0 and if \p apply is true, adds - /// a one on the diagonal of the matrix. - auto applyDirichletBC(std::vector<char> const& dirichletNodes) - { - // Define the property maps - auto row = mtl::mat::row_map(matrix_); - auto col = mtl::mat::col_map(matrix_); - auto value = mtl::mat::value_map(matrix_); - - // iterate over the matrix - for (auto r : mtl::rows_of(matrix_)) { // rows of the matrix - if (dirichletNodes[r.value()]) { - for (auto i : mtl::nz_of(r)) { // non-zeros within - // set identity row - value(i, (row(i) == col(i) ? value_type(1) : value_type(0)) ); - } - } - } - - return std::list<Triplet<value_type>>{}; - } - protected: // Estimates the slot-size used in the inserter to reserve enough space per row. void calculateSlotSize()