Skip to content

Commit 0161632

Browse files
option for PMC boundary in mode solver
1 parent d079b2e commit 0161632

File tree

5 files changed

+38
-14
lines changed

5 files changed

+38
-14
lines changed

tests/test_components/test_mode.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,12 @@ def get_mode_sim_data():
244244
def test_mode_sim_data():
245245
sim_data = get_mode_sim_data()
246246
_ = sim_data.plot_field("Ey", ax=AX, mode_index=0, f=FS[0])
247+
248+
249+
def test_mode_boundary():
250+
251+
_ = td.ModeSpec(boundary="PEC")
252+
_ = td.ModeSpec(boundary="PMC")
253+
with pytest.raises(pydantic.ValidationError):
254+
_ = td.ModeSpec(boundary="PBC")
255+

tests/test_plugins/test_mode_solver.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,12 @@ def mock_download(resource_id, remote_filename, to_file, *args, **kwargs):
167167
)
168168

169169

170-
def test_compute_modes():
170+
@pytest.mark.parametrize("boundary", ("PEC", "PMC"))
171+
def test_compute_modes(boundary):
171172
"""Test direct call to `compute_modes`."""
172173
eps_cross = np.random.rand(10, 10)
173174
coords = np.arange(11)
174-
mode_spec = td.ModeSpec(num_modes=3, target_neff=2.0)
175+
mode_spec = td.ModeSpec(num_modes=3, target_neff=2.0, boundary=boundary)
175176
_ = compute_modes(
176177
eps_cross=[eps_cross] * 9,
177178
coords=[coords, coords],
@@ -736,14 +737,16 @@ def test_mode_bend_radius():
736737
assert np.allclose(data1.n_complex, data2.n_complex * 5.5 / 5.0)
737738

738739

739-
def test_mode_solver_2D():
740+
@pytest.mark.parametrize("boundary", ("PEC", "PMC"))
741+
def test_mode_solver_2D(boundary):
740742
"""Run mode solver in 2D simulations."""
741743
mode_spec = td.ModeSpec(
742744
num_modes=3,
743-
filter_pol="te",
745+
filter_pol="te" if boundary == "PEC" else "tm",
744746
precision="double",
745747
num_pml=(0, 10),
746748
track_freq="central",
749+
boundary=boundary,
747750
)
748751
simulation = td.Simulation(
749752
size=(0, SIM_SIZE[1], SIM_SIZE[2]),
@@ -764,9 +767,10 @@ def test_mode_solver_2D():
764767

765768
mode_spec = td.ModeSpec(
766769
num_modes=3,
767-
filter_pol="te",
770+
filter_pol="te" if boundary == "PEC" else "tm",
768771
precision="double",
769772
num_pml=(10, 0),
773+
boundary=boundary,
770774
)
771775
simulation = td.Simulation(
772776
size=(SIM_SIZE[0], SIM_SIZE[1], 0),

tidy3d/components/mode/derivatives.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@
66
from ...constants import EPSILON_0, ETA_0
77

88

9-
def make_dxf(dls, shape, pmc):
9+
def make_dxf(dls, shape, pmc_neg, pmc_pos):
1010
"""Forward derivative in x."""
1111
Nx, Ny = shape
1212
if Nx == 1:
1313
return sp.csr_matrix((Ny, Ny))
1414
dxf = sp.csr_matrix(sp.diags([-1, 1], [0, 1], shape=(Nx, Nx)))
15-
if not pmc:
15+
if pmc_pos:
16+
dxf[-1,-1] = 0.0
17+
if not pmc_neg:
1618
dxf[0, 0] = 0.0
1719
dxf = sp.diags(1 / dls).dot(dxf)
1820
dxf = sp.kron(dxf, sp.eye(Ny))
@@ -34,13 +36,15 @@ def make_dxb(dls, shape, pmc):
3436
return dxb
3537

3638

37-
def make_dyf(dls, shape, pmc):
39+
def make_dyf(dls, shape, pmc_neg, pmc_pos):
3840
"""Forward derivative in y."""
3941
Nx, Ny = shape
4042
if Ny == 1:
4143
return sp.csr_matrix((Nx, Nx))
4244
dyf = sp.csr_matrix(sp.diags([-1, 1], [0, 1], shape=(Ny, Ny)))
43-
if not pmc:
45+
if pmc_pos:
46+
dyf[-1, -1] = 0.0
47+
if not pmc_neg:
4448
dyf[0, 0] = 0.0
4549
dyf = sp.diags(1 / dls).dot(dyf)
4650
dyf = sp.kron(sp.eye(Nx), dyf)
@@ -62,15 +66,15 @@ def make_dyb(dls, shape, pmc):
6266
return dyb
6367

6468

65-
def create_d_matrices(shape, dls, dmin_pmc=(False, False)):
69+
def create_d_matrices(shape, dls, dmin_pmc=(False, False), dmax_pmc=False):
6670
"""Make the derivative matrices without PML. If dmin_pmc is True, the
6771
'backward' derivative in that dimension will be set to implement PMC
6872
boundary, otherwise it will be set to PEC."""
6973

7074
dlf, dlb = dls
71-
dxf = make_dxf(dlf[0], shape, dmin_pmc[0])
75+
dxf = make_dxf(dlf[0], shape, dmin_pmc[0], dmax_pmc)
7276
dxb = make_dxb(dlb[0], shape, dmin_pmc[0])
73-
dyf = make_dyf(dlf[1], shape, dmin_pmc[1])
77+
dyf = make_dyf(dlf[1], shape, dmin_pmc[1], dmax_pmc)
7478
dyb = make_dyb(dlb[1], shape, dmin_pmc[1])
7579

7680
return (dxf, dxb, dyf, dyb)

tidy3d/components/mode/solver.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ def compute_modes(
203203
always impose PEC boundary at the xmax and ymax interfaces, and on the xmin and ymin
204204
interfaces unless PMC symmetry is present. If so, the PMC boundary is imposed through the
205205
backward derivative matrices."""
206-
dmin_pmc = [sym == 1 for sym in symmetry]
206+
dmax_pmc = mode_spec.boundary == "PMC"
207+
dmin_pmc = [sym == 1 or (sym == 0 and dmax_pmc) for sym in symmetry]
207208

208209
# Primal grid steps for E-field derivatives
209210
dl_f = [new_cs[1:] - new_cs[:-1] for new_cs in new_coords]
@@ -213,7 +214,7 @@ def compute_modes(
213214
dls = (dl_f, dl_b)
214215

215216
# Derivative matrices with PEC boundaries by default and optional PMC at the near end
216-
der_mats_tmp = d_mats(Nxy, dls, dmin_pmc)
217+
der_mats_tmp = d_mats(Nxy, dls, dmin_pmc, dmax_pmc)
217218

218219
# PML matrices; do not impose PML on the bottom when symmetry present
219220
dmin_pml = np.array(symmetry) == 0

tidy3d/components/mode_spec.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ class ModeSpec(Tidy3dBaseModel):
160160
f"default of {GROUP_INDEX_STEP} is used.",
161161
)
162162

163+
boundary: Literal["PEC", "PMC"] = pd.Field(
164+
"PEC",
165+
title="Boundary Conditions",
166+
description="Boundary conditions to impose at the edges of the mode plane.",
167+
)
168+
163169
@pd.validator("bend_axis", always=True)
164170
@skip_if_fields_missing(["bend_radius"])
165171
def bend_axis_given(cls, val, values):

0 commit comments

Comments
 (0)