fix fdtd pmls
integrate them into the update operations
This commit is contained in:
		
							parent
							
								
									01b4971388
								
							
						
					
					
						commit
						c0bbc1f46d
					
				| @ -3,7 +3,8 @@ Math functions for finite difference simulations | ||||
| 
 | ||||
| Basic discrete calculus etc. | ||||
| """ | ||||
| from typing import Sequence, Tuple, Optional | ||||
| from typing import Sequence, Tuple, Optional, Callable | ||||
| 
 | ||||
| import numpy            # type: ignore | ||||
| 
 | ||||
| from .types import fdfield_t, fdfield_updater_t | ||||
| @ -109,3 +110,23 @@ def curl_back(dx_h: Optional[Sequence[numpy.ndarray]] = None) -> fdfield_updater | ||||
|     return ch_fun | ||||
| 
 | ||||
| 
 | ||||
| def curl_forward_parts(dx_e: Optional[Sequence[numpy.ndarray]] = None) -> Callable: | ||||
|     Dx, Dy, Dz = deriv_forward(dx_e) | ||||
| 
 | ||||
|     def mkparts_fwd(e: fdfield_t) -> Tuple[Tuple[fdfield_t, ...]]: | ||||
|         return ((-Dz(e[1]),  Dy(e[2])), | ||||
|                 ( Dz(e[0]), -Dx(e[2])), | ||||
|                 (-Dy(e[0]),  Dx(e[1]))) | ||||
| 
 | ||||
|     return mkparts_fwd | ||||
| 
 | ||||
| 
 | ||||
| def curl_back_parts(dx_h: Optional[Sequence[numpy.ndarray]] = None) -> Callable: | ||||
|     Dx, Dy, Dz = deriv_back(dx_e) | ||||
| 
 | ||||
|     def mkparts_back(h: fdfield_t) -> Tuple[Tuple[fdfield_t, ...]]: | ||||
|         return ((-Dz(h[1]),  Dy(h[2])), | ||||
|                 ( Dz(h[0]), -Dx(h[2])), | ||||
|                 (-Dy(h[0]),  Dx(h[1]))) | ||||
| 
 | ||||
|     return mkparts_back | ||||
|  | ||||
| @ -160,7 +160,7 @@ Boundary conditions | ||||
| """ | ||||
| 
 | ||||
| from .base import maxwell_e, maxwell_h | ||||
| from .pml import cpml | ||||
| from .pml import cpml_params, updates_with_cpml | ||||
| from .energy import (poynting, poynting_divergence, energy_hstep, energy_estep, | ||||
|                      delta_energy_h2e, delta_energy_j) | ||||
| from .boundaries import conducting_boundary | ||||
|  | ||||
| @ -7,31 +7,31 @@ PML implementations | ||||
| """ | ||||
| # TODO retest pmls! | ||||
| 
 | ||||
| from typing import List, Callable, Tuple, Dict, Any | ||||
| from typing import List, Callable, Tuple, Dict, Sequence, Any, Optional | ||||
| import numpy            # type: ignore | ||||
| 
 | ||||
| from ..fdmath import fdfield_t | ||||
| from ..fdmath import fdfield_t, dx_lists_t | ||||
| from ..fdmath.functional import deriv_forward, deriv_back | ||||
| 
 | ||||
| 
 | ||||
| __author__ = 'Jan Petykiewicz' | ||||
| 
 | ||||
| 
 | ||||
| def cpml(direction: int, | ||||
|          polarity: int, | ||||
|          dt: float, | ||||
|          epsilon: fdfield_t, | ||||
|          thickness: int = 8, | ||||
|          ln_R_per_layer: float = -1.6, | ||||
|          epsilon_eff: float = 1, | ||||
|          mu_eff: float = 1, | ||||
|          m: float = 3.5, | ||||
|          ma: float = 1, | ||||
|          cfs_alpha: float = 0, | ||||
|          dtype: numpy.dtype = numpy.float32, | ||||
|          ) -> Tuple[Callable, Callable, Dict[str, fdfield_t]]: | ||||
| def cpml_params( | ||||
|         axis: int, | ||||
|         polarity: int, | ||||
|         dt: float, | ||||
|         thickness: int = 8, | ||||
|         ln_R_per_layer: float = -1.6, | ||||
|         epsilon_eff: float = 1, | ||||
|         mu_eff: float = 1, | ||||
|         m: float = 3.5, | ||||
|         ma: float = 1, | ||||
|         cfs_alpha: float = 0, | ||||
|         ) -> Dict[str, Any]: | ||||
| 
 | ||||
|     if direction not in range(3): | ||||
|         raise Exception('Invalid direction: {}'.format(direction)) | ||||
|     if axis not in range(3): | ||||
|         raise Exception('Invalid axis: {}'.format(axis)) | ||||
| 
 | ||||
|     if polarity not in (-1, 1): | ||||
|         raise Exception('Invalid polarity: {}'.format(polarity)) | ||||
| @ -45,10 +45,8 @@ def cpml(direction: int, | ||||
|     sigma_max = -ln_R_per_layer / 2 * (m + 1) | ||||
|     kappa_max = numpy.sqrt(epsilon_eff * mu_eff) | ||||
|     alpha_max = cfs_alpha | ||||
|     transverse = numpy.delete(range(3), direction) | ||||
|     u, v = transverse | ||||
| 
 | ||||
|     xe = numpy.arange(1, thickness + 1, dtype=float) | ||||
|     xe = numpy.arange(1, thickness + 1, dtype=float)        # TODO: pass in dtype? | ||||
|     xh = numpy.arange(1, thickness + 1, dtype=float) | ||||
|     if polarity > 0: | ||||
|         xe -= 0.5 | ||||
| @ -59,8 +57,8 @@ def cpml(direction: int, | ||||
|     else: | ||||
|         raise Exception('Bad polarity!') | ||||
| 
 | ||||
|     expand_slice_l: List[Any] = [None] * 3 | ||||
|     expand_slice_l[direction] = slice(None) | ||||
|     expand_slice_l: List[Any] = [None, None, None] | ||||
|     expand_slice_l[axis] = slice(None) | ||||
|     expand_slice = tuple(expand_slice_l) | ||||
| 
 | ||||
|     def par(x: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]: | ||||
| @ -76,52 +74,145 @@ def cpml(direction: int, | ||||
|     p0e, p1e, p2e = par(xe) | ||||
|     p0h, p1h, p2h = par(xh) | ||||
| 
 | ||||
|     region_list = [slice(None)] * 3 | ||||
|     region_list = [slice(None), slice(None), slice(None)] | ||||
|     if polarity < 0: | ||||
|         region_list[direction] = slice(None, thickness) | ||||
|         region_list[axis] = slice(None, thickness) | ||||
|     elif polarity > 0: | ||||
|         region_list[direction] = slice(-thickness, None) | ||||
|         region_list[axis] = slice(-thickness, None) | ||||
|     else: | ||||
|         raise Exception('Bad polarity!') | ||||
|     region = tuple(region_list) | ||||
| 
 | ||||
|     se = 1 if direction == 1 else -1 | ||||
|     return { | ||||
|         'param_e': (p0e, p1e, p2e), | ||||
|         'param_h': (p0h, p1h, p2h), | ||||
|         'region': region, | ||||
|         } | ||||
| 
 | ||||
|     # TODO check if epsilon is uniform in pml region? | ||||
|     shape = list(epsilon[0].shape) | ||||
|     shape[direction] = thickness | ||||
|     psi_e = [numpy.zeros(shape, dtype=dtype), numpy.zeros(shape, dtype=dtype)] | ||||
|     psi_h = [numpy.zeros(shape, dtype=dtype), numpy.zeros(shape, dtype=dtype)] | ||||
| 
 | ||||
|     fields = { | ||||
|         'psi_e_u': psi_e[0], | ||||
|         'psi_e_v': psi_e[1], | ||||
|         'psi_h_u': psi_h[0], | ||||
|         'psi_h_v': psi_h[1], | ||||
|     } | ||||
| def updates_with_cpml( | ||||
|          cpml_params: Sequence[Sequence[Optional[Dict[str, Any]]]], | ||||
|          dt: float, | ||||
|          dxes: dx_lists_t, | ||||
|          epsilon: fdfield_t, | ||||
|          *, | ||||
|          dtype: numpy.dtype = numpy.float32, | ||||
|          ) -> Tuple[Callable[[fdfield_t, fdfield_t], None], | ||||
|                     Callable[[fdfield_t, fdfield_t], None]]: | ||||
| 
 | ||||
|     # Note that this is kinda slow -- would be faster to reuse dHv*p2h for the original | ||||
|     #  H update, but then you have multiple arrays and a monolithic (field + pml) update operation | ||||
|     def pml_e(e: fdfield_t, h: fdfield_t, epsilon: fdfield_t) -> Tuple[fdfield_t, fdfield_t]: | ||||
|         dHv = h[v][region] - numpy.roll(h[v], 1, axis=direction)[region] | ||||
|         dHu = h[u][region] - numpy.roll(h[u], 1, axis=direction)[region] | ||||
|         psi_e[0] *= p0e | ||||
|         psi_e[0] += p1e * dHv * p2e | ||||
|         psi_e[1] *= p0e | ||||
|         psi_e[1] += p1e * dHu * p2e | ||||
|         e[u][region] += se * dt / epsilon[u][region] * (psi_e[0] + (p2e - 1) * dHv) | ||||
|         e[v][region] -= se * dt / epsilon[v][region] * (psi_e[1] + (p2e - 1) * dHu) | ||||
|         return e, h | ||||
|     Dfx, Dfy, Dfz = deriv_forward(dxes[1]) | ||||
|     Dbx, Dby, Dbz = deriv_back(dxes[1]) | ||||
| 
 | ||||
|     def pml_h(e: fdfield_t, h: fdfield_t) -> Tuple[fdfield_t, fdfield_t]: | ||||
|         dEv = (numpy.roll(e[v], -1, axis=direction)[region] - e[v][region]) | ||||
|         dEu = (numpy.roll(e[u], -1, axis=direction)[region] - e[u][region]) | ||||
|         psi_h[0] *= p0h | ||||
|         psi_h[0] += p1h * dEv * p2h | ||||
|         psi_h[1] *= p0h | ||||
|         psi_h[1] += p1h * dEu * p2h | ||||
|         h[u][region] -= se * dt * (psi_h[0] + (p2h - 1) * dEv) | ||||
|         h[v][region] += se * dt * (psi_h[1] + (p2h - 1) * dEu) | ||||
|         return e, h | ||||
|     psi_E = [[None, None], [None, None], [None, None]] | ||||
|     psi_H = [[None, None], [None, None], [None, None]] | ||||
|     params_E = [[None, None], [None, None], [None, None]] | ||||
|     params_H = [[None, None], [None, None], [None, None]] | ||||
| 
 | ||||
|     return pml_e, pml_h, fields | ||||
|     for axis in range(3): | ||||
|         for pp, polarity in enumerate((-1, 1)): | ||||
|             if cpml_params[axis][pp] is None: | ||||
|                 psi_E[axis][pp] = (None, None) | ||||
|                 psi_H[axis][pp] = (None, None) | ||||
|                 continue | ||||
| 
 | ||||
|             cpml_param = cpml_params[axis][pp] | ||||
| 
 | ||||
|             region = cpml_param['region'] | ||||
|             region_shape = epsilon[0][region].shape | ||||
| 
 | ||||
|             psi_E[axis][pp] = (numpy.zeros(region_shape, dtype=dtype), | ||||
|                                     numpy.zeros(region_shape, dtype=dtype)) | ||||
|             psi_H[axis][pp] = (numpy.zeros(region_shape, dtype=dtype), | ||||
|                                     numpy.zeros(region_shape, dtype=dtype)) | ||||
|             params_E[axis][pp] = cpml_param['param_e'] + (region,) | ||||
|             params_H[axis][pp] = cpml_param['param_h'] + (region,) | ||||
| 
 | ||||
| 
 | ||||
|     pE = numpy.empty_like(epsilon, dtype=dtype) | ||||
|     pH = numpy.empty_like(epsilon, dtype=dtype) | ||||
| 
 | ||||
|     def update_E(e: fdfield_t, h: fdfield_t, epsilon: fdfield_t) -> None: | ||||
|         dyHx = Dby(h[0]) | ||||
|         dzHx = Dbz(h[0]) | ||||
|         dxHy = Dbx(h[1]) | ||||
|         dzHy = Dbz(h[1]) | ||||
|         dxHz = Dbx(h[2]) | ||||
|         dyHz = Dby(h[2]) | ||||
| 
 | ||||
|         dH = ((dxHy, dxHz), | ||||
|               (dyHx, dyHz), | ||||
|               (dzHx, dzHy)) | ||||
| 
 | ||||
|         pE.fill(0) | ||||
| 
 | ||||
|         for axis in range(3): | ||||
|             se = (-1, 1, -1)[axis] | ||||
|             transverse = numpy.delete(range(3), axis) | ||||
|             u, v = transverse | ||||
|             dHu, dHv = dH[axis] | ||||
| 
 | ||||
|             for pp in range(2): | ||||
|                 psi_Eu, psi_Ev = psi_E[axis][pp] | ||||
| 
 | ||||
|                 if psi_Eu is None: | ||||
|                     # No pml in this direction | ||||
|                     continue | ||||
| 
 | ||||
|                 p0e, p1e, p2e, region = params_E[axis][pp] | ||||
| 
 | ||||
|                 dHu[region] *= p2e | ||||
|                 dHv[region] *= p2e | ||||
|                 psi_Eu *= p0e | ||||
|                 psi_Ev *= p0e | ||||
|                 psi_Eu += p1e * dHv[region]    # note reversed u,v mapping | ||||
|                 psi_Ev += p1e * dHu[region] | ||||
|                 pE[u][region] += +se * psi_Eu | ||||
|                 pE[v][region] += -se * psi_Ev | ||||
| 
 | ||||
|         e[0] += dt / epsilon[0] * (dyHz - dzHy + pE[0]) | ||||
|         e[1] += dt / epsilon[1] * (dzHx - dxHz + pE[1]) | ||||
|         e[2] += dt / epsilon[2] * (dxHy - dyHx + pE[2]) | ||||
| 
 | ||||
| 
 | ||||
|     def update_H(e: fdfield_t, h: fdfield_t, mu: fdfield_t = (1, 1, 1)) -> None: | ||||
|         dyEx = Dfy(e[0]) | ||||
|         dzEx = Dfz(e[0]) | ||||
|         dxEy = Dfx(e[1]) | ||||
|         dzEy = Dfz(e[1]) | ||||
|         dxEz = Dfx(e[2]) | ||||
|         dyEz = Dfy(e[2]) | ||||
| 
 | ||||
|         dE = ((dxEy, dxEz), | ||||
|               (dyEx, dyEz), | ||||
|               (dzEx, dzEy)) | ||||
| 
 | ||||
|         pH.fill(0) | ||||
| 
 | ||||
|         for axis in range(3): | ||||
|             se = (-1, 1, -1)[axis] | ||||
|             transverse = numpy.delete(range(3), axis) | ||||
|             u, v = transverse | ||||
|             dEu, dEv = dE[axis] | ||||
| 
 | ||||
|             for pp in range(2): | ||||
|                 psi_Hu, psi_Hv = psi_H[axis][pp] | ||||
| 
 | ||||
|                 if psi_Hu is None: | ||||
|                     # No pml here | ||||
|                     continue | ||||
| 
 | ||||
|                 p0h, p1h, p2h, region = params_H[axis][pp] | ||||
| 
 | ||||
|                 dEu[region] *= p2h      # modifies d_E_ | ||||
|                 dEv[region] *= p2h | ||||
|                 psi_Hu *= p0h | ||||
|                 psi_Hv *= p0h | ||||
|                 psi_Hu += p1h * dEv[region]    # note reversed u,v mapping | ||||
|                 psi_Hv += p1h * dEu[region] | ||||
|                 pH[u][region] += +se * psi_Hu | ||||
|                 pH[v][region] += -se * psi_Hv | ||||
| 
 | ||||
|         h[0] -= dt / mu[0] * (dyEz - dzEy + pH[0]) | ||||
|         h[1] -= dt / mu[1] * (dzEx - dxEz + pH[1]) | ||||
|         h[2] -= dt / mu[2] * (dxEy - dyEx + pH[2]) | ||||
|     return update_E, update_H | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user