Source code for modes.waveguide

from collections.abc import Iterable
from typing import Callable, List, Union

import matplotlib.pylab as plt
import numpy as np

from modes._structure import RidgeWaveguide, WgArray
from modes.autoname import autoname
from modes.config import CONFIG
from modes.materials import nitride, si, sio2

WaveguideType = Union[RidgeWaveguide, WgArray]


[docs]@autoname def waveguide( x_step: float = 0.02, y_step: float = 0.02, thickness: float = 0.22, width: float = 0.5, slab_thickness: float = 0, sub_thickness: float = 0.5, sub_width: float = 2.0, clad_thickness: List[float] = [0.5], n_sub: Union[Callable, float] = sio2, n_wg: Union[Callable, float] = si, n_clads: List[Union[Callable, float]] = [sio2], wavelength: float = 1.55, angle: float = 90.0, ) -> RidgeWaveguide: """Return waveguide structure Args: x_step: x grid step (um) y_step: y grid step (um) thickness: waveguide thickness (um) width: 0.5 (um) slab_thickness: 0 (um) sub_width: 2.0 related to the total simulation width (um) sub_thickness: 0.5 bottom simulation margin (um) clad_thickness: [0.5] List of claddings (top simulation margin) n_sub: substrate index material n_wg: core waveguide index material n_clads: list of cladding refractive index or function [sio2] wavelength: 1.55 wavelength (um) angle: 90 sidewall angle (degrees) :: _________________________________ clad_thickness width <----------> ___________ _ _ _ _ _ _ | | _____| |____ | thickness slab_thickness | _______________________ _ _ _ _ __ sub_thickness _________________________________ <-------------------------------> sub_width To define a waveguide we need to define: - the material functions or refractive indices of box, waveguide and clad - thickness of each material - x and y_steps for structure grid - sidewall angle - wavelength that can be used in case the refractive index are a function of the wavelength Where all units are in um .. plot:: :include-source: import modes as ms wg = ms.waveguide(width=0.5, thickness=0.22, slab_thickness=0.09, angle=80) ms.write_material_index(wg) """ if not isinstance(n_clads, Iterable): raise ValueError(f"nclads not Iterable, got {n_clads}") if not isinstance(clad_thickness, Iterable): raise ValueError(f"clad_thickness not Iterable, got {clad_thickness}") n_wg = n_wg(wavelength) if callable(n_wg) else n_wg n_sub = n_sub(wavelength) if callable(n_sub) else n_sub n_clad = [n_clad(wavelength) if callable(n_clad) else n_clad for n_clad in n_clads] film_thickness = thickness thickness = film_thickness - slab_thickness return RidgeWaveguide( wavelength=wavelength, x_step=x_step, y_step=y_step, thickness=thickness, width=width, sub_thickness=sub_thickness, sub_width=sub_width, clad_thickness=clad_thickness, n_sub=n_sub, n_wg=n_wg, angle=angle, n_clad=n_clad, film_thickness=film_thickness, )
[docs]@autoname def waveguide_array( wg_gaps: List[float], widths: List[float], x_step: float = 0.02, y_step: float = 0.02, thickness: float = 0.22, slab_thickness: int = 0, sub_thickness: float = 0.5, sub_width: float = 2.0, clad_thickness: List[float] = [0.5], n_sub: Callable = sio2, n_wg: Callable = si, n_clads: List[Callable] = [sio2], wavelength: float = 1.55, angle: float = 90.0, ) -> WgArray: """Returns a evanescent coupled waveguides :: __________________________________________________________ clad_thickness widths[0] wg_gaps[0] widths[1] <-----------><----------><-----------> _ _ _ _ _ _ ___________ ___________ | | | | _____| |____________| |____ | thickness slab_thickness | ________________________________________________ _ _ _ _ _ sub_thickness __________________________________________________________ <--------------------------------------------------------> sub_width To define a waveguide we need to define Args: wg_gaps: between waveguides widths: of each waveguide (list) x_step: grid x step (um) y_step: grid y step(um) n_sub: substrate refractive index value or function(wavelength) n_wg: waveguide refractive index value or function(wavelength) n_clads: waveguide refractive index value or function(wavelength) slab_thickness: slab thickness (um) sub_thickness: substrate thickness (um) clad_thickness: cladding thickness (um) wavelength: in um angle: sidewall angle in degrees Where all units are in um .. plot:: :include-source: import modes as ms wg_array = ms.waveguide_array(wg_gaps=[0.2], widths=[0.5, 0.5], slab_thickness=0.09) ms.write_material_index(wg_array) """ n_wg = n_wg(wavelength) if callable(n_wg) else n_wg n_sub = n_sub(wavelength) if callable(n_sub) else n_sub n_clad = [n_clad(wavelength) if callable(n_clad) else n_clad for n_clad in n_clads] film_thickness = thickness thickness = film_thickness - slab_thickness return WgArray( widths=widths, wg_gaps=wg_gaps, wavelength=wavelength, x_step=x_step, y_step=y_step, thickness=thickness, sub_thickness=sub_thickness, sub_width=sub_width, clad_thickness=clad_thickness, n_sub=n_sub, n_wg=n_wg, angle=angle, n_clad=n_clad, film_thickness=film_thickness, )
def get_waveguide_filepath(wg): return CONFIG.cache / f"{wg.name}.dat" def write_material_index(wg, filepath=None): """ writes the waveguide refractive index into filepath""" filepath = filepath or get_waveguide_filepath(wg) wg.write_to_file(filepath) def test_waveguide_name() -> None: wg1 = waveguide(angle=80, width=0.5) wg2 = waveguide(width=0.5, angle=80) assert wg1.name == wg2.name, ( f"{wg1} and {wg2} waveguides have the same settings and should have the same" " name" ) def test_waveguide_material_index() -> None: wg = waveguide() n = wg.n sx, sy = np.shape(n) n_wg = wg.n[sx // 2][sy // 2] assert n_wg == si(wg._wl) def test_waveguide_array_material_index() -> None: wg = waveguide_array(wg_gaps=[0.2], widths=[0.5] * 2) n = wg.n sx, sy = np.shape(n) n_wg = wg.n[sx // 2][sy // 2] assert n_wg == sio2(wg._wl) if __name__ == "__main__": wg = waveguide( width=0.5, angle=80, n_wg=si, clad_thickness=[50e-3, 50e-3, 0.5], n_clads=[sio2, nitride, sio2], ) # wg = waveguide_array(widths=[0.5] * 2, wg_gaps=[0.2], slab_thickness=0.09) # print(wg) # test_waveguide_material_index() # test_waveguide_array_material_index() write_material_index(wg) plt.show()