Dune-Functions 2.11
Loading...
Searching...
No Matches
poisson-pq2.cc

This is the raw source code of the poisson-pq2.cc example. There is also a commented version of this example in the Examples section.

1// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2// vi: set et ts=4 sw=2 sts=2:
3
4// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md
5// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later
6
7#include <config.h>
8
9#include <vector>
10#include <cmath>
11
12#include <dune/common/bitsetvector.hh>
13#include <dune/common/rangeutilities.hh>
14
15#include <dune/geometry/quadraturerules.hh>
16
17#include <dune/grid/yaspgrid.hh>
18#include <dune/grid/uggrid.hh>
19#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
20
21#include <dune/istl/matrix.hh>
22#include <dune/istl/bcrsmatrix.hh>
23#include <dune/istl/matrixindexset.hh>
24#include <dune/istl/preconditioners.hh>
25#include <dune/istl/solvers.hh>
26
32
33using namespace Dune;
34
36// Compute the stiffness matrix for a single element
37template <class LocalView, class MatrixType>
38void getLocalMatrix(const LocalView& localView, MatrixType& elementMatrix)
39{
40 // Get the grid element from the local FE basis view
41 typedef typename LocalView::Element Element;
42 const Element& element = localView.element();
43
44 const int dim = Element::dimension;
45 auto geometry = element.geometry();
46
47 // Get set of shape functions for this element
48 const auto& localFiniteElement = localView.tree().finiteElement();
49
50 // Set all matrix entries to zero
51 elementMatrix.setSize(localFiniteElement.localBasis().size(),localFiniteElement.localBasis().size());
52 elementMatrix = 0; // fills the entire matrix with zeroes
53
54 // Get a quadrature rule
55 int order = 2*(dim*localFiniteElement.localBasis().order()-1);
56 const QuadratureRule<double, dim>& quad = QuadratureRules<double, dim>::rule(element.type(), order);
57
58 // Loop over all quadrature points
59 for (size_t pt=0; pt < quad.size(); pt++) {
60
61 // Position of the current quadrature point in the reference element
62 const FieldVector<double,dim>& quadPos = quad[pt].position();
63
64 // The inverse Jacobian of the map from the reference element to the element
65 const auto& jacobianInverse = geometry.jacobianInverse(quadPos);
66
67 // The multiplicative factor in the integral transformation formula
68 const double integrationElement = geometry.integrationElement(quadPos);
69
70 // The gradients of the shape functions on the reference element
71 std::vector<FieldMatrix<double,1,dim> > referenceJacobians;
72 localFiniteElement.localBasis().evaluateJacobian(quadPos, referenceJacobians);
73
74 // Compute the shape function gradients on the real element
75 std::vector<FieldMatrix<double,1,dim> > jacobians(referenceJacobians.size());
76 for (size_t i=0; i<jacobians.size(); i++)
77 jacobians[i] = referenceJacobians[i] * jacobianInverse;
78
79 // Compute the actual matrix entries
80 for (size_t i=0; i<elementMatrix.N(); i++)
81 for (size_t j=0; j<elementMatrix.M(); j++ )
82 elementMatrix[i][j] += (jacobians[i] * transpose(jacobians[j])) * quad[pt].weight() * integrationElement;
83
84 }
85
86}
88
90// Compute the source term for a single element
91template <class LocalView, class LocalVolumeTerm>
92void getVolumeTerm( const LocalView& localView,
93 BlockVector<double>& localRhs,
94 LocalVolumeTerm&& localVolumeTerm)
95{
96 // Get the grid element from the local FE basis view
97 typedef typename LocalView::Element Element;
98 const Element& element = localView.element();
99
100 const int dim = Element::dimension;
101
102 // Get set of shape functions for this element
103 const auto& localFiniteElement = localView.tree().finiteElement();
104
105 // Set all entries to zero
106 localRhs.resize(localFiniteElement.localBasis().size());
107 localRhs = 0;
108
109 // A quadrature rule
110 int order = dim*localFiniteElement.localBasis().order();
111 const QuadratureRule<double, dim>& quad = QuadratureRules<double, dim>::rule(element.type(), order);
112
113 // Loop over all quadrature points
114 for ( size_t pt=0; pt < quad.size(); pt++ ) {
115
116 // Position of the current quadrature point in the reference element
117 const FieldVector<double,dim>& quadPos = quad[pt].position();
118
119 // The multiplicative factor in the integral transformation formula
120 const double integrationElement = element.geometry().integrationElement(quadPos);
121
122 double functionValue = localVolumeTerm(quadPos);
123
124 // Evaluate all shape function values at this point
125 std::vector<FieldVector<double,1> > shapeFunctionValues;
126 localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues);
127
128 // Actually compute the vector entries
129 for (size_t i=0; i<localRhs.size(); i++)
130 localRhs[i] += shapeFunctionValues[i] * functionValue * quad[pt].weight() * integrationElement;
131
132 }
133
134}
136
138// Get the occupation pattern of the stiffness matrix
139template <class FEBasis>
140void getOccupationPattern(const FEBasis& feBasis, MatrixIndexSet& nb)
141{
142 // Total number of grid vertices
143 auto n = feBasis.size();
144
145 nb.resize(n, n);
146
147 // A view on the FE basis on a single element
148 auto localView = feBasis.localView();
149
150 // Loop over all leaf elements
151 for(const auto& e : elements(feBasis.gridView()))
152 {
153 // Bind the local FE basis view to the current element
154 localView.bind(e);
155
156 // There is a matrix entry a_ij if the i-th and j-th vertex are connected in the grid
157 for (size_t i=0; i<localView.tree().size(); i++) {
158
159 for (size_t j=0; j<localView.tree().size(); j++) {
160
161 auto iIdx = localView.index(i);
162 auto jIdx = localView.index(j);
163
164 // Add a nonzero entry to the matrix
165 nb.add(iIdx, jIdx);
166
167 }
168
169 }
170
171 }
172
173}
175
177
178template <class FEBasis, class VolumeTerm>
179void assembleLaplaceMatrix(const FEBasis& feBasis,
180 BCRSMatrix<double>& matrix,
181 BlockVector<double>& rhs,
182 VolumeTerm&& volumeTerm)
183{
184 // Get the grid view from the finite element basis
185 typedef typename FEBasis::GridView GridView;
186 GridView gridView = feBasis.gridView();
187
188 auto localVolumeTerm = localFunction(Functions::makeGridViewFunction(volumeTerm, gridView));
189
190
191
192
193 // MatrixIndexSets store the occupation pattern of a sparse matrix.
194 // They are not particularly efficient, but simple to use.
195 MatrixIndexSet occupationPattern;
196 getOccupationPattern(feBasis, occupationPattern);
197
198 // ... and give it the occupation pattern we want.
199 occupationPattern.exportIdx(matrix);
200
201 // set rhs to correct length -- the total number of basis vectors in the basis
202 rhs.resize(feBasis.size());
203
204 // Set all entries to zero
205 matrix = 0;
206 rhs = 0;
207
208 // A view on the FE basis on a single element
209 auto localView = feBasis.localView();
210
211 // A loop over all elements of the grid
212 for(const auto& e : elements(gridView))
213 {
214
215 // Bind the local FE basis view to the current element
216 localView.bind(e);
217
218 // Now let's get the element stiffness matrix
219 // A dense matrix is used for the element stiffness matrix
220 Matrix<double> elementMatrix;
221 getLocalMatrix(localView, elementMatrix);
222
223 // Add element stiffness matrix onto the global stiffness matrix
224 for (size_t i=0; i<elementMatrix.N(); i++) {
225
226 // The global index of the i-th local degree of freedom of the element 'e'
227 auto row = localView.index(i);
228
229 for (size_t j=0; j<elementMatrix.M(); j++ ) {
230
231 // The global index of the j-th local degree of freedom of the element 'e'
232 auto col = localView.index(j);
233 matrix[row][col] += elementMatrix[i][j];
234
235 }
236
237 }
238
239 // Now get the local contribution to the right-hand side vector
240 BlockVector<double> localRhs;
241 localVolumeTerm.bind(e);
242 getVolumeTerm(localView, localRhs, localVolumeTerm);
243
244 for (size_t i=0; i<localRhs.size(); i++) {
245
246 // The global index of the i-th vertex of the element 'e'
247 auto row = localView.index(i);
248 rhs[row] += localRhs[i];
249
250 }
251
252 }
253
254}
256
257// This method marks all Lagrange nodes on the boundary of the grid.
258// In our problem we want to use them as the Dirichlet nodes.
259// The result can be found in the 'dirichletNodes' variable. There, a bit
260// is set precisely when the corresponding Lagrange node is on the grid boundary.
261//
262// Since interpolating into a vector<bool> is currently not supported,
263// we use a vector<char> which, in contrast to vector<bool>
264// is a real container.
266template <class FEBasis>
267void boundaryTreatment (const FEBasis& feBasis, std::vector<char>& dirichletNodes )
268{
269 dirichletNodes.clear();
270 dirichletNodes.resize(feBasis.size(), false);
271
272 Functions::forEachBoundaryDOF(feBasis, [&] (auto&& index) {
273 dirichletNodes[index] = true;
274 });
275}
277
279auto createMixedGrid()
280{
281 using Grid = Dune::UGGrid<2>;
282 Dune::GridFactory<Grid> factory;
283 for(unsigned int k : Dune::range(9))
284 factory.insertVertex({0.5*(k%3), 0.5*(k/3)});
285 factory.insertElement(Dune::GeometryTypes::cube(2), {0, 1, 3, 4});
286 factory.insertElement(Dune::GeometryTypes::cube(2), {1, 2, 4, 5});
287 factory.insertElement(Dune::GeometryTypes::simplex(2), {3, 4, 6});
288 factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 7, 6});
289 factory.insertElement(Dune::GeometryTypes::simplex(2), {4, 5, 7});
290 factory.insertElement(Dune::GeometryTypes::simplex(2), {5, 8, 7});
291 return factory.createGrid();
292}
294
296int main (int argc, char *argv[]) try
297{
298 // Set up MPI, if available
299 MPIHelper::instance(argc, argv);
301
303 // Generate the grid
305
307 auto grid = createMixedGrid();
308
309 grid->globalRefine(4);
310
311 auto gridView = grid->leafGridView();
312 using GridView = decltype(gridView);
314
316 // Choose a finite element space
318
321 FEBasis feBasis(gridView);
323
325 // Stiffness matrix and right hand side vector
327
329 using VectorType = BlockVector<double>;
330 using MatrixType = BCRSMatrix<double>;
331
332 VectorType rhs;
333 MatrixType stiffnessMatrix;
334
336 // Assemble the system
338
339 std::cout << "Number of DOFs is " << feBasis.dimension() << std::endl;
340
341 auto rightHandSide = [] (const auto& x) { return 10;};
342
343 Dune::Timer timer;
344 assembleLaplaceMatrix(feBasis, stiffnessMatrix, rhs, rightHandSide);
345 std::cout << "Assembling the problem took " << timer.elapsed() << "s" << std::endl;
346
348 // Choose an initial iterate
350 VectorType x(feBasis.size());
351 x = 0;
352
353 // Determine Dirichlet dofs
354 std::vector<char> dirichletNodes;
355 boundaryTreatment(feBasis, dirichletNodes);
356
357
358
359 // Don't trust on non-standard M_PI.
360 auto pi = std::acos(-1.0);
361 auto dirichletValueFunction = [pi](const auto& x){ return std::sin(2*pi*x[0]); };
362
363 // Interpolate dirichlet values at the boundary nodes
364 interpolate(feBasis, x, dirichletValueFunction, dirichletNodes);
365
367 // Incorporate Dirichlet values in a symmetric way
369
370 // Compute residual for non-homogeneous Dirichlet values
371 // stored in x. Since x is zero for non-Dirichlet DOFs,
372 // we can simply multiply by the matrix.
373 stiffnessMatrix.mmv(x, rhs);
374
375 // Change Dirichlet matrix rows and columns to the identity.
376 for (size_t i=0; i<stiffnessMatrix.N(); i++) {
377 if (dirichletNodes[i]) {
378 rhs[i] = x[i];
379 // loop over nonzero matrix entries in current row
380 for (auto&& [entry, idx] : sparseRange(stiffnessMatrix[i]))
381 entry = (i==idx) ? 1.0 : 0.0;
382 }
383 else {
384 // loop over nonzero matrix entries in current row
385 for (auto&& [entry, idx] : sparseRange(stiffnessMatrix[i]))
386 if (dirichletNodes[idx])
387 entry = 0.0;
388 }
389 }
390
392 // Compute solution
394
395 // Technicality: turn the matrix into a linear operator
396 MatrixAdapter<MatrixType,VectorType,VectorType> op(stiffnessMatrix);
397
398 // Sequential incomplete LU decomposition as the preconditioner
399 SeqILDL<MatrixType,VectorType,VectorType> ildl(stiffnessMatrix,1.0);
400
401 // Preconditioned conjugate-gradient solver
402 CGSolver<VectorType> cg(op,
403 ildl, // preconditioner
404 1e-4, // desired residual reduction factor
405 100, // maximum number of iterations
406 2); // verbosity of the solver
407
408 // Object storing some statistics about the solving process
409 InverseOperatorResult statistics;
410
411 // Solve!
412 cg.apply(x, rhs, statistics);
413
415 // Make a discrete function from the FE basis and the coefficient vector
417
418 auto xFunction = Functions::makeDiscreteGlobalBasisFunction<double>(feBasis, x);
419
421 // Write result to VTK file
422 // We need to subsample, because VTK cannot natively display real second-order functions
424 SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(2));
425 vtkWriter.addVertexData(xFunction, VTK::FieldInfo("x", VTK::FieldInfo::Type::scalar, 1));
426 vtkWriter.write("poisson-pq2");
427
428 }
429// Error handling
430 catch (Exception& e) {
431 std::cout << e.what() << std::endl;
432 }
auto makeDiscreteGlobalBasisFunction(B &&basis, V &&vector)
Generate a DiscreteGlobalBasisFunction.
Definition discreteglobalbasisfunction.hh:458
DefaultGlobalBasis< LagrangePreBasis< GV, k, R > > LagrangeBasis
Nodal basis of a scalar k-th-order Lagrangean finite element space.
Definition lagrangebasis.hh:532
void forEachBoundaryDOF(const Basis &basis, F &&f)
Loop over all DOFs on the boundary.
Definition boundarydofs.hh:40
auto elements(const SubDomainGridView< HostGridView > &subDomainGridView)
ADL findable access to element range for a SubDomainGridView.
Definition subdomain.hh:487
Definition monomialset.hh:19
void interpolate(const B &basis, C &&coeff, const F &f, const BV &bv, const NTRE &nodeToRangeEntry)
Interpolate given function in discrete function space.
Definition interpolate.hh:205
std::decay_t< F > makeGridViewFunction(F &&f, const GridView &gridView)
Construct a function modeling GridViewFunction from function and grid view.
Definition gridviewfunction.hh:72