1
2 """Operators for Poisson problems."""
3
4 from __future__ import division
5
6 __copyright__ = "Copyright (C) 2007 Andreas Kloeckner"
7
8 __license__ = """
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see U{http://www.gnu.org/licenses/}.
21 """
22
23
24
25
26 import numpy
27 import numpy.linalg as la
28
29 from hedge.models import Operator
30 import hedge.data
31 import hedge.iterative
32 from pytools import memoize_method
38 """Implements the Local Discontinuous Galerkin (LDG) Method for elliptic
39 operators.
40
41 See P. Castillo et al.,
42 Local discontinuous Galerkin methods for elliptic problems",
43 Communications in Numerical Methods in Engineering 18, no. 1 (2002): 69-75.
44 """
45
50 """Initialize the weak Poisson operator.
51
52 @arg flux: Either C{"ip"} or C{"ldg"} to indicate which type of flux is
53 to be used. IP tends to be faster, and is therefore the default.
54 """
55 self.dimensions = dimensions
56 assert isinstance(dimensions, int)
57
58 self.flux_type = flux
59
60
61 if diffusion_tensor is None:
62 diffusion_tensor = hedge.data.ConstantGivenFunction(
63 numpy.eye(dimensions))
64
65 self.diffusion_tensor = diffusion_tensor
66
67 self.dirichlet_bc = dirichlet_bc
68 self.dirichlet_tag = dirichlet_tag
69 self.neumann_bc = neumann_bc
70 self.neumann_tag = neumann_tag
71
72
117
118
120 from hedge.optemplate import Field, pair_with_boundary, get_flux_operator, \
121 make_stiffness_t, InverseMassOperator
122
123 stiff_t = make_stiffness_t(self.dimensions)
124 m_inv = InverseMassOperator()
125
126 u = Field("u")
127
128 fs = self.get_weak_flux_set(self.flux_type)
129
130 flux_u = get_flux_operator(fs.flux_u)
131 flux_u_dbdry = get_flux_operator(fs.flux_u_dbdry)
132 flux_u_nbdry = get_flux_operator(fs.flux_u_nbdry)
133
134 return m_inv * (
135 - (stiff_t * u)
136 + flux_u*u
137 + flux_u_dbdry*pair_with_boundary(u, 0, self.dirichlet_tag)
138 + flux_u_nbdry*pair_with_boundary(u, 0, self.neumann_tag)
139 )
140
142 from hedge.optemplate import make_vector_field, pair_with_boundary, \
143 make_stiffness_t, InverseMassOperator, get_flux_operator
144
145 d = self.dimensions
146 w = make_vector_field("w", 1+d)
147 v = w[1:]
148 dir_bc_w = make_vector_field("dir_bc_w", 1+d)
149 neu_bc_w = make_vector_field("neu_bc_w", 1+d)
150
151 stiff_t = make_stiffness_t(d)
152 m_inv = InverseMassOperator()
153
154 fs = self.get_weak_flux_set(self.flux_type)
155
156 flux_v = get_flux_operator(fs.flux_v)
157 flux_v_dbdry = get_flux_operator(fs.flux_v_dbdry)
158 flux_v_nbdry = get_flux_operator(fs.flux_v_nbdry)
159
160 result = (
161 -numpy.dot(stiff_t, v)
162 + flux_v * w
163 + flux_v_dbdry * pair_with_boundary(w, dir_bc_w, self.dirichlet_tag)
164 + flux_v_nbdry * pair_with_boundary(w, neu_bc_w, self.neumann_tag)
165 )
166
167 if apply_minv:
168 return InverseMassOperator() * result
169 else:
170 return result
171
172 @memoize_method
183
184
215
216 @property
218 return self.discr.default_scalar_type
219
220 @property
224
225
227 return self.grad_c(u=u)
228
229 - def div(self, v, u=None, apply_minv=True):
230 """Compute the divergence of v using an LDG operator.
231
232 The divergence computation is unaffected by the scaling
233 effected by the diffusion tensor.
234
235 @param apply_minv: Bool specifying whether to compute a complete
236 divergence operator. If False, the final application of the inverse
237 mass operator is skipped. This is used in L{op}() in order to reduce
238 the scheme M{M^{-1} S u = f} to M{S u = M f}, so that the mass operator
239 only needs to be applied once, when preparing the right hand side
240 in @L{prepare_rhs}.
241 """
242 from hedge.tools import join_fields
243
244 dim = self.discr.dimensions
245
246 if u is None:
247 u = self.discr.volume_zeros()
248 w = join_fields(u, v)
249
250 dir_bc_w = join_fields(0, [0]*dim)
251 neu_bc_w = join_fields(0, [0]*dim)
252
253 if apply_minv:
254 div_tpl = self.minv_div_c
255 else:
256 div_tpl = self.div_c
257
258 return div_tpl(w=w, dir_bc_w=dir_bc_w, neu_bc_w=neu_bc_w)
259
260 - def op(self, u, apply_minv=False):
261 from hedge.tools import ptwise_dot
262
263
264 if self.poincare_mean_value_hack:
265
266 state_int = self.discr.integral(u)
267
268 mean_state = state_int / self.discr.mesh_volume()
269 m_mean_state = mean_state * self.discr._mass_ones()
270
271 else:
272 m_mean_state = 0
273
274 return self.div(
275 ptwise_dot(2, 1, self.diffusion, self.grad(u)),
276 u, apply_minv=apply_minv) \
277 - m_mean_state
278
279 __call__ = op
280
282 """Prepare the right-hand side for the linear system op(u)=rhs(f).
283
284 In matrix form, LDG looks like this:
285
286 Mv = Cu + g
287 Mf = Av + Bu + h
288
289 where v is the auxiliary vector, u is the argument of the operator, f
290 is the result of the grad operator, g and h are inhom boundary data, and
291 A,B,C are some operator+lifting matrices.
292
293 M f = A Minv(Cu + g) + Bu + h
294
295 so the linear system looks like
296
297 M f = A Minv Cu + A Minv g + Bu + h
298 M f - A Minv g - h = (A Minv C + B)u (*)
299 --------rhs-------
300
301 So the right hand side we're putting together here is really
302
303 M f - A Minv g - h
304
305 Finally, note that the operator application above implements
306 the equation (*) left-multiplied by Minv, so that the
307 right-hand-side becomes
308
309 f - Minv( A Minv g + h)
310 """
311 dim = self.discr.dimensions
312
313 pop = self.poisson_op
314
315 dtag = pop.dirichlet_tag
316 ntag = pop.neumann_tag
317
318 dir_bc_u = pop.dirichlet_bc.boundary_interpolant(self.discr, dtag)
319 vpart = self.grad_bc_c(dir_bc_u=dir_bc_u)
320
321 from hedge.tools import ptwise_dot
322 diff_v = ptwise_dot(2, 1, self.diffusion, vpart)
323
324 def neu_bc_v():
325 return ptwise_dot(2, 1, self.neu_diff,
326 self.neumann_normals*
327 pop.neumann_bc.boundary_interpolant(self.discr, ntag))
328
329 from hedge.tools import join_fields
330 w = join_fields(0, diff_v)
331 dir_bc_w = join_fields(dir_bc_u, [0]*dim)
332 neu_bc_w = join_fields(0, neu_bc_v())
333
334 from hedge.optemplate import MassOperator
335
336 return (MassOperator().apply(self.discr,
337 rhs.volume_interpolant(self.discr))
338 - self.div_c(w=w, dir_bc_w=dir_bc_w, neu_bc_w=neu_bc_w))
339
340
341 - def bind(self, discr):
348