File: poisson-pq2.py

package info (click to toggle)
dune-functions 2.10.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,544 kB
  • sloc: cpp: 14,241; python: 661; makefile: 3
file content (177 lines) | stat: -rw-r--r-- 5,532 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file AUTHORS.md
# SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception OR LGPL-3.0-or-later

import numpy as np
import scipy.sparse.linalg
from scipy.sparse import lil_matrix
from io import StringIO

import dune.geometry
import dune.grid as grid
import dune.functions as functions

# Compute element stiffness matrix and element load vector
#
# TODO: This assembler loop is very inefficient in terms of run time and should be improved using Python vectorization.
# See discussion at https://gitlab.dune-project.org/staging/dune-functions/-/merge_requests/295 for hints and code pointers.
def localAssembler(localView, volumeTerm):

    # Number of degrees of freedom on this element
    n = len(localView)

    # The grid element
    element = localView.element()

    # The set of shape functions
    localBasis = localView.tree().finiteElement().localBasis

    # Initialize element matrix and load vector
    localA = np.zeros((n,n))
    localB = np.zeros(n)

    # choose a high enough quadrature order
    quadOrder = 4

    # create a quadrature rule and integrate
    quadRule = dune.geometry.quadratureRule(element.type, quadOrder)
    for pt in quadRule:

        # Position of the quadrature point
        quadPos = pt.position

        # The determinant that appears in the integral transformation formula
        integrationElement = element.geometry.integrationElement(quadPos)

        # Evaluate all shape functions (The return value is an array!)
        values = localBasis.evaluateFunction(quadPos)

        # Evaluate the shape function Jacobians on the reference element (array of arrays)
        referenceJacobians = localBasis.evaluateJacobian(quadPos)

        # Transform the reference Jacobians to the actual element
        geometryJacobianInverse = element.geometry.jacobianInverse(quadPos)
        jacobians = [ np.dot(np.array(g)[0], geometryJacobianInverse) for g in referenceJacobians ]

        quadPosGlobal = element.geometry.toGlobal(quadPos)

        for i in range( n ):
            for j in range( n ):
                localA[i,j] += pt.weight * integrationElement * np.dot(jacobians[i], jacobians[j])

            localB[i] += pt.weight * integrationElement * values[i] * volumeTerm(quadPosGlobal)

    return localA, localB


# The assembler for the global stiffness matrix
def assembleLaplaceMatrix(basis, volumeTerm):

    # Total number of degrees of freedom
    n = len(basis)

    # Make empty sparse matrix
    A = lil_matrix((n,n))

    # Make empty rhs vector
    b = np.zeros(n)

    # View on the finite element basis on a single element
    localView = basis.localView()

    # Loop over all grid elements
    grid = basis.gridView
    for element in grid.elements:

        # Bind the localView to the current element
        localView.bind(element)

        # Number of degrees of freedom on the current element
        localN = len(localView)

        # Assemble the local stiffness matrix and load vector
        localA, localb = localAssembler(localView, volumeTerm)

        # Copy the local entries into the global matrix and vector
        for i in range(localN):

            gi = localView.index(i)[0]

            b[gi] += localb[i]

            for j in range(localN):
                gj = localView.index(j)[0]
                A[gi, gj] += localA[i, j]

    # Convert matrix to CSR format
    return A.tocsr(), b

# Mark all degrees of freedom on the grid boundary
#
# This method simply calls the corresponding C++ code.  A more Pythonic solution
# is planned to appear eventually...
def markBoundaryDOFs(basis, vector):
    code="""
    #include<utility>
    #include<functional>
    #include<dune/functions/functionspacebases/boundarydofs.hh>
    template<class Basis, class Vector>
    void run(const Basis& basis, Vector& vector)
    {
      auto vectorBackend = vector.mutable_unchecked();
      Dune::Functions::forEachBoundaryDOF(basis, [&] (auto&& index) {
          vectorBackend[index] = true;
      });
    }
    """
    dune.generator.algorithm.run("run",StringIO(code), basis, vector)


############################  main program  ###################################

# Number of grid elements in one direction
gridSize = 32

# Create a grid of the unit square
grid = grid.structuredGrid([0,0],[1,1],[gridSize,gridSize])

# Create a second-order Lagrange FE basis
basis = functions.defaultGlobalBasis(grid, functions.Lagrange(order=2))

# Load term
rightHandSide = lambda x : 10

# Compute stiffness matrix and rhs vector
A,b = assembleLaplaceMatrix(basis, rightHandSide)

# Determine all coefficients that are on the boundary
isDirichlet = np.zeros(len(basis))
markBoundaryDOFs(basis, isDirichlet)

# The function that implements the Dirichlet values
dirichletValueFunction = lambda x : np.sin(2*np.pi*x[0])

# Get coefficients of a Lagrange-FE approximation of the Dirichlet values
dirichletCoeffs = np.zeros(len(basis))
basis.interpolate(dirichletCoeffs, dirichletValueFunction)

# Integrate Dirichlet conditions into the matrix and rhs vector
rows, cols = A.nonzero()

for i,j in zip(rows, cols):
    if isDirichlet[i]:
        if i==j:
            A[i,j] = 1.0
        else:
            A[i,j] = 0
        b[i] = dirichletCoeffs[i]


# Solve linear system!
x = scipy.sparse.linalg.spsolve(A, b)

# Write result as vtu file
u = basis.asFunction(x)

vtk = grid.vtkWriter(2)
u.addToVTKWriter("sol", vtk, dune.grid.DataType.PointData)
vtk.write("poisson-pq2-result")