1
2
3
4
5import numpy
6import scipy.sparse.linalg
7from scipy.sparse import lil_matrix
8
9import dune.geometry
10import dune.grid as grid
11import dune.functions as functions
12
13
14
15
16def getLocalMatrix(localView):
17
18
19 element = localView.element()
20
21
22 n = len(localView)
23 elementMatrix = numpy.zeros((n,n))
24
25
26 fluxLocalFiniteElement = localView.tree().child(0).finiteElement()
27 pressureLocalFiniteElement = localView.tree().child(1).finiteElement()
28
29
30 localFluxBasis = fluxLocalFiniteElement.localBasis
31 localPressureBasis = pressureLocalFiniteElement.localBasis
32
33 nFlux = len(fluxLocalFiniteElement)
34 nPressure = len(pressureLocalFiniteElement)
35
36
37 fluxOrder = element.mydimension * fluxLocalFiniteElement.localBasis.order
38 pressureOrder = element.mydimension * pressureLocalFiniteElement.localBasis.order
39 quadOrder = numpy.max((2*fluxOrder, (fluxOrder-1)+pressureOrder))
40
41 quadRule = dune.geometry.quadratureRule(element.type, quadOrder)
42
43
44 for quadPoint in quadRule:
45
46
47 quadPos = quadPoint.position
48
49
50 geometryJacobianInverse = element.geometry.jacobianInverse(quadPos)
51
52
53 integrationElement = element.geometry.integrationElement(quadPos)
54
55
56
57
58
59
60 fluxValues = localFluxBasis.evaluateFunction(quadPos)
61
62
63 fluxReferenceJacobians = localFluxBasis.evaluateJacobian(quadPos)
64
65 fluxDivergence = numpy.zeros(nFlux)
66
67
68
69 for i in range(nFlux):
70 fluxDivergence[i] = numpy.trace(numpy.array(fluxReferenceJacobians[i]) * geometryJacobianInverse)
71
72
73
74
75
76
77 pressureValues = localPressureBasis.evaluateFunction(quadPos)
78
79
80
81
82
83 for i in range(nFlux):
84
85 row = localView.tree().child(0).localIndex(i)
86
87 for j in range(nFlux):
88
89 col = localView.tree().child(0).localIndex(j)
90
91
92 elementMatrix[row,col] += numpy.dot(fluxValues[i], fluxValues[j]) * quadPoint.weight * integrationElement
93
94
95
96
97
98 for i in range(nFlux):
99
100 fluxIndex = localView.tree().child(0).localIndex(i)
101
102 for j in range(nPressure):
103
104 pressureIndex = localView.tree().child(1).localIndex(j)
105
106
107 tmp = (fluxDivergence[i] * pressureValues[j][0]) * quadPoint.weight * integrationElement
108
109
110 elementMatrix[fluxIndex, pressureIndex] += tmp
111 elementMatrix[pressureIndex, fluxIndex] += tmp
112
113 return elementMatrix
114
115
116
117
118
119def getVolumeTerm(localView, localVolumeTerm):
120
121
122 element = localView.element()
123
124
125 localRhs = numpy.zeros(len(localView))
126
127
128
129 pressureLocalFiniteElement = localView.tree().child(1).finiteElement()
130
131
132 quadOrder = 2*element.mydimension*pressureLocalFiniteElement.localBasis.order
133 quadRule = dune.geometry.quadratureRule(element.type, quadOrder)
134
135 nPressure = len(pressureLocalFiniteElement)
136
137
138 for quadPoint in quadRule:
139
140
141 quadPos = quadPoint.position
142
143
144 integrationElement = element.geometry.integrationElement(quadPos)
145
146
147 functionValue = localVolumeTerm(quadPos)
148
149
150 pressureValues = pressureLocalFiniteElement.localBasis.evaluateFunction(quadPos)
151
152
153 for j in range(nPressure):
154 pressureIndex = localView.tree().child(1).localIndex(j)
155 localRhs[pressureIndex] += - pressureValues[j][0] * functionValue * quadPoint.weight * integrationElement
156
157 return localRhs
158
159
160
161
162
163def assembleMixedPoissonMatrix(basis):
164
165
166 n = len(basis)
167
168
169 stiffnessMatrix = lil_matrix( (n,n) )
170
171
172 localView = basis.localView()
173
174
175 for element in basis.gridView.elements:
176
177
178 localView.bind(element)
179
180
181 elementMatrix = getLocalMatrix(localView)
182
183
184 for i in range(len(localView)):
185
186
187 row = localView.index(i)[0]
188
189 for j in range(len(localView)):
190
191
192 col = localView.index(j)[0]
193 stiffnessMatrix[row,col] += elementMatrix[i, j];
194
195
196 return stiffnessMatrix.tocsr()
197
198
199
200
201
202def assembleMixedPoissonRhs(basis, volumeTerm):
203
204
205 pressureBasis = functions.subspaceBasis(basis, 1)
206
207
208 volumeTermCoeff = numpy.zeros(len(basis))
209 pressureBasis.interpolate(volumeTermCoeff, volumeTerm)
210 volumeTermGF = pressureBasis.asFunction(volumeTermCoeff)
211
212
213 localVolumeTerm = volumeTermGF.localFunction()
214
215
216 b = numpy.zeros(len(basis))
217
218
219 localView = basis.localView()
220
221
222 for element in basis.gridView.elements:
223
224
225 localView.bind(element)
226
227
228 localVolumeTerm.bind(element)
229 localRhs = getVolumeTerm(localView, localVolumeTerm)
230
231
232 for i in range(len(localRhs)):
233
234 row = localView.index(i)[0]
235 b[row] += localRhs[i]
236
237 return b
238
239
240
241
242
243
244
245
246
247def markBoundaryDOFsByIndicator(basis, vector, indicator):
248 from io import StringIO
249 code="""
250 #include<utility>
251 #include<functional>
252 #include<dune/common/fvector.hh>
253 #include<dune/functions/functionspacebases/boundarydofs.hh>
254 template<class Basis, class Vector, class Indicator>
255 void run(const Basis& basis, Vector& vector, const Indicator& indicator)
256 {
257 auto vectorBackend = vector.mutable_unchecked();
258 Dune::Functions::forEachBoundaryDOF(basis, [&] (auto&& localIndex, const auto& localView, const auto& intersection) {
259 if (indicator(intersection.geometry().center()).template cast<bool>())
260 vectorBackend[localView.index(localIndex)] = true;
261 });
262 }
263 """
264 dune.generator.algorithm.run("run",StringIO(code), basis, vector, indicator)
265
266
267
268
269
270
271
272
273def incorporateEssentialConstraints(A, b, isConstrained, x):
274 b -= A*(x*isConstrained)
275 rows, cols = A.nonzero()
276 for i,j in zip(rows, cols):
277 if isConstrained[i] or isConstrained[j]:
278 A[i,j] = 0
279 for i in range(len(b)):
280 if isConstrained[i]:
281 A[i,i] = 1
282 b[i] = x[i]
283
284
285
286
287
288
289
290upperRight = [1, 1]
291elements = [50, 50]
292
293
294gridView = grid.structuredGrid([0,0],upperRight,elements)
295
296
297
298
299
300
301basis = functions.defaultGlobalBasis(gridView,
302 functions.Composite(functions.RaviartThomas(order=0),
303 functions.Lagrange(order=0)))
304
305fluxBasis = functions.subspaceBasis(basis, 0);
306pressureBasis = functions.subspaceBasis(basis, 1);
307
308
309
310
311stiffnessMatrix = assembleMixedPoissonMatrix(basis)
312
313
314volumeTerm = lambda x : 2
315
316b = assembleMixedPoissonRhs(basis, volumeTerm)
317
318
319
320
321fluxDirichletIndicator = lambda x : 1.*((x[1] > upperRight[1] - 1e-8) or (x[1] < 1e-8))
322
323isDirichlet = numpy.zeros(len(basis))
324
325
326
327markBoundaryDOFsByIndicator(fluxBasis, isDirichlet, fluxDirichletIndicator);
328
329
330
336
337
338normal = lambda x : numpy.array([0.,1.]) if numpy.abs(x[1]-1) < 1e-8 else numpy.array([0., -1.])
339fluxDirichletValues = lambda x : numpy.sin(2.*numpy.pi*x[0]) * normal(x)
340
341
342fluxDirichletCoefficients = numpy.zeros(len(basis))
343fluxBasis.interpolate(fluxDirichletCoefficients, fluxDirichletValues);
344
345
346
347
348
349
350incorporateEssentialConstraints(stiffnessMatrix, b, isDirichlet, fluxDirichletCoefficients)
351
352
353
354
355
356
357x = scipy.sparse.linalg.spsolve(stiffnessMatrix, b)
358
359
360
361
362
363
364
365
366
367vtkWriter = gridView.vtkWriter(subsampling=2)
368
369fluxFunction = fluxBasis.asFunction(x)
370fluxFunction.addToVTKWriter("flux", vtkWriter, grid.DataType.PointVector)
371
372pressureFunction = pressureBasis.asFunction(x)
373pressureFunction.addToVTKWriter("pressure", vtkWriter, grid.DataType.PointData)
374
375vtkWriter.write("poisson-mfem-result")
376