version 3.11-dev
l2_projection.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_DISCRETIZATION_L2_PROJECTION_HH
13#define DUMUX_DISCRETIZATION_L2_PROJECTION_HH
14
15#include <array>
16#include <optional>
17#include <type_traits>
18#include <vector>
19
20#include <dune/common/fvector.hh>
21#include <dune/common/fmatrix.hh>
22#include <dune/common/parametertree.hh>
23#include <dune/common/typetraits.hh>
24#include <dune/geometry/quadraturerules.hh>
25#include <dune/istl/bcrsmatrix.hh>
26#include <dune/istl/bvector.hh>
27#ifdef HAVE_DUNE_FUNCTIONS
28#include <dune/functions/gridfunctions/gridviewfunction.hh>
29#endif
35
36namespace Dumux {
37
38namespace Detail {
39
43template<class Function, class GridView>
44auto makeLocalFunction(Function&& f, const GridView& gridView)
45{
46 using Element = typename GridView::template Codim<0>::Entity;
47 using LocalCoord = typename Element::Geometry::LocalCoordinate;
48 constexpr bool hasLocalInterface =
49 requires(std::decay_t<Function>& lf, const Element& e, const LocalCoord& x)
50 {
51 lf.bind(e);
52 lf(x);
53 };
54
55 // If f already provides the local-function interface, use it directly.
56 // This must be checked first to avoid incorrectly re-wrapping it via dune-functions.
57 if constexpr (hasLocalInterface)
58 return std::forward<Function>(f);
59#ifdef HAVE_DUNE_FUNCTIONS
60 else
61 return localFunction(Dune::Functions::makeGridViewFunction(std::forward<Function>(f), gridView));
62#else
63 else
64 DUNE_THROW(Dune::InvalidStateException, "Function must provide bind(element) and operator()(localCoord), "
65 "or dune-functions must be available to wrap a global f(globalPos) callable.");
66#endif
67}
68
69} // end namespace Detail
70
79template<class GridDiscretization>
81{
82 using GV = typename GridDiscretization::GridView;
83 using Element = typename GV::template Codim<0>::Entity;
84 using FE = typename GridDiscretization::FeCache::FiniteElementType;
85
86public:
87 using GridView = GV;
88
89 struct LocalTree
90 {
91 using FiniteElement = FE;
92 const FE* fe_ = nullptr;
93 const FiniteElement& finiteElement() const { return *fe_; }
94 };
95
96 struct LocalView
97 {
98 using Tree = LocalTree;
99
100 explicit LocalView(const GridDiscretization& gg) : gg_(gg) {}
101
102 void bind(const Element& element)
103 {
104 element_ = element;
105 tree_.fe_ = &gg_.feCache().get(element.type());
106 }
107
108 const Tree& tree() const { return tree_; }
109
110 std::size_t index(std::size_t index) const
111 {
112 const auto& localKey = tree_.fe_->localCoefficients().localKey(index);
113 // TODO: Currently we assume that this is the default dof mapping when having multiple dofs per sub-entity
114 // meaning that they are localKey.index() larger than 0
115 return gg_.dofMapper().subIndex(*element_, localKey.subEntity(), localKey.codim()) + localKey.index();
116 }
117
118 private:
119 const GridDiscretization& gg_;
120 std::optional<Element> element_;
121 Tree tree_;
122 };
123
124 explicit FEBasisFromCVFEGridDiscretization(const GridDiscretization& gg) : gg_(gg) {}
125
126 std::size_t size() const { return gg_.numDofs(); }
127 const GridView& gridView() const { return gg_.gridView(); }
128 LocalView localView() const { return LocalView(gg_); }
129
130private:
131 const GridDiscretization& gg_;
132};
133
134template <class FEBasis>
136{
137 static constexpr int dim = FEBasis::GridView::dimension;
138 using FiniteElement = typename FEBasis::LocalView::Tree::FiniteElement;
139 using Scalar = typename FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType;
140 using ShapeValue = typename FiniteElement::Traits::LocalBasisType::Traits::RangeType;
141 static_assert(ShapeValue::dimension == 1, "Only scalar-valued shape functions are supported for L2 projection.");
143
144public:
145 template<int numEq = 1>
146 using CoefficientVector = Dune::BlockVector<Dune::FieldVector<Scalar, numEq>>;
147
149 struct Params
150 {
151 std::size_t maxIterations{100};
152 Scalar residualReduction{1e-13};
153 int verbosity{0};
154 };
155
156 L2Projection(const FEBasis& feBasis)
157 : feBasis_(feBasis)
158 , solver_()
159 {
160 solver_.setMatrix(std::make_shared<Matrix>(createMassMatrix_(feBasis)));
161 }
162
163 template <class Function>
164 auto project(Function&& function, const Params& params = Params{}) const
165 {
166 auto localFunc = Detail::makeLocalFunction(std::forward<Function>(function), feBasis_.gridView());
167 using LocalPosition = typename FEBasis::GridView::template Codim<0>::Entity::Geometry::LocalCoordinate;
168 using ReturnType = std::invoke_result_t<Function, LocalPosition>;
169 constexpr int numEq = []() {
170 if constexpr (Dune::IsNumber<ReturnType>::value) return 1;
171 else return ReturnType::dimension;
172 }();
173 using CoeffVec = CoefficientVector<numEq>;
174
175 const auto numDofs = feBasis_.size();
176 // assemble one RHS per equation component
177 std::array<Dune::BlockVector<Scalar>, numEq> rhs;
178 for (auto& r : rhs) { r.resize(numDofs); r = 0.0; }
179
180 // assemble right hand side
181 auto localView = feBasis_.localView();
182 for (const auto& element : elements(feBasis_.gridView()))
183 {
184 localView.bind(element);
185 localFunc.bind(element);
186
187 const auto& localFiniteElement = localView.tree().finiteElement();
188 const int order = dim*localFiniteElement.localBasis().order();
189 const auto& quad = Dune::QuadratureRules<Scalar, dim>::rule(element.type(), order);
190 const auto geometry = element.geometry();
191
192 for (auto&& qp : quad)
193 {
194 const auto weight = qp.weight();
195 const auto ie = geometry.integrationElement(qp.position());
196
197 std::vector<ShapeValue> shapeValues;
198 localFiniteElement.localBasis().evaluateFunction(qp.position(), shapeValues);
199 const auto functionValue = localFunc(qp.position());
200
201 for (int i = 0; i < localFiniteElement.localBasis().size(); ++i)
202 {
203 const auto globalI = localView.index(i);
204 const auto w = ie*weight*shapeValues[i][0];
205 if constexpr (numEq == 1)
206 rhs[0][globalI] += w*functionValue;
207 else
208 for (int compIdx = 0; compIdx < numEq; compIdx++)
209 rhs[compIdx][globalI] += w*functionValue[compIdx];
210 }
211 }
212 }
213
214 // solve numEq independent systems in parallel (same matrix, different RHS)
215 CoeffVec coeffs(numDofs);
216 Dumux::parallelFor(numEq, [&](std::size_t compIdx)
217 {
218 // each thread needs its own solver copy (not thread-safe to share)
219 auto solver = solver_;
220 Dune::ParameterTree solverParams;
221 solverParams["maxit"] = std::to_string(params.maxIterations);
222 solverParams["reduction"] = std::to_string(params.residualReduction);
223 solverParams["verbose"] = std::to_string(params.verbosity);
224 solver.setParams(solverParams);
225
226 Dune::BlockVector<Scalar> sol(numDofs); sol = 0.0;
227 solver.solve(sol, rhs[compIdx]);
228
229 for (std::size_t i = 0; i < numDofs; ++i)
230 coeffs[i][compIdx] = sol[i];
231 });
232
233 return coeffs;
234 }
235
236private:
237 Matrix createMassMatrix_(const FEBasis& feBasis) const
238 {
239 Matrix massMatrix;
240
241 auto pattern = getFEJacobianPattern(feBasis);
242 pattern.exportIdx(massMatrix);
243 massMatrix = 0.0;
244
245 auto localView = feBasis.localView();
246 for (const auto& element : elements(feBasis.gridView()))
247 {
248 localView.bind(element);
249
250 const auto& localFiniteElement = localView.tree().finiteElement();
251 const int order = 2*dim*localFiniteElement.localBasis().order();
252 const auto& quad = Dune::QuadratureRules<Scalar, dim>::rule(element.type(), order);
253 const auto geometry = element.geometry();
254
255 for (auto&& qp : quad)
256 {
257 const auto weight = qp.weight();
258 const auto ie = geometry.integrationElement(qp.position());
259
260 std::vector<ShapeValue> shapeValues;
261 localFiniteElement.localBasis().evaluateFunction(qp.position(), shapeValues);
262
263 for (int i = 0; i < localFiniteElement.localBasis().size(); ++i)
264 {
265 const auto globalI = localView.index(i);
266 massMatrix[globalI][globalI] += ie*weight*shapeValues[i]*shapeValues[i];
267
268 for (int j = i+1; j < localFiniteElement.localBasis().size(); ++j)
269 {
270 const auto globalJ = localView.index(j);
271 const auto value = ie*weight*shapeValues[i]*shapeValues[j];
272 massMatrix[globalI][globalJ] += value;
273 massMatrix[globalJ][globalI] += value;
274 }
275 }
276 }
277 }
278
279 return massMatrix;
280 }
281
282 const FEBasis& feBasis_;
284 SeqLinearSolverTraits, LinearAlgebraTraits<Matrix, Dune::BlockVector<Scalar>>
285 > solver_;
286};
287
288} // end namespace Dumux
289
290#endif
Wraps a CVFE grid discretization to expose the FE basis interface expected by L2Projection.
Definition: l2_projection.hh:81
GV GridView
Definition: l2_projection.hh:87
FEBasisFromCVFEGridDiscretization(const GridDiscretization &gg)
Definition: l2_projection.hh:124
const GridView & gridView() const
Definition: l2_projection.hh:127
LocalView localView() const
Definition: l2_projection.hh:128
std::size_t size() const
Definition: l2_projection.hh:126
Definition: l2_projection.hh:136
auto project(Function &&function, const Params &params=Params{}) const
Definition: l2_projection.hh:164
L2Projection(const FEBasis &feBasis)
Definition: l2_projection.hh:156
Dune::BlockVector< Dune::FieldVector< Scalar, numEq > > CoefficientVector
Definition: l2_projection.hh:146
Definition: matrix.hh:20
Dune::MatrixIndexSet getFEJacobianPattern(const FEBasis &feBasis)
Helper function to generate Jacobian pattern for finite element scheme.
Definition: jacobianpattern.hh:106
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition: localview.hh:26
Detail::IstlIterativeLinearSolver< LSTraits, LATraits, Dune::CGSolver< typename LATraits::Vector >, Detail::IstlSolvers::IstlDefaultBlockLevelPreconditionerFactory< Dune::SeqSSOR > > SSORCGIstlSolver
An SSOR-preconditioned CG solver using dune-istl.
Definition: istlsolvers.hh:706
void parallelFor(const std::size_t count, const FunctorType &functor)
A parallel for loop (multithreading)
Definition: parallel_for.hh:160
Linear solvers from dune-istl.
Helper function to generate Jacobian pattern for different discretization methods.
Define traits for linear algebra backends.
Define traits for linear solvers.
auto makeLocalFunction(Function &&f, const GridView &gridView)
Create a local function from a given function.
Definition: l2_projection.hh:44
Definition: adapt.hh:17
Parallel for loop (multithreading)
const FiniteElement & finiteElement() const
Definition: l2_projection.hh:93
FE FiniteElement
Definition: l2_projection.hh:91
const FE * fe_
Definition: l2_projection.hh:92
LocalView(const GridDiscretization &gg)
Definition: l2_projection.hh:100
const Tree & tree() const
Definition: l2_projection.hh:108
LocalTree Tree
Definition: l2_projection.hh:98
void bind(const Element &element)
Definition: l2_projection.hh:102
std::size_t index(std::size_t index) const
Definition: l2_projection.hh:110
Parameters that can be passed to project()
Definition: l2_projection.hh:150
int verbosity
Definition: l2_projection.hh:153
Scalar residualReduction
Definition: l2_projection.hh:152
std::size_t maxIterations
Definition: l2_projection.hh:151