version 3.11-dev
Loading...
Searching...
No Matches
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
30#include <dumux/io/format.hh>
36
37namespace Dumux {
38
39namespace Detail {
40
44template<class Function, class GridView>
45auto makeLocalFunction(Function&& f, const GridView& gridView)
46{
47 using Element = typename GridView::template Codim<0>::Entity;
48 using LocalCoord = typename Element::Geometry::LocalCoordinate;
49 constexpr bool hasLocalInterface =
50 requires(std::decay_t<Function>& lf, const Element& e, const LocalCoord& x)
51 {
52 lf.bind(e);
53 lf(x);
54 };
55
56 // If f already provides the local-function interface, use it directly.
57 // This must be checked first to avoid incorrectly re-wrapping it via dune-functions.
58 if constexpr (hasLocalInterface)
59 return std::forward<Function>(f);
60#ifdef HAVE_DUNE_FUNCTIONS
61 else
62 return localFunction(Dune::Functions::makeGridViewFunction(std::forward<Function>(f), gridView));
63#else
64 else
65 DUNE_THROW(Dune::InvalidStateException, "Function must provide bind(element) and operator()(localCoord), "
66 "or dune-functions must be available to wrap a global f(globalPos) callable.");
67#endif
68}
69
70} // end namespace Detail
71
80template<class GridDiscretization>
82{
83 using GV = typename GridDiscretization::GridView;
84 using Element = typename GV::template Codim<0>::Entity;
85 using FE = typename GridDiscretization::FeCache::FiniteElementType;
86
87public:
88 using GridView = GV;
89
90 struct LocalTree
91 {
92 using FiniteElement = FE;
93 const FE* fe_ = nullptr;
94 const FiniteElement& finiteElement() const { return *fe_; }
95 };
96
97 struct LocalView
98 {
99 using Tree = LocalTree;
100
101 explicit LocalView(const GridDiscretization& gg) : gg_(gg) {}
102
103 void bind(const Element& element)
104 {
105 element_ = element;
106 tree_.fe_ = &gg_.feCache().get(element.type());
107 }
108
109 const Tree& tree() const { return tree_; }
110
111 std::size_t index(std::size_t index) const
112 {
113 const auto& localKey = tree_.fe_->localCoefficients().localKey(index);
114 // TODO: Currently we assume that this is the default dof mapping when having multiple dofs per sub-entity
115 // meaning that they are localKey.index() larger than 0
116 return gg_.dofMapper().subIndex(*element_, localKey.subEntity(), localKey.codim()) + localKey.index();
117 }
118
119 private:
120 const GridDiscretization& gg_;
121 std::optional<Element> element_;
122 Tree tree_;
123 };
124
125 explicit FEBasisFromCVFEGridDiscretization(const GridDiscretization& gg) : gg_(gg) {}
126
127 std::size_t size() const { return gg_.numDofs(); }
128 const GridView& gridView() const { return gg_.gridView(); }
129 LocalView localView() const { return LocalView(gg_); }
130
131private:
132 const GridDiscretization& gg_;
133};
134
135template <class FEBasis>
137{
138 static constexpr int dim = FEBasis::GridView::dimension;
139 using FiniteElement = typename FEBasis::LocalView::Tree::FiniteElement;
140 using Scalar = typename FiniteElement::Traits::LocalBasisType::Traits::RangeFieldType;
141 using ShapeValue = typename FiniteElement::Traits::LocalBasisType::Traits::RangeType;
142 static_assert(ShapeValue::dimension == 1, "Only scalar-valued shape functions are supported for L2 projection.");
143 using Matrix = Dune::BCRSMatrix<Scalar>;
144
145public:
146 template<int numEq = 1>
147 using CoefficientVector = Dune::BlockVector<Dune::FieldVector<Scalar, numEq>>;
148
150 struct Params
151 {
152 std::size_t maxIterations{100};
153 Scalar residualReduction{1e-13};
154 int verbosity{0};
155 };
156
157 L2Projection(const FEBasis& feBasis)
158 : feBasis_(feBasis)
159 , solver_()
160 {
161 solver_.setMatrix(std::make_shared<Matrix>(createMassMatrix_(feBasis)));
162 }
163
164 template <class Function>
165 auto project(Function&& function, const Params& params = Params{}) const
166 {
167 auto localFunc = Detail::makeLocalFunction(std::forward<Function>(function), feBasis_.gridView());
168 using LocalPosition = typename FEBasis::GridView::template Codim<0>::Entity::Geometry::LocalCoordinate;
169 using ReturnType = std::invoke_result_t<Function, LocalPosition>;
170 constexpr int numEq = []() {
171 if constexpr (Dune::IsNumber<ReturnType>::value) return 1;
172 else return ReturnType::dimension;
173 }();
174 using CoeffVec = CoefficientVector<numEq>;
175
176 const auto numDofs = feBasis_.size();
177 // assemble one RHS per equation component
178 std::array<Dune::BlockVector<Scalar>, numEq> rhs;
179 for (auto& r : rhs) { r.resize(numDofs); r = 0.0; }
180
181 // assemble right hand side
182 auto localView = feBasis_.localView();
183 for (const auto& element : elements(feBasis_.gridView()))
184 {
185 localView.bind(element);
186 localFunc.bind(element);
187
188 const auto& localFiniteElement = localView.tree().finiteElement();
189 const int order = dim*localFiniteElement.localBasis().order();
190 const auto& quad = Dune::QuadratureRules<Scalar, dim>::rule(element.type(), order);
191 const auto geometry = element.geometry();
192
193 for (auto&& qp : quad)
194 {
195 const auto weight = qp.weight();
196 const auto ie = geometry.integrationElement(qp.position());
197
198 std::vector<ShapeValue> shapeValues;
199 localFiniteElement.localBasis().evaluateFunction(qp.position(), shapeValues);
200 const auto functionValue = localFunc(qp.position());
201
202 for (int i = 0; i < localFiniteElement.localBasis().size(); ++i)
203 {
204 const auto globalI = localView.index(i);
205 const auto w = ie*weight*shapeValues[i][0];
206 if constexpr (numEq == 1)
207 rhs[0][globalI] += w*functionValue;
208 else
209 for (int compIdx = 0; compIdx < numEq; compIdx++)
210 rhs[compIdx][globalI] += w*functionValue[compIdx];
211 }
212 }
213 }
214
215 // solve numEq independent systems in parallel (same matrix, different RHS)
216 CoeffVec coeffs(numDofs);
217 Dumux::parallelFor(numEq, [&](std::size_t compIdx)
218 {
219 // each thread needs its own solver copy (not thread-safe to share)
220 auto solver = solver_;
221 Dune::ParameterTree solverParams;
222 solverParams["maxit"] = std::to_string(params.maxIterations);
223 solverParams["reduction"] = Fmt::format("{}", params.residualReduction);
224 solverParams["verbose"] = std::to_string(params.verbosity);
225 solver.setParams(solverParams);
226
227 Dune::BlockVector<Scalar> sol(numDofs); sol = 0.0;
228 solver.solve(sol, rhs[compIdx]);
229
230 for (std::size_t i = 0; i < numDofs; ++i)
231 coeffs[i][compIdx] = sol[i];
232 });
233
234 return coeffs;
235 }
236
237private:
238 Matrix createMassMatrix_(const FEBasis& feBasis) const
239 {
240 Matrix massMatrix;
241
242 auto pattern = getFEJacobianPattern(feBasis);
243 pattern.exportIdx(massMatrix);
244 massMatrix = 0.0;
245
246 auto localView = feBasis.localView();
247 for (const auto& element : elements(feBasis.gridView()))
248 {
249 localView.bind(element);
250
251 const auto& localFiniteElement = localView.tree().finiteElement();
252 const int order = 2*dim*localFiniteElement.localBasis().order();
253 const auto& quad = Dune::QuadratureRules<Scalar, dim>::rule(element.type(), order);
254 const auto geometry = element.geometry();
255
256 for (auto&& qp : quad)
257 {
258 const auto weight = qp.weight();
259 const auto ie = geometry.integrationElement(qp.position());
260
261 std::vector<ShapeValue> shapeValues;
262 localFiniteElement.localBasis().evaluateFunction(qp.position(), shapeValues);
263
264 for (int i = 0; i < localFiniteElement.localBasis().size(); ++i)
265 {
266 const auto globalI = localView.index(i);
267 massMatrix[globalI][globalI] += ie*weight*shapeValues[i]*shapeValues[i];
268
269 for (int j = i+1; j < localFiniteElement.localBasis().size(); ++j)
270 {
271 const auto globalJ = localView.index(j);
272 const auto value = ie*weight*shapeValues[i]*shapeValues[j];
273 massMatrix[globalI][globalJ] += value;
274 massMatrix[globalJ][globalI] += value;
275 }
276 }
277 }
278 }
279
280 return massMatrix;
281 }
282
283 const FEBasis& feBasis_;
285 SeqLinearSolverTraits, LinearAlgebraTraits<Matrix, Dune::BlockVector<Scalar>>
286 > solver_;
287};
288
289} // end namespace Dumux
290
291#endif
GV GridView
Definition l2_projection.hh:88
FEBasisFromCVFEGridDiscretization(const GridDiscretization &gg)
Definition l2_projection.hh:125
const GridView & gridView() const
Definition l2_projection.hh:128
LocalView localView() const
Definition l2_projection.hh:129
std::size_t size() const
Definition l2_projection.hh:127
auto project(Function &&function, const Params &params=Params{}) const
Definition l2_projection.hh:165
Dune::BlockVector< Dune::FieldVector< Scalar, numEq > > CoefficientVector
Definition l2_projection.hh:147
L2Projection(const FEBasis &feBasis)
Definition l2_projection.hh:157
Definition matrix.hh:20
Formatting based on the fmt-library which implements std::format of C++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
@ element
Definition fieldtype.hh:23
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:743
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.
Definition cvfelocalresidual.hh:25
auto makeLocalFunction(Function &&f, const GridView &gridView)
Create a local function from a given function.
Definition l2_projection.hh:45
Definition adapt.hh:17
const Scalar PengRobinsonMixture< Scalar, StaticParameters >::w
Definition pengrobinsonmixture.hh:140
Parallel for loop (multithreading).
const FiniteElement & finiteElement() const
Definition l2_projection.hh:94
FE FiniteElement
Definition l2_projection.hh:92
const FE * fe_
Definition l2_projection.hh:93
LocalView(const GridDiscretization &gg)
Definition l2_projection.hh:101
const Tree & tree() const
Definition l2_projection.hh:109
LocalTree Tree
Definition l2_projection.hh:99
void bind(const Element &element)
Definition l2_projection.hh:103
std::size_t index(std::size_t index) const
Definition l2_projection.hh:111
Parameters that can be passed to project().
Definition l2_projection.hh:151
int verbosity
Definition l2_projection.hh:154
Scalar residualReduction
Definition l2_projection.hh:153
std::size_t maxIterations
Definition l2_projection.hh:152