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. | Basic discrete calculus etc. | ||||||
| """ | """ | ||||||
| from typing import Sequence, Tuple, Optional | from typing import Sequence, Tuple, Optional, Callable | ||||||
|  | 
 | ||||||
| import numpy            # type: ignore | import numpy            # type: ignore | ||||||
| 
 | 
 | ||||||
| from .types import fdfield_t, fdfield_updater_t | 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 |     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 .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, | from .energy import (poynting, poynting_divergence, energy_hstep, energy_estep, | ||||||
|                      delta_energy_h2e, delta_energy_j) |                      delta_energy_h2e, delta_energy_j) | ||||||
| from .boundaries import conducting_boundary | from .boundaries import conducting_boundary | ||||||
|  | |||||||
| @ -7,19 +7,20 @@ PML implementations | |||||||
| """ | """ | ||||||
| # TODO retest pmls! | # 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 | 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' | __author__ = 'Jan Petykiewicz' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def cpml(direction: int, | def cpml_params( | ||||||
|  |         axis: int, | ||||||
|         polarity: int, |         polarity: int, | ||||||
|         dt: float, |         dt: float, | ||||||
|          epsilon: fdfield_t, |  | ||||||
|         thickness: int = 8, |         thickness: int = 8, | ||||||
|         ln_R_per_layer: float = -1.6, |         ln_R_per_layer: float = -1.6, | ||||||
|         epsilon_eff: float = 1, |         epsilon_eff: float = 1, | ||||||
| @ -27,11 +28,10 @@ def cpml(direction: int, | |||||||
|         m: float = 3.5, |         m: float = 3.5, | ||||||
|         ma: float = 1, |         ma: float = 1, | ||||||
|         cfs_alpha: float = 0, |         cfs_alpha: float = 0, | ||||||
|          dtype: numpy.dtype = numpy.float32, |         ) -> Dict[str, Any]: | ||||||
|          ) -> Tuple[Callable, Callable, Dict[str, fdfield_t]]: |  | ||||||
| 
 | 
 | ||||||
|     if direction not in range(3): |     if axis not in range(3): | ||||||
|         raise Exception('Invalid direction: {}'.format(direction)) |         raise Exception('Invalid axis: {}'.format(axis)) | ||||||
| 
 | 
 | ||||||
|     if polarity not in (-1, 1): |     if polarity not in (-1, 1): | ||||||
|         raise Exception('Invalid polarity: {}'.format(polarity)) |         raise Exception('Invalid polarity: {}'.format(polarity)) | ||||||
| @ -45,10 +45,8 @@ def cpml(direction: int, | |||||||
|     sigma_max = -ln_R_per_layer / 2 * (m + 1) |     sigma_max = -ln_R_per_layer / 2 * (m + 1) | ||||||
|     kappa_max = numpy.sqrt(epsilon_eff * mu_eff) |     kappa_max = numpy.sqrt(epsilon_eff * mu_eff) | ||||||
|     alpha_max = cfs_alpha |     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) |     xh = numpy.arange(1, thickness + 1, dtype=float) | ||||||
|     if polarity > 0: |     if polarity > 0: | ||||||
|         xe -= 0.5 |         xe -= 0.5 | ||||||
| @ -59,8 +57,8 @@ def cpml(direction: int, | |||||||
|     else: |     else: | ||||||
|         raise Exception('Bad polarity!') |         raise Exception('Bad polarity!') | ||||||
| 
 | 
 | ||||||
|     expand_slice_l: List[Any] = [None] * 3 |     expand_slice_l: List[Any] = [None, None, None] | ||||||
|     expand_slice_l[direction] = slice(None) |     expand_slice_l[axis] = slice(None) | ||||||
|     expand_slice = tuple(expand_slice_l) |     expand_slice = tuple(expand_slice_l) | ||||||
| 
 | 
 | ||||||
|     def par(x: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]: |     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) |     p0e, p1e, p2e = par(xe) | ||||||
|     p0h, p1h, p2h = par(xh) |     p0h, p1h, p2h = par(xh) | ||||||
| 
 | 
 | ||||||
|     region_list = [slice(None)] * 3 |     region_list = [slice(None), slice(None), slice(None)] | ||||||
|     if polarity < 0: |     if polarity < 0: | ||||||
|         region_list[direction] = slice(None, thickness) |         region_list[axis] = slice(None, thickness) | ||||||
|     elif polarity > 0: |     elif polarity > 0: | ||||||
|         region_list[direction] = slice(-thickness, None) |         region_list[axis] = slice(-thickness, None) | ||||||
|     else: |     else: | ||||||
|         raise Exception('Bad polarity!') |         raise Exception('Bad polarity!') | ||||||
|     region = tuple(region_list) |     region = tuple(region_list) | ||||||
| 
 | 
 | ||||||
|     se = 1 if direction == 1 else -1 |     return { | ||||||
| 
 |         'param_e': (p0e, p1e, p2e), | ||||||
|     # TODO check if epsilon is uniform in pml region? |         'param_h': (p0h, p1h, p2h), | ||||||
|     shape = list(epsilon[0].shape) |         'region': region, | ||||||
|     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], |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     # 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 |  | ||||||
| 
 | 
 | ||||||
|     def pml_h(e: fdfield_t, h: fdfield_t) -> Tuple[fdfield_t, fdfield_t]: | def updates_with_cpml( | ||||||
|         dEv = (numpy.roll(e[v], -1, axis=direction)[region] - e[v][region]) |          cpml_params: Sequence[Sequence[Optional[Dict[str, Any]]]], | ||||||
|         dEu = (numpy.roll(e[u], -1, axis=direction)[region] - e[u][region]) |          dt: float, | ||||||
|         psi_h[0] *= p0h |          dxes: dx_lists_t, | ||||||
|         psi_h[0] += p1h * dEv * p2h |          epsilon: fdfield_t, | ||||||
|         psi_h[1] *= p0h |          *, | ||||||
|         psi_h[1] += p1h * dEu * p2h |          dtype: numpy.dtype = numpy.float32, | ||||||
|         h[u][region] -= se * dt * (psi_h[0] + (p2h - 1) * dEv) |          ) -> Tuple[Callable[[fdfield_t, fdfield_t], None], | ||||||
|         h[v][region] += se * dt * (psi_h[1] + (p2h - 1) * dEu) |                     Callable[[fdfield_t, fdfield_t], None]]: | ||||||
|         return e, h |  | ||||||
| 
 | 
 | ||||||
|     return pml_e, pml_h, fields |     Dfx, Dfy, Dfz = deriv_forward(dxes[1]) | ||||||
|  |     Dbx, Dby, Dbz = deriv_back(dxes[1]) | ||||||
|  | 
 | ||||||
|  |     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]] | ||||||
|  | 
 | ||||||
|  |     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