diff --git a/gplugins/tidy3d/get_simulation_grating_coupler.py b/gplugins/tidy3d/get_simulation_grating_coupler.py index 1bb81780..02ba7faf 100644 --- a/gplugins/tidy3d/get_simulation_grating_coupler.py +++ b/gplugins/tidy3d/get_simulation_grating_coupler.py @@ -1,34 +1,26 @@ -"""Returns tidy3d simulation from gdsfactory Component.""" - from __future__ import annotations import warnings -from functools import partial import gdsfactory as gf import gdstk import matplotlib.pyplot as plt import numpy as np import tidy3d as td -from gdsfactory import logger -from gdsfactory.add_padding import add_padding_container from gdsfactory.component import Component -from gdsfactory.pdk import get_layer_stack -from gdsfactory.technology import LayerStack -from gdsfactory.typings import CrossSectionSpec -from tidy3d.plugins.mode import ModeSolver +from gdsfactory.pdk import get_layer, get_layer_stack +from gdsfactory.technology import DerivedLayer, LayerStack, LogicalLayer +from gdsfactory.typings import CrossSectionSpec, LayerSpecs from gplugins.common.base_models.component import move_polar_rad_copy -from gplugins.tidy3d.materials import ( - get_index, - get_medium, -) +from gplugins.tidy3d.materials import get_medium def get_simulation_grating_coupler( component: Component, port_extension: float = 10.0, layer_stack: LayerStack | None = None, + exclude_layers: LayerSpecs | None = None, thickness_pml: float = 1.0, xmargin: float = 0, ymargin: float = 0, @@ -52,24 +44,23 @@ def get_simulation_grating_coupler( num_modes: int = 2, run_time_ps: float = 10.0, fiber_port_prefix: str = "o2", - fiber_xoffset: float = -7, + fiber_xoffset: float = 0, fiber_z: float = 2, fiber_mfd: float = 10.4, fiber_angle_deg: float = 20.0, - material_name_to_tidy3d: None | dict[str, str] = None, + material_name_to_tidy3d: dict[str, str] | None = None, is_3d: bool = True, with_all_monitors: bool = False, boundary_spec: td.BoundarySpec | None = None, grid_spec: td.GridSpec | None = None, sidewall_angle_deg: float = 0, dilation: float = 0.0, - padding_layer: tuple[int, int] = (67, 0), cross_section: CrossSectionSpec | None = None, **kwargs, ) -> td.Simulation: r"""Returns Simulation object from a gdsfactory grating coupler component. - injects a Gaussian beam from above and monitors the transmission into the waveguide. + Injects a Gaussian beam from above and monitors the transmission into the waveguide. based on grating coupler example https://docs.simulation.cloud/projects/tidy3d/en/latest/notebooks/GratingCoupler.html @@ -129,6 +120,7 @@ def get_simulation_grating_coupler( port_extension: extend ports beyond the PML. layer_stack: contains layer to thickness, zmin and material. Defaults to active pdk.layer_stack. + exclude_layers: list of layers to exclude. thickness_pml: PML thickness (um). xmargin: left/right distance from component to PML. xmargin_left: left distance from component to PML. @@ -180,7 +172,6 @@ def get_simulation_grating_coupler( Angle of the sidewall. ``sidewall_angle=0`` (default) specifies vertical wall, while ``0 0.001 else 0 + size_y = size_y if size_y > 0.001 else 0 size_y = size_y if is_3d else td.inf - size_z = core_thickness + zmargin + box_thickness + size_z = ( + max(layer_stack.get_layer_to_thickness().values(), default=0) + + zmargin + + box_thickness + ) waveguide_port_size = [size_x, size_y, size_z] + xy_shifted = move_polar_rad_copy( - np.array(port.dcenter), angle=angle * np.pi / 180, length=port_waveguide_offset + np.array(port.dcenter), angle=np.deg2rad(angle), length=port_waveguide_offset ) - waveguide_port_center = xy_shifted.tolist() + [ - (core_thickness + zmargin - box_thickness) / 2 - ] # (x, y, z) + waveguide_port_center = [xy_shifted[0], xy_shifted[1], (size_z) / 2] waveguide_monitor = td.ModeMonitor( center=waveguide_port_center, @@ -447,17 +441,30 @@ def get_simulation_grating_coupler( name="waveguide", ) - # Add fiber monitor - fiber_port = component_ref.ports[fiber_port_name] - fiber_port_x = fiber_port.dx + fiber_xoffset + # Identify fiber port + fiber_port_name = None + for port in component_with_booleans.ports: + port_name = port.name + if port_name.startswith(fiber_port_prefix): + fiber_port_name = port_name + break - if fiber_port_x < -sim_size[0] / 2 or fiber_port_x > sim_size[0] / 2: + if fiber_port_name is None: raise ValueError( - f"component_ref.ports[{fiber_port_name!r}] + (fiber_xoffset = {fiber_xoffset}) = " - f"{int(fiber_port_x)} needs to be between {-sim_size[0]/2} and {+sim_size[0]/2}" + f"No port starting with prefix '{fiber_port_prefix}' found in component ports {list(component_with_booleans.ports.keys())}." ) - # inject Gaussian beam from above and monitors the transmission into the waveguide. + fiber_port = component_with_booleans.ports[fiber_port_name] + fiber_port_x = fiber_port.dcenter[0] + fiber_xoffset - component_with_booleans.dx + + if not (-sim_size[0] / 2 <= fiber_port_x <= sim_size[0] / 2): + xmin = float(np.round(-sim_size[0] / 2, 3)) + xmax = -xmin + raise ValueError( + f"Fiber port x-position {fiber_port_x} is outside the simulation domain {xmin=}, {xmax=}." + ) + + # Define Gaussian beam source gaussian_beam = td.GaussianBeam( size=(td.inf, td.inf, 0), center=[fiber_port_x, 0, fiber_z], @@ -469,8 +476,9 @@ def get_simulation_grating_coupler( pol_angle=np.pi / 2, ) + # Define additional monitors plane_monitor = td.FieldMonitor( - center=[0, 0, (zmax + zmin) / 2], + center=[0, 0, sim_zsize / 2], size=[sim_size[0], sim_size[1], 0], freqs=[freq0], name="full_domain_fields", @@ -490,11 +498,12 @@ def get_simulation_grating_coupler( name="radiated_near_fields", ) + # Compile list of monitors monitors = [waveguide_monitor] - monitors += ( - [plane_monitor, rad_monitor, near_field_monitor] if with_all_monitors else [] - ) + if with_all_monitors: + monitors += [plane_monitor, rad_monitor, near_field_monitor] + # Assemble simulation structures sim = td.Simulation( size=sim_size, structures=structures, @@ -506,11 +515,12 @@ def get_simulation_grating_coupler( **kwargs, ) + # Optional: Plotting modes if plot_modes: src_plane = td.Box(center=waveguide_port_center, size=waveguide_port_size) mode_spec = td.ModeSpec(num_modes=num_modes) - ms = ModeSolver( + ms = td.ModeSolver( simulation=sim, plane=src_plane, freqs=[freq0], @@ -520,13 +530,13 @@ def get_simulation_grating_coupler( print( "Effective index of computed modes: ", - ", ".join([f"{n_eff:1.4f}" for n_eff in modes.n_eff.isel(f=0).values]), + ", ".join([f"{n_eff:.4f}" for n_eff in modes.n_eff.isel(f=0).values]), ) if is_3d: - fig, axs = plt.subplots(num_modes, 2, figsize=(12, 12)) + fig, axs = plt.subplots(num_modes, 2, figsize=(12, 6 * num_modes)) else: - fig, axs = plt.subplots(num_modes, 3, figsize=(12, 12)) + fig, axs = plt.subplots(num_modes, 3, figsize=(18, 6 * num_modes)) for mode_ind in range(num_modes): if is_3d: @@ -536,6 +546,8 @@ def get_simulation_grating_coupler( modes.Ez.isel(mode_index=mode_ind).abs.plot( x="y", y="z", cmap="magma", ax=axs[mode_ind, 1] ) + axs[mode_ind, 0].set_title(f"|Ey|: mode_index={mode_ind}") + axs[mode_ind, 1].set_title(f"|Ez|: mode_index={mode_ind}") else: modes.Ex.isel(mode_index=mode_ind).abs.plot(ax=axs[mode_ind, 0]) modes.Ey.isel(mode_index=mode_ind).abs.plot(ax=axs[mode_ind, 1]) @@ -546,9 +558,11 @@ def get_simulation_grating_coupler( axs[mode_ind, 2].set_title(f"|Ez|: mode_index={mode_ind}") if is_3d: - axs[mode_ind, 0].set_aspect("equal") - axs[mode_ind, 1].set_aspect("equal") + for ax in axs.flat: + ax.set_aspect("equal") + plt.tight_layout() plt.show() + return sim @@ -556,31 +570,18 @@ def get_simulation_grating_coupler( import gplugins.tidy3d as gt c = gf.components.grating_coupler_elliptical_trenches() + # c = gf.components.grating_coupler_elliptical_lumerical() + # Example of using an arbitrary grating coupler # c = gf.components.grating_coupler_elliptical_arbitrary( # widths=[0.343] * 25, gaps=[0.345] * 25 # ) + sim = get_simulation_grating_coupler( c, plot_modes=False, is_3d=False, fiber_angle_deg=20, ) - gt.plot_simulation(sim) # make sure simulations looks good - - # c = gf.components.grating_coupler_elliptical_lumerical() # inverse design grating - # sim = get_simulation_grating_coupler(c, plot_modes=False, fiber_angle_deg=-5) - # sim_data = gt.get_results(sim).result() - # freq0 = td.constants.C_0 / 1.55 - # fig, (ax1, ax2, ax3) = plt.subplots(3, 1, tight_layout=True, figsize=(14, 16)) - # sim_data.plot_field("full_domain_fields", "Ey", freq=freq0, z=0, ax=ax1) - # sim_data.plot_field("radiated_near_fields", "Ey", freq=freq0, z=0, ax=ax2) - # sim_data.plot_field("radiated_fields", "Ey", freq=freq0, y=0, ax=ax3) - - # plt.figure() - # waveguide = sim_data["waveguide"] - # plt.plot(np.abs(waveguide.amps.values[0].flatten()), label="+") - # plt.plot(np.abs(waveguide.amps.values[1].flatten()), label="-") - # plt.legend() - # plt.show() - # print(f"waveguide in waveguide / waveguide in = {float(waveguide.amps.values):.2f} ") + gt.plot_simulation(sim) # Ensure simulation looks good + c.show() diff --git a/notebooks/tidy3d_00_tidy3d.ipynb b/notebooks/tidy3d_00_tidy3d.ipynb index 0f091fe0..476ff9c7 100644 --- a/notebooks/tidy3d_00_tidy3d.ipynb +++ b/notebooks/tidy3d_00_tidy3d.ipynb @@ -225,7 +225,7 @@ "# we can plot the tidy3d simulation setup\n", "fig, ax = plt.subplots(2, 1)\n", "modeler.plot_sim(z=c.get_layer_center(\"core\")[2], ax=ax[0])\n", - "modeler.plot_sim(x=c.ports[0].center[0], ax=ax[1])\n", + "modeler.plot_sim(x=c.ports[0].dcenter[0], ax=ax[1])\n", "fig.tight_layout()\n", "plt.show()" ] @@ -856,7 +856,7 @@ "outputs": [], "source": [ "sim = gt.get_simulation_grating_coupler(\n", - " c, is_3d=False, fiber_angle_deg=fiber_angle_deg, fiber_xoffset=-5\n", + " c, is_3d=False, fiber_angle_deg=fiber_angle_deg, fiber_xoffset=0\n", ")\n", "f = gt.plot_simulation(sim)" ] @@ -869,7 +869,7 @@ "outputs": [], "source": [ "sim = gt.get_simulation_grating_coupler(\n", - " c, is_3d=False, fiber_angle_deg=fiber_angle_deg, fiber_xoffset=+5\n", + " c, is_3d=False, fiber_angle_deg=fiber_angle_deg, fiber_xoffset=0\n", ")\n", "f = gt.plot_simulation(sim)" ]