def test_poisson_2d_unity_dielectric(): x = np.linspace(0, 5, 11) y = np.linspace(0, 5, 11) X, Y = np.meshgrid(x, y, indexing='ij') er = np.ones_like(X[1:, 1:]) v0 = 10 V = fdm.poisson_2d(X, Y, v_left=v0, conv=1e-7) Ver = fdm.poisson_2d(X, Y, dielectric=er, v_left=v0, conv=1e-7) assert V == approx(Ver)
def test_poisson_2d_dielectric(): w = 2.0 h = 1.0 x = np.linspace(0, w, 41) y = np.linspace(0, h, 21) X, Y = np.meshgrid(x, y, indexing='ij') bc = {'v_top': 10, 'v_left': 5, 'v_right': -2, 'v_bottom': -4} er = np.ones_like(X)[:-1, :-1] V1 = fdm.poisson_2d(X, Y, **bc, conv=1e-3) V2 = fdm.poisson_2d(X, Y, **bc, dielectric=er, conv=1e-3) assert V1 == approx(V2)
def test_poisson_2d(): w = 2.0 h = 1.0 x = np.linspace(0, w, 101) y = np.linspace(0, h, 51) X, Y = np.meshgrid(x, y, indexing='ij') bc = {'v_top': 10, 'v_left': 5, 'v_right': -2, 'v_bottom': -4} V = fdm.poisson_2d(X, Y, **bc, conv=1e-5) Va = fdm.trough_analytical(X, Y, **bc) # Exclude boundaries due to analytical error at corners assert V[1:-1, 1:-1] == approx(Va[1:-1, 1:-1], abs=0.1)
def test_poisson_2d_bc_slice(): x = np.linspace(0, 1, 5) y = np.linspace(0, 1, 5) X, Y = np.meshgrid(x, y, indexing='ij') vbc = 16 bc_val = np.zeros_like(X) bc_val[1:4, 2] = vbc bc_bool = bc_val > 0 bc = (bc_bool, bc_val) V = fdm.poisson_2d(X, Y, bc=bc, conv=1e-6, sor=1) assert V[1:4, 2] == approx(vbc)
def test_poisson_2d_bc(): x = np.linspace(0, 1, 5) y = np.linspace(0, 1, 5) X, Y = np.meshgrid(x, y, indexing='ij') bc_val = np.zeros_like(X) bc_val[2, 2] = 12.0 bc_bool = bc_val > 0 bc = (bc_bool, bc_val) V = fdm.poisson_2d(X, Y, bc=bc, conv=1e-6, sor=1) assert V == approx( np.array([[0, 0, 0, 0, 0], [0, 2, 4, 2, 0], [0, 4, 12, 4, 0], [0, 2, 4, 2, 0], [0, 0, 0, 0, 0]]))
def test_poisson_2d_bc_mult(): x = np.linspace(0, 1, 5) y = np.linspace(0, 1, 5) X, Y = np.meshgrid(x, y, indexing='ij') vbc1 = 13 vbc2 = 7 bc_val = np.zeros_like(X) bc_val[1, 3] = vbc1 bc_val[3, 1] = vbc2 bc_bool = bc_val > 0 bc = (bc_bool, bc_val) V = fdm.poisson_2d(X, Y, bc=bc, conv=1e-6, sor=1) assert V[1, 3] == approx(vbc1) assert V[3, 1] == approx(vbc2)
def test_poisson_2d_coax_xysym(): ri = 2.0e-3 ro = 4.0e-3 w = 1.1 * ro dx = ri / 40 Va = 10.0 x = np.arange(0, w, dx) y = np.arange(0, w, dx) X, Y = np.meshgrid(x, y, indexing='ij') R = np.sqrt(X**2 + Y**2) bc_bool = np.logical_or(R < ri, R > ro) bc_val = np.select([R < ri, R > ro], [Va, 0]) bc = (bc_bool, bc_val) cc = CoaxCapacitor(ri, 1.0, ro - ri) expected = cc.potential(X, Y, Va=Va) potential = fdm.poisson_2d(X, Y, bc=bc, conv=1e-6, xsym=True, ysym=True) assert potential == approx(expected, abs=0.4)
def test_poisson_2d_coax(): ri = 2.0e-3 ro = 4.0e-3 w = 1.1 * ro N = 101 Va = 10.0 x = np.linspace(-w, w, N) y = np.linspace(-w, w, N) X, Y = np.meshgrid(x, y, indexing='ij') R = np.sqrt(X**2 + Y**2) bc_bool = np.logical_or(R < ri, R > ro) bc_val = np.select([R < ri, R > ro], [Va, 0]) bc = (bc_bool, bc_val) cc = CoaxCapacitor(ri, 1.0, ro - ri) expected = cc.potential(X, Y, Va=Va) potential = fdm.poisson_2d(X, Y, bc=bc, conv=1e-5) assert potential == approx(expected, abs=0.4)
def test_poisson_2d_coax_2layer(): ri = 2.0e-3 re = 2.8e-3 ro = 4.0e-3 er1, er2 = 4.0, 1.0 w = 1.1 * ro N = 101 Va = 10.0 x = np.linspace(-w, w, N) y = np.linspace(-w, w, N) X, Y = np.meshgrid(x, y, indexing='ij') R = np.sqrt(X**2 + Y**2) er = np.select([R <= re, R > re], [er1, er2])[:-1, :-1] bc_bool = np.logical_or(R < ri, R > ro) bc_val = np.select([R < ri, R > ro], [Va, 0]) bc = (bc_bool, bc_val) cc = CoaxCapacitor(ri, (er1, er2), (re - ri, ro - re)) expected = cc.potential(X, Y, Va=Va) np.savetxt('e2d.csv', expected, delimiter=',', fmt='%.3f') potential = fdm.poisson_2d(X, Y, dielectric=er, bc=bc, conv=1e-5) assert potential == approx(expected, abs=0.6)
class WireMtl(): """Multi-conductor transmission line composed of round wires. The reference conductor can be a wire, plane, or round shield """ def __init__(self, wires: list, ref, er: float = 1.0): """Create a wire-type MTL with the given wires. The wires are provided as Wire objects The wires are immersed in a relative permittivity of er ref is the reference conductor, of type Wire, Plane or Shield """ if type(ref) not in (Wire, Plane, Shield): raise TypeError('ref must be wire, plane or shield') if len(wires) < 1: raise ValueError( 'Requires at least 2 wires, or a reference plane or shield') for w in wires: if type(w) is not Wire: raise TypeError(f'Bad wire type') # TODO validate positions and radii self.wires = wires self.er = er self.ref = ref def get_tline(self, freq) -> TLine: return TLine(self.inductance(), self.capacitance(), freq=freq) def capacitance(self, /, method: str = None, fdm_params: dict = {}) -> np.ndarray: """Calculate and return the capacitance matrix.""" for wire in self.wires: # Insulation is not yet supported assert wire.ins_thickness == 0.0 if method is None or method.lower() == 'ana': return MU0 * EPS0 * self.er * np.linalg.inv(self.inductance()) elif method.lower() == 'fdm': if type(self.ref) is not Wire or len(self.wires) != 1: raise Exception( 'FDM currently supports only two wires, no reference') # Define simulation region pad = 20 # Zero potential boundaries must be far away w_list = np.array([[wi.x - pad * wi.radius for wi in self.wires], [wi.x + pad * wi.radius for wi in self.wires]]) h_list = np.array([[wi.y - pad * wi.radius for wi in self.wires], [wi.y + pad * wi.radius for wi in self.wires]]) # Grid based on wire size or user configured x0, y0, r0 = self.ref.x, self.ref.y, self.ref.radius x1, y1, r1 = self.wires[0].x, self.wires[0].y, self.wires[0].radius dx = fdm_params.get('dx', min(r0, r1) / 6) x = np.arange(w_list.min(), w_list.max(), dx) y = np.arange(h_list.min(), h_list.max(), dx) X, Y = np.meshgrid(x, y, indexing='ij') print( f'{x.max() - x.min():.3e}, {y.max() - y.min():.3e} dimensions') print(f'{X.shape}, {dx:.3e} grid') print(f'{X.size} points') # Solve for potential V1 = 100.0 bc1 = np.sqrt((X - x0)**2 + (Y - y0)**2) <= r0 bc2 = np.sqrt((X - x1)**2 + (Y - y1)**2) <= r1 bc_val = np.zeros_like(X) bc_val[bc1] = 0.5 * V1 bc_val[bc2] = -0.5 * V1 bc_bool = bc_val != 0.0 V = fdm.poisson_2d(X, Y, bc=(bc_bool, bc_val), conv=1e-5) er = self.er * np.ones_like(X)[:-1, :-1] # Calculate charge and capacitances C = np.zeros((len(self.wires), len(self.wires))) for i, wi in enumerate(self.wires): # Keep some distance from wire surface, but cannot overlap other wire # TODO make this more robust gpad = wi.radius + 4 * dx xi1 = np.searchsorted(x, wi.x - gpad) xi2 = np.searchsorted(x, wi.x + gpad) yi1 = np.searchsorted(y, wi.y - gpad) yi2 = np.searchsorted(y, wi.y + gpad) print( f'({x[xi1]:.3e}, {x[xi2]:.3e}), ({y[yi1]:.3e}, {y[yi2]:.3e}) gauss' ) C[i] = abs(fdm.gauss_2d(X, Y, V, er, xi1, xi2, yi1, yi2) / V1) return np.array([C[0]]) else: raise Exception(f'Invalid method specified: {method}')
def test_poisson_2d_indexing(): x = np.linspace(0, 2.0, 21) y = np.linspace(0, 1.0, 11) X, Y = np.meshgrid(x, y, indexing='ij') with pytest.raises(Exception): fdm.poisson_2d(X, Y)