Skip to content

Commit

Permalink
Added EME solver
Browse files Browse the repository at this point in the history
  • Loading branch information
caseyflex committed Mar 20, 2024
1 parent 5d1927c commit c17f6ec
Show file tree
Hide file tree
Showing 22 changed files with 3,213 additions and 38 deletions.
2 changes: 1 addition & 1 deletion docs/faq
Submodule faq updated 227 files
2 changes: 1 addition & 1 deletion docs/notebooks
Submodule notebooks updated 68 files
+2 −2 .github/workflows/sync-to-proxy-repo.yaml
+3 −0 .gitignore
+4 −4 AdjointPlugin10YBranchLevelSet.ipynb
+23 −16 AdjointPlugin11CircuitMZI.ipynb
+62 −64 AdjointPlugin12LightExtractor.ipynb
+1,746 −0 AdjointPlugin13Metasurface.ipynb
+2,953 −0 AdjointPlugin14PreFab.ipynb
+10 −14 AdjointPlugin3InverseDesign.ipynb
+4 −2 AdjointPlugin5BoundaryGradients.ipynb
+11 −12 AdjointPlugin6GratingCoupler.ipynb
+5 −3 AdjointPlugin7Metalens.ipynb
+4 −2 AdjointPlugin8WaveguideBend.ipynb
+115 −327 AdjointPlugin9WDM.ipynb
+2,242 −0 AllDielectricStructuralColor.ipynb
+1 −1 BullseyeCavityPSO.ipynb
+1,945 −2,809 Design.ipynb
+4 −52 Dispersion.ipynb
+16 −16 Fitting.ipynb
+595 −1 GDSExport.ipynb
+8 −8 GeometryTransformations.ipynb
+1 −0 MIMResonator.ipynb
+8 −2 MetalHeaterPhaseShifter.ipynb
+215 −216 ParameterScan.ipynb
+19 −108 PhotonicCrystalWaveguidePolarizationFilter.ipynb
+2,996 −1 ResonanceFinder.ipynb
+242 −356 StripToSlotConverters.ipynb
+5 −1 ThermallyTunedRingResonator.ipynb
+4,887 −0 ThermoOpticDopedModulator.ipynb
+113 −113 TimeModulationTutorial.ipynb
+3,113 −0 WaveguideGratingAntenna.ipynb
+157 −182 WaveguideToRingCoupling.ipynb
+73 −705 XarrayTutorial.ipynb
+104 −2,414 YJunction.ipynb
+3 −1 docs/case_studies/metamaterials_gratings_periodic.rst
+2 −2 docs/case_studies/photonic_opt.rst
+1 −0 docs/case_studies/pic.rst
+1 −0 docs/case_studies/pic_active.rst
+5 −3 docs/features/adjoint.rst
+2 −1 docs/features/medium.rst
+1 −1 docs/features/parameter_sweep.rst
+ img/adjoint_11.png
+ img/adjoint_12.png
+ img/adjoint_13.png
+ img/adjoint_3.png
+ img/adjoint_5.png
+ img/adjoint_6.png
+ img/adjoint_7.png
+ img/adjoint_8.png
+ img/adjoint_9.png
+ img/all_dielectric.png
+ img/doped_silicon_heater.png
+ img/mim.png
+ img/ppp_junction.png
+ img/prefab_intro.png
+ img/prefab_target.png
+ img/thermally_tuned_waveguide.png
+ img/wga_schematic.png
+ img/wga_schematic_2.png
+0 −16 metadata/description_length_checker.py
+0 −681 metadata/metadata.txt
+0 −16 metadata/title_length_checker.py
+0 −30 metadata/update_notebook_metadata.py
+ misc/grating_coupler_history_no_penalty.pkl
+1 −1 misc/import_file_mapping.json
+ misc/logo.png
+22 −0 misc/my_medium.json
+ misc/prefab_base_sim.hdf5
+ misc/prefab_gc.gds
214 changes: 214 additions & 0 deletions tests/test_components/test_eme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import pytest
import pydantic.v1 as pd
import numpy as np
from matplotlib import pyplot as plt

import tidy3d as td

from ..utils import STL_GEO, assert_log_level, log_capture


def make_eme_sim():
# general simulation parameters
lambda0 = 1
freq0 = td.C_0 / lambda0
freqs = [freq0]
sim_size = 3 * lambda0, 3 * lambda0, 3 * lambda0
waveguide_size = (lambda0 / 2, lambda0, td.inf)
min_steps_per_wvl = 20

# EME parameters
monitor_size = (2 * lambda0, 2 * lambda0, 0.1 * lambda0)
eme_num_cells = 5 # EME grid num cells
eme_axis = 2

# Structures and FDTD grid
waveguide_geometry = td.Box(size=waveguide_size)
waveguide_medium = td.Medium(permittivity=2, conductivity=1e-6)
waveguide = td.Structure(geometry=waveguide_geometry, medium=waveguide_medium)
grid_spec = td.GridSpec.auto(wavelength=lambda0, min_steps_per_wvl=min_steps_per_wvl)

# EME setup
mode_spec = td.ModeSpec(num_modes=5, num_pml=(10, 10))
eme_grid_spec = td.EMEUniformGrid(num_cells=eme_num_cells, mode_spec=mode_spec)

# field monitor stores field on FDTD grid
field_monitor = td.EMEFieldMonitor(size=(0, td.inf, td.inf), name="field", colocate=True)

coeff_monitor = td.EMECoefficientMonitor(
size=monitor_size,
name="coeffs",
)

mode_monitor = td.EMEModeSolverMonitor(
size=(td.inf, td.inf, td.inf),
name="modes",
)

monitors = [mode_monitor, coeff_monitor, field_monitor]
structures = [waveguide]

sim = td.EMESimulation(
size=sim_size,
monitors=monitors,
structures=structures,
grid_spec=grid_spec,
axis=eme_axis,
eme_grid_spec=eme_grid_spec,
freqs=freqs,
)
return sim


def test_eme_grid():
sim_geom = td.Box(size=(4, 4, 4), center=(0, 0, 0))
axis = 2

# make a uniform grid
mode_spec = td.ModeSpec(num_modes=4)
uniform_grid_spec = td.EMEUniformGrid(num_cells=4, mode_spec=mode_spec)
uniform_grid = uniform_grid_spec.make_grid(sim_geom.center, sim_geom.size, axis)

# make a nonuniform grid
mode_spec1 = td.ModeSpec(num_modes=3)
mode_spec2 = td.ModeSpec(num_modes=1)
uniform_grid1 = td.EMEUniformGrid(num_cells=2, mode_spec=mode_spec1)
uniform_grid2 = td.EMEUniformGrid(num_cells=4, mode_spec=mode_spec2)
composite_grid_spec = td.EMECompositeGrid(
subgrids=[uniform_grid1, uniform_grid2], subgrid_boundaries=[0]
)
composite_grid = composite_grid_spec.make_grid(sim_geom.center, sim_geom.size, axis)

# test grid generation
assert uniform_grid.axis == 2
assert composite_grid.axis == 2

assert uniform_grid.mode_specs == [mode_spec] * 4
assert composite_grid.mode_specs == [mode_spec1] * 2 + [mode_spec2] * 4

assert np.array_equal(uniform_grid.boundaries, [-2, -1, 0, 1, 2])
assert np.array_equal(composite_grid.boundaries, [-2, -1, 0, 0.5, 1, 1.5, 2])

assert np.array_equal(uniform_grid.centers, [-1.5, -0.5, 0.5, 1.5])
assert np.array_equal(composite_grid.centers, [-1.5, -0.5, 0.25, 0.75, 1.25, 1.75])

assert np.array_equal(uniform_grid.lengths, [1, 1, 1, 1])
assert np.array_equal(composite_grid.lengths, [1, 1, 0.5, 0.5, 0.5, 0.5])

assert uniform_grid.num_cells == 4
assert composite_grid.num_cells == 6

# test that mode planes span sim and lie at cell centers
for grid in [uniform_grid, composite_grid]:
for center, mode_plane in zip(grid.centers, grid.mode_planes):
for dim in [0, 1, 2]:
if dim == axis:
assert mode_plane.center[dim] == center
assert mode_plane.size[dim] == 0
else:
assert mode_plane.center[dim] == sim_geom.center[dim]
assert mode_plane.size[dim] == sim_geom.size[dim]

# test that boundary planes span sim and lie at cell boundaries
for grid in [uniform_grid, composite_grid]:
for boundary, boundary_plane in zip(grid.boundaries, grid.boundary_planes):
for dim in [0, 1, 2]:
if dim == axis:
assert boundary_plane.center[dim] == boundary
assert boundary_plane.size[dim] == 0
else:
assert boundary_plane.center[dim] == sim_geom.center[dim]
assert boundary_plane.size[dim] == sim_geom.size[dim]

# test that cells have correct centers and sizes
for grid in [uniform_grid, composite_grid]:
for center, length, cell in zip(grid.centers, grid.lengths, grid.cells):
for dim in [0, 1, 2]:
if dim == axis:
assert cell.center[dim] == center
assert cell.size[dim] == length
else:
assert boundary_plane.center[dim] == sim_geom.center[dim]
assert boundary_plane.size[dim] == sim_geom.size[dim]

# test cell_indices_in_box
box = td.Box(center=(0, 0, 0.75), size=(td.inf, td.inf, 0.6))
assert uniform_grid.cell_indices_in_box(box) == [2, 3]
assert composite_grid.cell_indices_in_box(box) == [2, 3, 4]

# test composite grid subgrid boundaries validator
with pytest.raises(pd.ValidationError):
# need right number
_ = composite_grid_spec.updated_copy(subgrid_boundaries=[0, 2])
with pytest.raises(pd.ValidationError):
# need increasing
_ = composite_grid_spec.updated_copy(
subgris=[uniform_grid1, uniform_grid2, uniform_grid1], subgrid_boundaries=[0, -2]
)

# test grid boundaries validator
# fine to not span entire simulation
_ = uniform_grid.updated_copy(boundaries=[-1.5, -1, 0, 1, 1.5])
with pytest.raises(pd.ValidationError):
# need inside sim domain
_ = uniform_grid.updated_copy(boundaries=[-2, -1, 0, 1, 3])
with pytest.raises(pd.ValidationError):
# need inside sim domain
_ = uniform_grid.updated_copy(boundaries=[-3, -1, 0, 1, 2])
with pytest.raises(pd.ValidationError):
# need increasing
_ = uniform_grid.updated_copy(boundaries=[-2, -1, 0, 1, 0.5])
with pytest.raises(pd.ValidationError):
# need one more boundary than mode_Spec
_ = uniform_grid.updated_copy(boundaries=[-2, -1, 0, 1])


def test_eme_monitor():
_ = td.EMEModeSolverMonitor(
center=(1, 2, 3), size=(2, 2, 2), freqs=[300e12], mode_indices=[0, 1], name="eme_modes"
)
_ = td.EMEFieldMonitor(
center=(1, 2, 3),
size=(2, 2, 0),
freqs=[300e12],
mode_indices=[0, 1],
colocate=False,
name="eme_field",
)
_ = td.EMECoefficientMonitor(
center=(1, 2, 3), size=(2, 2, 2), freqs=[300e12], mode_indices=[0, 1], name="eme_coeffs"
)


def test_eme_simulation(log_capture):
sim = make_eme_sim()

# need at least one freq
with pytest.raises(pd.ValidationError):
_ = sim.updated_copy(freqs=[])
with pytest.raises(pd.ValidationError):
_ = sim.updated_copy(freqs=None)

# test warning for not providing wavelength in autogrid
log_capture
assert_log_level(log_capture, None)
grid_spec = td.GridSpec.auto(min_steps_per_wvl=20)
sim = sim.updated_copy(grid_spec=grid_spec)
assert_log_level(log_capture, "WARNING")
# multiple freqs are ok, but not for autogrid
_ = sim.updated_copy(grid_spec=td.GridSpec.uniform(dl=1), freqs=[1e10, 2e10])
# TODO: validator on grid_spec doesn't run when freqs is updated
# with pytest.raises(pd.ValidationError):
# _ = sim.updated_copy(freqs=[1, 2])


def test_eme_dataset():
pass


def test_eme_monitor_data():
pass


def test_eme_sim_data():
pass
19 changes: 19 additions & 0 deletions tests/test_data/test_data_arrays.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,22 @@ def test_spatial_data_array():

with pytest.raises(DataError):
reflected = arr.reflect(axis=2, center=2.5)


def test_eme_scalar_field_data_array():
x = [1, 2]
y = [2, 3, 4]
z = [3, 4, 5, 6]
f = [2e14, 3e14]
mode_index = np.arange(5)
port_index = [0, 1]
coords = dict(x=x, y=y, z=z, f=f, mode_index=mode_index, port_index=port_index)
_ = td.EMEScalarFieldDataArray((1 + 1j) * np.random.random((2, 3, 4, 2, 5, 2)), coords=coords)


def test_eme_smatrix_data_array():
mode_index_in = [0, 1]
mode_index_out = [0, 1, 2]
f = [2e14]
coords = dict(f=f, mode_index_out=mode_index_out, mode_index_in=mode_index_in)
_ = td.EMESMatrixDataArray((1 + 1j) * np.random.random((1, 3, 2)), coords=coords)
Loading

0 comments on commit c17f6ec

Please sign in to comment.