1
2 """Hedge operators modelling electromagnetic phenomena."""
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 from pytools import memoize_method
26
27 import hedge.mesh
28 from hedge.models import TimeDependentOperator
34 """A 3D Maxwell operator with PEC boundaries.
35
36 Field order is [Ex Ey Ez Hx Hy Hz].
37 """
38
39 _default_dimensions = 3
40
41 - def __init__(self, epsilon, mu,
42 flux_type,
43 bdry_flux_type=None,
44 pec_tag=hedge.mesh.TAG_ALL,
45 absorb_tag=hedge.mesh.TAG_NONE,
46 incident_tag=hedge.mesh.TAG_NONE,
47 incident_bc=None, current=None, dimensions=None):
48 """
49 @arg flux_type: can be in [0,1] for anything between central and upwind,
50 or "lf" for Lax-Friedrichs.
51 """
52
53 self.dimensions = dimensions or self._default_dimensions
54
55 space_subset = [True]*self.dimensions + [False]*(3-self.dimensions)
56
57 e_subset = self.get_eh_subset()[0:3]
58 h_subset = self.get_eh_subset()[3:6]
59
60 from hedge.tools import SubsettableCrossProduct
61 self.space_cross_e = SubsettableCrossProduct(
62 op1_subset=space_subset,
63 op2_subset=e_subset,
64 result_subset=h_subset)
65 self.space_cross_h = SubsettableCrossProduct(
66 op1_subset=space_subset,
67 op2_subset=h_subset,
68 result_subset=e_subset)
69
70 from math import sqrt
71
72 self.epsilon = epsilon
73 self.mu = mu
74 self.c = 1/sqrt(mu*epsilon)
75
76 self.Z = sqrt(mu/epsilon)
77 self.Y = 1/self.Z
78
79 self.flux_type = flux_type
80 if bdry_flux_type is None:
81 self.bdry_flux_type = flux_type
82 else:
83 self.bdry_flux_type = bdry_flux_type
84
85 self.pec_tag = pec_tag
86 self.absorb_tag = absorb_tag
87 self.incident_tag = incident_tag
88
89 self.current = current
90 self.incident_bc_data = incident_bc
91
92 - def flux(self, flux_type):
93 from hedge.flux import make_normal, FluxVectorPlaceholder
94 from hedge.tools import join_fields
95
96 normal = make_normal(self.dimensions)
97
98 from hedge.tools import count_subset
99 w = FluxVectorPlaceholder(count_subset(self.get_eh_subset()))
100 e, h = self.split_eh(w)
101
102 if flux_type == "lf":
103 return join_fields(
104
105 1/2*(
106 -1/self.epsilon*self.space_cross_h(normal, h.int-h.ext)
107 -self.c/2*(e.int-e.ext)
108 ),
109
110 1/2*(
111 1/self.mu*self.space_cross_e(normal, e.int-e.ext)
112 -self.c/2*(h.int-h.ext))
113 )
114 elif isinstance(flux_type, (int, float)):
115
116 return join_fields(
117
118 1/self.epsilon*(
119 -1/2*self.space_cross_h(normal,
120 h.int-h.ext
121 -flux_type/self.Z*self.space_cross_e(
122 normal, e.int-e.ext))
123 ),
124
125 1/self.mu*(
126 1/2*self.space_cross_e(normal,
127 e.int-e.ext
128 +flux_type/(self.Y)*self.space_cross_h(
129 normal, h.int-h.ext))
130 ),
131 )
132 else:
133 raise ValueError, "maxwell: invalid flux_type (%s)" % self.flux_type
134
140
141 def h_curl(field):
142 return self.space_cross_h(nabla, field)
143
144 from hedge.optemplate import make_nabla
145 from hedge.tools import join_fields, count_subset
146
147 nabla = make_nabla(self.dimensions)
148
149 if self.current is not None:
150 from hedge.optemplate import make_vector_field
151 j = make_vector_field("j",
152 count_subset(self.get_eh_subset()[:3]))
153 else:
154 j = 0
155
156
157 return join_fields(
158 1/self.epsilon * (j - h_curl(h)),
159 1/self.mu * e_curl(e),
160 )
161
162 from hedge.optemplate import pair_with_boundary, \
163 InverseMassOperator, get_flux_operator, \
164 BoundarizeOperator
165
167 from hedge.tools import count_subset
168 fld_cnt = count_subset(self.get_eh_subset())
169 if w is None:
170 from hedge.optemplate import make_vector_field
171 w = make_vector_field("w", fld_cnt)
172
173 return w
174
183
204
206 e, h = self.split_eh(self.field_placeholder(w))
207
208 from hedge.tools import count_subset
209 fld_cnt = count_subset(self.get_eh_subset())
210
211 if self.incident_bc_data is not None:
212 from hedge.optemplate import make_common_subexpression
213 return make_common_subexpression(
214 -make_vector_field("incident_bc", fld_cnt))
215 else:
216 from hedge.tools import make_obj_array
217 return make_obj_array([0]*fld_cnt)
218
238
239 - def bind(self, discr, **extra_context):
240 from hedge.mesh import check_bc_coverage
241 check_bc_coverage(discr.mesh, [
242 self.pec_tag, self.absorb_tag, self.incident_tag])
243
244 compiled_op_template = discr.compile(self.op_template())
245
246 from hedge.tools import full_to_subset_indices
247 e_indices = full_to_subset_indices(self.get_eh_subset()[0:3])
248 all_indices = full_to_subset_indices(self.get_eh_subset())
249
250 def rhs(t, w):
251 if self.current is not None:
252 j = self.current.volume_interpolant(t, discr)[e_indices]
253 else:
254 j = 0
255
256 if self.incident_bc_data is not None:
257 incident_bc_data = self.incident_bc_data.boundary_interpolant(
258 t, discr, self.incident_tag)[all_indices]
259 else:
260 incident_bc_data = 0
261
262 return compiled_op_template(
263 w=w, j=j, incident_bc=incident_bc_data, **extra_context)
264
265 return rhs
266
268 if discr is None:
269 def zero(): return 0
270 else:
271 def zero(): return discr.volume_zeros()
272
273 from hedge.tools import count_subset
274 e_components = count_subset(self.get_eh_subset()[0:3])
275 h_components = count_subset(self.get_eh_subset()[3:6])
276
277 def default_fld(fld, comp):
278 if fld is None:
279 return [zero() for i in xrange(comp)]
280 else:
281 return fld
282
283 e = default_fld(e, e_components)
284 h = default_fld(h, h_components)
285
286 from hedge.tools import join_fields
287 return join_fields(e, h)
288
289 @memoize_method
291 e_subset = self.get_eh_subset()[0:3]
292 h_subset = self.get_eh_subset()[3:6]
293
294 from hedge.tools import partial_to_all_subset_indices
295 return tuple(partial_to_all_subset_indices(
296 [e_subset, h_subset]))
297
308
310 """Return a 6-tuple of C{bool}s indicating whether field components
311 are to be computed. The fields are numbered in the order specified
312 in the class documentation.
313 """
314 return 6*(True,)
315
317 """Return the largest eigenvalue of Maxwell's equations as a hyperbolic system."""
318 from math import sqrt
319 return 1/sqrt(self.mu*self.epsilon)
320
325 """A 2D TM Maxwell operator with PEC boundaries.
326
327 Field order is [Ez Hx Hy].
328 """
329
330 _default_dimensions = 2
331
333 return (
334 (False,False,True)
335 +
336 (True,True,False)
337 )
338
343 """A 2D TE Maxwell operator.
344
345 Field order is [Ex Ey Hz].
346 """
347
348 _default_dimensions = 2
349
351 return (
352 (True,True,False)
353 +
354 (False,False,True)
355 )
356
358 """A 1D TE Maxwell operator.
359
360 Field order is [Ex Ey Hz].
361 """
362
363 _default_dimensions = 1
364
366 return (
367 (True,True,False)
368 +
369 (False,False,True)
370 )
371
374 """A 1D TE Maxwell operator.
375
376 Field order is [Ey Hz].
377 """
378
379 _default_dimensions = 1
380
382 return (
383 (False,True,False)
384 +
385 (False,False,True)
386 )
387