From b3e83574fa2f9e0d6a748654c032be4cbe7bcc52 Mon Sep 17 00:00:00 2001 From: joelslaby Date: Thu, 31 Aug 2023 10:14:48 -0400 Subject: [PATCH 1/4] fixed sidewall angle and multiple mode simulation --- gplugins/gmeep/get_meep_geometry.py | 2 +- gplugins/gmeep/get_simulation.py | 6 +- gplugins/gmeep/write_sparameters_meep.py | 110 ++++++++++++++++------- 3 files changed, 81 insertions(+), 37 deletions(-) diff --git a/gplugins/gmeep/get_meep_geometry.py b/gplugins/gmeep/get_meep_geometry.py index 550ec833..984e358a 100644 --- a/gplugins/gmeep/get_meep_geometry.py +++ b/gplugins/gmeep/get_meep_geometry.py @@ -63,7 +63,7 @@ def get_meep_geometry_from_component( mp.Prism( vertices=vertices, height=height, - sidewall_angle=layer_to_sidewall_angle[layer] + sidewall_angle=np.pi*layer_to_sidewall_angle[layer]/180 if is_3d else 0, material=material, diff --git a/gplugins/gmeep/get_simulation.py b/gplugins/gmeep/get_simulation.py index 656048fc..1da1c7fc 100644 --- a/gplugins/gmeep/get_simulation.py +++ b/gplugins/gmeep/get_simulation.py @@ -39,6 +39,7 @@ def get_simulation( wavelength_points: int = 50, dfcen: float = 0.2, port_source_name: str = "o1", + port_source_mode: int = 0, port_margin: float = 3, distance_source_to_monitors: float = 0.2, port_source_offset: float = 0, @@ -183,7 +184,7 @@ def get_simulation( "Did you passed the correct layer_stack?" ) - t_core = max(layers_thickness) + t_core = sum(layers_thickness) # This isn't exactly what we want but I think it's better than max cell_thickness = tpml + zmargin_bot + t_core + zmargin_top + tpml if is_3d else 0 cell_size = mp.Vector3( @@ -239,7 +240,7 @@ def get_simulation( src=mp.GaussianSource(fcen, fwidth=frequency_width), size=size, center=center, - eig_band=1, + eig_band = port_source_mode + 1, eig_parity=mp.NO_PARITY if is_3d else mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True, eig_kpoint=-1 * mp.Vector3(x=1).rotate(mp.Vector3(z=1), angle_rad), @@ -294,6 +295,7 @@ def get_simulation( monitors=monitors, sources=sources, port_source_name=port_source_name, + port_source_mode=port_source_mode, initialized=False, ) diff --git a/gplugins/gmeep/write_sparameters_meep.py b/gplugins/gmeep/write_sparameters_meep.py index a93baab5..f399586c 100644 --- a/gplugins/gmeep/write_sparameters_meep.py +++ b/gplugins/gmeep/write_sparameters_meep.py @@ -52,7 +52,7 @@ def remove_simulation_kwargs(d: dict[str, Any]) -> dict[str, Any]: return d -def parse_port_eigenmode_coeff(port_name: str, ports: dict[str, Port], sim_dict: dict): +def parse_port_eigenmode_coeff(port_name: str, ports: dict[str, Port], sim_dict: dict, port_mode: int = 0): """Returns the coefficients relative to whether the wavevector is entering or \ exiting simulation. @@ -98,7 +98,7 @@ def parse_port_eigenmode_coeff(port_name: str, ports: dict[str, Port], sim_dict: # Get port coeffs monitor_coeff = sim.get_eigenmode_coefficients( - monitors[port_name], [1], kpoint_func=lambda f, n: kpoint + monitors[port_name], [port_mode + 1], kpoint_func=lambda f, n: kpoint ) coeff_in = monitor_coeff.alpha[ @@ -115,6 +115,8 @@ def parse_port_eigenmode_coeff(port_name: str, ports: dict[str, Port], sim_dict: def write_sparameters_meep( component: ComponentSpec, port_source_names: list[str] | None = None, + port_source_modes: dict[str, list] = None, + port_modes: list[int] = None, port_symmetries: PortSymmetries | None = None, resolution: int = 30, wavelength_start: float = 1.5, @@ -128,20 +130,26 @@ def write_sparameters_meep( filepath: Path | None = None, overwrite: bool = False, animate: bool = False, + animate_center: tuple = None, + animate_size: tuple = None, lazy_parallelism: bool = False, run: bool = True, dispersive: bool = False, xmargin: float = 0, - ymargin: float = 3, + ymargin: float = 0, + zmargin: float = 0, xmargin_left: float = 0, xmargin_right: float = 0, ymargin_top: float = 0, ymargin_bot: float = 0, + zmargin_top: float = 0, + zmargin_bot: float = 0, decay_by: float = 1e-3, is_3d: bool = False, z: float = 0, plot_args: dict | None = None, only_return_filepath_sim_settings=False, + verbosity: int = 0, **settings, ) -> dict[str, np.ndarray]: r"""Returns Sparameters and writes them to npz filepath. @@ -235,6 +243,8 @@ def write_sparameters_meep( ymargin: top and bottom distance from component to PML. ymargin_top: north distance from component to PML. ymargin_bot: south distance from component to PML. + zmargin_top: +z distance from component to PML. + zmargin_bot: -z distance from component to PML. is_3d: if True runs in 3D (much slower). z: for 2D plot. plot_args: if animate or not run, customization keyword arguments passed to @@ -282,6 +292,9 @@ def write_sparameters_meep( ymargin_top = ymargin_top or ymargin ymargin_bot = ymargin_bot or ymargin + zmargin_top = zmargin_top or zmargin + zmargin_bot = zmargin_bot or zmargin + sim_settings = dict( resolution=resolution, port_symmetries=port_symmetries, @@ -292,6 +305,8 @@ def write_sparameters_meep( port_monitor_offset=port_monitor_offset, port_source_offset=port_source_offset, dispersive=dispersive, + zmargin_top = zmargin_top, + zmargin_bot = zmargin_bot, ymargin_top=ymargin_top, ymargin_bot=ymargin_bot, xmargin_left=xmargin_left, @@ -331,6 +346,18 @@ def write_sparameters_meep( right=xmargin_right, ) + component_ref = component.ref() + ports = component_ref.ports + port_names = [port.name for port in list(ports.values())] + port_source_names = port_source_names or port_names + port_source_modes = port_source_modes or {key:[0] for key in port_source_names} + port_modes = port_modes or [0] + + num_sims = len(port_source_names) - len(port_symmetries) + + # set verbosity + mp.verbosity(verbosity) + if not run: sim_dict = get_simulation( component=component, @@ -338,11 +365,15 @@ def write_sparameters_meep( wavelength_stop=wavelength_stop, wavelength_points=wavelength_points, layer_stack=layer_stack, + port_source_name=port_source_names[0], port_margin=port_margin, port_monitor_offset=port_monitor_offset, port_source_offset=port_source_offset, dispersive=dispersive, is_3d=is_3d, + resolution=resolution, + zmargin_top=zmargin_top, + zmargin_bot=zmargin_bot, **settings, ) sim = sim_dict["sim"] @@ -365,12 +396,6 @@ def write_sparameters_meep( elif overwrite: filepath.unlink() - component_ref = component.ref() - ports = component_ref.ports - port_names = [port.name for port in list(ports.values())] - port_source_names = port_source_names or port_names - num_sims = len(port_source_names) - len(port_symmetries) - sp = {} # Sparameters dict start = time.time() @@ -380,10 +405,13 @@ def sparameter_calculation( component: Component, port_symmetries: PortSymmetries | None = port_symmetries, port_names: list[str] = port_names, + port_source_mode: int = 0, wavelength_start: float = wavelength_start, wavelength_stop: float = wavelength_stop, wavelength_points: int = wavelength_points, animate: bool = animate, + animate_center: tuple = animate_center, + animate_size: tuple = animate_size, plot_args: dict = plot_args, dispersive: bool = dispersive, decay_by: float = decay_by, @@ -393,6 +421,7 @@ def sparameter_calculation( sim_dict = get_simulation( component=component, port_source_name=port_source_name, + port_source_mode=port_source_mode, resolution=resolution, wavelength_start=wavelength_start, wavelength_stop=wavelength_stop, @@ -425,34 +454,45 @@ def sparameter_calculation( if "eps_parameters" not in plot_args: plot_args["eps_parameters"] = {"contour": True} if "fields" not in plot_args: - plot_args["fields"] = mp.Ez + if is_3d: + plot_args["fields"] = mp.Hz + else: + plot_args["fields"] = mp.Ez if "realtime" not in plot_args: plot_args["realtime"] = True if "normalize" not in plot_args: plot_args["normalize"] = True sim.use_output_directory() - animate = mp.Animate2D( - sim, - **plot_args, - ) + if is_3d: + animate = mp.Animate2D( + sim, + output_plane = mp.Volume(center=mp.Vector3(*animate_center), size=mp.Vector3(*animate_size)), + **plot_args, + ) + else: + animate = mp.Animate2D( + sim, + **plot_args, + ) sim.run(mp.at_every(1, animate), until_after_sources=termination) - animate.to_mp4(30, f"{component.name}_{port_source_name}.mp4") + animate.to_mp4(30, f"{component.name}_{port_source_name}_{port_source_mode}.mp4") else: sim.run(until_after_sources=termination) # Calculate mode overlaps # Get source monitor results source_entering, _ = parse_port_eigenmode_coeff( - port_source_name, component.ports, sim_dict + port_source_name, component.ports, sim_dict, port_mode = port_source_mode ) # Get coefficients for port_name in port_names: - _, monitor_exiting = parse_port_eigenmode_coeff( - port_name, component.ports, sim_dict - ) - key = f"{port_name}@0,{port_source_name}@0" - sp[key] = monitor_exiting / source_entering + for port_mode in port_modes: + _, monitor_exiting = parse_port_eigenmode_coeff( + port_name, component.ports, sim_dict, port_mode = port_mode, + ) + key = f"{port_name}@{port_mode},{port_source_name}@{port_source_mode}" + sp[key] = monitor_exiting / source_entering if bool(port_symmetries): for key, symmetries in port_symmetries.items(): @@ -462,7 +502,7 @@ def sparameter_calculation( return sp - if lazy_parallelism: + if lazy_parallelism: #TODO: FIX Port modes from mpi4py import MPI cores = min([num_sims, multiprocessing.cpu_count()]) @@ -507,19 +547,21 @@ def sparameter_calculation( else: for port_source_name in tqdm(port_source_names): - sp.update( - sparameter_calculation( - port_source_name, - component=component, - port_symmetries=port_symmetries, - wavelength_start=wavelength_start, - wavelength_stop=wavelength_stop, - wavelength_points=wavelength_points, - animate=animate, - port_names=port_names, - **settings, + for port_source_mode in port_source_modes[port_source_name]: + sp.update( + sparameter_calculation( + port_source_name, + port_source_mode = port_source_mode, + component=component, + port_symmetries=port_symmetries, + wavelength_start=wavelength_start, + wavelength_stop=wavelength_stop, + wavelength_points=wavelength_points, + animate=animate, + port_names=port_names, + **settings, + ) ) - ) sp["wavelengths"] = np.linspace( wavelength_start, wavelength_stop, wavelength_points ) From 832ffff8f2dde39cc71c16429a6e8c4ac6949000 Mon Sep 17 00:00:00 2001 From: joelslaby Date: Wed, 8 Nov 2023 18:43:14 -0500 Subject: [PATCH 2/4] updated gmeep docs for example multimode --- docs/notebooks/meep_01_sparameters.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/notebooks/meep_01_sparameters.py b/docs/notebooks/meep_01_sparameters.py index 0fb0ce1c..ac8117f7 100644 --- a/docs/notebooks/meep_01_sparameters.py +++ b/docs/notebooks/meep_01_sparameters.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # --- # jupyter: # jupytext: @@ -265,6 +266,31 @@ # %% [markdown] # As you can see this crossing looks beautiful but is quite **lossy** (9 dB @ 1550 nm) +# %% [markdown] +# ## Multimode Simulations +# You can also simulate structures that source and measure multiple modes. We define the `port_source_modes` as a dictionary with the keys as the source keys and the values as list of mode numbers to output (starting at 0 for TE00). Similarly on the output ports, `port_modes` is a list of modes to measure at every output port. + +# %% +c = gf.components.straight(length=5, width=2) +sp = gm.write_sparameters_meep(c, run=False, ymargin_top=3, ymargin_bot=3, is_3d=False,resolution=20,) + +# %% +sp = gm.write_sparameters_meep( + c, + run=True, + ymargin_top=3, + ymargin_bot=3, + is_3d=False, + port_source_names = ['o1'], + port_source_modes = {'o1': [0, 1]}, + port_modes = [0, 1], + resolution=20, + lazy_parallelism=False, + animate=True, + overwrite=False, + ) +gm.plot.plot_sparameters(sp, with_simpler_labels=False) + # %% [markdown] # ## Parallel Simulation (multicore/MPI) # From 94492d69c861f0d8abe995cf5a053166f03f20a0 Mon Sep 17 00:00:00 2001 From: joelslaby Date: Wed, 8 Nov 2023 18:45:11 -0500 Subject: [PATCH 3/4] small fix to gmeep port flags in docs for multimode --- docs/notebooks/meep_01_sparameters.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/notebooks/meep_01_sparameters.py b/docs/notebooks/meep_01_sparameters.py index ac8117f7..67d430e2 100644 --- a/docs/notebooks/meep_01_sparameters.py +++ b/docs/notebooks/meep_01_sparameters.py @@ -285,8 +285,6 @@ port_source_modes = {'o1': [0, 1]}, port_modes = [0, 1], resolution=20, - lazy_parallelism=False, - animate=True, overwrite=False, ) gm.plot.plot_sparameters(sp, with_simpler_labels=False) From d0d77bfefe5ac717c967754a2c9465c162ee09a3 Mon Sep 17 00:00:00 2001 From: joelslaby Date: Wed, 8 Nov 2023 18:51:45 -0500 Subject: [PATCH 4/4] pre-commit fixes --- docs/notebooks/meep_01_sparameters.py | 26 ++++++++++++-------- gplugins/gmeep/get_meep_geometry.py | 2 +- gplugins/gmeep/get_simulation.py | 6 +++-- gplugins/gmeep/write_sparameters_meep.py | 30 ++++++++++++++++-------- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/docs/notebooks/meep_01_sparameters.py b/docs/notebooks/meep_01_sparameters.py index 67d430e2..a4f124f9 100644 --- a/docs/notebooks/meep_01_sparameters.py +++ b/docs/notebooks/meep_01_sparameters.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # --- # jupyter: # jupytext: @@ -272,21 +271,28 @@ # %% c = gf.components.straight(length=5, width=2) -sp = gm.write_sparameters_meep(c, run=False, ymargin_top=3, ymargin_bot=3, is_3d=False,resolution=20,) +sp = gm.write_sparameters_meep( + c, + run=False, + ymargin_top=3, + ymargin_bot=3, + is_3d=False, + resolution=20, +) # %% sp = gm.write_sparameters_meep( - c, - run=True, - ymargin_top=3, - ymargin_bot=3, + c, + run=True, + ymargin_top=3, + ymargin_bot=3, is_3d=False, - port_source_names = ['o1'], - port_source_modes = {'o1': [0, 1]}, - port_modes = [0, 1], + port_source_names=["o1"], + port_source_modes={"o1": [0, 1]}, + port_modes=[0, 1], resolution=20, overwrite=False, - ) +) gm.plot.plot_sparameters(sp, with_simpler_labels=False) # %% [markdown] diff --git a/gplugins/gmeep/get_meep_geometry.py b/gplugins/gmeep/get_meep_geometry.py index 984e358a..ac7979ff 100644 --- a/gplugins/gmeep/get_meep_geometry.py +++ b/gplugins/gmeep/get_meep_geometry.py @@ -63,7 +63,7 @@ def get_meep_geometry_from_component( mp.Prism( vertices=vertices, height=height, - sidewall_angle=np.pi*layer_to_sidewall_angle[layer]/180 + sidewall_angle=np.pi * layer_to_sidewall_angle[layer] / 180 if is_3d else 0, material=material, diff --git a/gplugins/gmeep/get_simulation.py b/gplugins/gmeep/get_simulation.py index 1da1c7fc..639a0cbf 100644 --- a/gplugins/gmeep/get_simulation.py +++ b/gplugins/gmeep/get_simulation.py @@ -184,7 +184,9 @@ def get_simulation( "Did you passed the correct layer_stack?" ) - t_core = sum(layers_thickness) # This isn't exactly what we want but I think it's better than max + t_core = sum( + layers_thickness + ) # This isn't exactly what we want but I think it's better than max cell_thickness = tpml + zmargin_bot + t_core + zmargin_top + tpml if is_3d else 0 cell_size = mp.Vector3( @@ -240,7 +242,7 @@ def get_simulation( src=mp.GaussianSource(fcen, fwidth=frequency_width), size=size, center=center, - eig_band = port_source_mode + 1, + eig_band=port_source_mode + 1, eig_parity=mp.NO_PARITY if is_3d else mp.EVEN_Y + mp.ODD_Z, eig_match_freq=True, eig_kpoint=-1 * mp.Vector3(x=1).rotate(mp.Vector3(z=1), angle_rad), diff --git a/gplugins/gmeep/write_sparameters_meep.py b/gplugins/gmeep/write_sparameters_meep.py index f399586c..83876705 100644 --- a/gplugins/gmeep/write_sparameters_meep.py +++ b/gplugins/gmeep/write_sparameters_meep.py @@ -52,7 +52,9 @@ def remove_simulation_kwargs(d: dict[str, Any]) -> dict[str, Any]: return d -def parse_port_eigenmode_coeff(port_name: str, ports: dict[str, Port], sim_dict: dict, port_mode: int = 0): +def parse_port_eigenmode_coeff( + port_name: str, ports: dict[str, Port], sim_dict: dict, port_mode: int = 0 +): """Returns the coefficients relative to whether the wavevector is entering or \ exiting simulation. @@ -305,8 +307,8 @@ def write_sparameters_meep( port_monitor_offset=port_monitor_offset, port_source_offset=port_source_offset, dispersive=dispersive, - zmargin_top = zmargin_top, - zmargin_bot = zmargin_bot, + zmargin_top=zmargin_top, + zmargin_bot=zmargin_bot, ymargin_top=ymargin_top, ymargin_bot=ymargin_bot, xmargin_left=xmargin_left, @@ -350,7 +352,7 @@ def write_sparameters_meep( ports = component_ref.ports port_names = [port.name for port in list(ports.values())] port_source_names = port_source_names or port_names - port_source_modes = port_source_modes or {key:[0] for key in port_source_names} + port_source_modes = port_source_modes or {key: [0] for key in port_source_names} port_modes = port_modes or [0] num_sims = len(port_source_names) - len(port_symmetries) @@ -467,7 +469,10 @@ def sparameter_calculation( if is_3d: animate = mp.Animate2D( sim, - output_plane = mp.Volume(center=mp.Vector3(*animate_center), size=mp.Vector3(*animate_size)), + output_plane=mp.Volume( + center=mp.Vector3(*animate_center), + size=mp.Vector3(*animate_size), + ), **plot_args, ) else: @@ -476,20 +481,25 @@ def sparameter_calculation( **plot_args, ) sim.run(mp.at_every(1, animate), until_after_sources=termination) - animate.to_mp4(30, f"{component.name}_{port_source_name}_{port_source_mode}.mp4") + animate.to_mp4( + 30, f"{component.name}_{port_source_name}_{port_source_mode}.mp4" + ) else: sim.run(until_after_sources=termination) # Calculate mode overlaps # Get source monitor results source_entering, _ = parse_port_eigenmode_coeff( - port_source_name, component.ports, sim_dict, port_mode = port_source_mode + port_source_name, component.ports, sim_dict, port_mode=port_source_mode ) # Get coefficients for port_name in port_names: for port_mode in port_modes: _, monitor_exiting = parse_port_eigenmode_coeff( - port_name, component.ports, sim_dict, port_mode = port_mode, + port_name, + component.ports, + sim_dict, + port_mode=port_mode, ) key = f"{port_name}@{port_mode},{port_source_name}@{port_source_mode}" sp[key] = monitor_exiting / source_entering @@ -502,7 +512,7 @@ def sparameter_calculation( return sp - if lazy_parallelism: #TODO: FIX Port modes + if lazy_parallelism: # TODO: FIX Port modes from mpi4py import MPI cores = min([num_sims, multiprocessing.cpu_count()]) @@ -551,7 +561,7 @@ def sparameter_calculation( sp.update( sparameter_calculation( port_source_name, - port_source_mode = port_source_mode, + port_source_mode=port_source_mode, component=component, port_symmetries=port_symmetries, wavelength_start=wavelength_start,