def pml_local_op(self, w): sub_e, sub_h, sub_p, sub_q = self.split_ehpq(w) e_subset = self.get_eh_subset()[0:3] h_subset = self.get_eh_subset()[3:6] dim_subset = (True,) * self.dimensions + (False,) * (3-self.dimensions) def pad_vec(v, subset): result = numpy.zeros((3,), dtype=object) result[numpy.array(subset, dtype=bool)] = v return result from hedge.optemplate import make_vector_field sig = pad_vec( make_vector_field("sigma", self.dimensions), dim_subset) sig_prime = pad_vec( make_vector_field("sigma_prime", self.dimensions), dim_subset) if self.add_decay: tau = pad_vec( make_vector_field("tau", self.dimensions), dim_subset) else: tau = numpy.zeros((3,)) e = pad_vec(sub_e, e_subset) h = pad_vec(sub_h, h_subset) p = pad_vec(sub_p, dim_subset) q = pad_vec(sub_q, dim_subset) rhs = numpy.zeros(12, dtype=object) for mx in range(3): my = (mx+1) % 3 mz = (mx+2) % 3 from hedge.tools.mathematics import levi_civita assert levi_civita((mx,my,mz)) == 1 rhs[mx] += -sig[my]/self.epsilon*(2*e[mx]+p[mx]) - 2*tau[my]/self.epsilon*e[mx] rhs[my] += -sig[mx]/self.epsilon*(2*e[my]+p[my]) - 2*tau[mx]/self.epsilon*e[my] rhs[3+mz] += 1/(self.epsilon*self.mu) * ( sig_prime[mx] * q[mx] - sig_prime[my] * q[my]) rhs[6+mx] += sig[my]/self.epsilon*e[mx] rhs[6+my] += sig[mx]/self.epsilon*e[my] rhs[9+mx] += -sig[mx]/self.epsilon*q[mx] - (e[my] + e[mz]) from hedge.tools import full_to_subset_indices sub_idx = full_to_subset_indices(e_subset+h_subset+dim_subset+dim_subset) return rhs[sub_idx]
def field_placeholder(self, w=None): "A placeholder for u and v." fld_cnt = self.dimensions*2 if w is None: from hedge.optemplate import make_vector_field w = make_vector_field("w", fld_cnt) return w
def local_derivatives(self, w=None): """Template for the spatial derivatives of the relevant components of E and H""" e, h = self.split_eh(self.field_placeholder(w)) def e_curl(field): return self.space_cross_e(nabla, field) def h_curl(field): return self.space_cross_h(nabla, field) from hedge.optemplate import make_nabla from hedge.tools import join_fields, count_subset nabla = make_nabla(self.dimensions) if self.current is not None: from hedge.optemplate import make_vector_field j = make_vector_field("j", count_subset(self.get_eh_subset()[:3])) else: j = 0 # in conservation form: u_t + A u_x = 0 return join_fields( (j - h_curl(h)), e_curl(e) )
def apply_diff_tensor(v): if isinstance(self.diffusion_tensor, numpy.ndarray): sym_diff_tensor = self.diffusion_tensor else: sym_diff_tensor = (make_vector_field( "diffusion", self.dimensions**2) .reshape(self.dimensions, self.dimensions)) return numpy.dot(sym_diff_tensor, v)
def field_placeholder(self, w=None): "A placeholder for E and H." from hedge.tools import count_subset fld_cnt = count_subset(self.get_eh_subset()) if w is None: from hedge.optemplate import make_vector_field w = make_vector_field("w", fld_cnt) return w
def op_template(self, w=None): from hedge.tools import count_subset fld_cnt = count_subset(self.get_eh_subset()) if w is None: from hedge.optemplate import make_vector_field w = make_vector_field("w", fld_cnt+2*self.dimensions) from hedge.tools import join_fields return join_fields( MaxwellOperator.op_template(self, w[:fld_cnt]), numpy.zeros((2*self.dimensions,), dtype=object) ) + self.pml_local_op(w)
def op_template(self): from hedge.mesh import TAG_ALL from hedge.optemplate import make_vector_field, BoundaryPair, \ get_flux_operator, make_nabla, InverseMassOperator nabla = make_nabla(self.dimensions) m_inv = InverseMassOperator() v = make_vector_field("v", self.arg_count) bc = make_vector_field("bc", self.arg_count) local_op_result = 0 idx = 0 for i, i_enabled in enumerate(self.subset): if i_enabled and i < self.dimensions: local_op_result += nabla[i]*v[idx] idx += 1 flux_op = get_flux_operator(self.flux()) return local_op_result - m_inv( flux_op(v) + flux_op(BoundaryPair(v, bc, TAG_ALL)))
def bind_characteristic_velocity(self, discr): from hedge.optemplate.operators import ( ElementwiseMaxOperator) from hedge.optemplate import make_vector_field velocity_vec = make_vector_field("v", self.dimensions) velocity = ElementwiseMaxOperator()( numpy.dot(velocity_vec, velocity_vec)**0.5) from hedge.optemplate import Field compiled = discr.compile(velocity) def do(t, u): return compiled(v=self.advec_v.volume_interpolant(t, discr)) return do
def dirichlet_bc(self, w=None): """ Flux term for dirichlet (displacement) boundary conditions """ u, v = self.split_vars(self.field_placeholder(w)) if self.dirichlet_bc_data is not None: from hedge.optemplate import make_vector_field dir_field = cse( -make_vector_field("dirichlet_bc", 3)) else: dir_field = make_obj_array([0,]*3) from hedge.tools import join_fields return join_fields(dir_field, [0]*3, [0,]*9)
def calculate_piola(self,u=None): from hedge.optemplate import make_nabla from hedge.optemplate import make_vector_field u = make_vector_field('u', 3) nabla = make_nabla(self.dimensions) ij = 0 F = [0,]*9 for i in range(self.dimensions): for j in range(self.dimensions): F[ij] = nabla[i](u[j]) if i == j: F[ij] = F[ij] + 1 ij = ij + 1 return make_obj_array(self.material.stress(F, self.dimensions))
def incident_bc(self, w=None): "Flux terms for incident boundary conditions" # NOTE: Untested for inhomogeneous materials, but would usually be # physically meaningless anyway (are there exceptions to this?) e, h = self.split_eh(self.field_placeholder(w)) if not self.fixed_material: from warnings import warn if self.incident_tag != hedge.mesh.TAG_NONE: warn("Incident boundary conditions assume homogeneous"+ " background material, results may be unphysical") from hedge.tools import count_subset from hedge.tools import join_fields fld_cnt = count_subset(self.get_eh_subset()) if self.incident_bc_data is not None: from hedge.optemplate import make_vector_field inc_field = cse( -make_vector_field("incident_bc", fld_cnt)) else: inc_field = make_obj_array([0]*fld_cnt) return inc_field
def op_template(self): from hedge.tools import join_fields from hedge.optemplate import Field, make_vector_field, BoundaryPair, \ BoundarizeOperator, make_normal, get_flux_operator, \ make_nabla, InverseMassOperator w = make_vector_field("w", self.component_count) e, h, phi = self.split_ehphi(w) rho = Field("rho") # local part ---------------------------------------------------------- nabla = make_nabla(self.maxwell_op.dimensions) # in conservation form: u_t + A u_x = 0 # we're describing the A u_x part, the sign gets reversed # below. max_local_op = join_fields(self.maxwell_op.local_derivatives(w), 0) c = self.maxwell_op.c chi = self.chi hyp_local_operator = max_local_op + join_fields( c * chi * (nabla * phi), 0 * h, c * chi * numpy.dot(nabla, e) - c * chi * rho / self.maxwell_op.epsilon # sign gets reversed below, so this is actually # the decay it advertises to be. + self.phi_decay * phi) # BCs ----------------------------------------------------------------- pec_tag = self.maxwell_op.pec_tag pec_e = BoundarizeOperator(pec_tag)(e) pec_h = BoundarizeOperator(pec_tag)(h) pec_phi = BoundarizeOperator(pec_tag)(phi) pec_n = make_normal(pec_tag, self.maxwell_op.dimensions) bc = "prev" print "HYP CLEAN BC", bc if bc == "char": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 3 / 2 * pec_n * numpy.dot(pec_n, pec_e) + 1 / 2 * pec_phi * pec_n, pec_h, 1 / 2 * (pec_phi + numpy.dot(pec_n, pec_e))) elif bc == "invent": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, pec_phi) elif bc == "munz": # Munz et al pec_bc = join_fields(-pec_e, pec_h, pec_phi - numpy.dot(pec_n, pec_e)) elif bc == "prev": # previous condition pec_bc = join_fields(-pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, -pec_phi) # assemble operator --------------------------------------------------- flux_op = get_flux_operator(self.flux()) return -hyp_local_operator + InverseMassOperator()( flux_op(w) + flux_op(BoundaryPair(w, pec_bc, pec_tag)))
def op_template(self): from hedge.tools import join_fields from hedge.optemplate import Field, make_vector_field, BoundaryPair, \ BoundarizeOperator, make_normal, get_flux_operator, \ make_nabla, InverseMassOperator w = make_vector_field("w", self.component_count) e, h, phi = self.split_ehphi(w) rho = Field("rho") # local part ---------------------------------------------------------- nabla = make_nabla(self.maxwell_op.dimensions) # in conservation form: u_t + A u_x = 0 # we're describing the A u_x part, the sign gets reversed # below. max_local_op = join_fields(self.maxwell_op.local_derivatives(w), 0) c = self.maxwell_op.c chi = self.chi hyp_local_operator = max_local_op + join_fields( c*chi*(nabla*phi), 0*h, c*chi*numpy.dot(nabla, e) - c*chi*rho/self.maxwell_op.epsilon # sign gets reversed below, so this is actually # the decay it advertises to be. + self.phi_decay*phi ) # BCs ----------------------------------------------------------------- pec_tag = self.maxwell_op.pec_tag pec_e = BoundarizeOperator(pec_tag)(e) pec_h = BoundarizeOperator(pec_tag)(h) pec_phi = BoundarizeOperator(pec_tag)(phi) pec_n = make_normal(pec_tag, self.maxwell_op.dimensions) bc = "prev" print "HYP CLEAN BC", bc if bc == "char": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 3/2 * pec_n * numpy.dot(pec_n, pec_e) + 1/2 * pec_phi * pec_n , pec_h, 1/2*(pec_phi+numpy.dot(pec_n, pec_e)) ) elif bc == "invent": # see hedge/doc/maxima/eclean.mac for derivation pec_bc = join_fields( -pec_e + 2 * pec_n * numpy.dot(pec_n, pec_e), pec_h, pec_phi) elif bc == "munz": # Munz et al pec_bc = join_fields( -pec_e, pec_h, pec_phi-numpy.dot(pec_n, pec_e)) elif bc == "prev": # previous condition pec_bc = join_fields( -pec_e +2*pec_n * numpy.dot(pec_n, pec_e), pec_h, -pec_phi) # assemble operator --------------------------------------------------- flux_op = get_flux_operator(self.flux()) return -hyp_local_operator + InverseMassOperator()( flux_op(w) + flux_op(BoundaryPair(w, pec_bc, pec_tag)))
def op_template(self, with_sensor=False): # {{{ operator preliminaries ------------------------------------------ from hedge.optemplate import (Field, BoundaryPair, get_flux_operator, make_stiffness_t, InverseMassOperator, make_vector_field, ElementwiseMaxOperator, BoundarizeOperator) from hedge.tools.symbolic import make_common_subexpression as cse from hedge.optemplate.operators import ( QuadratureGridUpsampler, QuadratureInteriorFacesGridUpsampler) to_quad = QuadratureGridUpsampler("quad") to_if_quad = QuadratureInteriorFacesGridUpsampler("quad") from hedge.tools import join_fields, \ ptwise_dot u = Field("u") v = make_vector_field("v", self.dimensions) c = ElementwiseMaxOperator()(ptwise_dot(1, 1, v, v)) quad_u = cse(to_quad(u)) quad_v = cse(to_quad(v)) w = join_fields(u, v, c) quad_face_w = to_if_quad(w) # }}} # {{{ boundary conditions --------------------------------------------- from hedge.mesh import TAG_ALL bc_c = to_quad(BoundarizeOperator(TAG_ALL)(c)) bc_u = to_quad(Field("bc_u")) bc_v = to_quad(BoundarizeOperator(TAG_ALL)(v)) if self.bc_u_f is "None": bc_w = join_fields(0, bc_v, bc_c) else: bc_w = join_fields(bc_u, bc_v, bc_c) minv_st = make_stiffness_t(self.dimensions) m_inv = InverseMassOperator() flux_op = get_flux_operator(self.flux()) # }}} # {{{ diffusion ------------------------------------------------------- if with_sensor or ( self.diffusion_coeff is not None and self.diffusion_coeff != 0): if self.diffusion_coeff is None: diffusion_coeff = 0 else: diffusion_coeff = self.diffusion_coeff if with_sensor: diffusion_coeff += Field("sensor") from hedge.second_order import SecondDerivativeTarget # strong_form here allows IPDG to reuse the value of grad u. grad_tgt = SecondDerivativeTarget( self.dimensions, strong_form=True, operand=u) self.diffusion_scheme.grad(grad_tgt, bc_getter=None, dirichlet_tags=[], neumann_tags=[]) div_tgt = SecondDerivativeTarget( self.dimensions, strong_form=False, operand=diffusion_coeff*grad_tgt.minv_all) self.diffusion_scheme.div(div_tgt, bc_getter=None, dirichlet_tags=[], neumann_tags=[]) diffusion_part = div_tgt.minv_all else: diffusion_part = 0 # }}} to_quad = QuadratureGridUpsampler("quad") quad_u = cse(to_quad(u)) quad_v = cse(to_quad(v)) return m_inv(numpy.dot(minv_st, cse(quad_v*quad_u)) - (flux_op(quad_face_w) + flux_op(BoundaryPair(quad_face_w, bc_w, TAG_ALL)))) \ + diffusion_part
def op_template(self): from hedge.optemplate import \ make_vector_field, \ BoundaryPair, \ get_flux_operator, \ make_nabla, \ InverseMassOperator, \ BoundarizeOperator d = self.dimensions w = make_vector_field("w", d + 1) u = w[0] v = w[1:] # boundary conditions ------------------------------------------------- from hedge.tools import join_fields # dirichlet BCs ------------------------------------------------------- from hedge.optemplate import make_normal, Field dir_normal = make_normal(self.dirichlet_tag, d) dir_u = BoundarizeOperator(self.dirichlet_tag) * u dir_v = BoundarizeOperator(self.dirichlet_tag) * v if self.dirichlet_bc_f: # FIXME from warnings import warn warn("Inhomogeneous Dirichlet conditions on the wave equation " "are still having issues.") dir_g = Field("dir_bc_u") dir_bc = join_fields(2 * dir_g - dir_u, dir_v) else: dir_bc = join_fields(-dir_u, dir_v) # neumann BCs --------------------------------------------------------- neu_u = BoundarizeOperator(self.neumann_tag) * u neu_v = BoundarizeOperator(self.neumann_tag) * v neu_bc = join_fields(neu_u, -neu_v) # radiation BCs ------------------------------------------------------- from hedge.optemplate import make_normal rad_normal = make_normal(self.radiation_tag, d) rad_u = BoundarizeOperator(self.radiation_tag) * u rad_v = BoundarizeOperator(self.radiation_tag) * v rad_bc = join_fields( 0.5 * (rad_u - self.sign * numpy.dot(rad_normal, rad_v)), 0.5 * rad_normal * (numpy.dot(rad_normal, rad_v) - self.sign * rad_u)) # entire operator ----------------------------------------------------- nabla = make_nabla(d) flux_op = get_flux_operator(self.flux()) from hedge.tools import join_fields result = ( -join_fields(-self.c * numpy.dot(nabla, v), -self.c * (nabla * u)) + InverseMassOperator() * (flux_op(w) + flux_op(BoundaryPair(w, dir_bc, self.dirichlet_tag)) + flux_op(BoundaryPair(w, neu_bc, self.neumann_tag)) + flux_op( BoundaryPair(w, rad_bc, self.radiation_tag)))) if self.source_f is not None: result[0] += Field("source_u") return result
def op_template(self, with_sensor=False): from hedge.optemplate import \ Field, \ make_vector_field, \ BoundaryPair, \ get_flux_operator, \ make_nabla, \ InverseMassOperator, \ BoundarizeOperator d = self.dimensions w = make_vector_field("w", d + 1) u = w[0] v = w[1:] from hedge.tools import join_fields c = Field("c") flux_w = join_fields(c, w) # {{{ boundary conditions from hedge.flux import make_normal normal = make_normal(d) from hedge.tools import join_fields # Dirichlet dir_c = BoundarizeOperator(self.dirichlet_tag) * c dir_u = BoundarizeOperator(self.dirichlet_tag) * u dir_v = BoundarizeOperator(self.dirichlet_tag) * v dir_bc = join_fields(dir_c, -dir_u, dir_v) # Neumann neu_c = BoundarizeOperator(self.neumann_tag) * c neu_u = BoundarizeOperator(self.neumann_tag) * u neu_v = BoundarizeOperator(self.neumann_tag) * v neu_bc = join_fields(neu_c, neu_u, -neu_v) # Radiation from hedge.optemplate import make_normal rad_normal = make_normal(self.radiation_tag, d) rad_c = BoundarizeOperator(self.radiation_tag) * c rad_u = BoundarizeOperator(self.radiation_tag) * u rad_v = BoundarizeOperator(self.radiation_tag) * v rad_bc = join_fields( rad_c, 0.5 * (rad_u - self.time_sign * numpy.dot(rad_normal, rad_v)), 0.5 * rad_normal * (numpy.dot(rad_normal, rad_v) - self.time_sign * rad_u)) # }}} # {{{ diffusion ------------------------------------------------------- from pytools.obj_array import with_object_array_or_scalar def make_diffusion(arg): if with_sensor or (self.diffusion_coeff is not None and self.diffusion_coeff != 0): if self.diffusion_coeff is None: diffusion_coeff = 0 else: diffusion_coeff = self.diffusion_coeff if with_sensor: diffusion_coeff += Field("sensor") from hedge.second_order import SecondDerivativeTarget # strong_form here allows the reuse the value of grad u. grad_tgt = SecondDerivativeTarget( self.dimensions, strong_form=True, operand=arg) self.diffusion_scheme.grad( grad_tgt, bc_getter=None, dirichlet_tags=[], neumann_tags=[]) div_tgt = SecondDerivativeTarget( self.dimensions, strong_form=False, operand=diffusion_coeff * grad_tgt.minv_all) self.diffusion_scheme.div( div_tgt, bc_getter=None, dirichlet_tags=[], neumann_tags=[]) return div_tgt.minv_all else: return 0 # }}} # entire operator ----------------------------------------------------- nabla = make_nabla(d) flux_op = get_flux_operator(self.flux()) return ( -join_fields( -self.time_sign * c * numpy.dot(nabla, v) - make_diffusion(u), -self.time_sign * c * (nabla * u) - with_object_array_or_scalar(make_diffusion, v)) + InverseMassOperator() * (flux_op(flux_w) + flux_op( BoundaryPair(flux_w, dir_bc, self.dirichlet_tag)) + flux_op( BoundaryPair(flux_w, neu_bc, self.neumann_tag)) + flux_op( BoundaryPair(flux_w, rad_bc, self.radiation_tag))))
def f_bar(self): from hedge.optemplate import make_vector_field return make_vector_field("f_bar", len(self.method))