# -*- coding: utf-8 -*-
"""
Created on Fri Feb 10 08:31:42 2023
@author: Martin.Mihaylov
"""
import numpy as np
import sys
import matplotlib.pyplot as plt
[docs]
def loadingBar(count, total, size=1):
"""
Parameters
----------
count : int
count is the loop iteration variable. For example count = i in case "for i in range(...):" is used.
total : int
Total number of bars that must be loaded for the respective process. For example total = 10 in case "for i in range(10):"
size : int, optional
Defines how many "=" signs are used when the loading bar is printed in the console. The default is 1.
Returns
-------
None.
"""
percent = float(count+1) / float(total) * 100
sys.stdout.write("\r" + str(int(count+1)).rjust(3, '0') + "/" +
str(int(total)).rjust(3, '0') + ' [' + '=' * int(percent / 10) *
size + ' ' * (10 - int(percent / 10)) * size + ']')
# Array with arange including the last element
[docs]
def arange(start, stop, step=1, endpoint=True):
arr = np.arange(start, stop, step)
if endpoint and arr[-1] != stop:
arr = np.append(arr, stop)
return arr
[docs]
class Constructor:
# Init Programm
def __init__(self, file, Mode, MaterialLib = None):
'''
Parameters
----------
file : str
Path to the lumerical python file.
Mode : str
Solver Type, can be set to FDTD or EME.
MaterialLib : str, optional
Path to the lumerical material lib. The default is None.
Raises
------
ValueError
Error when wrong solver is given.
Returns
-------
None.
'''
self.file = file
self.MaterialLib = MaterialLib
if self.MaterialLib is None:
pass
else:
self.MaterialLib = MaterialLib
# Check Python Version !!! No import imp module after python 3.11
PyVersion = sys.version
try:
if PyVersion.split(" ")[0] > "3.11.0":
import importlib.util
import importlib.machinery
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
self.lumpai = load_source('lumapi', self.file)
else:
import imp
self.lumpi = imp.load_source('lumapi', self.file)
except ValueError as e:
print(f"ValueError encountered: {e}")
except ImportError as e:
print(f"ImportError encountered: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
self.Mode = Mode
self.Struct = None
self.SolverInfo = {}
if self.Mode == "FDTD":
self.FDTD()
self.SolverInfo["Solver Used"] = "FDTD"
elif self.Mode == "EME":
self.EME()
self.SolverInfo["Solver Used"] = "EME"
else:
raise ValueError("Non Valid Solver was choosen. Please pass on one of the two supported solvers ['FDTD' or 'EME']")
# Close Programm
[docs]
def Close(self):
'''
Returns
-------
Close Lumerical GUI
'''
self.lum.close()
print('Lumerical API is closed')
# Remove the object and solver region
[docs]
def removeObject(self):
'''
Switch from simulation to layout mode.
Remove all objects.
Returns
-------
None.
'''
self.lum.switchtolayout()
self.lum.deleteall()
[docs]
def Help(self, Subject = None):
if Subject is None:
print("i am in None section")
raise ValueError("Help can be called with Help(str(subject)). Subject can be choosen from 'Objects', 'Solvers', 'Start Simulation', 'Results', 'Loading Bar' and 'Log File' !! ")
# Check Type
elif type(Subject) == dict:
print("i am in Dict section")
StartNumber = [1,2,3]
ResultNumber = [1,2,3,4]
StrucNumbers = [1,2,3,4,5,6,7,8,9,10,11,12]
SolverNumber = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
listSub = ['Objects', 'Solvers', 'Start Simulation', 'Results', 'Loading Bar', 'Log File']
key = list(Subject.keys())
val = list(Subject.values())
_help = HelpSubject()
if key[0] == "Objects" and val[0] in StrucNumbers:
_help.Help_Objects()
_help.Structures(int(val[0]))
elif key[0] == "Solvers" and val[0] in SolverNumber:
_help.Help_Solvers()
_help.Solvers(int(val[0]))
elif key[0] == "Start Simulation" and val[0] in StartNumber:
_help.Help_StartSimulation()
_help.StartSolver(int(val[0]))
elif key[0] == "Results" and val[0] in ResultNumber:
_help.Help_Results()
_help.Result_extraction(int(val[0]))
elif key[0] == "Loading Bar":
_help.Help_LoadingBar()
elif key[0] == "Remove Object":
_help.Help_RemoveObject()
elif key[0] == "Log File":
_help.Help_LogFile()
elif key[0] not in listSub:
raise ValueError("Help can be called with Help(str(subject)). Subject can be choosen from 'Objects', 'Solvers', 'Start Simulation', 'Results', 'Loading Bar' and 'Log File' !! ")
#elif val[0] not in StartNumber or ResultNumber or StrucNumbers or StrucNumbers:
elif type(Subject) == str :
print("i am in STR section")
_help = HelpSubject()
if Subject == "Objects":
_help.Help_Objects()
elif Subject == "Solvers":
_help.Help_Solvers()
elif Subject == "Start Simulation":
_help.Help_StartSimulation()
elif Subject == "Results":
_help.Help_Results()
elif Subject == "Loading Bar":
_help.Help_LoadingBar()
elif Subject == "Remove Object":
_help.Help_RemoveObject()
elif Subject == "Log File":
_help.Help_LogFile()
else:
raise ValueError("Help can be called with Help(str(subject)). Subject can be choosen from 'Objects', 'Solvers', 'Start Simulation', 'Results', 'Loading Bar' and 'Log File' !! ")
# Choose Solver! Only FDTD and EME supported
[docs]
def FDTD(self):
'''
Calls the FDTD Solver.
Returns
-------
None.
'''
self.lum = self.lumpai.FDTD()
if self.MaterialLib is None:
pass
else:
self.lum.importmaterialdb(self.MaterialLib)
print('Lumerical FDTD API is started')
[docs]
def EME(self):
'''
Calls the EME solver.
Returns
-------
None.
'''
self.lum = self.lumpai.MODE()
if self.MaterialLib is None:
pass
else:
self.lum.importmaterialdb(self.MaterialLib)
print('Lumerical EME API is started')
# Solvers Function
[docs]
def Solver(self, Structure, Type, Parameters):
"""
Parameters
----------
Structure : str
Object that will be simulated.
Type : str
Solver Type.
Parameters : dict
Dictionary of solver parameters.
Raises
------
ValueError
Error when wrong solver type selected.
Returns
-------
None.
"""
if Type == "FDTD":
self.FDTD_Solver(Structure, Parameters)
elif Type == "EME":
self.EME_Solver(Structure, Parameters)
elif Type == "FDE":
self.FDE_Solver(Structure, Parameters)
else:
raise ValueError(
"Invalid Solver Type! Possible Options are FDE - For Waveguides, EME - for MMI , directional Couplers and FDTD - for MMI.")
[docs]
def StartSimFunction(self, Type):
'''
Parameters
----------
Type : str
Solver Type.
Raises
------
ValueError
Error when wrong solver type selected.
Returns
-------
None.
'''
if Type == "FDTD":
self.StartFDTDSimulation()
self.SolverInfo["Simulation Type"] = "FDTD Simulation"
elif Type == "EME":
self.StartEMESimulation()
self.SolverInfo["Simulation Type"] = "EME Simulation"
elif Type == "FDE":
self.StartFDESimulation()
self.SolverInfo["Simulation Type"] = "FDE Simulation"
else:
raise ValueError("Invalid simulation type. Possibpe options for Type are: 'FDTD', 'EME', 'FDE' !")
[docs]
def ExtrtactData(self, Type, Parameters):
'''
Parameters
----------
Type : str
Solver Type.
Parameters : boolen
Need to be set True if S-Parameter analysis in FDTD solver is wanted. Otherwise False.
Raises
------
ValueError
DESCRIPTION.
Returns
-------
TYPE
Error when wrong structure was selected.
'''
if Type == "FDTD":
if self.Struct == "MMI2x1":
S_Param3, Power3 = self.ExtractFDTDResults(3, Parameters)
return S_Param3, Power3
elif self.Struct == "MMI2x2":
S_Param4 = self.ExtractFDTDResults(4, Parameters)
return S_Param4
elif self.Struct == "DirectionalCoupler":
S_Param4, Power4 = self.ExtractFDTDResults(4, Parameters)
return S_Param4, Power4
elif self.Struct == "ArcWaveguide":
S_Param2, Power2 = self.ExtractFDTDResults(2, Parameters)
return S_Param2, Power2
elif self.Struct == "BendWaveguide":
S_Param22, Power22 = self.ExtractFDTDResults(2, Parameters)
return S_Param22, Power22
elif self.Struct == "StraightWaveguide":
S_Param22, Power22 = self.ExtractFDTDResults(2, Parameters)
return S_Param22, Power22
else:
raise ValueError("Incorect Structure was given. Possible structures are 'MMI2x1', 'MMI2x2', 'DirectionalCoupler', 'ArcWaveguide' and 'BendWaveguide' !")
elif Type == "EME":
OptionsMonitor, EME_Data = self.ExtractEMEResults('monitor', Type)
return OptionsMonitor, EME_Data
elif Type == "FDE":
TEModes, TEData, TMModes, TMData = self.ExtractFDEResultsExtendet(Parameters['Effective Index'])
return TEModes, TEData, TMModes, TMData
else:
raise ValueError(
"Invalid solver Type was given. To extract data from the simulation please give a correct simulation Type! Possible Types are: 'FDTD', 'EME' and 'FDE'.")
[docs]
def OverlapFDEModes(self, Parameters):
'''
Parameters
----------
Parameters : dict
Dictionary with parameters needed for the FDE analysis.
Returns
-------
None.
'''
self.Waveguide(Parameters)
self.FDE_Solver("Waveguide", Parameters)
self.StartFDESimulation()
TEModes, TMModes = self.ExtractFDEModes(EffIndexValue=Parameters["Effective Index"])
self.CoppyDcard(list(TEModes.keys())[0], list(TMModes.keys())[0])
self.removeObject()
print(f"TE Mode {list(TEModes.keys())[0]} and TM Mode {list(TMModes.keys())[0]} have been copyed to the Lumerical Dcard.")
# =============================================================================
# Selection of possible Functions
# =============================================================================
[docs]
def FDTD_Solver(self, Structure, Parameters):
'''
Parameters
----------
Structure : str
Structure to be simulated.
Parameters : dict
Dictionary with solver parameters.
Raises
------
ValueError
Error when wrong structure was selected.
Returns
-------
None.
'''
if Structure == "MMI2x1":
self.Struct = "MMI2x1"
self.setMMI2x1FDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "MMI2x1"
elif Structure == "MMI2x2":
self.Struct = "MMI2x2"
self.setMMI2x2FDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "MMI2x2"
elif Structure == "DirectionalCoupler":
self.Struct = "DirectionalCoupler"
self.setDCFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "Directional Coupler"
elif Structure == "BendWaveguide":
self.Struct = "BendWaveguide"
self.setBendWaveguideFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "Bend Waveguide"
elif Structure == "ArcWaveguide":
self.Struct = "ArcWaveguide"
self.setArcWaveguideFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "Arc Waveguide"
elif Structure == "StraightWaveguide":
self.Struct = "StraightWaveguide"
self.setStraightWaveguideFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "Straight Waveguide"
# elif Structure == "CascadetMMI":
# self.Struct = "CascadetMMI"
# self.setCascadetMMIFDTDSolver(Parameters, SpaceX, SpaceY)
elif Structure == "InverseTaper":
self.Strcut = "InverseTaper"
self.setInverseTaperFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "Inverse Taper"
elif Structure == "LenseFiber":
self.Struct = "LenseFiber"
self.setLenseFiberFDTD(Parameters)
self.SolverInfo["Simulated Object"] = "Lense Fiber"
elif Structure == "GratingCoupler":
self.Struct = "GratingCoupler"
self.setGratingCouplerFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "GratingCoupler"
elif Structure == "RingGratingCoupler":
self.Struct = "RingGratingCoupler"
self.setRingGratingCouplerFDTDSolver(Parameters)
self.SolverInfo["Simulated Object"] = "RingGratingCoupler"
else:
raise ValueError("Invalid Strucute for FDTD Solver is selected. Possible Strucures are MMI2x1, MMI2x2, DirectionalCoupler, BendWaveguide, ArcWaveguide, StraightWaveguide, InverseTaper, GratingCoupler and RingGratingCoupler")
[docs]
def EME_Solver(self, Structure, Parameters):
'''
Parameters
----------
Structure : str
Structure to be simulated.
Parameters : dict
Dictionary with solver parameters.
Raises
------
ValueError
Error when wrong structure was selected.
Returns
-------
None.
'''
if Structure == "MMI2x1":
self.setMMI2x1EMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "MMI2x1"
elif Structure == "MMI2x2":
self.setMMI2x2EMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "MMI2x2"
elif Structure == "DirectionalCoupler":
self.setDCEMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "Directional Coupler"
elif Structure == "WDM":
self.setWDMEMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "Angled Wavelength Division Multiplexer"
elif Structure == "StraightWaveguide":
self.setStraightWaveguideEMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "Straight Waveguide"
elif Structure == "InverseTaper":
self.setInverseTaperEMESolver(Parameters)
self.SolverInfo["Simulated Object"] = "Inverse Tapers"
else:
raise ValueError("Invalid Structure for EME Solver is selected. Possible Strucutres are MMI2x1, MMI2x2, DirectionalCoupler, WDM, StraightWaveguide andInverseTaper ")
[docs]
def FDE_Solver(self, Structure, Parameters):
'''
Parameters
----------
Structure : str
Structure to be simulated.
Parameters : dict
Dictionary with solver parameters.
Raises
------
ValueError
Error when wrong structure was selected.
Returns
-------
None
'''
if Structure == "Waveguide":
self.setWaveguideFDESolver(Parameters)
self.SolverInfo["Simulated Object"] = "Waveguide"
else:
raise ValueError("Invalid Structure for FDE Solver is selected. Possible Structure is Waveguide.")
[docs]
def ReturnLogInfo(self):
'''
Returns
-------
TYPE
Solver log information from last simulation.
'''
return self.SolverInfo
[docs]
def Script(self):
'''
Returns
-------
myscript : str
small script syntax for creating Tapers tooken from Lumerical . It is used only for check comperisons.
'''
myscript = 'delta_w = 2*thickness*tan((angle_side)*pi/180); \n'
myscript = myscript + '?"width_l = " + num2str(width_l); \n'
myscript = myscript + '?"width_r = " + num2str(width_r) + endl; \n'
myscript = myscript + 'width_top_l = width_l - (1-hfrac_ref)*delta_w; \n'
myscript = myscript + 'width_top_r = width_r - (1-hfrac_ref)*delta_w; \n'
myscript = myscript + '?"width_top_l = " + num2str(width_top_l); \n'
myscript = myscript + '?"width_top_r = " + num2str(width_top_r) + endl; \n'
myscript = myscript + 'width_bot_l = width_l + hfrac_ref*delta_w; \n'
myscript = myscript + 'width_bot_r = width_r + hfrac_ref*delta_w; \n'
myscript = myscript + '?"width_bot_l = " + num2str(width_bot_l); \n'
myscript = myscript + '?"width_bot_r = " + num2str(width_bot_r) + endl; \n'
myscript = myscript + 'if ((hfrac_ref>1) or (hfrac_ref<0)){?"Error: hfrac_ref must be between 0 and 1.";break;} \n'
myscript = myscript + 'if ((width_top_l<0) or (width_top_r<0) or (width_bot_l<0) or (width_bot_r<0)){?"Error: width and angle values are not correct.";break;} \n'
myscript = myscript + 'zmin = -thickness/2; \n'
myscript = myscript + 'zmax = thickness/2; \n'
myscript = myscript + 'xmin = -len/2; \n'
myscript = myscript + 'xmax = len/2; \n'
myscript = myscript + 'ymin_bot_l = -width_bot_l/2; \n'
myscript = myscript + 'ymax_bot_l = width_bot_l/2; \n'
myscript = myscript + 'ymin_bot_r = -width_bot_r/2; \n'
myscript = myscript + 'ymax_bot_r = width_bot_r/2; \n'
myscript = myscript + 'ymin_top_l = -width_top_l/2; \n'
myscript = myscript + 'ymax_top_l = width_top_l/2; \n'
myscript = myscript + 'ymin_top_r = -width_top_r/2; \n'
myscript = myscript + 'ymax_top_r = width_top_r/2; \n'
myscript = myscript + 'vtx= [xmin,ymin_bot_l,zmin; xmax,ymin_bot_r,zmin; xmax,ymax_bot_r,zmin; xmin,ymax_bot_l,zmin; xmin,ymin_top_l,zmax; xmax,ymin_top_r,zmax; xmax,ymax_top_r,zmax; xmin,ymax_top_l,zmax]; \n'
myscript = myscript + 'a = cell(6); \n'
myscript = myscript + 'for(i = 1:6){ a{i} = cell(1);} \n'
myscript = myscript + 'a{1}{1} = [1,4,3,2]; \n'
myscript = myscript + 'a{2}{1} = [1,2,6,5]; \n'
myscript = myscript + 'a{3}{1} = [2,3,7,6]; \n'
myscript = myscript + 'a{4}{1} = [3,4,8,7]; \n'
myscript = myscript + 'a{5}{1} = [1,5,8,4]; \n'
myscript = myscript + 'a{6}{1} = [5,6,7,8]; \n'
myscript = myscript + 'addplanarsolid(vtx,a); \n'
myscript = myscript + 'if (material=="<Object defined dielectric>"){setnamed("solid", "index",index);} \n'
myscript = myscript + 'else{setnamed("solid", "material",material);} \n'
return myscript
# =============================================================================
# Structures
# =============================================================================
[docs]
def Waveguide(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the Wavaguide.
Parameters['Substrate Height'] : int7float
Height of the substrate.
Parameters['WG Length']: int/float
Length of the Waveguide.
Parameters['WG Height'] : int/float
Height of the Waveguide
Parameters['WG Width'] : int/float
Width of the Waveguide
Parameters['angle'] : int
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Raises
------
ValueError
Value Error
Returns
-------
None.
'''
WG_Length = Parameters['WG Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters['angle']
Slab_Height = Parameters['Slab Height']
Material = Parameters['Material']
SubstrateHight = Parameters['Substrate Height']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
# Material defginition
if len(Material) < 2:
raise ValueError("List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
maxWGL = WG_Length / 2
minWGL = -WG_Length / 2
min_subW = -15e-6
max_subW = 15e-6
# Procentage deviation of the height between slab and waveguide
min_slabH = 0
max_slabH = Slab_Height
min_WGH = max_slabH
max_WGH = WG_Height
min_BoxH = -4e-6
# Triangle EQ for waveguide Width
x = abs(max_WGH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_WGH ** 2)
WG_W = WG_Width + 2 * extention
if Slab_Height == 0:
# Waveguide
self.lum.addwaveguide()
self.lum.set("name", 'Waveguide')
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", WG_Height/2)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL, 0], [minWGL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
# Mesh
self.lum.addmesh()
self.lum.set("name", 'Mesh_Waveguide')
self.lum.set('based on a structure', 1)
self.lum.set('structure', 'Waveguide')
self.lum.set('override x mesh', 0)
self.lum.set('dy', 0.01e-6)
self.lum.set('dz', 0.005e-6)
# Add SiO2 Slubstrate
self.lum.addrect()
self.lum.set("name", "Silicon")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z", -SubstrateHight/2)
self.lum.set("z span",SubstrateHight)
self.lum.set("x min", minWGL)
self.lum.set("x max", maxWGL)
self.lum.set("material", MaterialSub)
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set('z', max_WGH / 2 + min_WGH)
self.lum.set('z span', 2 * WG_Height)
self.lum.set("x min", minWGL)
self.lum.set("x max", maxWGL)
self.lum.set("material", MaterialClad)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
else:
pass
else:
# Slab
self.lum.addrect()
self.lum.set("name", "LN_slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", minWGL)
self.lum.set("x max", maxWGL)
self.lum.set("material", MaterialWG)
# Waveguide
self.lum.addwaveguide()
self.lum.set("name", 'Waveguide')
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", max_WGH / 2 + min_WGH)
self.lum.set("base width", WG_W)
self.lum.set("base height", max_WGH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL, 0], [minWGL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
# Mesh
self.lum.addmesh()
self.lum.set("name", 'Mesh_Waveguide')
self.lum.set('based on a structure', 1)
self.lum.set('structure', 'Waveguide')
# self.lum.set('dx', 0.01e-6)
self.lum.set('dy', 0.01e-6)
self.lum.set('dz', 0.005e-6)
# Add SiO2 Box
self.lum.addrect()
self.lum.set("name", "Silicon")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_BoxH)
self.lum.set("z max", min_slabH)
self.lum.set("x min", minWGL)
self.lum.set("x max", maxWGL)
self.lum.set("material", MaterialSub)
if Cladding == True:
# Create Cladding
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set('z', max_WGH / 2 + min_WGH)
self.lum.set('z span', 2 * WG_Height)
self.lum.set("x min", minWGL)
self.lum.set("x max", maxWGL)
self.lum.set("material", MaterialClad)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
else:
pass
[docs]
def StraightWaveguide(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the Straight Wavaguide.
Parameters['Substrate Height'] : int float
Substrate Height
Parameters['WG Height'] : int/float
Height of the Waveguide
Parameters['WG Width'] : int/float
Width of the Waveguide
Parameters['WG Length'] : int/float
Length of the Waveguide
Parameters['Taper'] : boolen
If Taper == False, only Waveguiedes will be constructed.
If Taper == True, only an single Taper will be constructed
Parameters['Taper Length'] : int/float
Taper Length
Parameters['Taper Width'] : int/float
Taper backside width, the frontside width is the waveguide width
Parameters['angle'] : int
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height.
Parameters['Material'] : list
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["Wavelength"] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
Bending angle of the Waveguide. Set it to 0 to simulate the straight waveguide.
If Waveguide Angle is different then 0, then the straight waveguide will be tilted
at the choosen degrees.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Taper Type is selected the user need to provide additional information:
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
Raises
------
ValueError
Value Error
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters["WG Length"]
Taper = Parameters['Taper']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
angle = Parameters['angle']
Slab_Height = Parameters['Slab Height']
Material = Parameters['Material']
WaveLength = Parameters["Wavelength"]
Side_Angle = Parameters["Waveguide Angle"]
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
if "Taper Type" in list(Parameters.keys()):
TaperType = "Inverse"
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
else:
TaperType = "Normal"
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
# Device specifications
Device_Width = 2*WG_Length #+ 1e-6 * 2
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
if Taper == False:
if Side_Angle == 0:
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + WG_Width)
self.lum.set("material", MaterialSub)
if Slab_Height != 0:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + WG_Width)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select("Slab")
self.lum.addtogroup("Straight Waveguide")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", max_slabH)
self.lum.set("z max", max_slabH*2)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + 1e-6)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Straight Waveguide")
else:
pass
else:
z_Offset = max_subH + WG_Height / 2
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", max_subH)
self.lum.set("z max", max_subH*2)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + 1e-6)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Straight Waveguide")
else:
pass
# Triangle EQ for waveguide Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
names = ["Waveguide"]
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[-0.5e-6, 0], [WG_Length+0.5e-6, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("first axis", 'z')
self.lum.set("rotation 1", Side_Angle)
self.lum.select("Waveguide")
self.lum.addtogroup("Straight Waveguide")
else:
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", WG_Length / 4)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + WG_Width)
self.lum.set("material", MaterialSub)
if Slab_Height == 0:
z_Offset = max_subH + WG_Height / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", WG_Length / 4)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + WG_Width)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select("Slab")
self.lum.addtogroup("Straight Waveguide")
# Triangle EQ for waveguide Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
names = ["Waveguide"]
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[-1e-6, 0], [WG_Length+1e-6, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("first axis", 'z')
self.lum.set("rotation 1", Side_Angle)
self.lum.select("Waveguide")
self.lum.addtogroup("Straight Waveguide")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", WG_Length / 4)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", min_slabH*2)
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length + WG_Width)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Straight Waveguide")
else:
pass
elif Taper == True:
# Device specifications
Device_Width = 2*TaperLength + WaveLength * 2 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", 0)
self.lum.set("x span", TaperLength + WG_Width)
self.lum.set("material", MaterialSub)
if Slab_Height != 0:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", 0)
self.lum.set("x span", TaperLength + WG_Width)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
# Taper Widths on Bott Cal
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
TaperSideWidth = TaperWidth + 2 * extention
WG_W = WG_Width + 2 * extention
z_Offset = max_slabH
self.lum.select("Slab")
self.lum.addtogroup("Straight Waveguide")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", max_slabH)
self.lum.set("z max", max_slabH*2)
self.lum.set("x",0)
self.lum.set("x span", TaperLength + WG_Width)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Straight Waveguide")
else:
pass
else:
z_Offset = max_subH + WG_Height / 2
# Taper Widths on Bott Cal
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
TaperSideWidth = TaperWidth + 2 * extention
WG_W = WG_Width + 2 * extention
z_Offset = max_subH
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", max_subH)
self.lum.set("z max", max_subH*2)
self.lum.set("x",0)
self.lum.set("x span", TaperLength + WG_Width)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Straight Waveguide")
else:
pass
if TaperType == "Normal":
# PWD Taper Hights
TaperZmin = z_Offset
TaperZmax = z_Offset + WG_Height
# PWB Taper Length
TaperXmax = TaperLength/2
TaperXmin = -TaperLength/2
# Create PWB Taper
ymin_bot_l = - WG_W / 2
ymax_bot_l = WG_W / 2
ymin_bot_r = - TaperSideWidth / 2
ymax_bot_r = TaperSideWidth / 2
ymin_top_l = - WG_Width / 2
ymax_top_l = WG_Width / 2
ymin_top_r = - TaperWidth / 2
ymax_top_r = TaperWidth / 2
vtx = np.array([[TaperXmin, ymin_bot_l, TaperZmin], # 1
[TaperXmax, ymin_bot_r, TaperZmin], # 2
[TaperXmax, ymax_bot_r, TaperZmin], # 3
[TaperXmin, ymax_bot_l, TaperZmin], # 4
[TaperXmin, ymin_top_l, TaperZmax], # 5
[TaperXmax, ymin_top_r, TaperZmax], # 6
[TaperXmax, ymax_top_r, TaperZmax], # 7
[TaperXmin, ymax_top_l, TaperZmax], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set('name', "Taper")
self.lum.select("Taper")
self.lum.addtogroup("Straight Waveguide")
else:
# PWB Taper Length
PWB_TaperXmax = TaperLength/2
PWB_TaperXmin = -TaperLength/2
# PWB Taper Hights
TaperZmin = z_Offset
TaperZmaxF = TaperHightF + Substrate_Height
TaperZmaxB = TaperHightB + Substrate_Height
# PWD Taper Y-Parameters
PWB_TaperPosYMax_BotR = [(TaperWidthF / 2)]
PWB_TaperPosYMin_BotR = [(- TaperWidthF / 2)]
PWB_TaperPosYMax_TopR = [(TaperWidthF / 2)]
PWB_TaperPosYMin_TopR = [(- TaperWidthF / 2)]
PWB_TaperPosYMax_BotL = [(TaperWidthB / 2)]
PWB_TaperPosYMin_BotL = [(- TaperWidthB / 2)]
PWB_TaperPosYMax_TopL = [(TaperWidthB / 2)]
PWB_TaperPosYMin_TopL = [(- TaperWidthB / 2)]
# Create PWB Taper
ymin_bot_l = PWB_TaperPosYMin_BotL[0]
ymax_bot_l = PWB_TaperPosYMax_BotL[0]
ymin_bot_r = PWB_TaperPosYMin_BotR[0]
ymax_bot_r = PWB_TaperPosYMax_BotR[0]
ymin_top_l = PWB_TaperPosYMin_TopL[0]
ymax_top_l = PWB_TaperPosYMax_TopL[0]
ymin_top_r = PWB_TaperPosYMin_TopR[0]
ymax_top_r = PWB_TaperPosYMax_TopR[0]
vtx = np.array([[PWB_TaperXmin, ymin_bot_l, TaperZmin], # 1
[PWB_TaperXmax, ymin_bot_r, TaperZmin], # 2
[PWB_TaperXmax, ymax_bot_r, TaperZmin], # 3
[PWB_TaperXmin, ymax_bot_l, TaperZmin], # 4
[PWB_TaperXmin, ymin_top_l, TaperZmaxB], # 5
[PWB_TaperXmax, ymin_top_r, TaperZmaxF], # 6
[PWB_TaperXmax, ymax_top_r, TaperZmaxF], # 7
[PWB_TaperXmin, ymax_top_l, TaperZmaxB], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set("override mesh order from material database",1)
self.lum.set("mesh order",3)
self.lum.set('name', "Taper")
self.lum.select("Taper")
self.lum.addtogroup("Straight Waveguide")
self.lum.select("Straight Waveguide::cladding")
self.lum.delete()
[docs]
def BendWaveguide(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the Bend Wavaguide.
Parameters['Substrate Height'] : int/float
Substrate Height
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide width
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Material'] : list
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["x span"] : int/float
Length of the S Curve. Span of the object.
Parameters["y span"] : int/float
Height of the curve. Difference between the input and output of the S-curve.
Parameters["poles"] : boolen
If Parameters["poles"] = True an Bezier Curbe will be made.
if Parameters["poles"] = False an Cosinus Curve = y_span*(cos((pi/(2*x_span))*t)^2) will be made. Where
t is in the range of 0 - y_span
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Raises
------
ValueError
Value Error Massage.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters['angle']
Slab_Height = Parameters['Slab Height']
Material = Parameters['Material']
x_span = Parameters["x span"]
y_span = Parameters["y span"]
polesList = Parameters["poles"]
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
if polesList == False:
pole = np.array([[0, 0], [x_span / 2, 0], [x_span / 2, y_span], [x_span, y_span]])
elif polesList == True:
K = ((np.pi -2)/np.pi)*100
pole = np.array([[0, 0], [0+(Parameters["x span"]*(K/100)), 0], [(Parameters["x span"])-(Parameters["x span"]*(K/100)), Parameters["y span"]], [Parameters["x span"], Parameters["y span"]]])
else:
raise ValueError('Parameters["poles"] should be an boolen variable! Please set Parameters["poles"] to True if Bezier Curves needed. Set Parameters["poles"] to False for Cosinus Curve!')
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
# Device specifications
Device_Width = y_span + 3e-6 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
min_subL = 0
max_subL = x_span
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
if Slab_Height == 0:
z_Offset = max_subH + WG_Height / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select('Slab')
self.lum.addtogroup("S Bend")
# Triangle EQ for waveguide Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
names = ["S-Bend"]
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
# if polesList == None:
# pole = np.array([[0, 0], [x_span / 2, 0], [x_span / 2, y_span], [x_span, y_span]])
# self.lum.set("poles", pole)
# self.lum.set("material", MaterialWG)
# else:
# pole = np.array(polesList)
# self.lum.set("poles", pole)
# self.lum.set("material", MaterialWG)
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "Cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set("z", z_Offset)
self.lum.set("z span", WG_Height*2)
self.lum.set("x", x_span / 2)
self.lum.set("x span", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("Cladding")
self.lum.addtogroup("S Bend")
else:
pass
self.lum.select("S-Bend")
self.lum.addtogroup("S Bend")
[docs]
def ArcWaveguide(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the Arc Wavaguide.
Parameters['Substrate Height'] : int/float
Substrate height
Parameters['WG Height'] : int/float
Waveguide height
Parameters['WG Width'] : int/float
Waveguide width
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Material'] : list
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["Wavelength"] : int/float
Wavelength
Parameters["S_Band Radius"] : int/float
Radius of the Circle in um
Parameters['Arc deg'] : int
Can be 90 or 180 for 1/4 of a cirle or 1/2 of a circle.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Raises
------
ValueError
Value Error
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters['angle']
Slab_Height = Parameters['Slab Height']
Material = Parameters['Material']
radius = Parameters["S_Band Radius"]
arc = Parameters['Arc deg']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
if arc == 90:
# magic number
# The cubic Bezier curve using this magic number in the pole points approximates the semi-circile with least error
m = 0.55191502449
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
# max_subW = Device_Width / 2
# min_subW = -Device_Width / 2
# min_subL = 0
# max_subL = radius
# Device specifications
# Device_Width = 2 * radius + WaveLength * 2 + WG_Width * 2 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
# max_subW = Device_Width / 2
# min_subW = -Device_Width / 2
# min_subL = 0
# max_subL = radius
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", (m * radius))
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width))
self.lum.set("material", MaterialSub)
if Slab_Height == 0:
# creating the thin film
z_Offset = max_subH + WG_Height / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width))
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select('Slab')
self.lum.addtogroup("90 Grad Bend")
# Triangle EQ for waveguide Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
names = ['Bend_Waveguide']
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[radius * 0, radius * 1], [radius * m, radius * 1], [radius * 1, radius * m],
[radius * 1, radius * 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.addwaveguide()
self.lum.set("name", "In_STR")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[0, radius * 1], [-1e-6, radius * 1]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.addwaveguide()
self.lum.set("name", "Out_STR")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[radius * 1, 0], [radius * 1 , -1e-6]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.select('Bend_Waveguide')
self.lum.addtogroup("90 Grad Bend")
self.lum.select('In_STR')
self.lum.addtogroup("90 Grad Bend")
self.lum.select('Out_STR')
self.lum.addtogroup("90 Grad Bend")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", max_slabH)
self.lum.set("z max", max_slabH*2)
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width))
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select('cladding')
self.lum.addtogroup("180 Grad Bend")
else:
pass
elif arc == 180:
# Device specifications
# Device_Width = 2 * radius + WaveLength * 2 + WG_Width * 2 # MMI_Width
# magic number
# The cubic Bezier curve using this magic number in the pole points approximates the semi-circile with least error
m = 0.55191502449
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y", (m * radius))
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width) * 2)
self.lum.set("material", MaterialSub)
if Slab_Height == 0:
z_Offset = max_subH + WG_Height / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width) * 2)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select('Slab')
self.lum.addtogroup("180 Grad Bend")
# Triangle EQ for waveguide Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
names = ['Arc Waveguide1', 'Arc Waveguie2']
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array(
[[radius * 0, radius * 1], [radius * m, radius * 1], [radius * 1, radius * m],
[radius * 1, radius * 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("first axis", 'z')
self.lum.set("rotation 1", 0)
self.lum.addwaveguide()
self.lum.set("name", names[1])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array(
[[radius * 0, radius * 1], [radius * m, radius * 1], [radius * 1, radius * m],
[radius * 1, radius * 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("first axis", 'z')
self.lum.set("rotation 1", 90)
self.lum.select('Arc Waveguide1')
self.lum.addtogroup("180 Grad Bend")
self.lum.select('Arc Waveguie2')
self.lum.addtogroup("180 Grad Bend")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z min", max_slabH)
self.lum.set("z max", max_slabH*2)
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width)*2)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select('cladding')
self.lum.addtogroup("180 Grad Bend")
else:
pass
else:
raise ValueError(
"Incorrect Arc Value. The arc can only be an integer and can only be arc = 90 or arc = 180!")
[docs]
def MMI2x2(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the 2x2 MMI.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Taper'] : boolen
Taper can be set to be True ot False.
if Taper == False - No Taper used
if Taper == True - Taper placed
Parameters['Taper Length'] : int/float
If Taper == True, then this will set the Tapers length. If Taper == False
this will be ignored and some random value can be given.
Parameters['Taper Width'] : int/float
If Taper == True, then this will set the Tapers width. If Taper == False
this will be ignored and some random value can be given.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Raises
------
ValueError
Value Error.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
Material = Parameters['Material']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
posOffset = Parameters['Position Offset']
Slab_Height = Parameters['Slab Height']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
Taper = Parameters['Taper']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + 3e-6 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
max_subW = Device_Width / 2
min_subW = -Device_Width / 2
min_subL = -Device_Length / 2
max_subL = Device_Length / 2
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select('Slab')
self.lum.addtogroup("MMI Object")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
WG_Width_top = WG_W
OffMax = MMI_Width / 2
if Taper == False:
#Too Fara and Too close
offset_WG = posOffset / 2 + WG_Width / 2 + WG_W / 2
offset_WG2 = posOffset / 2
if offset_WG > MMI_Wid / 2:
self.lum.deleteall()
raise ValueError('You are Trying to move the Waveguide outside the MMI. This is not possible!')
# elif offset_WG2 <0.5e-6:
# self.lum.deleteall()
# raise ValueError('The distance between the Tapers is less then 1 um !')
else:
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, WG_Length, 0, 0]
minWGL = [0, 0, -WG_Length, -WG_Length]
xPos = [max_MMIL, max_MMIL, min_MMIL, min_MMIL]
yPos = [WG_Width / 2 + posOffset / 2, -(WG_Width / 2 + posOffset / 2), WG_Width / 2 + posOffset / 2,
-(WG_Width / 2 + posOffset / 2)]
# Names of the WGs
names = ['Input WG_L', 'Input WG_R', 'Output WG_L', 'Output WG_R']
# create loop
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", xPos[i])
self.lum.set("y", yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select('MMI')
self.lum.addtogroup("MMI Object")
self.lum.select('Input WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Input WG_R')
self.lum.addtogroup("MMI Object")
self.lum.select('Output WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Output WG_R')
self.lum.addtogroup("MMI Object")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW )
self.lum.set("z", z_Offset)
self.lum.set("z span", max_MMIH * 2)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select('cladding')
self.lum.addtogroup("MMI Object")
else:
pass
elif Taper == True:
# Delate the Structure to start new
self.lum.deleteall()
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length + 2 * TaperLength
Device_Width = MMI_Width + 3e-6 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
max_subW = Device_Width / 2
min_subW = -Device_Width / 2
min_subL = -Device_Length / 2
max_subL = Device_Length / 2
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select('Slab')
self.lum.addtogroup("MMI Object")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# New x Length of the Tapers
maxLength = max_MMIL + TaperLength
minLength = min_MMIL - TaperLength
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, WG_Length, 0, 0]
minWGL = [0, 0, -WG_Length, -WG_Length]
xPos = [maxLength, maxLength, minLength, minLength]
yPos = [WG_Width / 2 + posOffset / 2, -(WG_Width / 2 + posOffset / 2), WG_Width / 2 + posOffset / 2,
-(WG_Width / 2 + posOffset / 2)]
# Names of the WGs
names = ['Input WG_L', 'Input WG_R', 'Output WG_L', 'Output WG_R']
TapersNames = ['Taper Input WG_L', 'Taper Input WG_R', 'Taper Output WG_L', 'Taper Output WG_R']
# Taper Widths on Bott Cal
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
TaperSideWidth = TaperWidth + 2 * extention
TaperPosXmin = [max_MMIL, max_MMIL, min_MMIL, min_MMIL]
TaperPosXmax = [max_MMIL + TaperLength, max_MMIL + TaperLength, min_MMIL - TaperLength,
min_MMIL - TaperLength]
PosOffset = [(posOffset / 2 + WG_Width / 2), - (posOffset / 2 + WG_Width / 2),
(posOffset / 2 + WG_Width / 2), -(posOffset / 2 + WG_Width / 2)]
TaperPosYMax_BotR = [(WG_W / 2), (WG_W / 2), (- WG_W / 2), (- WG_W / 2)]
TaperPosYMin_BotR = [(- WG_W / 2), (-WG_W / 2), (WG_W / 2), (WG_W / 2)]
TaperPosYMax_TopR = [(WG_Width / 2), (WG_Width / 2), (-WG_Width / 2), (- WG_Width / 2)]
TaperPosYMin_TopR = [(- WG_Width / 2), (-WG_Width / 2), (WG_Width / 2), (WG_Width / 2)]
TaperPosYMax_BotL = [(TaperSideWidth / 2), (TaperSideWidth / 2), (- TaperSideWidth / 2),
(- TaperSideWidth / 2)]
TaperPosYMin_BotL = [(- TaperSideWidth / 2), (- TaperSideWidth / 2), (TaperSideWidth / 2),
(TaperSideWidth / 2)]
TaperPosYMax_TopL = [(TaperWidth / 2), (TaperWidth / 2), (- TaperWidth / 2), (- TaperWidth / 2)]
TaperPosYMin_TopL = [(- TaperWidth / 2), (- TaperWidth / 2), (TaperWidth / 2), (TaperWidth / 2)]
offset_Taper = posOffset / 2 + WG_Width / 2 + TaperWidth / 2 # + WG_W / 2
BotCornerDistance = posOffset - TaperWidth / 2
# offset_Set_R = posOffset/2+TaperWidth/2
# if OffsetInpit <= OffMin or OffsetInpit >= OffMax or posOffset <= OffMin or posOffset >= OffMax:
if offset_Taper > OffMax:
self.lum.deleteall()
raise ValueError('You are Trying to move the Taper outside the MMI. This is not possible!')
# elif BotCornerDistance < 1e-6:
# self.lum.deleteall()
# raise ValueError('The distance between the Tapers is less then 1 um !')
else:
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, WG_Length, 0, 0]
minWGL = [0, 0, -WG_Length, -WG_Length]
# xPos = [max_MMIL, max_MMIL, min_MMIL, min_MMIL]
xPos = [maxLength, maxLength, minLength, minLength]
for i in range(len(xPos)):
TaperZmin = max_slabH
TaperZmax = max_slabH + max_MMIH
TaperXmin = TaperPosXmin[i]
TaperXmax = TaperPosXmax[i]
ymin_bot_l = TaperPosYMin_BotL[i]
ymax_bot_l = TaperPosYMax_BotL[i]
ymin_bot_r = TaperPosYMin_BotR[i]
ymax_bot_r = TaperPosYMax_BotR[i]
ymin_top_l = TaperPosYMin_TopL[i]
ymax_top_l = TaperPosYMax_TopL[i]
ymin_top_r = TaperPosYMin_TopR[i]
ymax_top_r = TaperPosYMax_TopR[i]
vtx = np.array([[TaperXmin, ymin_bot_l, TaperZmin], # 1
[TaperXmax, ymin_bot_r, TaperZmin], # 2
[TaperXmax, ymax_bot_r, TaperZmin], # 3
[TaperXmin, ymax_bot_l, TaperZmin], # 4
[TaperXmin, ymin_top_l, TaperZmax], # 5
[TaperXmax, ymin_top_r, TaperZmax], # 6
[TaperXmax, ymax_top_r, TaperZmax], # 7
[TaperXmin, ymax_top_l, TaperZmax], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set('name', TapersNames[i])
self.lum.set('y', PosOffset[i])
# create loop
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", xPos[i])
self.lum.set("y", yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select('MMI')
self.lum.addtogroup("MMI Object")
self.lum.select('Input WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Input WG_R')
self.lum.addtogroup("MMI Object")
self.lum.select('Output WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Output WG_R')
self.lum.addtogroup("MMI Object")
self.lum.select('Taper Input WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Taper Input WG_R')
self.lum.addtogroup("MMI Object")
self.lum.select('Taper Output WG_L')
self.lum.addtogroup("MMI Object")
self.lum.select('Taper Output WG_R')
self.lum.addtogroup("MMI Object")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z", z_Offset)
self.lum.set("z span", max_MMIH * 2)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select('cladding')
self.lum.addtogroup("MMI Object")
else:
pass
else:
raise ValueError(
"Incorect Taper input. Taper must be an boolen. You can choose from Taper = True or Taper = False!")
[docs]
def MMI2x1(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the 2x1 MMI.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Offset of the input waveguide.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Taper'] : boolen
Taper can be set to be True ot False.
if Taper == False - No Taper used
if Taper == True - Taper placed
Parameters['Taper Length'] : int/float
If Taper == True, then this will set the Tapers length. If Taper == False
this will be ignored and some random value can be given.
Parameters['Taper Width'] : int/float
If Taper == True, then this will set the Tapers width. If Taper == False
this will be ignored and some random value can be given.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
Raises
------
ValueError
DESCRIPTION.
Returns
-------
None.
'''
Material = Parameters['Material']
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
OffsetInput = Parameters['Offset Input']
posOffset = Parameters['Position Offset']
Slab_Height = Parameters['Slab Height']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
Taper = Parameters['Taper']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
if 'Offset Output' not in list(Parameters.keys()):
OffsetOutput = None
else:
OffsetOutput = Parameters['Offset Output']
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + 3e-6 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
max_subW = Device_Width / 2
min_subW = -Device_Width / 2
min_subL = -Device_Length / 2
max_subL = Device_Length / 2
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select("Slab")
self.lum.addtogroup("MMI Object")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
WG_Width_top = WG_W
OffMax = MMI_Width / 2
offset_Taper = posOffset / 2 + WG_Width / 2 + TaperWidth / 2 # + WG_W / 2
BotCornerDistance = posOffset/2 - TaperWidth / 2
offset_WG = posOffset / 2 + WG_Width / 2 + WG_W / 2
offset_WG2 = posOffset / 2
# if offset_WG2 < 0.5e-6:
# self.lum.deleteall()
# raise ValueError('The distance between the Tapers is less then 1 um !')
# else:
if Taper == False:
# if offset_WG > OffMax:
# self.lum.deleteall()
# raise ValueError('You are Trying to move the Waveguide outside the MMI. This is not possible!')
# else:
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, 0, 0]
minWGL = [0, -WG_Length, -WG_Length]
xPos = [max_MMIL, min_MMIL, min_MMIL]
if OffsetOutput == None:
yPos = [0 + OffsetInput, (WG_Width / 2 + posOffset / 2) , (- WG_Width / 2 - posOffset / 2) ]
else:
yPos = [0 + OffsetInput, (WG_Width / 2 + posOffset / 2) + OffsetOutput, (- WG_Width / 2 - posOffset / 2) + OffsetOutput ]
# Names of the WGs
names = ['Input WG', 'Output WG_L', 'Output WG_R']
# create loop
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", xPos[i])
self.lum.set("y", yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select("MMI")
self.lum.addtogroup("MMI Object")
self.lum.select("Input WG")
self.lum.addtogroup("MMI Object")
self.lum.select("Output WG_L")
self.lum.addtogroup("MMI Object")
self.lum.select("Output WG_R")
self.lum.addtogroup("MMI Object")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z", z_Offset)
self.lum.set("z span", max_MMIH + 0.4e-6)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("MMI Object")
else:
pass
elif Taper == True:
if offset_Taper > OffMax:
self.lum.deleteall()
raise ValueError('You are Trying to move the Taper outside the MMI. This is not possible!')
else:
# Delate the Structure to start new
self.lum.deleteall()
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length + 2*TaperLength
Device_Width = MMI_Width + 3e-6 # WaveLength * 2
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
max_subW = Device_Width / 2
min_subW = -Device_Width / 2
min_subL = -Device_Length / 2
max_subL = Device_Length / 2
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select("Slab")
self.lum.addtogroup("MMI Object")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# New x Length of the Tapers
maxLength = max_MMIL + TaperLength
minLength = min_MMIL - TaperLength
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, 0, 0]
minWGL = [0, -WG_Length, -WG_Length]
xPos = [maxLength, minLength, minLength]
yPos = [0 + OffsetInput, WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
# Names of the WGs
names = ['Input WG', 'Output WG_L', 'Output WG_R']
TapersNames = ['Taper Input WG', 'Taper Output WG_L', 'Taper Output WG_R']
# Taper loop
# Taper Widths on Bott Cal
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
TaperSideWidth = TaperWidth + 2 * extention
TaperPosXmin = [max_MMIL, min_MMIL, min_MMIL]
TaperPosXmax = [max_MMIL + TaperLength, min_MMIL - TaperLength, min_MMIL - TaperLength]
PosOffset = [0, (posOffset / 2 + WG_Width / 2), -(posOffset / 2 + WG_Width / 2)]
TaperPosYMax_BotR = [(0 + OffsetInput + WG_W / 2), (-WG_W / 2), (-WG_W / 2)]
TaperPosYMin_BotR = [(0 + OffsetInput - WG_W / 2), (WG_W / 2), (WG_W / 2)]
TaperPosYMax_TopR = [(0 + OffsetInput + WG_Width / 2), (-WG_Width / 2), (-WG_Width / 2)]
TaperPosYMin_TopR = [(0 + OffsetInput - WG_Width / 2), (+WG_Width / 2), (+WG_Width / 2)]
TaperPosYMax_BotL = [(0 + OffsetInput + TaperSideWidth / 2), (-TaperSideWidth / 2),
(-TaperSideWidth / 2)]
TaperPosYMin_BotL = [(0 + OffsetInput - TaperSideWidth / 2), (+TaperSideWidth / 2),
(+TaperSideWidth / 2)]
TaperPosYMax_TopL = [(0 + OffsetInput + TaperWidth / 2), (-TaperWidth / 2), (-TaperWidth / 2)]
TaperPosYMin_TopL = [(0 + OffsetInput - TaperWidth / 2), (+TaperWidth / 2), (+TaperWidth / 2)]
for i in range(len(xPos)):
TaperZmin = max_slabH
TaperZmax = max_slabH + max_MMIH
TaperXmin = TaperPosXmin[i]
TaperXmax = TaperPosXmax[i]
ymin_bot_l = TaperPosYMin_BotL[i]
ymax_bot_l = TaperPosYMax_BotL[i]
ymin_bot_r = TaperPosYMin_BotR[i]
ymax_bot_r = TaperPosYMax_BotR[i]
ymin_top_l = TaperPosYMin_TopL[i]
ymax_top_l = TaperPosYMax_TopL[i]
ymin_top_r = TaperPosYMin_TopR[i]
ymax_top_r = TaperPosYMax_TopR[i]
vtx = np.array([[TaperXmin, ymin_bot_l, TaperZmin], # 1
[TaperXmax, ymin_bot_r, TaperZmin], # 2
[TaperXmax, ymax_bot_r, TaperZmin], # 3
[TaperXmin, ymax_bot_l, TaperZmin], # 4
[TaperXmin, ymin_top_l, TaperZmax], # 5
[TaperXmax, ymin_top_r, TaperZmax], # 6
[TaperXmax, ymax_top_r, TaperZmax], # 7
[TaperXmin, ymax_top_l, TaperZmax], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set('name', TapersNames[i])
self.lum.set('y', PosOffset[i])
# create loop
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", xPos[i])
self.lum.set("y", yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select("MMI")
self.lum.addtogroup("MMI Object")
self.lum.select("Input WG")
self.lum.addtogroup("MMI Object")
self.lum.select("Output WG_L")
self.lum.addtogroup("MMI Object")
self.lum.select("Output WG_R")
self.lum.addtogroup("MMI Object")
self.lum.select("Taper Input WG")
self.lum.addtogroup("MMI Object")
self.lum.select("Taper Output WG_L")
self.lum.addtogroup("MMI Object")
self.lum.select("Taper Output WG_R")
self.lum.addtogroup("MMI Object")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z", z_Offset)
self.lum.set("z span", max_MMIH + 0.4e-6)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("MMI Object")
else:
pass
else:
raise ValueError(
"Incorect Taper input. Taper must be an boolen. You can choose from Taper = True or Taper = False!")
[docs]
def DirectionalCoupler(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the Directional Coupler.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['Substrate Width'] : int/float
Substrate Width.
Parameters['DC Length'] : int/float
Length of the directional coupler
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Position Offset'] : int/float
Offset between the waveguides. The minimum distance between Waveguides is 1 um
becouse of manufactering restrictions in the University.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Raises
------
ValueError
DESCRIPTION.
Returns
-------
None.
'''
Material = Parameters['Material']
Substrate_Width = Parameters['Substrate Width']
Substrate_Height = Parameters['Substrate Height']
DC_Lenght = Parameters['DC Length']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
posOffset = Parameters['Position Offset']
Slab_Height = Parameters['Slab Height']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
print(Material)
# Device specifications
Device_Length = DC_Lenght
Device_Width = Substrate_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
max_subW = Device_Width / 2
min_subW = -Device_Width / 2
min_subL = -Device_Length / 2
max_subL = Device_Length / 2
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
if Slab_Height == 0:
z_Offset = max_subH + WG_Height / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + WG_Height / 2
self.lum.select("Slab")
self.lum.addtogroup("Directional Coupler")
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
WG_Width_bott = WG_W
offset_WG = posOffset / 2 + WG_Width / 2 + WG_W / 2
if offset_WG > Device_Width / 2:
self.lum.deleteall()
raise ValueError('You are Trying to move the Waveguide outside the MMI. This is not possible!')
else:
# Mirror the In and Out WG on both sides
maxWGL = [DC_Lenght / 2, DC_Lenght / 2]
minWGL = [-DC_Lenght / 2, -DC_Lenght / 2]
xPos = [0, 0]
yPos = [WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
# Names of the WGs
names = ['Top WG', 'Bottom WG']
# create loop
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", xPos[i])
self.lum.set("y", yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_bott)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.select("Top WG")
self.lum.addtogroup("Directional Coupler")
self.lum.select("Bottom WG")
self.lum.addtogroup("Directional Coupler")
if Cladding == True:
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y min", min_subW)
self.lum.set("y max", max_subW)
self.lum.set("z min", max_subH)
self.lum.set("z max", 2e-6)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("Directional Coupler")
else:
pass
[docs]
def WDM(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the WDM.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Angle Thetha'] :
Input and output angle of the waveguide. This is only temporally
Parameters['Taper Width'] : int/float
Backside width of the Taper, frontside width is the waveguide width
Parameters['Taper Length'] : int/float
Length of the Taper.
Parameters['Taper'] : boolen
If Taper == False, no Taper will be placed with the Waveguides.
If Taper == True, tapers will be placed with the waveguides
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Raises
------
ValueError
DESCRIPTION.
Returns
-------
None.
'''
Material = Parameters['Material']
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WaveLength = Parameters['Wavelength']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
Slab_Height = Parameters['Slab Height']
angleTheta = Parameters['Angle Thetha']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
Taper = Parameters['Taper']
if "Cladding" in list(Parameters.keys()):
Cladding = True
else:
Cladding = False
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
if Taper == False:
# Device specifications
Device_Length = MMI_Length + 4 * WG_Length
Device_Width = MMI_Width + 2*WG_Length + 3e-6 # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("material", MaterialSub)
self.lum.select("Substrate")
self.lum.addtogroup("WDM")
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("material", MaterialSlab)
self.lum.select("Slab")
self.lum.addtogroup("WDM")
# creating the MMI
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
z_Offset = max_slabH + max_MMIH / 2
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
# self.lum.addwaveguide()
# self.lum.set("name", "MMI")
# self.lum.set("base height", max_MMIH)
# self.lum.set("base angle", 90 - angle)
# self.lum.set("x", 0)
# self.lum.set("y", 0)
# self.lum.set("z", z_Offset)
# pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
# self.lum.set("poles", pole)
# self.lum.set("material", MaterialWG)
# self.lum.set("base width", MMI_Wid)
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
# Correction in Y-Axis
difY = (TaperLength / 2) * np.cos(angleTheta * np.pi / 180)
xLen = 2 * (TaperLength / 2) * np.sin((angleTheta / 2) * np.pi / 180)
Diff = WG_W - WG_Width
NewY = -MMI_Width / 2 + TaperWidth / 2 - np.sqrt((TaperLength / 2) ** 2 - difY ** 2) # - xLen - Diff/2 #
Li = (4 * 2.5 * MMI_Width ** 2) / WaveLength
#Creaate MMI
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
pole = np.array([[max_MMIL + WG_Length - xLen, 0], [min_MMIL - xLen / 2, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
self.lum.select("MMI")
self.lum.addtogroup("WDM")
# Names of the WGs
Waveguides = ['LN_Input', 'LN_Output']
myscript = self.Script()
self.lum.addstructuregroup()
self.lum.set("name", Waveguides[0])
self.lum.set("construction group", 1)
self.lum.adduserprop("thickness", 2, WG_Height)
self.lum.adduserprop("angle_side", 0, angle)
self.lum.adduserprop("width_l", 2, WG_Width)
self.lum.adduserprop("width_r", 2, WG_Width)
self.lum.adduserprop("hfrac_ref", 0, 1)
self.lum.adduserprop("len", 2, WG_Length)
self.lum.adduserprop("material", 5, MaterialWG)
self.lum.adduserprop("index", 0, 1)
self.lum.set("script", myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1", angleTheta)
self.lum.set("x", -MMI_Length / 2 - TaperLength / 2) # -MMI_Length / 2
self.lum.set("z", z_Offset)
self.lum.set("y", NewY)
self.lum.addstructuregroup()
self.lum.set("name", Waveguides[1])
self.lum.set("construction group", 1)
self.lum.adduserprop("thickness", 2, WG_Height)
self.lum.adduserprop("angle_side", 0, angle)
self.lum.adduserprop("width_l", 2, WG_Width)
self.lum.adduserprop("width_r", 2, WG_Width)
self.lum.adduserprop("hfrac_ref", 0, 1)
self.lum.adduserprop("len", 2, WG_Length)
self.lum.adduserprop("material", 5, MaterialWG)
self.lum.adduserprop("index", 0, 1)
self.lum.set("script", myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1", angleTheta)
self.lum.set("x", MMI_Length / 2 + TaperLength / 2) # + MMI_Length / 2
self.lum.set("z", z_Offset)
self.lum.set("y", -NewY)
if Cladding == True:
# Create Cladding
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set('z min', max_slabH)
self.lum.set('z max', max_slabH + 4 * WG_Height)
self.lum.set("material", MaterialClad)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("WDM")
# self.lum.select("cladding")
# self.lum.addtogroup("Directional Coupler")
else:
pass
elif Taper == True:
# Device specifications
Device_Length = MMI_Length + 4 * WG_Length
Device_Width = MMI_Width + 2*WG_Length + WaveLength * 2
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
z_Offset = max_slabH + max_MMIH / 2
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
TaperNames = ["Input_Taper", "Output_Taper"]
Waveguides = ["Input_WG", "Output_WG"]
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
# Correction in Y-Axis
difY = (TaperLength/2) * np.cos(angleTheta*np.pi/180)
xLen = 2*(TaperLength/2)* np.sin((angleTheta/2) * np.pi/180)
Diff = WG_W - WG_Width
NewY = -MMI_Width / 2 + TaperWidth/2- np.sqrt( (TaperLength/2)**2 - difY**2) # - xLen - Diff/2 #
Li =( 4*2.5*MMI_Width**2)/WaveLength
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("material", MaterialSub)
self.lum.select("Substrate")
self.lum.addtogroup("WDM")
self.lum.addrect()
self.lum.set("name", "Slab")
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("material", MaterialSlab)
self.lum.select("Slab")
self.lum.addtogroup("WDM")
# creating the MMI
self.lum.addwaveguide()
self.lum.set("name", "MMI")
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Offset)
pole = np.array([[max_MMIL + WG_Length - xLen , 0], [min_MMIL- xLen/2, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
self.lum.select("MMI")
self.lum.addtogroup("WDM")
# Input and Output Tapers and WGs
myscript = self.Script()
self.lum.addstructuregroup()
self.lum.set("name",TaperNames[0])
self.lum.set("construction group",1)
self.lum.adduserprop("thickness",2, WG_Height)
self.lum.adduserprop("angle_side",0, angle)
self.lum.adduserprop("width_l",2, WG_Width)
self.lum.adduserprop("width_r",2, TaperWidth)
self.lum.adduserprop("hfrac_ref",0,1)
self.lum.adduserprop("len",2, TaperLength)
self.lum.adduserprop("material",5,MaterialWG)
self.lum.adduserprop("index",0,1)
self.lum.set("script",myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1",angleTheta)
self.lum.set("x", -MMI_Length / 2 - TaperLength/2 ) #-MMI_Length / 2
self.lum.set("z", z_Offset)
self.lum.set("y", NewY)
self.lum.addstructuregroup()
self.lum.set("name",Waveguides[0])
self.lum.set("construction group",1)
self.lum.adduserprop("thickness",2, WG_Height)
self.lum.adduserprop("angle_side",0, angle)
self.lum.adduserprop("width_l",2, WG_Width)
self.lum.adduserprop("width_r",2,WG_Width)
self.lum.adduserprop("hfrac_ref",0,1)
self.lum.adduserprop("len",2, WG_Length)
self.lum.adduserprop("material",5,MaterialWG)
self.lum.adduserprop("index",0,1)
self.lum.set("script",myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1",angleTheta)
self.lum.set("x", -MMI_Length / 2 - TaperLength/2 ) #-MMI_Length / 2
self.lum.set("z", z_Offset)
self.lum.set("y", NewY )
self.lum.addstructuregroup()
self.lum.set("name",TaperNames[1])
self.lum.set("construction group",1)
self.lum.adduserprop("thickness",2, WG_Height)
self.lum.adduserprop("angle_side",0, angle)
self.lum.adduserprop("width_l",2, TaperWidth)
self.lum.adduserprop("width_r",2,WG_Width)
self.lum.adduserprop("hfrac_ref",0,1)
self.lum.adduserprop("len",2, TaperLength)
self.lum.adduserprop("material",5,MaterialWG)
self.lum.adduserprop("index",0,1)
self.lum.set("script",myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1",angleTheta)
self.lum.set("x", MMI_Length / 2 + TaperLength/2 ) #+ MMI_Length / 2
self.lum.set("z", z_Offset)
self.lum.set("y", -NewY )
self.lum.addstructuregroup()
self.lum.set("name",Waveguides[1])
self.lum.set("construction group",1)
self.lum.adduserprop("thickness",2, WG_Height)
self.lum.adduserprop("angle_side",0, angle)
self.lum.adduserprop("width_l",2, WG_Width)
self.lum.adduserprop("width_r",2,WG_Width)
self.lum.adduserprop("hfrac_ref",0,1)
self.lum.adduserprop("len",2, WG_Length)
self.lum.adduserprop("material",5,MaterialWG)
self.lum.adduserprop("index",0,1)
self.lum.set("script",myscript)
self.lum.set("first axis", "z")
self.lum.set("rotation 1",angleTheta)
self.lum.set("x", MMI_Length / 2 + TaperLength/2 ) #+ MMI_Length / 2
self.lum.set("z", z_Offset )
self.lum.set("y", -NewY)
if Cladding == True:
# Create Cladding
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set('z min', max_slabH)
self.lum.set('z max', max_slabH + 4 * WG_Height)
self.lum.set("material", MaterialClad)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.select("cladding")
self.lum.addtogroup("WDM")
else:
pass
[docs]
def InverseTaper(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the InverseTaper.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
For this structure 3 Material will be needed
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width. Also in this function and ONLY in this function this will be the
ibverse Taper width!!!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Taper Length'] : int/float
Inverse Taper Length
Parameters['Taper Width'] : int/float
Front Width of the inverse Taper!! In this Function and ONLY in this function, the
Parameters['Taper Width'] is the frond width of the inverse Taper!
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Raises
------
ValueError
DESCRIPTION.
Returns
-------
None.
'''
Material = Parameters['Material']
Substrate_Height = Parameters['Substrate Height']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
Slab_Height = Parameters['Slab Height']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
# SMF Parameters
CoreDiameter = Parameters["SMF Core Diameter"]
CladdingDiameter = Parameters["SMF Cladding Diameter"]
CoreIndex = Parameters["SMF Core Index"]
CladdingIndex = Parameters["SMF Cladding Index"]
# Material definition
if len(Material) < 3:
raise ValueError(
"List of materials must contain at least 3 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material', 'Material Photonic Wire Bonding']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
MaterialPWB = Material[2]
# creating the substrate
max_subH = Substrate_Height/2
min_subH = -Substrate_Height/2
# make substrate
self.lum.addrect()
self.lum.set("name", "Substrate InverseTaper")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB * 2)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", -TaperLength_PWB/2)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length )
# self.lum.set("x", 10e-6)
# self.lum.set("x span", TaperLength_PWB * 2)
self.lum.set("material", MaterialSub)
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
# Create Slab and Check if Slab is needed
if Slab_Height == 0:
pass
else:
self.lum.addrect()
self.lum.set("name", "LN_slab")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("y", 0e-12)
self.lum.set("y span", TaperWidthB * 2)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", -TaperLength_PWB / 2 - 0.1e-6)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length)
# self.lum.set("x", 10e-6)
# self.lum.set("x span", TaperLength_PWB * 2)
self.lum.set("material", MaterialSlab)
# Names of the WGs
TapersNames = ['Taper_PWB', 'Inverse_Taper']
# Taper sideangle widths
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
TaperSideWidth = TaperWidth + 2 * extention
WG_W = WG_Width + 2 * extention
# PWD Taper Y-Parameters
PWB_TaperPosYMax_BotR = [(TaperWidthF / 2)]
PWB_TaperPosYMin_BotR = [(- TaperWidthF / 2)]
PWB_TaperPosYMax_TopR = [(TaperWidthF / 2)]
PWB_TaperPosYMin_TopR = [(- TaperWidthF / 2)]
PWB_TaperPosYMax_BotL = [(TaperWidthB / 2)]
PWB_TaperPosYMin_BotL = [(- TaperWidthB / 2)]
PWB_TaperPosYMax_TopL = [(TaperWidthB / 2)]
PWB_TaperPosYMin_TopL = [(- TaperWidthB / 2)]
# Inverse Taper Y-Parameters
TaperPosYMax_BotR = [(WG_W / 2)]
TaperPosYMin_BotR = [(- WG_W / 2)]
TaperPosYMax_TopR = [(WG_Width / 2)]
TaperPosYMin_TopR = [(- WG_Width / 2)]
TaperPosYMax_BotL = [(TaperSideWidth / 2)]
TaperPosYMin_BotL = [(- TaperSideWidth / 2)]
TaperPosYMax_TopL = [(TaperWidth / 2)]
TaperPosYMin_TopL = [(- TaperWidth / 2)]
if Slab_Height == 0:
z_Offset = max_subH
else:
z_Offset = max_slabH
# PWB Taper Hights
TaperZmin = z_Offset
TaperZmaxF = TaperHightF + TaperZmin
TaperZmaxB = TaperHightB + TaperZmin
# Inverse Taper Hights
TaperZmin = z_Offset
TaperZmax = z_Offset + WG_Height
# PWB Taper Length
PWB_TaperXmin = -TaperLength_PWB / 2
PWB_TaperXmax = TaperLength_PWB / 2
# Inverse Taper Length
TaperXmin = -TaperLength / 2
TaperXmax = TaperLength / 2
# Create Inverse Taper
ymin_bot_l = TaperPosYMin_BotL[0]
ymax_bot_l = TaperPosYMax_BotL[0]
ymin_bot_r = TaperPosYMin_BotR[0]
ymax_bot_r = TaperPosYMax_BotR[0]
ymin_top_l = TaperPosYMin_TopL[0]
ymax_top_l = TaperPosYMax_TopL[0]
ymin_top_r = TaperPosYMin_TopR[0]
ymax_top_r = TaperPosYMax_TopR[0]
vtx = np.array([[TaperXmin, ymin_bot_l, TaperZmin], # 1
[TaperXmax, ymin_bot_r, TaperZmin], # 2
[TaperXmax, ymax_bot_r, TaperZmin], # 3
[TaperXmin, ymax_bot_l, TaperZmin], # 4
[TaperXmin, ymin_top_l, TaperZmax], # 5
[TaperXmax, ymin_top_r, TaperZmax], # 6
[TaperXmax, ymax_top_r, TaperZmax], # 7
[TaperXmin, ymax_top_l, TaperZmax], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
Offset_InvTaper = PWB_TaperXmin + TaperXmax
if Offset_InvTaper - TaperXmax / 2 > PWB_TaperXmax:
raise ValueError(
"Inverse Taper is moved outside the PWB Taper! The maximal x-Offset of the Inverse Taper is Parameters['Offset Inverse Taper'] = " + str(
PWB_TaperXmax + TaperXmax))
else:
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set('name', TapersNames[1])
self.lum.set('second axis', 'z')
self.lum.set('rotation 2', 0)
self.lum.set('x', Offset_InvTaper)
# Create PWB Taper
ymin_bot_l = PWB_TaperPosYMin_BotL[0]
ymax_bot_l = PWB_TaperPosYMax_BotL[0]
ymin_bot_r = PWB_TaperPosYMin_BotR[0]
ymax_bot_r = PWB_TaperPosYMax_BotR[0]
ymin_top_l = PWB_TaperPosYMin_TopL[0]
ymax_top_l = PWB_TaperPosYMax_TopL[0]
ymin_top_r = PWB_TaperPosYMin_TopR[0]
ymax_top_r = PWB_TaperPosYMax_TopR[0]
vtx = np.array([[PWB_TaperXmin, ymin_bot_l, TaperZmin], # 1
[PWB_TaperXmax, ymin_bot_r, TaperZmin], # 2
[PWB_TaperXmax, ymax_bot_r, TaperZmin], # 3
[PWB_TaperXmin, ymax_bot_l, TaperZmin], # 4
[PWB_TaperXmin, ymin_top_l, TaperZmaxB], # 5
[PWB_TaperXmax, ymin_top_r, TaperZmaxF], # 6
[PWB_TaperXmax, ymax_top_r, TaperZmaxF], # 7
[PWB_TaperXmin, ymax_top_l, TaperZmaxB], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialPWB)
self.lum.set("override mesh order from material database",1)
self.lum.set("mesh order",3)
self.lum.set('name', TapersNames[0])
# Make Sqered WG-Extention of the PWB for mode Calculations
# Extra Waveguide Lenght
Ext_WGLength = 1e-6
# PWD_x_Offset = PWB_TaperXmin
# self.lum.addrect()
# self.lum.set('x min', PWD_x_Offset - Ext_WGLength)
# self.lum.set('x max',PWD_x_Offset)
# self.lum.set('y', 0)
# self.lum.set('y span', TaperWidthB)
# self.lum.set('z min', TaperZmin)
# self.lum.set('z max', TaperZmaxB)
# self.lum.set("override mesh order from material database", 1)
# self.lum.set("mesh order", 3)
# self.lum.set('name', 'WG_Extention_PWB')
# self.lum.set('material', MaterialPWB)
# Make Sqred WG-Extention for the inverse Taper
x_min = (Offset_InvTaper + TaperXmax)
x_max = PWB_TaperXmax + Ext_WGLength
center = TaperXmax + Offset_InvTaper
ZOffset_WG = TaperZmin+ WG_Height/2
self.lum.addwaveguide()
self.lum.set("name", 'WG_Extention_Inverse_Taper')
self.lum.set("x", center )
self.lum.set("y", 0)
self.lum.set('z', ZOffset_WG)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
pole = np.array([[0, 0], [WG_Length, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
# Create Taper abjects
self.lum.select("WG_Extention_Inverse_Taper")
self.lum.addtogroup('InverseTaper')
self.lum.select(TapersNames[1])
self.lum.addtogroup('InverseTaper')
# Create the SMF
self.lum.addcircle()
self.lum.set("name", "core")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 5)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 90)
self.lum.set("alpha", 1)
self.lum.set("radius", CoreDiameter/2)
self.lum.set("index", CoreIndex)
self.lum.set("x", PWB_TaperXmin)
self.lum.set("y", 0)
self.lum.set("z", CoreDiameter/2 + z_Offset)
self.lum.set("z span", 2e-6)
self.lum.addcircle()
self.lum.set("name", "cladding")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 6)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 90)
self.lum.set("alpha", 0.35)
self.lum.set("radius", CladdingDiameter/2)
self.lum.set("index", CladdingIndex)
self.lum.set("x", PWB_TaperXmin )
self.lum.set("y", 0)
self.lum.set("z", CoreDiameter/2 + z_Offset)
self.lum.set("z span", 2e-6)
self.lum.select("core")
self.lum.addtogroup('SMF')
self.lum.select("cladding")
self.lum.addtogroup('SMF')
self.lum.select('SMF')
self.lum.set("x", -1e-6)
[docs]
def CascadetMMI(self, Parameters, SpaceX, SpaceY):
'''
Parameters
----------
Parameters : TYPE
DESCRIPTION.
Returns
-------
None.
'''
Material = Parameters['Material']
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
OffsetInput = Parameters['Offset Input']
posOffset = Parameters['Position Offset']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
TaperWidth = Parameters['Taper Width']
Taper = Parameters['Taper']
x_span = Parameters["x span"]
y_span = Parameters["y span"]
polesList = Parameters["poles"]
# Material definition
if len(Material) < 2:
raise ValueError(
"List of materials must contain at least 2 materials!, Parameters['Material'] = ['Cladding/Substrat', 'Object Material']")
else:
MaterialSub = Material[0]
MaterialClad = Material[0]
MaterialSlab = Material[1]
MaterialWG = MaterialSlab
# Device specifications
Device_Length = (2*MMI_Length + 2 * WG_Length + 2*SpaceX )
Device_Width = (3*MMI_Width + 2 * WaveLength) + 3*SpaceY # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
min_subL = -3*(Device_Length /2)
max_subL = 3*(Device_Length / 2)
self.lum.addrect()
self.lum.set("name", "Substrate Main")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab Main")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select("Slab")
self.lum.addtogroup("Cascadet MMI")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
Parameters__Position_X = [0, -SpaceX -MMI_Length - 2*WG_Length , -SpaceX -MMI_Length - 2*WG_Length]
Parameters__Position_Y = [0, (MMI_Wid + SpaceY)/2, -(MMI_Wid + SpaceY)/2]
names_MMI = ["MMI In", "MMI Out1", "MMI Out2"]
for i in range(3):
self.lum.addwaveguide()
self.lum.set("name", names_MMI[i])
self.lum.set("x", Parameters__Position_X[i])
self.lum.set("y", Parameters__Position_Y[i])
self.lum.set("z", z_Offset)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
WG_Width_top = WG_W
OffMax = MMI_Width / 2
offset_Taper = posOffset / 2 + WG_Width / 2 + TaperWidth / 2 # + WG_W / 2
BotCornerDistance = posOffset/2 - TaperWidth / 2
offset_WG = posOffset / 2 + WG_Width / 2 + WG_W / 2
offset_WG2 = posOffset / 2
if offset_WG2 < 0.5e-6:
self.lum.deleteall()
raise ValueError('The distance between the Tapers is less then 1 um !')
else:
if Taper == False:
if offset_WG > OffMax:
self.lum.deleteall()
raise ValueError('You are Trying to move the Waveguide outside the MMI. This is not possible!')
else:
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, 0, 0]
minWGL = [0, -WG_Length, -WG_Length]
xPos = [max_MMIL, min_MMIL, min_MMIL]
yPos = [0 + OffsetInput, WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
# Create two Bends Cos or Bezier depending on the poles option
x_span = SpaceX
y_span = (Parameters__Position_Y[1] + yPos[0]) - (Parameters__Position_Y[0] + yPos[1] )
if polesList == False:
pole = np.array([[0, 0], [x_span / 2, 0], [x_span / 2, y_span], [x_span, y_span]])
pole1 = np.array([[0, y_span], [x_span / 2, y_span], [x_span / 2, 0], [x_span,0]])
elif polesList == True:
K = ((np.pi -2)/np.pi)*100
pole = np.array([[0, 0], [0+(x_span*(K/100)), 0], [(x_span)-(x_span*(K/100)), y_span], [x_span, y_span]])
pole1 = np.array([[0, y_span], [0+(x_span*(K/100)), y_span], [(x_span)-(x_span*(K/100)), 0], [x_span, 0]])
else:
raise ValueError('Parameters["poles"] should be an boolen variable! Please set Parameters["poles"] to True if Bezier Curves needed. Set Parameters["poles"] to False for Cosinus Curve!')
names = ["S-Bend Top", "S-Bend Bot"]
S_Bend_X_Offset = [(-SpaceX - MMI_Length/2 - WG_Length), (SpaceX/2 - SpaceX/2 - MMI_Length/2 - WG_Length)]
S_Bend_Y_Offset = [( WG_Width / 2 + posOffset / 2) ,-( WG_Width / 2 + posOffset / 2)]
z_rotation = [0,180]
poles = [pole1, pole]
for i in range(2):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", S_Bend_X_Offset[i])
self.lum.set("y", S_Bend_Y_Offset[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
self.lum.set("poles", poles[i])
self.lum.set("material", MaterialWG)
self.lum.set("first axis","z")
self.lum.set("rotation 1",z_rotation[i])
# Names of the WGs
names1 = ['MMI In_WG', 'MMI Out_WG_Top', 'MMI Out_WG_Bot']
names2 = ['MMIOut1 In_WG', 'MMIOut1 Out_WG_Top', 'MMIOut1 Out_WG_Bot']
names3 = ['MMIOut2 In_WG', 'MMIOut2 Out_WG_Top', 'MMIOut2 Out_WG_Bot']
names = [names1, names2, names3]
# create loop
for j in range(3):
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[j][i])
self.lum.set("x", Parameters__Position_X[j] + xPos[i])
self.lum.set("y", Parameters__Position_Y[j] + yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select("MMI In")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI Out1")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI Out2")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI In_WG")
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Out_WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Out_WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Out_WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Out_WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Out_WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Out_WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select("S-Bend Top")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("S-Bend Bot")
self.lum.addtogroup("Cascadet MMI")
elif Taper == True:
if offset_Taper > OffMax:
self.lum.deleteall()
raise ValueError('You are Trying to move the Taper outside the MMI. This is not possible!')
else:
# Delate the Structure to start new
self.lum.deleteall()
# Device specifications
Device_Length = 2*MMI_Length + 2 * WG_Length + 2*TaperLength + 2*SpaceX
Device_Width = (2*MMI_Width + 2 * WaveLength) + 3*SpaceY # MMI_Width
# creating the substrate
max_subH = Substrate_Height
min_subH = -Substrate_Height
min_subL = -3*(Device_Length / 2)
max_subL = 3*(Device_Length / 2)
self.lum.addrect()
self.lum.set("name", "Substrate_Main")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_subH)
self.lum.set("z max", max_subH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSub)
# creating the MMI
max_MMIH = WG_Height
max_MMIL = MMI_Length / 2
min_MMIL = -MMI_Length / 2
if Slab_Height == 0:
z_Offset = max_subH + max_MMIH / 2
else:
# creating the thin film
min_slabH = max_subH
max_slabH = max_subH + Slab_Height
self.lum.addrect()
self.lum.set("name", "Slab Mian")
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z min", min_slabH)
self.lum.set("z max", max_slabH)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("material", MaterialSlab)
z_Offset = max_slabH + max_MMIH / 2
self.lum.select("MMI In")
self.lum.addtogroup("Cascadet MMI")
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
Parameters__Position_X = [0, -SpaceX -MMI_Length -2*TaperLength - 2*WG_Length , -SpaceX -MMI_Length -2*TaperLength - 2*WG_Length]
Parameters__Position_Y = [0, (MMI_Wid + SpaceY)/2, -(MMI_Wid + SpaceY)/2]
names_MMI = ["MMI In", "MMI Out1", "MMI Out2"]
for i in range(3):
self.lum.addwaveguide()
self.lum.set("name", names_MMI[i])
self.lum.set("x", Parameters__Position_X[i])
self.lum.set("y", Parameters__Position_Y[i])
self.lum.set("z", z_Offset)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[max_MMIL, 0], [min_MMIL, 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialWG)
self.lum.set("base width", MMI_Wid)
# New x Length of the Tapers
maxLength = max_MMIL + TaperLength
minLength = min_MMIL - TaperLength
# Mirror the In and Out WG on both sides
maxWGL = [WG_Length, 0, 0]
minWGL = [0, -WG_Length, -WG_Length]
xPos = [maxLength, minLength, minLength]
yPos = [0 + OffsetInput, WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
# Taper loop
# Taper Widths on Bott Cal
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
TaperSideWidth = TaperWidth + 2 * extention
TaperPosXmin = [max_MMIL, min_MMIL, min_MMIL]
TaperPosXmax = [max_MMIL + TaperLength, min_MMIL - TaperLength, min_MMIL - TaperLength]
PosOffset = [0, (posOffset / 2 + WG_Width / 2), -(posOffset / 2 + WG_Width / 2)]
TaperPosYMax_BotR = [(0 + OffsetInput + WG_W / 2), (-WG_W / 2), (-WG_W / 2)]
TaperPosYMin_BotR = [(0 + OffsetInput - WG_W / 2), (WG_W / 2), (WG_W / 2)]
TaperPosYMax_TopR = [(0 + OffsetInput + WG_Width / 2), (-WG_Width / 2), (-WG_Width / 2)]
TaperPosYMin_TopR = [(0 + OffsetInput - WG_Width / 2), (+WG_Width / 2), (+WG_Width / 2)]
TaperPosYMax_BotL = [(0 + OffsetInput + TaperSideWidth / 2), (-TaperSideWidth / 2),
(-TaperSideWidth / 2)]
TaperPosYMin_BotL = [(0 + OffsetInput - TaperSideWidth / 2), (+TaperSideWidth / 2),
(+TaperSideWidth / 2)]
TaperPosYMax_TopL = [(0 + OffsetInput + TaperWidth / 2), (-TaperWidth / 2), (-TaperWidth / 2)]
TaperPosYMin_TopL = [(0 + OffsetInput - TaperWidth / 2), (+TaperWidth / 2), (+TaperWidth / 2)]
# Create two Bends Cos or Bezier depending on the poles option
x_span = SpaceX
y_span = (Parameters__Position_Y[1] + yPos[0]) - (Parameters__Position_Y[0] + yPos[1] )
if polesList == False:
pole = np.array([[0, 0], [x_span / 2, 0], [x_span / 2, y_span], [x_span, y_span]])
pole1 = np.array([[0, y_span], [x_span / 2, y_span], [x_span / 2, 0], [x_span,0]])
elif polesList == True:
K = ((np.pi -2)/np.pi)*100
pole = np.array([[0, 0], [0+(x_span*(K/100)), 0], [(x_span)-(x_span*(K/100)), y_span], [x_span, y_span]])
pole1 = np.array([[0, y_span], [0+(x_span*(K/100)), y_span], [(x_span)-(x_span*(K/100)), 0], [x_span, 0]])
else:
raise ValueError('Parameters["poles"] should be an boolen variable! Please set Parameters["poles"] to True if Bezier Curves needed. Set Parameters["poles"] to False for Cosinus Curve!')
names = ["S-Bend Top", "S-Bend Bot"]
S_Bend_X_Offset = [(-SpaceX - MMI_Length/2 - TaperLength - WG_Length), (SpaceX/2 - SpaceX/2 - MMI_Length/2 - TaperLength - WG_Length)]
S_Bend_Y_Offset = [( WG_Width / 2 + posOffset / 2) ,-( WG_Width / 2 + posOffset / 2)]
z_rotation = [0,180]
poles = [pole1, pole]
for i in range(2):
self.lum.addwaveguide()
self.lum.set("name", names[i])
self.lum.set("x", S_Bend_X_Offset[i])
self.lum.set("y", S_Bend_Y_Offset[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_W)
self.lum.set("base height", WG_Height)
self.lum.set("base angle", 90 - angle)
self.lum.set("poles", poles[i])
self.lum.set("material", MaterialWG)
self.lum.set("first axis","z")
self.lum.set("rotation 1",z_rotation[i])
TapersNames1 = ['MMI Taper In_WG', 'MMI Taper Out WG_Top', 'MMI Taper Out WG_Bot']
TapersNames2 = ['MMIOut1 Taper In_WG', 'MMIOut1 Taper Out WG_Top', 'MMIOut1 Taper Out WG_Bot']
TapersNames3 = ['MMIOut2 Taper In_WG', 'MMIOut2 Taper Out WG_Top', 'MMIOut2 Taper ut WG_Bot']
TapersNames = [TapersNames1, TapersNames2, TapersNames3]
for j in range(3):
for i in range(len(xPos)):
TaperZmin = max_slabH
TaperZmax = max_slabH + max_MMIH
TaperXmin = TaperPosXmin[i]
TaperXmax = TaperPosXmax[i]
ymin_bot_l = TaperPosYMin_BotL[i]
ymax_bot_l = TaperPosYMax_BotL[i]
ymin_bot_r = TaperPosYMin_BotR[i]
ymax_bot_r = TaperPosYMax_BotR[i]
ymin_top_l = TaperPosYMin_TopL[i]
ymax_top_l = TaperPosYMax_TopL[i]
ymin_top_r = TaperPosYMin_TopR[i]
ymax_top_r = TaperPosYMax_TopR[i]
vtx = np.array([[TaperXmin, ymin_bot_l, TaperZmin], # 1
[TaperXmax, ymin_bot_r, TaperZmin], # 2
[TaperXmax, ymax_bot_r, TaperZmin], # 3
[TaperXmin, ymax_bot_l, TaperZmin], # 4
[TaperXmin, ymin_top_l, TaperZmax], # 5
[TaperXmax, ymin_top_r, TaperZmax], # 6
[TaperXmax, ymax_top_r, TaperZmax], # 7
[TaperXmin, ymax_top_l, TaperZmax], # 8
])
a = [[np.array([[1, 4, 3, 2]], dtype=object)], [np.array([[1, 5, 8, 4]], dtype=object)],
[np.array([[1, 2, 6, 5]], dtype=object)], [np.array([[2, 6, 7, 3]], dtype=object)],
[np.array([[3, 4, 8, 7]], dtype=object)], [np.array([[5, 6, 7, 8]], dtype=object)]]
# Send Values to Lumerical and create solid
self.lum.putv('vertices', vtx)
self.lum.putv('facets', a)
self.lum.addplanarsolid(vtx, a)
self.lum.set('material', MaterialWG)
self.lum.set('name', TapersNames[j][i])
self.lum.set('y', Parameters__Position_Y[j] + PosOffset[i])
self.lum.set('x', Parameters__Position_X[j])
# Names of the WGs
# Names of the WGs
names1 = ['MMI In_WG', 'MMI Out WG_Top', 'MMI Out WG_Bot']
names2 = ['MMIOut1 In_WG', 'MMIOut1 Out WG_Top', 'MMIOut1 Out WG_Bot']
names3 = ['MMIOut2 In_WG', 'MMIOut2 Out WG_Top', 'MMIOut2 In WG_Bot']
names = [names1, names2, names3]
# create loop
for j in range(3):
for i in range(len(xPos)):
self.lum.addwaveguide()
self.lum.set("name", names[j][i])
self.lum.set("x", Parameters__Position_X[j] + xPos[i])
self.lum.set("y", Parameters__Position_Y[j] + yPos[i])
self.lum.set("z", z_Offset)
self.lum.set("base width", WG_Width_top)
self.lum.set("base height", max_MMIH)
self.lum.set("base angle", 90 - angle)
pole = np.array([[maxWGL[i], 0], [minWGL[i], 0]])
self.lum.set("poles", pole)
self.lum.set("material", MaterialSlab)
self.lum.select("MMI In")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI Out1")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI Out2")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("MMI In_WG")
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Out WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Out WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Out WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select("S-Bend Top")
self.lum.addtogroup("Cascadet MMI")
self.lum.select("S-Bend Bot")
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Taper In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Taper Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMI Taper Out WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Taper In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Taper Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut1 Taper Out WG_Bot')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Taper In_WG')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Taper Out WG_Top')
self.lum.addtogroup("Cascadet MMI")
self.lum.select('MMIOut2 Taper ut WG_Bot')
self.lum.addtogroup("Cascadet MMI")
else:
raise ValueError(
"Incorect Taper input. Taper must be an boolen. You can choose from Taper = True or Taper = False!")
# create_cover
self.lum.addrect()
self.lum.set("name", "cladding")
self.lum.set("material", MaterialClad)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", z_Offset)
self.lum.set("z span", max_MMIH * 2)
self.lum.set("x min", min_subL)
self.lum.set("x max", max_subL)
self.lum.set("override mesh order from material database", True)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
[docs]
def GratingCoupler(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the GratingCoupler.
Parameters['Material GC'] : list of str
List of Materials. the list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer. For Example "Parameters['Material GC'] = ["SiO2 (Glass) - Palik", "Si (Silicon) - Palik"]"
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["Etch Depth GC"]: int/float
How deep, taken from the Parameters["Hight GC"] will be the etchin depth of the gratings
Parameters["Duty Cycle"]: int/float
Duty cycle of the gratings. For example Parameters["Duty Cycle"] = 0.39 will result in 39% Duty Cycle
Parameters["Pitch GC"]: int/float
Pitch of the Grating Coupler. For Example Parameters["Pitch GC"] = 0.6e-6 will result in 6um Etch Space + Rib Space = 0.6um.
Parameters["Input Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect! In this case is used only when Parameters["Taper"] = True
Parameters["Taper"] : boolen
You can create an input Taper to your Grating Coupler structure
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width. Also in this function and ONLY in this function this will be the
ibverse Taper width!!!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Taper Length'] : int/float
Taper Length
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Parameters["SMF Theta"]: int/float
Tilting Angle of the Single Mode Fiber to the Grating Coupler. Normaly we choose Parameters["SMF Theta"] = 15
Parameters["SMF Z Span"]: int/float
Lenght/Span of the Single Mode Fiber
Returns
-------
None.
'''
# simplify variable names by removing spaces
TargetLength = Parameters["Length GC"]
WidthGC = Parameters["Width GC"]
TaperLength = Parameters['Taper Length']
Hight = Parameters["Hight GC"]
EtchDepth = Parameters["Etch Depth GC"]
DutyCycle = Parameters["Duty Cycle"]
Pitch = Parameters["Pitch GC"]
InputLlength = Parameters["Input Length GC"]
OutputLength = Parameters["Output Length GC"]
Material = Parameters["Material GC"]
CoreDiameter = Parameters["SMF Core Diameter"]
CladdingDiameter = Parameters["SMF Cladding Diameter"]
ZSpan = Parameters["SMF Z Span"]
Theta = Parameters["SMF Theta"]
CoreIndex = Parameters["SMF Core Index"]
CladdingIndex = Parameters["SMF Cladding Index"]
Taper = Parameters["Taper"]
SubstrateThickness = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters['angle']
# Create the MMI L2 Tapers for the Trapezoid
GCNames = "Grating Coupler"
FiberName = "SMF"
# Make the Grating Coupler
import math
n_periods = math.floor(TargetLength / Pitch)
fill_width = Pitch * DutyCycle
etch_width = Pitch * (1 - DutyCycle)
L = n_periods * Pitch + etch_width
spanX = OutputLength + TargetLength + InputLlength
if EtchDepth > Hight:
EtchDepth = Hight
elif EtchDepth < Hight:
self.lum.addrect()
self.lum.set("name", "lower layer")
self.lum.set("x min", 0)
self.lum.set("x max", L)
self.lum.set("z min", 0)
self.lum.set("z max", Hight - EtchDepth)
self.lum.addrect()
self.lum.set("name", "input waveguide")
self.lum.set("x min", -InputLlength)
self.lum.set("x max", 0)
self.lum.set("z min", 0)
self.lum.set("z max", Hight)
self.lum.addrect()
self.lum.set("name", "output waveguide")
self.lum.set("x min", L)
self.lum.set("x max", L + OutputLength)
self.lum.set("z min", 0)
self.lum.set("z max", Hight)
for i in range(1, n_periods+1):
self.lum.addrect()
self.lum.set("name", "post")
self.lum.set("x min", Pitch * (i - 1) + etch_width)
self.lum.set("x max", Pitch * i)
self.lum.set("z min", Hight - EtchDepth)
self.lum.set("z max", Hight)
self.lum.selectall()
self.lum.set("material", Material[0])
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.selectall()
self.lum.addtogroup(GCNames)
self.lum.select(GCNames)
self.lum.set("x", -TargetLength/2)
# Build SMF
core_index = CoreIndex
cladding_index = CladdingIndex
core_radius = CoreDiameter / 2
cladding_radius = CladdingDiameter/2
theta_rad = Theta / (180 / np.pi)
L = ZSpan / np.cos(theta_rad)
# Check if Material Cable is given
# if len(Material) == 2:
CoreIndex = CoreIndex
CladdingIndex = CladdingIndex
self.lum.addcircle()
self.lum.set("name", "core")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 10)
self.lum.set("alpha", 1)
self.lum.set("radius", core_radius)
self.lum.set("index", CoreIndex)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", L)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", Theta)
self.lum.addcircle()
self.lum.set("name", "cladding")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 5)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 10)
self.lum.set("alpha", 0.35)
self.lum.set("radius", cladding_radius)
self.lum.set("index", CladdingIndex)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", L)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", Theta)
# else:
# CoreMaterial = Material[2]
# CladdingMaterial = Material[3]
#
# self.lum.addcircle()
# self.lum.set("name", "core")
# self.lum.set("override mesh order from material database", 1)
# self.lum.set("mesh order", 4)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", 10)
# self.lum.set("alpha", 1)
# self.lum.set("radius", core_radius)
# self.lum.set("material", CoreMaterial)
# self.lum.set("x", 0)
# self.lum.set("y", 0)
# self.lum.set("z", 0)
# self.lum.set("z span", L)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", Theta)
#
# self.lum.addcircle()
# self.lum.set("name", "cladding")
# self.lum.set("override mesh order from material database", 1)
# self.lum.set("mesh order", 5)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", 10)
# self.lum.set("alpha", 0.35)
# self.lum.set("radius", cladding_radius)
# self.lum.set("material", CladdingMaterial)
# self.lum.set("x", 0)
# self.lum.set("y", 0)
# self.lum.set("z", 0)
# self.lum.set("z span", L)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", Theta)
self.lum.select("core")
self.lum.addtogroup('SMF')
self.lum.select("cladding")
self.lum.addtogroup('SMF')
if Taper == True:
# Add Taper
TaperNames = "Taper"
myscript = self.Script()
spanX = OutputLength+TargetLength + InputLlength
self.lum.addstructuregroup()
self.lum.set("name", TaperNames)
self.lum.set("construction group", 1)
self.lum.adduserprop("thickness", 2, Hight)
self.lum.adduserprop("angle_side", 0, angle)
self.lum.adduserprop("width_l", 2, WG_Width)
self.lum.adduserprop("width_r", 2, WidthGC)
self.lum.adduserprop("hfrac_ref", 0, 1)
self.lum.adduserprop("len", 2, TaperLength)
self.lum.adduserprop("material", 5, Material[0])
self.lum.adduserprop("index", 0, 1)
self.lum.set("script", myscript)
self.lum.set("x", -TargetLength/2 - InputLlength - TaperLength/2)
self.lum.set("z", Hight/2)
self.lum.set("y", 0)
else:
pass
# global substrate and Si-Layer
self.lum.addrect()
self.lum.set("name", "SubstrateGlobal")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z", -SubstrateThickness/2)
self.lum.set("z span", SubstrateThickness)
self.lum.set("material", Material[1])
self.lum.addrect()
self.lum.set("name", "Si_Layer Global")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z", - SubstrateThickness -(2e-6) / 2)
self.lum.set("z span", 2e-6)
self.lum.set("material", Material[0])
self.lum.addrect()
self.lum.set("name", "Cladding Global")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z min", 0)
self.lum.set("z max", Hight + 0.7e-6)
self.lum.set("material", Material[1])
self.lum.set("alpha", 0.7)
self.lum.set("override mesh order from material database",1)
self.lum.set("mesh order", 3)
[docs]
def RingGratingCoupler(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the GratingCoupler.
Parameters['Material GC'] : list of str
List of Materials. the list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer. For Example "Parameters['Material GC'] = ["SiO2 (Glass) - Palik", "Si (Silicon) - Palik"]"
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
Parameters["Etch Depth GC"]: int/float
How deep, taken from the Parameters["Hight GC"] will be the etchin depth of the gratings
Parameters["Duty Cycle"]: int/float
Duty cycle of the gratings. For example Parameters["Duty Cycle"] = 0.39 will result in 39% Duty Cycle
Parameters["Pitch GC"]: int/float
Pitch of the Grating Coupler. For Example Parameters["Pitch GC"] = 0.6e-6 will result in 6um Etch Space + Rib Space = 0.6um.
Parameters["Input LengthGC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Parameters["SMF Theta"]: int/float
Tilting Angle of the Single Mode Fiber to the Grating Coupler. Normaly we choose Parameters["SMF Theta"] = 15
Parameters["SMF Z Span"]: int/float
Lenght/Span of the Single Mode Fiber
Returns
-------
None.
'''
# simplify variable names by removing spaces
TargetLength = Parameters["Length GC"]
WidthGC = Parameters["Width GC"]
TaperLength = Parameters['Taper Length']
Hight = Parameters["Hight GC"]
EtchDepth = Parameters["Etch Depth GC"]
DutyCycle = Parameters["Duty Cycle"]
Pitch = Parameters["Pitch GC"]
InputLlength = Parameters["Input Length GC"]
OutputLength = Parameters["Output Length GC"]
Material = Parameters["Material GC"]
CoreDiameter = Parameters["SMF Core Diameter"]
CladdingDiameter = Parameters["SMF Cladding Diameter"]
ZSpan = Parameters["SMF Z Span"]
Theta = Parameters["SMF Theta"]
CoreIndex = Parameters["SMF Core Index"]
CladdingIndex = Parameters["SMF Cladding Index"]
Taper = Parameters["Taper"]
SubstrateThickness = Parameters['Substrate Height']
GCRadius = Parameters["GC Radius"]
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters['angle']
# Create the MMI L2 Tapers for the Trapezoid
GCNames = "Grating Coupler"
FiberName = "SMF"
# Make the Grating Coupler
import math
n_periods = math.floor(TargetLength / Pitch)
fill_width = Pitch * DutyCycle
etch_width = Pitch * (1 - DutyCycle)
L = n_periods * Pitch + etch_width
spanX = OutputLength + TargetLength + InputLlength
Theta = np.arcsin( 0.5*WidthGC/GCRadius ) * 180/np.pi
if EtchDepth > Hight:
EtchDepth = Hight
elif EtchDepth < Hight:
self.lum.addring()
self.lum.set("name", "lower layer")
self.lum.set("inner radius",GCRadius * np.cos(Theta*np.pi/180))
self.lum.set("outer radius",GCRadius +L + OutputLength)
self.lum.set("z min", 0)
self.lum.set("z max", Hight - EtchDepth)
self.lum.set("theta start",-Theta)
self.lum.set("theta stop",Theta)
# input section
self.lum.addring()
self.lum.set("name","input section")
# self.lum.set("inner radius",GCRadius * np.cos(Theta*np.pi/180))
self.lum.set("inner radius", 0)
self.lum.set("outer radius",GCRadius)
self.lum.set("x",0)
self.lum.set("y",0)
self.lum.set("z min",0)
self.lum.set("z max",Hight)
self.lum.set("theta start",-Theta)
self.lum.set("theta stop",Theta)
# output section
self.lum.addring()
self.lum.set("name","output section")
self.lum.set("inner radius",GCRadius + L)
self.lum.set("outer radius",GCRadius +L + OutputLength)
self.lum.set("x",0)
self.lum.set("y",0)
self.lum.set("z min",0)
self.lum.set("z max",Hight)
self.lum.set("theta start",-Theta)
self.lum.set("theta stop",Theta)
# Add Grating
for i in range(1, n_periods+1):
self.lum.addring()
self.lum.set("x",0)
self.lum.set("y",0)
self.lum.set("z min", Hight - EtchDepth)
self.lum.set("z max",Hight)
self.lum.set("theta start",-Theta)
self.lum.set("theta stop",Theta)
self.lum.set("inner radius",GCRadius + Pitch * (i - 1) + etch_width)
self.lum.set("outer radius",GCRadius + Pitch*i)
self.lum.set("name", "post")
self.lum.selectall()
self.lum.set("material", Material[0])
self.lum.selectall()
self.lum.addtogroup(GCNames)
self.lum.select(GCNames)
self.lum.set("x", -TargetLength/2)
# Build SMF
core_index = CoreIndex
cladding_index = CladdingIndex
core_radius = CoreDiameter / 2
cladding_radius = CladdingDiameter/2
theta_rad = Theta / (180 / np.pi)
L = ZSpan / np.cos(theta_rad)
# Check if Material Cable is given
# if len(Material) == 2:
CoreIndex = CoreIndex
CladdingIndex = CladdingIndex
self.lum.addcircle()
self.lum.set("name", "core")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 10)
self.lum.set("alpha", 1)
self.lum.set("radius", core_radius)
self.lum.set("index", CoreIndex)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", L)
self.lum.addcircle()
self.lum.set("name", "cladding")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 5)
self.lum.set("first axis", "y")
self.lum.set("rotation 1", 10)
self.lum.set("alpha", 0.35)
self.lum.set("radius", cladding_radius)
self.lum.set("index", CladdingIndex)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", L)
# else:
# CoreMaterial = Material[2]
# CladdingMaterial = Material[3]
#
# self.lum.addcircle()
# self.lum.set("name", "core")
# self.lum.set("override mesh order from material database", 1)
# self.lum.set("mesh order", 4)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", 10)
# self.lum.set("alpha", 1)
# self.lum.set("radius", core_radius)
# self.lum.set("material", CoreMaterial)
# self.lum.set("x", 0)
# self.lum.set("y", 0)
# self.lum.set("z", 0)
# self.lum.set("z span", L)
#
#
# self.lum.addcircle()
# self.lum.set("name", "cladding")
# self.lum.set("override mesh order from material database", 1)
# self.lum.set("mesh order", 5)
# self.lum.set("first axis", "y")
# self.lum.set("rotation 1", 10)
# self.lum.set("alpha", 0.35)
# self.lum.set("radius", cladding_radius)
# self.lum.set("material", CladdingMaterial)
# self.lum.set("x", 0)
# self.lum.set("y", 0)
# self.lum.set("z", 0)
# self.lum.set("z span", L)
self.lum.select("core")
self.lum.addtogroup('SMF')
self.lum.select("cladding")
self.lum.addtogroup('SMF')
self.lum.select('SMF')
self.lum.set("x", -TargetLength/2 +GCRadius + core_radius) #TargetLength
# Triangle EQ for waveguide Width
x = abs(Hight / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - Hight ** 2)
WG_W = WG_Width + 2 * extention
# Add Waveguide
names = ["Straight Waveguide"]
self.lum.addwaveguide()
self.lum.set("name", names[0])
self.lum.set("x", -GCRadius/2)
self.lum.set("y", 0)
self.lum.set("z", Hight/2)
self.lum.set("base width", WG_W)
self.lum.set("base height", Hight)
self.lum.set("base angle", 90 - angle)
pole = np.array([[-20e-6, 0], [5e-6, 0]])
self.lum.set("poles", pole)
self.lum.set("material", Material[0])
self.lum.select("Straight Waveguide")
self.lum.addtogroup('Input Waveguide')
# global substrate and Si-Layer
self.lum.addrect()
self.lum.set("name", "SubstrateGlobal")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z", -SubstrateThickness/2)
self.lum.set("z span", SubstrateThickness)
self.lum.set("material", Material[1])
self.lum.addrect()
self.lum.set("name", "Si_Layer Global")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z", - SubstrateThickness -(2e-6) / 2)
self.lum.set("z span", 2e-6)
self.lum.set("material", Material[0])
self.lum.addrect()
self.lum.set("name", "Cladding Global")
self.lum.set("x", 0)
self.lum.set("x span", 120e-6)
self.lum.set("y", 0)
self.lum.set("y span", 120e-6)
self.lum.set("z min", 0)
self.lum.set("z max", Hight + 0.7e-6)
self.lum.set("material", Material[1])
self.lum.set("alpha", 0.7)
self.lum.set("override mesh order from material database",1)
self.lum.set("mesh order", 3)
[docs]
def GratingCouplerNeff(self, Parameters):
Hight = Parameters["Hight GC"]
EtchDepth = Parameters["Etch Depth GC"]
Pitch = Parameters["Pitch GC"]
Material = Parameters["Material GC"]
# Make the Grating Coupler
Gap = 1e-6 - Pitch
# Create Ribs and Gaps
# for i in range(1, 2 ):
self.lum.addrect()
self.lum.set("name", "Pitch")
self.lum.set("x", 0)
self.lum.set("x span", Pitch)
self.lum.set("z min", Hight - EtchDepth)
self.lum.set("z max", Hight)
self.lum.set("material", Material[0])
self.lum.set("y", 0)
self.lum.set("y span", 5e-6)
#Add Bottom Layer of Material
self.lum.addrect()
self.lum.set("name", "lower layer")
self.lum.set("x", 0)
self.lum.set("x span", Pitch + 2*Gap)
self.lum.set("z min", 0)
self.lum.set("z max", Hight - EtchDepth)
self.lum.set("material", Material[0])
self.lum.set("y", 0)
self.lum.set("y span", 5e-6)
# Add Substrate
self.lum.addrect()
self.lum.set("name", "Substrate")
self.lum.set("x", 0)
self.lum.set("x span", Pitch + 2*Gap)
self.lum.set("z", -(1e-6) / 2)
self.lum.set("z span", 1e-6)
self.lum.set("y", 0)
self.lum.set("y span", 5e-6)
self.lum.set("material", Material[1])
# Add Cladding
self.lum.addrect()
self.lum.set("name", "Cladding")
self.lum.set("x", 0)
self.lum.set("x span", Pitch + 2*Gap)
self.lum.set("z min", Hight - EtchDepth)
self.lum.set("z max", Hight + 0.7e-6)
self.lum.set("y", 0)
self.lum.set("y span", 5e-6)
self.lum.set("material", Material[1])
self.lum.set('override mesh order from material database', 1)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.selectall()
self.lum.addtogroup("GC Rib")
self.lum.select("GC Rib")
self.lum.set("first axis", "z")
self.lum.set("rotation 1", 90)
[docs]
def LenseModeSystem(self,Parameters):
f_lense = Parameters["Focal Length"]
Outter_R = Parameters["Ring Outter Radius"]
Iner_R = Parameters["Ring Iner Radius"]
Lense_Thickness = Parameters["Lense Thickness"]
x_res = Parameters['x res']
Wavelength = Parameters['Wavelength']
Port_Span = Parameters["Port Span"]
Materials = Parameters['Material']
# SMF Parameters
Core_Diameter = Parameters["SMF Core Diameter"]
Cladding_Diameter = Parameters["SMF Cladding Diameter"]
Core_Index = Parameters["SMF Core Index"]
Cladding_Index = Parameters["SMF Cladding Index"]
self.lum.select("Straight Waveguide::Waveguide")
z_Pos = self.lum.get("z")
self.lum.addcircle()
self.lum.set("name", "core")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 1)
self.lum.set("radius", Core_Diameter / 2)
self.lum.set("index", Core_Index)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z min", z_Pos + f_lense )
self.lum.set("z max", z_Pos + f_lense + 2e-6)
self.lum.addcircle()
self.lum.set("name", "cladding")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 5)
self.lum.set("alpha", 0.35)
self.lum.set("radius", Cladding_Diameter / 2)
self.lum.set("index", Cladding_Index)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z min", z_Pos + f_lense)
self.lum.set("z max", z_Pos + f_lense + 2e-6)
self.lum.select("core")
self.lum.addtogroup('SMF')
self.lum.select("cladding")
self.lum.addtogroup('SMF')
# Extract SMF Positions
self.lum.select("SMF::core")
x_Pos = self.lum.get("x")
y_Pos = self.lum.get("y")
self.lum.select("Straight Waveguide::Waveguide")
z_Pos = self.lum.get("z")
self.lum.select("Substrate")
z_Sub = self.lum.get("z max")
self.lum.addring()
self.lum.set("name", "Support Ring Lense")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("alpha", 0.7)
self.lum.set("x", x_Pos)
self.lum.set("y", y_Pos)
self.lum.set("z min", z_Sub)
self.lum.set("z max", z_Pos + f_lense)
self.lum.set("outer radius", Outter_R)
self.lum.set("inner radius", Iner_R)
self.lum.set("theta start",0)
self.lum.set("theta stop", 0)
self.lum.set("material", Materials[2])
self.lum.addsphere()
self.lum.set("name", "Lense")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("x", x_Pos)
self.lum.set("y", y_Pos)
self.lum.set("z", z_Pos + f_lense)
self.lum.set("radius", Iner_R)
self.lum.set("make ellipsoid", 1)
self.lum.set("radius 2", Iner_R)
self.lum.set("radius 3", Lense_Thickness)
self.lum.set("material", Materials[2])
self.lum.select("Support Ring Lense")
self.lum.addtogroup('Funnel')
self.lum.select("Lense")
self.lum.addtogroup('Funnel')
self.lum.select("Substrate")
x_adj = self.lum.get("x min")
self.lum.select("SMF")
self.lum.set("x", (Parameters["SMF Core Diameter"]/2) -abs(x_adj))
self.lum.select("Funnel")
self.lum.set("x", (Parameters["SMF Core Diameter"]/2) - abs(x_adj))
self.lum.select("SMF")
x_Pos = self.lum.get("x")
self.lum.select("SMF::core")
y_Pos = self.lum.get("y")
z_Pos = self.lum.get("z max")
radius = self.lum.get("radius")
self.lum.select("Substrate")
Solver_X = self.lum.get("x")
Solver_X_Span = self.lum.get("x span")
Solver_X_max = self.lum.get("x max")
Solver_X_min = self.lum.get("x min")
Solver_Y = self.lum.get("y")
Solver_Y_Span = self.lum.get("y span")
Solver_Z = self.lum.get("z min")
# Solver Object
self.lum.addfdtd()
# self.lum.set("x", Solver_X)
# self.lum.set("x span", Solver_X_Span)
self.lum.set("x max", Solver_X_max)
self.lum.set("x min", Solver_X_min - radius)
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z min", Solver_Z)
self.lum.set("z max", z_Pos + 0.5e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', Wavelength)
self.lum.set('global source wavelength span', 0)
# Add Gausssian Source
self.lum.addgaussian()
self.lum.set("injection axis", "z-axis")
self.lum.set("direction","Backward")
self.lum.set("x", x_Pos)
self.lum.set("x span", radius * 4)
self.lum.set("y", y_Pos)
self.lum.set("y span", radius * 4)
self.lum.set("z", z_Pos)
self.lum.set("waist radius w0", radius)
self.lum.select("Straight Waveguide::Waveguide")
WG_Z = self.lum.get("z")
# Add Movie monitor
self.lum.addmovie()
self.lum.set("name", "Movie")
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set("z", WG_Z)
self.lum.set("x", Solver_X)
self.lum.set("x span", Solver_X_Span)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set("x", Solver_X)
self.lum.set("x span", Solver_X_Span)
self.lum.set("z", WG_Z)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
names = ["Input", "Output"]
self.lum.select("Straight Waveguide::Waveguide")
WG_X = self.lum.get("poles")
x_Monitor = [WG_X[0][0] + 0.1e-6, WG_X[1][0] - 0.1e-6]
self.lum.select("Substrate")
z_sub = self.lum.get("z max")
self.lum.select("SMF::core")
z_Min = self.lum.get("z min")
for i in range(len(names)):
self.lum.addpower()
self.lum.set('name', names[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x_Monitor[i] )
self.lum.set("y", Solver_Y)
self.lum.set("y span", Port_Span[1])
self.lum.set("z", WG_Z)
self.lum.set("z span", Port_Span[2])
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
for i in range(3):
self.lum.addpower()
self.lum.set('name', "Power Beam " + str(i))
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set("x", x_Pos)
self.lum.set("x span", Solver_X_Span)
self.lum.set("z", z_Min)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
z_Min = z_Min/2
# Monitor Behind the Port
self.lum.addpower()
self.lum.set('name', "Power Beam back")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set("x", x_Pos)
self.lum.set("x span", Solver_X_Span)
self.lum.set("z", z_Min)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Monitor Mode
self.lum.addprofile()
self.lum.set('name', "Beam Profile Monitor")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("y", 0)
self.lum.set("x", x_Pos)
self.lum.set("x span", Parameters["SMF Core Diameter"])
self.lum.set("z min", 0)
self.lum.set("z max", z_Pos + 0.5e-6)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Monitor Index
self.lum.addindex()
self.lum.set('name', "index_monitor")
self.lum.set('monitor type', '3D')
self.lum.set("x max", Solver_X_max)
self.lum.set("x min", Solver_X_min - radius)
self.lum.set("y", Solver_Y)
self.lum.set("y span", Solver_Y_Span)
self.lum.set("z min", Solver_Z)
self.lum.set("z max", z_Pos + 0.5e-6)
[docs]
def LenseFiber(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the GratingCoupler.
Take care since this object will create his own FDTD simulation object. No extra Solvers needed!
Parameters['Material'] : list of str
List of Materials. the list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer. For Example "Parameters['Material'] = ["SiO2 (Glass) - Palik", "Si (Silicon) - Palik"]"
Parameters["Lense Diameter"] : int/float
Lense diameter.
Parameters["Lense Thickness"]: int/float
Lens thickness
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Parameters['Support Cylunder Hight']: int/float
Hight of the Cylinder holding the Lense. This is needed so that the Lense can have flat fine serface to be printed on.
Parameters['Hexagon Hight']: optional int/float
Hight of the Hexagon holding the Cylinder. If the Parameter is not set the Hexagon will be made with 4 um thickness. Where 2 um
will go into the fiber array and 2 um will be above to print the cylinder on it. This is how is done in NanoPrintX.
Parameters['x res'] : int/float
Mesh step size
Parameters['Wavelength']: int/float
Simulation Wavelenght
Returns
-------
None.
'''
Lens_d = Parameters["Lense Diameter"]
Lense_Thickness = Parameters["Lense Thickness"]
Materials = Parameters['Material']
Cylinder_Hight = Parameters["Support Cylunder Hight"]
if "Hexagon Hight" in list(Parameters.keys()):
Hexagon_Hight = Parameters['Hexagon Hight']
else:
Hexagon_Hight = 4e-6
# SMF Parameters
Core_Diameter = Parameters["SMF Core Diameter"]
Cladding_Diameter = Parameters["SMF Cladding Diameter"]
Core_Index = Parameters["SMF Core Index"]
Cladding_Index = Parameters["SMF Cladding Index"]
#Create SMF
self.lum.addcircle()
self.lum.set("name", "core")
self.lum.set("override mesh order from material database", 0)
self.lum.set("alpha", 1)
self.lum.set("radius", Core_Diameter / 2)
self.lum.set("index", Core_Index)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", 4e-6)
self.lum.addcircle()
self.lum.set("name", "cladding")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 3)
self.lum.set("alpha", 0.35)
self.lum.set("radius", Cladding_Diameter / 2)
self.lum.set("index", Cladding_Index)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", 0)
self.lum.set("z span", 4e-6)
self.lum.select("core")
self.lum.addtogroup('SMF')
self.lum.select("cladding")
self.lum.addtogroup('SMF')
# Extract SMF Positions
self.lum.select("SMF::core")
x_Pos = self.lum.get("x")
y_Pos = self.lum.get("y")
z_Pos = self.lum.get("z")
# Create Hexagon support
self.lum.addpoly()
self.lum.set("name", "Support Hexagon")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
pole = np.array([[0, -Cladding_Diameter / 2], [np.sin(60*np.pi/180)*Cladding_Diameter / 2, -Cladding_Diameter / 4], [np.sin(60*np.pi/180)*Cladding_Diameter / 2, Cladding_Diameter / 4], [0,Cladding_Diameter / 2], [-np.sin(60*np.pi/180)*Cladding_Diameter / 2, Cladding_Diameter / 4], [-np.sin(60*np.pi/180)*Cladding_Diameter / 2, -Cladding_Diameter / 4]])
self.lum.set("vertices", pole)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Pos + Hexagon_Hight/2)
self.lum.set("z span", Hexagon_Hight)
self.lum.set("material", Materials[1])
# Set support ring with 0,5 um min thickness becouse of Voxel of Nanoscribe
z_Pos = self.lum.get("z max")
self.lum.addcircle()
self.lum.set("name", "Support Ring Lense")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("radius", Core_Diameter / 2)
self.lum.set("material", Materials[1])
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Pos + Cylinder_Hight/2)
self.lum.set("z span", Cylinder_Hight)
# Add lense to on top of the o,5um Voxel ring
z_Pos = self.lum.get("z max")
self.lum.addsphere()
self.lum.set("name", "Lense")
self.lum.set("override mesh order from material database", 1)
self.lum.set("mesh order", 4)
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("z", z_Pos )
self.lum.set("radius", Lens_d/2)
self.lum.set("make ellipsoid", 1)
self.lum.set("radius 2", Lens_d/2)
self.lum.set("radius 3", Lense_Thickness)
self.lum.set("material", Materials[1])
# =============================================================================
# Functions for the solvers
# =============================================================================
[docs]
def setStraightWaveguideFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the StraightWaveguideFDTDSolver.
Parameters : Dictionary
Dictionary with all the data needet for the Bend Wavaguide. Data needet:
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Length'] : int/float
Waveguide Length
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters["Taper"] : boolen
If Taper == False, only straight Waveguide will be simulated,
If Taper == True an Taper will be simulated
Parameters['Taper Width'] : int/float
Taper backside Width. Taper Fronside width is the width of the Waveguide
Parameters['Taper Length'] : int/float
Taper Length
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
This Parameter will set the theta ratation angle of the port. It can be 90 or 180.
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Parameters["Taper Type"] is given, themn the user need to set couple more parameters:
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Length = Parameters['WG Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters["Waveguide Angle"]
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
Taper = Parameters['Taper']
TaperWidth = Parameters['Taper Width']
TaperLength = Parameters['Taper Length']
if "Taper Type" in list(Parameters.keys()):
TaperType = "Inverse"
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
else:
TaperType = "Normal"
# Device specifications
Device_Width = 2*WG_Length + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height / 2
EME_WGLength = (WG_Length * np.cos(angle * np.pi / 180))
if Taper == False:
if angle == 0:
# Adds a FDTD Solver
self.lum.addfdtd()
self.lum.set("x", WG_Length / 2)
self.lum.set("x span", WG_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('x min bc', 'PML')
self.lum.set('x max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Define Ports
x = [0,EME_WGLength]
x_Monitor = [0+0.1e-6,EME_WGLength-0.1e-6]
yPos = [0, 0]
yPos_span = [y_Port_Span, y_Port_Span]
theta = [0, 0]
direction = ['Forward', 'Backward']
name = ['Input', 'Output']
for i in range(2):
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("injection axis", "x-axis")
self.lum.set("x", x[i])
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('monitor type', "2D X-normal")
self.lum.set('name', name[i])
self.lum.set("x", x_Monitor[i] )
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Movie monitor
self.lum.addmovie()
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
self.lum.set("x", WG_Length/2)
self.lum.set("x span", WG_Length)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("x", WG_Length/2)
self.lum.set("x span", WG_Length)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
else:
# Calc Output Loc
sideLength = np.cos(angle*np.pi/180)* WG_Length
sideHight = np.sqrt(WG_Length**2 - sideLength**2)
# Adds a FDTD Solver
self.lum.addfdtd()
self.lum.set("x", WG_Length/2)
self.lum.set("x span", WG_Length)
self.lum.set("y", WG_Length / 4)
self.lum.set("y span", Device_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Define Ports
x = [0, EME_WGLength]
x_Monitor = [0+0.1e-6,EME_WGLength-0.1e-6]
yPos = [0, 0 + sideHight]
yPos_span = [y_Port_Span, y_Port_Span]
direction = ['Forward', 'Backward']
name = ['Input', 'Output']
theta = [angle, angle]
for i in range(2):
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("injection axis", "x-axis")
self.lum.set("x", x[i])
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
self.lum.set('theta',theta[i])
# Power Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[i])
self.lum.set("x", x_Monitor[i] )
self.lum.set("y", yPos[i])
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Add Movie monitor
self.lum.addmovie()
self.lum.set("y", WG_Length/4)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
self.lum.set("x", WG_Length/2)
self.lum.set("x span", WG_Length)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", WG_Length/4)
self.lum.set("y span", Device_Width)
self.lum.set("x", WG_Length/2)
self.lum.set("x span", WG_Length)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
elif Taper == True:
# Device specifications
Device_Width = 2*TaperWidth + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
EME_WGLength = TaperLength * np.cos(angle * np.pi / 180)
if TaperType == "Normal":
# Adds a FDTD Solver
self.lum.addfdtd()
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('x min bc', 'PML')
self.lum.set('x max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Define Ports
Diff_Span = y_Port_Span - WG_Width
x = [-TaperLength/2+ 0.1e-6 , TaperLength/2 - 0.1e-6]
x_Monitor = [-TaperLength/2+ 0.1e-6+0.1e-6,TaperLength/2 - 0.1e-6 - 0.1e-6]
yPos = [0, 0]
yPos_span = [y_Port_Span, TaperWidth + Diff_Span]
theta = [0, 0]
direction = ['Forward', 'Backward']
name = ['Input', 'Output']
for i in range(2):
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("injection axis", "x-axis")
self.lum.set("x", x[i])
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Power Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[i])
self.lum.set("x", x_Monitor[i] )
self.lum.set("y", yPos[i])
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Add Movie monitor
self.lum.addmovie()
self.lum.set("y", 0)
self.lum.set("y span", 2*TaperWidth + WaveLength * 2)
self.lum.set("z", MonitorHeight)
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y",0)
self.lum.set("y span", 2*TaperWidth + WaveLength * 2)
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
else:
# Adds a FDTD Solver
self.lum.addfdtd()
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
self.lum.set("y", 0)
self.lum.set("y span", 2*TaperWidthB + 2e-6)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height + max_slabH + TaperHightB/2)
self.lum.set("z span", 2e-6 + TaperHightB )
self.lum.set('x min bc', 'PML')
self.lum.set('x max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Define Ports
Diff_Span = y_Port_Span - WG_Width
x = [-TaperLength/2+ 0.1e-6 , TaperLength/2 - 0.1e-6]
x_Monitor = [-TaperLength/2+ 0.1e-6+0.1e-6,TaperLength/2 - 0.1e-6 - 0.1e-6]
yPos = [0, 0]
yPos_span = [y_Port_Span, TaperWidthB + Diff_Span ]
theta = [0, 0]
direction = ['Forward', 'Backward']
name = ['Input', 'Output']
yPos_span = [y_Port_Span, TaperWidthF + Diff_Span ]
z_Pos = [Substrate_Height + max_slabH + TaperHightB/2, Substrate_Height + max_slabH + TaperHightF/2 ]
z_Span = [ TaperHightB + z_Port_Span , z_Port_Span]# TaperHightF/2 + z_Port_Span]
for i in range(2):
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("injection axis", "x-axis")
self.lum.set("x", x[i])
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", z_Pos[i])
self.lum.set("z span", z_Span[i])
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Power Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[i])
self.lum.set("x", x_Monitor[i] )
self.lum.set("y", yPos[i])
self.lum.set("z", z_Pos[i])
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Add Movie monitor
self.lum.addmovie()
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB + Diff_Span)
self.lum.set("z", z_Pos[1])
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y",0)
self.lum.set("y span", TaperWidthB + Diff_Span)
self.lum.set("x", 0)
self.lum.set("x span", TaperLength)
self.lum.set("z", z_Pos[1])
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
[docs]
def setArcWaveguideFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the ArcWaveguideFDTDSolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["S_Band Radius"] : int/float
S-Bend Radius in um.
Parameters['Arc deg'] : int/float
Arc define the Arc of the curve. It can be 90 or 180 degrees only.
This two will define an 1/4 of a circle or 1/2 of a circle.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
radius = Parameters["S_Band Radius"]
arc = Parameters['Arc deg']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if arc == 90:
# Device dimentions
# magic number
# The cubic Bezier curve using this magic number in the pole points approximates the semi-circile with least error
m = 0.55191502449
MonitorHeight = Substrate_Height + Slab_Height + WG_Height / 2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width)+2e-6)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", Substrate_Height + WG_Height*2)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [0-0.2e-6, radius]
direction = ['Forward', 'Forward']
name = ['Input', 'Output']
y = [radius, 0-0.2e-6]
# Port 1
self.lum.addport()
self.lum.set('name', name[0])
self.lum.set("injection axis", "x-axis")
self.lum.set("x", x[0])
self.lum.set("y", y[0])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[0])
self.lum.set('mode selection', Mode)
# self.lum.set("bent waveguide", 1)
# self.lum.set("bend radius", radius)
# Time Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[0])
self.lum.set("x", x[0] + 0.2e-6)
self.lum.set("y", y[0])
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "2D X-mormal Input Power Monitor")
self.lum.set("monitor type", "2D X-normal")
self.lum.set("x", x[0] + 0.1e-6)
self.lum.set("y", y[0])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Port 2
self.lum.addport()
self.lum.set('name', name[1])
self.lum.set("injection axis", "y-axis")
self.lum.set("x", x[1])
self.lum.set("x span", y_Port_Span) # Only becouse we just rotate the previus WG on 90 degrees!!!
self.lum.set("y", y[1])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[1])
self.lum.set('mode selection', Mode)
# self.lum.set("bent waveguide", 1)
# self.lum.set("bend radius", radius)
# self.lum.set("bend orientation", 90)
# Time Monitor Port 2
self.lum.addtime()
self.lum.set('name', name[1])
self.lum.set("x", x[1])
self.lum.set("y", y[1] + 0.2e-6)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Power Monitor Port 2
self.lum.addpower()
self.lum.set('name', "2D Y-mormal Input Power Monitor")
self.lum.set("monitor type","2D Y-normal")
self.lum.set("x", x[1])
self.lum.set("x span", y_Port_Span) # Only becouse we just rotate the previus WG on 90 degrees!!!
self.lum.set("y", y[1]+0.1e-6)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
# Add Movie Monitor
self.lum.addmovie()
self.lum.set("y", (m * radius))
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z", MonitorHeight)
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width))
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("y", (m * radius))
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("x", m * radius)
self.lum.set("x span", (m * radius * 2 + WG_Width))
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
elif arc == 180:
# magic number
# The cubic Bezier curve using this magic number in the pole points approximates the semi-circile with least error
m = 0.55191502449
MonitorHeight = Substrate_Height + Slab_Height + WG_Height / 2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width) * 2)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", Substrate_Height + WG_Height*2)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [-radius, radius]
direction = ['Forward', 'Forward']
name = ['Input', 'Output']
y = [0, 0]
self.lum.addport()
self.lum.set('name', name[0])
self.lum.set("injection axis", "y-axis")
self.lum.set("x", x[0])
self.lum.set("x span", y_Port_Span) # Only becouse we just rotate the previus WG on 90 degrees!!!
self.lum.set("y", y[0])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[0])
self.lum.set('mode selection', Mode)
self.lum.set("bent waveguide", 1)
self.lum.set("bend radius", radius)
self.lum.set("bend orientation", -90)
# Power Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[0])
self.lum.set("x", x[0])
self.lum.set("y", y[0] + 0.2e-6)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.addport()
self.lum.set('name', name[1])
self.lum.set("injection axis", "y-axis")
self.lum.set("x", x[1])
self.lum.set("x span", y_Port_Span) # Only becouse we just rotate the previus WG on 90 degrees!!!
self.lum.set("y", y[1])
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[1])
self.lum.set('mode selection', Mode)
self.lum.set("bent waveguide", 1)
self.lum.set("bend radius", radius)
self.lum.set("bend orientation", 90)
# Power Monitor Port 1
self.lum.addtime()
self.lum.set('name', name[1])
self.lum.set("x", x[1])
self.lum.set("y", y[1] + 0.2e-6)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.addmovie()
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width) * 2)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z", MonitorHeight)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", 0)
self.lum.set("x span", (m * radius * 2 + WG_Width) * 2)
self.lum.set("y", radius * m)
self.lum.set("y span", m * radius * 2 + WG_Width)
self.lum.set("z", MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
[docs]
def setBendWaveguideFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the BendWaveguideFDTDSolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["x span"] : int/float
Length of the S-Bend Waveguide
Parameters["y span"] : int/float
Width of the S-Bend Waveguide
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
x_span = Parameters["x span"]
y_span = Parameters["y span"]
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
# Device specifications
Device_Length = x_span
Device_Width = y_span + WaveLength * 2
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height / 2
# Check Object Geometry
self.lum.select("S Bend::S-Bend")
z_Structure = self.lum.get("z")
z_Object = self.lum.get("Base Height")
Z = z_Structure + z_Object
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x", Device_Length / 2)
self.lum.set("x span", Device_Length)
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", Z + 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [0, x_span]
direction = ['Forward', 'Backward']
name = ['Input', 'Output']
y = [0, y_span]
self.lum.addport()
self.lum.set('name', name[0])
self.lum.set("x", x[0])
self.lum.set("y", y[0])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[0])
self.lum.set('mode selection', Mode)
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', name[0])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[0]+0.1e-6)
self.lum.set("y", y[0])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[1])
self.lum.set("x", x[1])
self.lum.set("y", y[1])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[1])
self.lum.set('mode selection', Mode)
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('monitor type', '2D X-normal')
self.lum.set('name', name[1])
self.lum.set("x", x[1]-0.1e-6)
self.lum.set("y", y[1])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addmovie()
self.lum.set('x', x_span / 2)
self.lum.set("x span", x_span)
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('name', 'FieldMonitor')
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", x_span / 2)
self.lum.set("x span", x_span)
self.lum.set("y", y_span / 2)
self.lum.set("y span", Device_Width)
self.lum.set('z', MonitorHeight)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
[docs]
def setMMI2x2FDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the MMI2x2FDTDSolver.
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['x res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
posOffset = Parameters['Position Offset']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
Taper = Parameters['Taper']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if Taper == False:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [(Device_Length / 2), (Device_Length / 2), -(Device_Length / 2), -(Device_Length / 2)]
direction = ['Backward', 'Backward', 'Forward', 'Forward']
name = ['Input_L', 'Input_R', 'Output_L', 'Output_R']
yPort_vec = [posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2)]
overLapp = yPort_vec[0] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, -0.1e-6, 0.1e-6, 0.1e-6]
for i in range(4):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_"+ name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i]+ PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Ports
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
elif Taper == True:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length + 2*TaperLength
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [(Device_Length / 2), (Device_Length / 2), -(Device_Length / 2), -(Device_Length / 2)]
direction = ['Backward', 'Backward', 'Forward', 'Forward']
name = ['Input_L', 'Input_R', 'Output_L', 'Output_R']
yPort_vec = [posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2)]
overLapp = yPort_vec[0] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, -0.1e-6, 0.1e-6, 0.1e-6]
for i in range(4):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_"+ name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i]+ PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Ports
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
else:
raise ValueError("Incorect Taper variable!. Possible Taper values are Taper = False or Taper = True.")
[docs]
def setMMI2x1FDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the MMI2x1FDTDSolver.
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Input waveguide/taper offset.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['x res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
OffsetInput = Parameters['Offset Input']
posOffset = Parameters['Position Offset']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
Taper = Parameters['Taper']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if 'Offset Output' not in list(Parameters.keys()):
OffsetOutput = None
else:
OffsetOutput = Parameters['Offset Output']
if Taper == False:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
# Ports_mid = Substrate_Height + Slab_Height + WG_Height / 2
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH + WG_Height/2
# WG_H = WG_Height
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = [(Device_Length / 2), -(Device_Length / 2), -(Device_Length / 2)]
direction = ['Backward', 'Forward', 'Forward']
name = ['Input', 'Output_L', 'Output_R']
if OffsetOutput == None:
yPort_vec = [OffsetInput, -(posOffset / 2 + WG_Width / 2) , (posOffset / 2 + WG_Width / 2) ]
else:
yPort_vec = [OffsetInput, -(posOffset / 2 + WG_Width / 2) + OffsetOutput, (posOffset / 2 + WG_Width / 2) + OffsetOutput]
overLapp = yPort_vec[2] - y_Port_Span/2
if overLapp < 0:
y_Port_Span_old = y_Port_Span
y_Port_Span = yPort_vec[2]*2
print((f"!!! CAUTION !!! - The Ports are overlapping at the middle! So the y Span of the ports will be reduze from y_span = {y_Port_Span_old} to y_span_new = {y_Port_Span}"))
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6]
for i in range(3):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_" + name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span_old)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span_old)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
print("YOu are in the else part Now")
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6]
for i in range(3):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_"+ name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
elif Taper == True:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length +2*TaperLength
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH + WG_Height/2
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
# WG_H = WG_Height
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0);
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = [(Device_Length / 2), -(Device_Length / 2), -(Device_Length / 2)]
direction = ['Backward', 'Forward', 'Forward']
name = ['Input', 'Output_L', 'Output_R']
yPort_vec = [OffsetInput, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2]
overLapp = yPort_vec[2] - y_Port_Span/2
if overLapp < 0:
y_Port_Span_old = y_Port_Span
y_Port_Span = yPort_vec[2] * 2
print(f"!!! CAUTION !!! - The Ports are overlapping at the middle! So the y Span of the ports will be reduze from y_span = {y_Port_Span_old} to y_span_new = {y_Port_Span}")
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6]
for i in range(3):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_" + name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6]
for i in range(3):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_"+ name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
else:
raise ValueError("Incorect Taper variable!. Possible Taper values are Taper = False or Taper = True.")
[docs]
def setDCFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the DCFDTDSolver.
Parameters['Substrate Height'] : float/int
Height of the Substrate
Parameters['Substrate Width'] : float/int
Width of the MMI
Parameters['DC Length'] : float/int
Length of the Directional coupler
Parameters['WG Height'] : float/int
Height of the Waveguide
Parameters['WG Width'] : float/int
Waveguide Width
Parameters['Position Offset'] : float/int
Positional offser of the waveguides. If posOffset the two Waveguides
will be offset of the middle position (y = 0) by the half of there
Width. In this case they will not overlap if the Offset is 0.
Parameters['x res'] : float/int
Mesh resolution for the x-Axis
Parameters['Slab Height'] : float/int
Slab height.
Parameters['Wavelength'] : float/int
Wavelength
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
Substrate_Width = Parameters['Substrate Width']
DC_Lenght = Parameters['DC Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
posOffset = Parameters['Position Offset']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
# Device specifications
Device_Length = DC_Lenght
Device_Width = Substrate_Width
max_slabH = Slab_Height
Ports_mid = Substrate_Height + max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
x = [(Device_Length / 2), (Device_Length / 2), -(Device_Length / 2), -(Device_Length / 2)]
direction = ['Backward', 'Backward', 'Forward', 'Forward']
yPort_vec = [posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2)]
name = ['Input_L', 'Input_R', 'Output_L', 'Output_R']
overLapp = yPort_vec[0] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
self.lum.addmovie()
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
for i in range(4):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_"+ name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i]+0.1e-6)
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -(0.5e-6 + Device_Length / 2))
self.lum.set("x max", (0.5e-6 + Device_Length / 2))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# def setWDMFDTDSolver(self, Parameters):
# '''
# Parameters
# ----------
# Parameters : Dictionary
# Dictionary with all the data needet for the Bend Wavaguide. Data needet:
# Parameters
# ----------
# Parameters['Substrate Height'] : int/float
# Substrate height.
# Parameters['MMI Width'] : int/float
# Width of the MMI.
# Parameters['MMI Length'] : int/float
# Length of the MMI.
# Parameters['WG Height' : int/float
# Waveguide hight. Also the height of the MMI section
# Parameters['WG Width'] : int/float
# Waveguide width.
# Parameters['WG Length'] : int/float
# Waveguide length.
# Parameters['y res'] : int/float
# Mesh y-Axis
# Parameters['z res'] : int/float
# Mesh z-Axis
# Parameters['Slab Height'] : int/float
# Slab Height.
# Parameters['Wavelength'] : int/float
# Wavelength
# Parameters['Angle Thetha'] : boolen
# Angle for the input and output waveguides
# Parameters["Mode"] : str
# Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
# Returns
# -------
# None.
# '''
# Substrate_Height = Parameters['Substrate Height']
# MMI_Width = Parameters['MMI Width']
# MMI_Length = Parameters['MMI Length']
# WG_Height = Parameters['WG Height']
# WG_Width = Parameters['WG Width']
# WG_Length = Parameters['WG Length']
# y_res = Parameters['y res']
# z_res = Parameters['z res']
# Slab_Height = Parameters['Slab Height']
# WaveLength = Parameters['Wavelength']
# angleTheta = Parameters['Angle Thetha']
# Mode = Parameters["Mode"]
# # Device specifications
# Device_Length = MMI_Length + 2 * WG_Length
# Device_Width = MMI_Width + 2*(WG_Length) + WaveLength * 2 # MMI_Width
# max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
# Ports_mid = (max_slabH + WG_Height) / 2
# # Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
# self.lum.addeme()
# self.lum.set('simulation temperature', 273.15 + 20)
# self.lum.set("x min", 1e-6)
# self.lum.set("y", 0)
# self.lum.set("y span", Device_Width)
# self.lum.set("z", Substrate_Height)
# self.lum.set("z span", 4e-6)
# self.lum.set("wavelength", WaveLength)
# self.lum.set("z min bc", "PML")
# self.lum.set("z max bc", "PML")
# self.lum.set("y min bc", "PML")
# self.lum.set("y max bc", "PML")
# # set cell properties
# self.lum.set("number of cell groups", 3)
# self.lum.set("group spans", np.array([[WG_Length - 1e-6], [MMI_Length], [WG_Length - 1e-6]]))
# self.lum.set("cells", np.array([[3], [3], [3]]))
# self.lum.set("subcell method", np.array([[0], [0], [0]]))
# # Modes to Calculate
# self.lum.set('number of modes for all cell groups', 20)
# # Mesh Cells
# self.lum.set("define y mesh by", "maximum mesh step")
# self.lum.set("dy", y_res)
# self.lum.set("define z mesh by", "maximum mesh step")
# self.lum.set("dz", z_res)
# self.lum.set('fit materials with multi-coefficient model', 1)
# self.lum.set('wavelength start', 0.4e-6)
# self.lum.set('wavelength stop', 2e-6)
# # max_yPos = [(WG_W / 2 + OffsetInput) * 2, 0, (WG_Width / 2 + posOffset / 2) * 2]
# # min_yPos = [-(WG_W / 2 + OffsetInput) * 2, -(WG_Width / 2 + posOffset / 2) * 2, 0]
# Input_yPos = -MMI_Width / 2 + WG_Width / 2
# y = WG_Length * np.tan(angleTheta * np.pi / 180)
# Input_Y = Input_yPos - y + WG_Width
# Output_yPos = MMI_Width / 2 - WG_Width / 2
# y2 = WG_Length * np.tan(angleTheta * np.pi / 180)
# Output_Y = Output_yPos + y2 - WG_Width / 2
# portLoc = ["left", "right"]
# self.lum.select("EME::Ports::port_" + str(1))
# self.lum.set("port location", portLoc[0])
# self.lum.set("use full simulation span", 0)
# # self.lum.set("y min", min_yPos[1])
# # self.lum.set("y max", max_yPos[1])
# self.lum.set("y", Input_Y)
# self.lum.set("y span", WG_Width + 2e-6)
# self.lum.set("z", Ports_mid)
# self.lum.set("z span", 2e-6)
# self.lum.set("mode selection", Mode)
# self.lum.set("theta", angleTheta)
# self.lum.select("EME::Ports::port_" + str(2))
# self.lum.set("port location", portLoc[1])
# self.lum.set("use full simulation span", 0)
# # self.lum.set("y min", min_yPos[2])
# # self.lum.set("y max", max_yPos[2])
# self.lum.set("y", Output_Y)
# self.lum.set("y span", WG_Width + 2e-6)
# self.lum.set("z", Ports_mid)
# self.lum.set("z span", 2e-6)
# self.lum.set("mode selection", Mode)
# self.lum.set("theta", angleTheta)
# # Add monitor
# # x_MMI = Device_Length / 2
# self.lum.addemeprofile()
# self.lum.set("x", Device_Length / 2)
# self.lum.set("x span", Device_Length)
# # self.lum.set("x min", -(Device_Length / 2))
# # self.lum.set("x max", (Device_Length / 2))
# self.lum.set("y", 0)
# self.lum.set("y span", Device_Width )
# # self.lum.set("y min", -Device_Width / 2)
# # self.lum.set("y max", Device_Width / 2)
# self.lum.set("z", MonitorHeight)
[docs]
def setInverseTaperFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the InverseTaperFDTDSolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters['x res'] : int/float
Mesh x-Axis
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
Slab_Height = Parameters['Slab Height']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperLength_PWB = Parameters['PWB Taper Length']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
x_res = Parameters['x res']
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
# SMF Parameters
CoreDiameter = Parameters["SMF Core Diameter"]
CladdingDiameter = Parameters["SMF Cladding Diameter"]
if Slab_Height == 0:
# # Device specifications
Ports_mid = Substrate_Height/2 + WG_Height/2
Ports_PWB_mid = Substrate_Height + CoreDiameter/2 # TaperHightB/2
self.lum.select("SMF")
xPos_SMF = self.lum.get("x")
X_min = -TaperLength_PWB/2 - abs(xPos_SMF)
else:
# # Device specifications
max_slabH = Slab_Height
Ports_mid = max_slabH + Substrate_Height/2 + WG_Height/2
Ports_PWB_mid = max_slabH + CoreDiameter/2 # TaperHightB/2
self.lum.select("SMF")
xPos_SMF = self.lum.get("x")
X_min = -TaperLength_PWB/2 - abs(xPos_SMF)
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", X_min)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length)
self.lum.set("y", 0)
self.lum.set("y span", 2*TaperWidthB )# + TaperWidthB/2
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height/2 + CoreDiameter/2 ) #Substrate_Height
self.lum.set("z span", TaperHightB*2)
self.lum.set('x min bc', 'PML')
self.lum.set('x max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Ports Positions
xPort_Pos = [X_min, TaperLength_PWB]
yPort_Pos = [CoreDiameter + 2e-6, y_Port_Span]
zPort_Pos = [CoreDiameter+2e-6, z_Port_Span]
direction = ['Backward', 'Forward', 'Forward', 'Forward', 'Forward']
name = ['SMF Port', 'Waveguide Port']
x =[X_min, TaperLength_PWB/2 + WG_Length]
PortCorrection = [0.1e-6, -0.1e-6]
zMid_Pos_Ports = [Ports_PWB_mid, Ports_mid]
direction = ["Forward", "Backward"]
# Gaussian Source
self.lum.addgaussian()
self.lum.set("injection axis", "x-axis")
self.lum.set("direction", direction[0])
self.lum.set("x", x[0])
self.lum.set("y", 0)
self.lum.set("y span", 2*TaperWidthB) # yPort_Pos[0]
self.lum.set("z", zMid_Pos_Ports[0])
self.lum.set("z span", TaperHightB*2) # zPort_Pos[0]
self.lum.set("waist radius w0", CoreDiameter/2)
self.lum. set("distance from waist",0)
# Add Ports to Structure and power monitors
for i in range(2):
self.lum.addpower()
self.lum.set('name', "Power_" + name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", 0)
self.lum.set("y span", yPort_Pos[i])
self.lum.set("z", zMid_Pos_Ports[i])
self.lum.set("z span", zPort_Pos[i])
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# self.lum.addport()
# self.lum.set('name', name[i])
# self.lum.set("x", x[i])
# self.lum.set("y", 0)
# self.lum.set("y span", yPort_Pos[i])
# self.lum.set("z", zMid_Pos_Ports[i])
# self.lum.set("z span", zPort_Pos[i])
# self.lum.set('direction', direction[i])
# self.lum.set('mode selection', Mode)
# Add Z Monitor over stucute
self.lum.addpower()
self.lum.set('name', "Power 2D Z-Normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", X_min)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length)
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB + TaperWidthB/2)
self.lum.set("z", Ports_mid) #Substrate_Height
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Movi Monitor Z-Normal over structure
self.lum.addmovie()
self.lum.set("x min", X_min)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length)
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB + TaperWidthB/2)
self.lum.set("z", Ports_mid)
# Add Y Monitor over stucute
self.lum.addpower()
self.lum.set('name', "Power 2D Y-Normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", X_min)
self.lum.set("x max", TaperLength_PWB/2 + WG_Length)
self.lum.set("y", 0)
self.lum.set("z", Substrate_Height/2 + CoreDiameter/2 ) #Substrate_Height
self.lum.set("z span", TaperHightB*2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# #Add Extra Mesch
# self.lum.addmesh()
# self.lum.set("name", "Mesh PWB")
# self.lum.set("based on a structure",1)
# self.lum.set("structure", "Taper_PWB")
# self.lum.set("set maximum mesh step",1)
# self.lum.set("override x mesh",0)
# # self.lum.set("dx", x_res)
# self.lum.set("override y mesh",1)
# self.lum.set("dy", x_res)
# self.lum.set("override z mesh",1)
# self.lum.set("dz", x_res)
# self.lum.addmesh()
# self.lum.set("name", "Mesh Inverse Taper")
# self.lum.set("based on a structure",1)
# self.lum.set("structure", "InverseTaper")
# self.lum.set("set maximum mesh step",1)
# self.lum.set("override x mesh",0)
# # self.lum.set("dx", x_res)
# self.lum.set("override y mesh",1)
# self.lum.set("dy", x_res)
# self.lum.set("override z mesh",1)
# self.lum.set("dz", x_res)
[docs]
def setCascadetMMIFDTDSolver(self, Parameters, SpaceX, SpaceY):
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
OffsetInput = Parameters['Offset Input']
posOffset = Parameters['Position Offset']
x_res = Parameters['x res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
Taper = Parameters['Taper']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if Taper == False:
# Device specifications
Device_Length = 2*MMI_Length + 4 * WG_Length + SpaceX
Device_Width = 3*MMI_Width + WaveLength * 2 + 2*SpaceY# MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
# Ports_mid = Substrate_Height + Slab_Height + WG_Height / 2
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH
# WG_H = WG_Height
x_Offset = MMI_Length + WG_Length
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x", -x_Offset)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
yPos = [0 + OffsetInput, WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
MMI_Wid = MMI_Width
Parameters__Position_X = [0, -SpaceX -MMI_Length - 2*WG_Length , -SpaceX -MMI_Length - 2*WG_Length]
Parameters__Position_Y = [0, (MMI_Wid + SpaceY), -(MMI_Wid + SpaceY)]
y_span = (Parameters__Position_Y[1] + yPos[0]) - (Parameters__Position_Y[0] + yPos[1] )
x = [(MMI_Length/2 + WG_Length ) , -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX ), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX ), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX )]
direction = ['Backward', 'Forward', 'Forward', 'Forward', 'Forward']
name = ['Input', 'Output1_Top', 'Output1_Bot', 'Output2_Top', 'Output2_Bot']
yPort_vec = [OffsetInput, y_span + WG_Width + posOffset + WG_Width/2, y_span + WG_Width / 2, -y_span - WG_Width / 2, - y_span - WG_Width- posOffset - WG_Width/2]
overLapp = yPort_vec[2] - y_Port_Span/2
# if overLapp < 0:
# y_Port_Span_old = y_Port_Span
# y_Port_Span = yPort_vec[2]*2
# print((f"!!! CAUTION !!! - The Ports are overlapping at the middle! So the y Span of the ports will be reduze from y_span = {y_Port_Span_old} to y_span_new = {y_Port_Span}"))
self.lum.addmovie()
self.lum.set("x", -x_Offset)
self.lum.set("x span", Device_Length)
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6,0.1e-6, 0.1e-6]
for i in range(5):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_" + name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", -(0.5e-6 + x_Offset))
self.lum.set("x span", (0.5e-6 + Device_Length))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", -(0.5e-6 + x_Offset))
self.lum.set("x span", (0.5e-6 + Device_Length ))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
elif Taper == True:
# Device specifications
Device_Length = 2*MMI_Length +4 * WG_Length + 4*TaperLength + SpaceX
Device_Width = 3*MMI_Width + WaveLength * 2 + 2*SpaceY# MMI_Width
max_slabH = Slab_Height
# Ports_mid = Substrate_Height + (max_slabH + WG_Height) / 2
Ports_mid = Substrate_Height + max_slabH
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
# WG_H = WG_Height
x_Offset = MMI_Length + WG_Length + TaperLength
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x", -x_Offset)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0);
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
yPos = [0 + OffsetInput, WG_Width / 2 + posOffset / 2, - WG_Width / 2 - posOffset / 2]
MMI_Wid = MMI_Width
Parameters__Position_X = [0, -SpaceX -MMI_Length - 2*WG_Length , -SpaceX -MMI_Length - 2*WG_Length]
Parameters__Position_Y = [0, (MMI_Wid + SpaceY), -(MMI_Wid + SpaceY)]
y_span = (Parameters__Position_Y[1] + yPos[0]) - (Parameters__Position_Y[0] + yPos[1] )
x = [(MMI_Length/2 + WG_Length + TaperLength ) , -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX+ 3*TaperLength ), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX+ 3*TaperLength), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX+ 3*TaperLength ), -(MMI_Length/2 + MMI_Length+ 3*WG_Length + SpaceX+ 3*TaperLength )]
direction = ['Backward', 'Forward', 'Forward', 'Forward', 'Forward']
name = ['Input', 'Output1_Top', 'Output1_Bot', 'Output2_Top', 'Output2_Bot']
yPort_vec = [OffsetInput, y_span + WG_Width + posOffset + WG_Width/2, y_span + WG_Width / 2, -y_span - WG_Width / 2, - y_span - WG_Width- posOffset - WG_Width/2]
overLapp = yPort_vec[2] - y_Port_Span/2
self.lum.addmovie()
self.lum.set("x", -x_Offset)
self.lum.set("x span", Device_Length)
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set("z", Ports_mid)
PortCorrection = [-0.1e-6, 0.1e-6, 0.1e-6,0.1e-6, 0.1e-6]
for i in range(5):
# Power Monitor Port 1
self.lum.addpower()
self.lum.set('name', "Power_" + name[i])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", x[i] + PortCorrection[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
self.lum.addport()
self.lum.set('name', name[i])
self.lum.set("x", x[i])
self.lum.set("y", yPort_vec[i])
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set('direction', direction[i])
self.lum.set('mode selection', Mode)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", -(0.5e-6 + x_Offset))
self.lum.set("x span", (0.5e-6 + Device_Length))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Power and Freq Monitor
self.lum.addpower()
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", -(0.5e-6 + x_Offset))
self.lum.set("x span", (0.5e-6 + Device_Length ))
self.lum.set("y min", -(Device_Width + 1e-6))
self.lum.set("y max", (Device_Width + 1e-6))
self.lum.set('z', Substrate_Height + Slab_Height + WG_Height / 2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
else:
raise ValueError("Incorect Taper variable!. Possible Taper values are Taper = False or Taper = True.")
[docs]
def setGratingCouplerFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the GratingCouplerFDTDSolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Input Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters['Taper'] : boolen
Add Taper to structure
Parameters['Taper Length'] : int/float
Length of the Taper
Parameters['Wavelength'] : int/float
Wavelength
Parameters['x res'] : int/float
Mesh x-Axis
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
Returns
-------
None.
'''
SubstrateThickness = Parameters['Substrate Height']
GC_SectionLenght = Parameters["Length GC"]
InputLenght = Parameters["Input Length GC"]
OutputLenght = Parameters["Output Length GC"]
WidthGC = Parameters["Width GC"]
Hight = Parameters["Hight GC"]
Taper = Parameters["Taper"]
TaperLength = Parameters['Taper Length']
ZSpan = Parameters["SMF Z Span"]
Theta = Parameters["SMF Theta"]
CoreDiameter = Parameters["SMF Core Diameter"]
x_res = Parameters['x res']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
CladdingThickness = 0.7e-6
# Device specifications
Device_Length = GC_SectionLenght + InputLenght + OutputLenght + TaperLength
FDTD_ZSpan = Hight + CladdingThickness + SubstrateThickness
# Define Ports
Port_Names = ["Input_SMF_Port", "Output"]
self.lum.select("SMF")
fiber_xpos = self.lum.get("x")
if Taper == True:
# Adds a Finite-Difference Time-Domain (FDTD) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set("x max", fiber_xpos + CoreDiameter )
# self.lum.set("x", -TaperLength / 2)
# self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set("z", 0)
self.lum.set("z span", (ZSpan / 2))
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Detect Fiber position for Port exact aligment
self.lum.select("SMF")
fiber_xpos = self.lum.get("x")
fiber_ypos = self.lum.get("y")
fiber_zpos = self.lum.get("z")
self.lum.select("SMF::core")
fiber_core_diameter = 2 * self.lum.get("radius")
fiber_core_index = self.lum.get("index")
fiber_theta = Theta
# Faser Port
self.lum.addport()
self.lum.set('name', Port_Names[0])
self.lum.set('injection axis', "z-axis")
self.lum.set('direction', "Backward")
self.lum.set('mode selection', Mode)
self.lum.set('theta', Theta)
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
# self.lum.set("x", 0)
# self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("rotation offset", ZSpan / 4)
self.lum.set("z", (SubstrateThickness + 2e-6) / 2 )
# Output Port
self.lum.addport()
self.lum.set('name', Port_Names[1])
self.lum.set("x", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set('x span', CoreDiameter)
self.lum.set('y', 0)
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Hight/2 )
self.lum.set("z span", z_Port_Span )
self.lum.set('injection axis', "x-axis")
self.lum.set('direction', "Forward")
self.lum.set('mode selection', Mode)
# Power Monitor SMF Port
self.lum.addpower()
self.lum.set('name', "Power_"+ Port_Names[0])
self.lum.set('monitor type', '2D Z-normal')
# self.lum.set("x", 0)
# self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("z", (ZSpan / 4) - 0.3e-6)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Power Monitor Output Port
self.lum.addpower()
self.lum.set('name', "Power_" + Port_Names[1])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("z", Hight/2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Power and Freq Monitor Z-Normal
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set('z', Hight/2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Movie Monitor Z-Normal
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set('z', 0)
# Add Global Power and Freq Monitor Y-Axis
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Movie Monitor Y-Axis
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght - TaperLength)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", 2e-6)
# Select Source
self.lum.select('FDTD::ports')
self.lum.set('source port', 'Input_SMF_Port')
else:
# Adds a Finite-Difference Time-Domain (FDTD) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght )
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set("z", 0)
self.lum.set("z span", (ZSpan / 2))
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Detect Fiber position for Port exact aligment
self.lum.select("SMF")
fiber_xpos = self.lum.get("x")
fiber_ypos = self.lum.get("y")
fiber_zpos = self.lum.get("z")
self.lum.select("SMF::core")
fiber_core_diameter = 2 * self.lum.get("radius")
fiber_core_index = self.lum.get("index")
fiber_theta = Theta
# Faser Port
self.lum.addport()
self.lum.set('name', Port_Names[0])
self.lum.set('injection axis', "z-axis")
self.lum.set('direction', "Backward")
self.lum.set('mode selection', Mode)
self.lum.set('theta', Theta)
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("rotation offset", ZSpan / 4)
self.lum.set("z", (ZSpan / 4) - 0.3e-6)
# Output Port
self.lum.addport()
self.lum.set('name', Port_Names[1])
self.lum.set("x", -GC_SectionLenght / 2 - InputLenght)
self.lum.set('x span', CoreDiameter)
self.lum.set('y', 0)
self.lum.set('y span', WidthGC)
self.lum.set("z", Hight / 2)
self.lum.set("z span", z_Port_Span)
self.lum.set('injection axis', "x-axis")
self.lum.set('direction', "Forward")
self.lum.set('mode selection', Mode)
# Power Monitor SMF Port
self.lum.addpower()
self.lum.set('name', "Power_" + Port_Names[0])
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("z", (ZSpan / 4) - 0.3e-6)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Power Monitor Output Port
self.lum.addpower()
self.lum.set('name', "Power_" + Port_Names[1])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", -GC_SectionLenght / 2 - InputLenght)
self.lum.set('y', 0)
self.lum.set('y span', WidthGC)
self.lum.set("z", Hight / 2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Power and Freq Monitor Z-Normal
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set('z', Hight/2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Movie Monitor Z-Normal
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", WidthGC)
self.lum.set('z', 0)
# Add Global Power and Freq Monitor Y-Axis
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Movie Monitor Y-Axis
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - InputLenght)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", 2e-6)
# Select Source
self.lum.select('FDTD::ports')
self.lum.set('source port', 'Input_SMF_Port')
[docs]
def setRingGratingCouplerFDTDSolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the RingGratingCouplerFDTDSolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Input Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
Parameters['Taper Length'] : int/float
Length of the input Taper
Parameters['Wavelength'] : int/float
Wavelength
Parameters['x res'] : int/float
Mesh x-Axis
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Returns
-------
None.
'''
SubstrateThickness = Parameters['Substrate Height']
GC_SectionLenght = Parameters["Length GC"]
InputLenght = Parameters["Input Length GC"]
OutputLenght = Parameters["Output Length GC"]
WidthGC = Parameters["Width GC"]
Hight = Parameters["Hight GC"]
GCRadius = Parameters["GC Radius"]
TaperLength = Parameters['Taper Length']
ZSpan = Parameters["SMF Z Span"]
Theta = Parameters["SMF Theta"]
CoreDiameter = Parameters["SMF Core Diameter"]
x_res = Parameters['x res']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
CladdingThickness = 0.7e-6
# Device specifications
Device_Length = GC_SectionLenght + OutputLenght + TaperLength
Device_Width = WidthGC + ( GCRadius + GC_SectionLenght + OutputLenght - GCRadius)
FDTD_ZSpan = Hight + CladdingThickness + SubstrateThickness
# Define Ports
Port_Names = ["Input_SMF_Port", "Output"]
self.lum.select("SMF")
fiber_xpos = self.lum.get("x")
# Adds a Finite-Difference Time-Domain (FDTD) solver region to the MODE simulation environment.
self.lum.addfdtd()
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 / 2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght +1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", Device_Width/2)
self.lum.set("z", 0)
self.lum.set("z span", SubstrateThickness + 2e-6)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('y min bc', 'Anti-Symmetric')
self.lum.set('y max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', WaveLength)
self.lum.set('global source wavelength span', 0)
# Detect Fiber position for Port exact aligment
self.lum.select("SMF")
fiber_xpos = self.lum.get("x")
fiber_ypos = self.lum.get("y")
fiber_zpos = self.lum.get("z")
self.lum.select("SMF::core")
fiber_core_diameter = 2 * self.lum.get("radius")
fiber_core_index = self.lum.get("index")
fiber_theta = Theta
# Faser Port
self.lum.addport()
self.lum.set('name', Port_Names[0])
self.lum.set('injection axis', "z-axis")
self.lum.set('direction', "Backward")
self.lum.set('mode selection', Mode)
self.lum.set('theta', Theta)
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("z", (SubstrateThickness + 2e-6) / 2 )
self.lum.set("rotation offset", ZSpan / 4)
# Output Port
self.lum.addport()
self.lum.set('name', Port_Names[1])
self.lum.set("x", -GC_SectionLenght/2 - 8e-6 /2)
self.lum.set('x span', CoreDiameter)
self.lum.set('y', 0)
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Hight/2 )
self.lum.set("z span", z_Port_Span )
self.lum.set('injection axis', "x-axis")
self.lum.set('direction', "Forward")
self.lum.set('mode selection', Mode)
# Power Monitor SMF Port
self.lum.addpower()
self.lum.set('name', "Power_"+ Port_Names[0])
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x", fiber_xpos)
self.lum.set('x span', CoreDiameter + CoreDiameter / 2)
self.lum.set('y', 0)
self.lum.set('y span', CoreDiameter + CoreDiameter / 2)
self.lum.set("z", (ZSpan / 4) - 0.3e-6)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Power Monitor Output Port
self.lum.addpower()
self.lum.set('name', "Power_" + Port_Names[1])
self.lum.set('monitor type', '2D X-normal')
self.lum.set("x", -GC_SectionLenght/2 - 8e-6 /2)
self.lum.set('y', 0)
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Hight/2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Power and Freq Monitor
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 /2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght +1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", Device_Width/2)
self.lum.set('z', Hight/2)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Power and Freq Monitor
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Z-normal")
self.lum.set('monitor type', '2D Z-normal')
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 /2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght +1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set("y span", Device_Width/2)
self.lum.set('z', 0)
# Add Global Power and Freq Monitor Y-Axis
self.lum.addpower()
self.lum.set('name', "Global_Power_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 /2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght + 1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", z_Port_Span)
self.lum.set('output Px', 1)
self.lum.set('output Py', 1)
self.lum.set('output Pz', 1)
self.lum.set('output power', 1)
# Add Global Movie Monitor Y-Axis
self.lum.addmovie()
self.lum.set('name', "Global_Movie_Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 /2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght + 1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", 2e-6)
# Add Refractive index Monitor
self.lum.addindex()
self.lum.set('name', "Refractive Index Monitor Y-normal")
self.lum.set('monitor type', '2D Y-normal')
self.lum.set("x min", -GC_SectionLenght/2 - 10e-6 /2 - 1e-6)
# self.lum.set("x max", GCRadius + GC_SectionLenght/2 + OutputLenght + 1e-6)
self.lum.set("x max", fiber_xpos + CoreDiameter )
self.lum.set("y", 0)
self.lum.set('z', Hight / 2)
self.lum.set("z span", z_Port_Span)
# Select Source
self.lum.select('FDTD::ports')
self.lum.set('source port', 'Input_SMF_Port')
[docs]
def setLenseFiberFDTD(self, Parameters):
Lens_d = Parameters["Lense Diameter"]
x_res = Parameters['x res']
Wavelength = Parameters['Wavelength']
# Set FDTD Solver directly
self.lum.addfdtd()
self.lum.set("x", 0)
self.lum.set("x span", 15e-6)
self.lum.set("y", 0)
self.lum.set("y span", 15e-6)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z min", -4e-6)
self.lum.set("z max", 50e-6)
self.lum.set('z min bc', 'PML')
self.lum.set('z max bc', 'PML')
self.lum.set('mesh type', 'auto non-uniform')
self.lum.set('min mesh step', x_res)
self.lum.set('set simulation bandwidth', 0)
self.lum.set('global source center wavelength', Wavelength)
self.lum.set('global source wavelength span', 0)
# Add Gausssian Source
self.lum.addgaussian()
self.lum.set("injection axis", "z-axis")
self.lum.set("direction", "Forward")
self.lum.set("x", 0)
self.lum.set("x span", Lens_d)
self.lum.set("y", 0)
self.lum.set("y span", Lens_d)
self.lum.set("z", -2e-6)
self.lum.set("waist radius w0", Lens_d / 2)
# Add Index Monitor
self.lum.addindex()
self.lum.set("name", "index")
self.lum.set("monitor type", "2D Y-normal")
self.lum.set("x", 0)
self.lum.set("x span", 10e-6)
self.lum.set("y", 0)
self.lum.set("z min", -3e-6)
self.lum.set("z max", 50e-6)
# Add Movie Monitor
self.lum.addmovie()
self.lum.set("name", "movie")
self.lum.set("monitor type", "2D Y-normal")
self.lum.set("x", 0)
self.lum.set("x span", 10e-6)
self.lum.set("y", 0)
self.lum.set("z min", -3e-6)
self.lum.set("z max", 50e-6)
# Add Power and field 3D monitor
self.lum.addpower()
self.lum.set("name", "Power 3D monitor")
self.lum.set("monitor type", "3D")
self.lum.set("x", 0)
self.lum.set("x span", 10e-6)
self.lum.set("y", 0)
self.lum.set("y span", 10e-6)
self.lum.set("z min", -3e-6)
self.lum.set("z max", 50e-6)
self.lum.set("output Px", 1)
self.lum.set("output Py", 1)
self.lum.set("output Pz", 1)
# Reflection 2D Monitor
self.lum.addpower()
self.lum.set("name", "2D Monitor")
self.lum.set("monitor type", "2D X-normal")
self.lum.set("x", 0)
self.lum.set("y", 0)
self.lum.set("y span", 10e-6)
self.lum.set("z", -2e-6)
self.lum.set("z max", 50e-6)
self.lum.set("output Px", 1)
self.lum.set("output Py", 1)
self.lum.set("output Pz", 1)
self.lum.addpower()
self.lum.set("name", "R")
self.lum.set("monitor type", "2D Z-normal")
self.lum.set("x", 0)
self.lum.set("x span", 10e-6)
self.lum.set("y", 0)
self.lum.set("y span", 10e-6)
self.lum.set("z", -3e-6)
self.lum.set("output Px", 1)
self.lum.set("output Py", 1)
self.lum.set("output Pz", 1)
[docs]
def setStraightWaveguideEMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the StraightWaveguideEMESolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Length'] : int/float
Waveguide Length
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters["Taper"] : boolen
If Taper == False, only straight Waveguide will be simulated,
If Taper == True an Taper will be simulated
Parameters['Taper Width'] : int/float
Taper backside Width. Taper Fronside width is the width of the Waveguide
Parameters['Taper Length'] : int/float
Taper Length
Parameters['y res']: int/float
EME Mesh resolutio,
Parameters['z res']: int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
This Parameter will set the theta ratation angle of the port. It can be 90 or 180.
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Parameters["Taper Type"] is given, themn the user need to set couple more parameters:
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Length = Parameters['WG Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
angle = Parameters["Waveguide Angle"]
y_res = Parameters['y res']
z_res = Parameters['z res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
Taper = Parameters['Taper']
TaperWidth = Parameters['Taper Width']
TaperLength = Parameters['Taper Length']
# Device specifications
Device_Length = WG_Length
Device_Width = 2*WG_Length + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
EME_WGLength = WG_Length * np.cos(angle * np.pi / 180)
if Taper == False:
if angle == 0:
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("x min", 0)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 1)
self.lum.set("group spans", np.array([[EME_WGLength]]))
self.lum.set("cells", np.array([[30]]))
self.lum.set("subcell method", np.array([[1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# Define Ports
yPos = [0, 0]
yPos_span = [y_Port_Span, y_Port_Span]
portLoc = ["left", "right"]
theta = [0, 0]
for i in range(2):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", theta[i])
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", (Device_Length / 2))
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
else:
# Calc Output Loc
sideLength = np.cos(angle*np.pi/180)* WG_Length
sideHight = np.sqrt(WG_Length**2 - sideLength**2)
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", 0)
self.lum.set("y", WG_Length / 4)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 1)
self.lum.set("group spans", np.array([[EME_WGLength]]))
self.lum.set("cells", np.array([[30]]))
self.lum.set("subcell method", np.array([[1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# Define Ports
yPos = [-WG_Length / 4, -WG_Length / 4 + sideHight]
yPos_span = [y_Port_Span, y_Port_Span]
portLoc = ["left", "right"]
theta = [angle, angle]
for i in range(2):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", theta[i])
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", (Device_Length / 2))
self.lum.set("x span", Device_Length)
self.lum.set("y", (Device_Length / 4))
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
elif Taper == True:
# Device specifications
Device_Length = TaperLength
Device_Width = 2 * TaperLength + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
Ports_mid = max_slabH + WG_Height/2
EME_WGLength = TaperLength * np.cos(angle * np.pi / 180)
if Slab_Height == 0:
# # Device specifications
MonitorHeight = Substrate_Height + WG_Height/2
Ports_mid = Substrate_Height + WG_Height/2
else:
# # Device specifications
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
Ports_mid = max_slabH + Substrate_Height + WG_Height/2
if "Taper Type" in list(Parameters.keys()):
TaperType = "Inverse"
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
else:
TaperType = "Normal"
if TaperType == "Normal":
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("x min", -TaperLength/2 + 0.1e-6)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 1)
self.lum.set("group spans", np.array([[EME_WGLength - 0.2e-6]]))
self.lum.set("cells", np.array([[30]]))
self.lum.set("subcell method", np.array([[1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# Define Ports
Diff_Span = y_Port_Span - WG_Width
yPos = [0, 0]
yPos_span = [y_Port_Span, TaperWidth + Diff_Span]
portLoc = ["left", "right"]
theta = [0, 0]
for i in range(2):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", theta[i])
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
else:
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("x min", -TaperLength/2 + 0.1e-6)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
# self.lum.set("z", Substrate_Height)
# self.lum.set("z span", 4e-6)
self.lum.set("z min", -Substrate_Height )
self.lum.set("z max", TaperHightB*2)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 1)
self.lum.set("group spans", np.array([[EME_WGLength - 0.2e-6]]))
self.lum.set("cells", np.array([[30]]))
self.lum.set("subcell method", np.array([[1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# Define Ports
Diff_Span = y_Port_Span - WG_Width
yPos = [0, 0]
yPos_span = [y_Port_Span, TaperWidth + Diff_Span]
theta = [0, 0]
portLoc = ["left", "right"]
name = ['Input', 'Output']
self.lum.select("EME")
Span_z = self.lum.get("z span")
yPos_span = [ TaperWidthB + Diff_Span , y_Port_Span ]
z_Pos = [ -Span_z/2 + Substrate_Height + max_slabH + TaperHightB/2, -Span_z/2 + Substrate_Height + max_slabH + TaperHightF/2 ]
z_Span = [ TaperHightB + z_Port_Span , TaperHightF/2 + z_Port_Span]
for i in range(2):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set("y", yPos[i])
self.lum.set("y span", yPos_span[i])
self.lum.set("z", z_Pos[i])
self.lum.set("z span", z_Span[i])
self.lum.set("mode selection", Mode)
self.lum.set("theta", theta[i])
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("z", MonitorHeight)
[docs]
def setDCEMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the DCEMESolver.
Parameters['Substrate Height'] : float/int
Height of the Substrate
Parameters['Substrate Width'] : float/int
Width of the MMI
Parameters['DC Length'] : float/int
Length of the Directional coupler
Parameters['WG Height'] : float/int
Height of the Waveguide
Parameters['WG Width'] : float/int
Waveguide Width
Parameters['Position Offset'] : float/int
Positional offser of the waveguides. If posOffset the two Waveguides
will be offset of the middle position (y = 0) by the half of there
Width. In this case they will not overlap if the Offset is 0.
Parameters['y res'] : float/int
Mesh resolution for the y-Axis
Parameters['z res'] : float/int
Mesh resolution for the z Axis
Parameters['Slab Height'] : float/int
Slab height.
Parameters['Wavelength'] : float/int
Wavelength
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
DC_Lenght = Parameters['DC Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
posOffset = Parameters['Position Offset']
y_res = Parameters['y res']
z_res = Parameters['z res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
# Device specifications
Device_Length = DC_Lenght
Device_Width = WG_Width * 10 + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
Ports_mid = max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 1)
self.lum.set("group spans", np.array([[DC_Lenght]]))
self.lum.set("cells", np.array([[10]]))
self.lum.set("subcell method", np.array([[0]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# Define Ports
yPort_vec = [posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2)]
portLoc = ["left", "left", "right", "right"]
overLapp = yPort_vec[0] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
for i in range(2):
self.lum.addemeport()
for i in range(4):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
# Add monitor
self.lum.addemeprofile()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y min", -Device_Width / 2)
self.lum.set("y max", Device_Width / 2)
self.lum.set("z", MonitorHeight)
[docs]
def setMMI2x1EMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the MMI2x1EMESolver.
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Input waveguide/taper offset.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
angle = Parameters['angle']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
OffsetInput = Parameters['Offset Input']
posOffset = Parameters['Position Offset']
y_res = Parameters['y res']
z_res = Parameters['z res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
Taper = Parameters['Taper']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if Taper == False:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
# 1x2 MMI
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", -(Device_Length / 2)+0.1e-6)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties no Taper
self.lum.set("number of cell groups", 3)
self.lum.set("group spans", np.array([[WG_Length-0.1e-6], [MMI_Length], [WG_Length-0.1e-6]]))
self.lum.set("cells", np.array([[20], [3], [20]]))
self.lum.set("subcell method", np.array([[0], [0], [0]]))
elif Taper == True:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length + 2 * TaperLength
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
# 1x2 MMI
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", -(Device_Length / 2) + 0.1e-6)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
Device_Length = MMI_Length + 2 * WG_Length + 2 * TaperLength
# set cell properties
self.lum.set("number of cell groups", 5)
self.lum.set("group spans",
np.array([[WG_Length-0.1e-6], [TaperLength], [MMI_Length], [TaperLength], [WG_Length-0.1e-6]]))
self.lum.set("cells", np.array([[3], [20, ], [3], [20], [3]]))
self.lum.set("subcell method", np.array([[0], [1], [0], [1], [0]]))
else:
raise ValueError("Incorect Taper variable!. Possible Taper values are Taper = False or Taper = True.")
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# creating the MMI
max_MMIH = WG_Height
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
MMI_Wid = MMI_Width + 2 * extention
# Positions of the Input and Output WGs
# Triangle EQ for MMI Width
x = abs(max_MMIH / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - max_MMIH ** 2)
WG_W = WG_Width + 2 * extention
OffMin = -MMI_Wid / 2
OffMax = MMI_Wid / 2
offset_Set_R = posOffset / 2 + WG_W / 2 + WG_Width / 2
if OffsetInput - WG_W <= OffMin or OffsetInput + WG_W >= OffMax or offset_Set_R > OffMax:
self.lum.deleteall()
raise ValueError(
'You are Trying to move the input or output Waveguide outside the MMI area. This is not possible! Max Input/Output Offset = ' + str(
OffMax) + ' and Min Input/output Offset = ' + str(OffMin))
# Define Ports
else:
# if OffsetInput > 0:
# max_yPos = [(OffsetInput) * 2, 0, (WG_Width / 2 + posOffset / 2) * 2]
# min_yPos = [0, -(WG_Width / 2 + posOffset / 2) * 2, 0]
# portLoc = ["right", "left", "left"]
# elif OffsetInput < 0:
# max_yPos = [0, 0, (WG_Width / 2 + posOffset / 2) * 2]
# min_yPos = [-(abs(OffsetInput)) * 2, -(WG_Width / 2 + posOffset / 2) * 2, 0]
# portLoc = ["right", "left", "left"]
# else:
# max_yPos = [(WG_W / 2 + OffsetInput) * 2, 0, (WG_Width / 2 + posOffset / 2) * 2]
# min_yPos = [-(WG_W / 2 + OffsetInput) * 2, -(WG_Width / 2 + posOffset / 2) * 2, 0]
# portLoc = ["right", "left", "left"]
yPort_vec = [OffsetInput, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2]
portLoc = ["right", "left", "left"]
overLapp = yPort_vec[2] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
for i in range(1):
self.lum.addemeport()
for i in range(3):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
# self.lum.set("y min", min_yPos[i])
# self.lum.set("y max", max_yPos[i])
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
# Define the Motinot
self.lum.addemeprofile()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y min", -Device_Width / 2)
self.lum.set("y max", Device_Width / 2)
self.lum.set("z", MonitorHeight)
[docs]
def setMMI2x2EMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the MMI2x2EMESolver.
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell size.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
posOffset = Parameters['Position Offset']
y_res = Parameters['y res']
z_res = Parameters['z res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
TaperLength = Parameters['Taper Length']
Taper = Parameters['Taper']
Mode = Parameters["Mode"]
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
if Taper == False:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", -(Device_Length / 2)+0.1e-6)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 3)
self.lum.set("group spans", np.array([[WG_Length-0.1e-6], [MMI_Length], [WG_Length-0.1e-6]]))
self.lum.set("cells", np.array([[20], [3], [20]]))
self.lum.set("subcell method", np.array([[0], [0], [0]]))
elif Taper == True:
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length + 2 * TaperLength
Device_Width = MMI_Width + WaveLength * 2 # MMI_Width
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", -(Device_Length / 2)+0.1e-6)
self.lum.set("y", 0)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y span", Device_Width)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
Device_Length = MMI_Length + 2 * WG_Length + 2 * TaperLength
# set cell properties
self.lum.set("number of cell groups", 5)
self.lum.set("group spans",
np.array([[WG_Length-0.1e-6], [TaperLength], [MMI_Length], [TaperLength], [WG_Length-0.1e-6]]))
self.lum.set("cells", np.array([[3], [20], [3], [20], [3]]))
self.lum.set("subcell method", np.array([[0], [1], [0], [1], [0]]))
else:
raise ValueError("Incorect Taper variable!. Possible Taper values are Taper = False or Taper = True.")
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# define two values for upper and lower limit of the WG offsets
OffMin = -MMI_Width / 2
OffMax = MMI_Width / 2
if posOffset <= OffMin or posOffset >= OffMax:
self.lum.deleteall()
raise ValueError('You are Trying to move the Waveguide outside the MMI. This is not possible!')
else:
# Define Ports
yPort_vec = [posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2), posOffset / 2 + WG_Width / 2, -(posOffset / 2 + WG_Width / 2)]
portLoc = ["left", "left", "right", "right"]
overLapp = yPort_vec[0] - y_Port_Span/2
if overLapp < 0:
raise ValueError("!!! CAUTION !!! - The Ports are overlapping at the middle! Please change the Y Port Span or move the Waveguides away from each other!")
else:
pass
for i in range(2):
self.lum.addemeport()
for i in range(4):
self.lum.select("EME::Ports::port_" + str(i + 1))
self.lum.set("port location", portLoc[i])
self.lum.set("use full simulation span", 0)
self.lum.set('y', yPort_vec[i])
self.lum.set('y span', y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
# Add monitor
self.lum.addemeprofile()
self.lum.set("x min", -(Device_Length / 2))
self.lum.set("x max", (Device_Length / 2))
self.lum.set("y min", -Device_Width / 2)
self.lum.set("y max", Device_Width / 2)
self.lum.set("z", MonitorHeight)
[docs]
def setWDMEMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the WDMEMESolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Taper Width'] : int/float
Taper backside width, frontside width is the waveguide width.
Parameters['Taper Length'] : int/float
Taper Length
Parameters['y res'] : int/float
Mesh y-Axis
Parameters['z res'] : int/float
Mesh z-Axis
Parameters['Slab Height'] : int/float
Slab Height.
Parameters['Wavelength'] : int/float
Wavelength
Parameters['Angle Thetha'] : boolen
Angle for the input and output waveguides
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Raises
------
ValueError
DESCRIPTION.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
MMI_Width = Parameters['MMI Width']
MMI_Length = Parameters['MMI Length']
WG_Height = Parameters['WG Height']
WG_Length = Parameters['WG Length']
WG_Width = Parameters['WG Width']
y_res = Parameters['y res']
z_res = Parameters['z res']
Slab_Height = Parameters['Slab Height']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
angleTheta = Parameters['Angle Thetha']
TaperWidth = Parameters['Taper Width']
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
TaperLength = Parameters['Taper Length']
# Device specifications
Device_Length = MMI_Length + 2 * WG_Length
# Device_Width = MMI_Width + 2*WG_Length + WaveLength * 2 # MMI_Width
Device_Width = MMI_Width + 2*WG_Length + WaveLength * 2
max_slabH = Slab_Height
# MonitorHeight = Substrate_Height + (max_slabH + WG_Height) / 2
MonitorHeight = Substrate_Height + max_slabH + WG_Height/2
# Ports_mid = (max_slabH + WG_Height) / 2
Ports_mid = max_slabH + WG_Height/2
# EME Boundary Length
BoardLen = np.cos(angleTheta * np.pi / 180) * TaperLength
X_min = -BoardLen - MMI_Length/2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("y", 0)
self.lum.set("y span", Device_Width)
self.lum.set("x min", X_min + 0.1e-6)
self.lum.set("z", Substrate_Height)
self.lum.set("z span", 4e-6)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.set("number of cell groups", 3)
self.lum.set("group spans", np.array([[BoardLen ], [MMI_Length], [BoardLen ]]))
self.lum.set("cells", np.array([[15], [10], [15]]))
self.lum.set("subcell method", np.array([[1], [1], [1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
# max_yPos = [(WG_W / 2 + OffsetInput) * 2, 0, (WG_Width / 2 + posOffset / 2) * 2]
# min_yPos = [-(WG_W / 2 + OffsetInput) * 2, -(WG_Width / 2 + posOffset / 2) * 2, 0]
if angleTheta <= 20:
Input_yPos = -MMI_Width / 2 + TaperWidth/2 # Correction factor so that Ports are in the WG 0.1e-6
Input_Y = Input_yPos - (np.sqrt((TaperLength)**2 - BoardLen**2))
Output_yPos = MMI_Width / 2 -TaperWidth/2
Output_Y = Output_yPos + (np.sqrt((TaperLength)**2 - BoardLen**2))
portLoc = ["left", "right"]
self.lum.select("EME::Ports::port_" + str(1))
self.lum.set("port location", portLoc[0])
self.lum.set("use full simulation span", 0)
self.lum.set("y", Input_Y)
self.lum.set("y span", y_Port_Span )
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", angleTheta)
self.lum.select("EME::Ports::port_" + str(2))
self.lum.set("port location", portLoc[1])
self.lum.set("use full simulation span", 0)
self.lum.set("y", Output_Y)
self.lum.set("y span", y_Port_Span )
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", angleTheta)
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
# self.lum.set("x min", -(Device_Length / 2))
# self.lum.set("x max", (Device_Length / 2))
self.lum.set("y min", -Device_Width / 2)
self.lum.set("y max", Device_Width / 2)
self.lum.set("z", MonitorHeight)
else:
# Correction in Y-Axis
difY = (TaperLength / 2) * np.cos(angleTheta * np.pi / 180)
NewY = -MMI_Width / 2 + TaperWidth / 2 - np.sqrt((TaperLength / 2) ** 2 - difY ** 2) # - xLen - Diff/2 #
Input_yPos = -MMI_Width / 2 + TaperWidth/2
Corr = TaperLength *np.sin((angleTheta*np.pi/180)/2)
Input_Y = NewY - difY/2
Output_yPos = MMI_Width / 2 - TaperWidth/2
Output_Y = Output_yPos + (np.sqrt((TaperLength)**2 - BoardLen**2)) - WG_Width
Output_Y = -NewY + difY / 2
portLoc = ["left", "right"]
self.lum.select("EME::Ports::port_" + str(1))
self.lum.set("port location", portLoc[0])
self.lum.set("use full simulation span", 0)
self.lum.set("y", Input_Y)
self.lum.set("y span", y_Port_Span*3 )
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", angleTheta)
self.lum.select("EME::Ports::port_" + str(2))
self.lum.set("port location", portLoc[1])
self.lum.set("use full simulation span", 0)
self.lum.set("y", Output_Y)
self.lum.set("y span", y_Port_Span*3 )
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
self.lum.set("theta", angleTheta)
# Add monitor
self.lum.addemeprofile()
self.lum.set("x", 0)
self.lum.set("x span", Device_Length)
# self.lum.set("x min", -(Device_Length / 2))
# self.lum.set("x max", (Device_Length / 2))
self.lum.set("y min", -Device_Width / 2)
self.lum.set("y max", Device_Width / 2)
self.lum.set("z", MonitorHeight)
[docs]
def setInverseTaperEMESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the InverseTaperEMESolver.
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters['y res'] : int/float
Mesh y-Axis
Parameters['z res'] : int/float
Mesh z-Axis
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Returns
-------
None.
'''
Substrate_Height = Parameters['Substrate Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
Slab_Height = Parameters['Slab Height']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperLength_PWB = Parameters['PWB Taper Length']
WaveLength = Parameters['Wavelength']
Mode = Parameters["Mode"]
y_res = Parameters['y res']
z_res = Parameters['z res']
y_Port_Span = Parameters["Port Span"][1]
z_Port_Span = Parameters["Port Span"][2]
# SMF Parameters
CoreDiameter = Parameters["SMF Core Diameter"]
CladdingDiameter = Parameters["SMF Cladding Diameter"]
if Slab_Height == 0:
# # Device specifications
MonitorHeight = Substrate_Height/2 + WG_Height/2
Ports_mid = -(Substrate_Height/2 + CoreDiameter/2) + MonitorHeight
Ports_PWB_mid = CoreDiameter/2 # TaperHightB/2
self.lum.select("SMF")
xPos_SMF = self.lum.get("x")
X_min = -TaperLength_PWB/2 - abs(xPos_SMF)
else:
# # Device specifications
max_slabH = Slab_Height
MonitorHeight = Substrate_Height/2 + max_slabH + WG_Height/2
Ports_mid = -(Substrate_Height/2 + CoreDiameter/2) + MonitorHeight
#Ports_mid = max_slabH + Substrate_Height/2 - CoreDiameter/2 + WG_Width/2
Ports_PWB_mid = max_slabH + CoreDiameter/2 # TaperHightB/2
self.lum.select("SMF")
xPos_SMF = self.lum.get("x")
X_min = -TaperLength_PWB/2 - abs(xPos_SMF)
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addeme()
self.lum.set("x min", X_min)
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB + TaperWidthB/2)
self.lum.set('simulation temperature', 273.15 + 20)
self.lum.set("z", Substrate_Height/2 + CoreDiameter/2 ) #Substrate_Height
self.lum.set("z span", TaperHightB*2)
self.lum.set("wavelength", WaveLength)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
# set cell properties
self.lum.select("InverseTaper::WG_Extention_Inverse_Taper")
ExtWG_Lenght = self.lum.get("poles")[1][0]
self.lum.select("EME")
self.lum.set("number of cell groups", 3)
self.lum.set("group spans", np.array([[0.1e-6], [TaperLength_PWB], [ExtWG_Lenght]]))
self.lum.set("cells", np.array([[3], [80], [1]]))
self.lum.set("subcell method", np.array([[1], [1], [1]]))
# Modes to Calculate
self.lum.set('number of modes for all cell groups', 20)
# Mesh Cells
self.lum.set("define y mesh by", "maximum mesh step")
self.lum.set("dy", y_res)
self.lum.set("define z mesh by", "maximum mesh step")
self.lum.set("dz", z_res)
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
portLoc = ["left", "right"]
# z_span_PWB = CoreDiameter/2 + (z_Port_Span - WG_Height)/2
self.lum.select("EME::Ports::port_" + str(1))
self.lum.set("port location", portLoc[0])
# self.lum.set("use full simulation span", 1)
self.lum.set("use full simulation span", 0)
self.lum.set("y", 0)
self.lum.set("y span", CoreDiameter + 2e-6)
self.lum.set("z", Substrate_Height/2)
self.lum.set("z span", CoreDiameter + 2e-6 )
self.lum.set("mode selection", Mode)
self.lum.select("EME::Ports::port_" + str(2))
self.lum.set("port location", portLoc[1])
# self.lum.set("use full simulation span", 1)
self.lum.set("use full simulation span", 0)
self.lum.set("y", 0)
self.lum.set("y span", y_Port_Span)
self.lum.set("z", Ports_mid)
self.lum.set("z span", z_Port_Span)
self.lum.set("mode selection", Mode)
# Add monitor Horizondal
self.lum.addemeprofile()
# self.lum.set("x", 0)
# self.lum.set("x span", TaperLength_PWB+11e-6)
# self.lum.set("x min", X_min - 5e-6)
# self.lum.set("x max", TaperLength_PWB/2 + 15e-6)
self.lum.set("x min", -TaperLength_PWB/2)
self.lum.set("x max", TaperLength_PWB )
self.lum.set("y", 0)
self.lum.set("y span", TaperWidthB*2)
self.lum.set("z", MonitorHeight)
# Add monitor Vertical
self.lum.addemeprofile()
self.lum.set('monitor type', "2D Y-normal")
# self.lum.set("x", 0)
# self.lum.set("x span", TaperLength_PWB+11e-6)
self.lum.set("x min", -TaperLength_PWB/2)
self.lum.set("x max", TaperLength_PWB )
# self.lum.set("x min", X_min - 5e-6)
# self.lum.set("x max", TaperLength_PWB/2 + 15e-6)
self.lum.set("y", 0)
# self.lum.set("y span", TaperWidthB*2)
self.lum.set("z", MonitorHeight)
self.lum.set("z span", TaperHightB*2)
[docs]
def setWaveguideFDESolver(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary
Dictionary with all the data needet for the WaveguideFDESolver.
Parameters : Dictionary
Dictionary with all the data needet for the Bend Wavaguide. Data needet:
Parameters['Substrate Height']: int/float
Substrate height
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Returns
-------
None.
'''
WG_Height = Parameters['WG Height']
dy = Parameters['y res']
dz = Parameters['z res']
Slab_Height = Parameters['Slab Height']
SubstrateHight = Parameters['Substrate Height']
WaveLength = Parameters['Wavelength']
# FDE Dimensions
WG_Mid = Slab_Height + WG_Height / 2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfde()
self.lum.set('solver type', "2D X normal")
self.lum.set("x", 0)
self.lum.set('define y mesh by', 'maximum mesh step')
self.lum.set('dy', dy)
self.lum.set('define z mesh by', 'maximum mesh step')
self.lum.set('dz', dz)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
self.lum.set("y max", 5e-6)
self.lum.set("y min", -5e-6)
self.lum.set('z', WG_Mid)
self.lum.set('z span', WG_Height + Slab_Height + SubstrateHight/2)
self.lum.set("wavelength", WaveLength)
[docs]
def setGratingCouplerNeffFDE(self, Parameters):
GC_Height = Parameters["Hight GC"]
Pitch = Parameters["Pitch GC"]
dy = Parameters['y res']
dz = Parameters['z res']
WaveLength = Parameters['Wavelength']
# FDE Dimensions
Gap = 1e-6 - Pitch
WG_Mid = GC_Height / 2
# Adds a Eigenmode Expansion (EME) solver region to the MODE simulation environment.
self.lum.addfde()
self.lum.set('solver type', "2D X normal")
self.lum.set("x", 0)
self.lum.set('define y mesh by', 'maximum mesh step')
self.lum.set('dy', dy)
self.lum.set('define z mesh by', 'maximum mesh step')
self.lum.set('dz', dz)
self.lum.set("z min bc", "PML")
self.lum.set("z max bc", "PML")
self.lum.set("y min bc", "PML")
self.lum.set("y max bc", "PML")
self.lum.set('fit materials with multi-coefficient model', 1)
self.lum.set('wavelength start', 0.4e-6)
self.lum.set('wavelength stop', 2e-6)
self.lum.set("y", 0)
self.lum.set("y span", Pitch+Gap)
self.lum.set('z', WG_Mid)
self.lum.set('z span',1e-6)
self.lum.set("wavelength", WaveLength)
# =============================================================================
# Functions
# =============================================================================
[docs]
def StartEMESimulation(self):
'''
This Function will save a the construted object under the name "SimRun1"
then will run the simulation. The simulation cannot be done without first
saving the object. On the end the 'emepropagation' of the mode 1 port 1
will be done.
Returns
-------
None.
'''
self.lum.save('SimRun1')
self.lum.run()
self.lum.emepropagate()
[docs]
def StartFDTDSimulation(self):
'''
This Function will save a the construted object under the name "SimRun1"
then will run the simulation. The simulation cannot be done without first
saving the object. On the end the 'emepropagation' of the mode 1 port 1
will be done.
Returns
-------
None.
'''
self.lum.save('SimRun1')
self.lum.run()
[docs]
def StartFDTDOptimizerRingGratingCoupler(self, Tranmission_on_Port, pos):
self.lum.select("SMF")
self.lum.set("x", pos)
self.lum.select("FDTD::ports::Input_SMF_Port")
self.lum.set("x", pos)
self.lum.select("Power_Input_SMF_Port")
self.lum.set("x", pos)
self.lum.save('SimRun1')
self.lum.run()
data = np.abs(self.lum.getresult("FDTD::ports::" + str(Tranmission_on_Port), 'T')["T"])
self.lum.switchtolayout()
return data
[docs]
def StartFDESimulation(self):
'''
This Function will save a the construted object under the name "SimRun1"
then will run the simulation. The simulation cannot be done without first
saving the object. On the end the 'findmodes' will simulate all 20 modes
that are set by defoult to be calculated.
Returns
-------
None.
'''
self.lum.save('SimRun1')
self.lum.run()
self.lum.findmodes()
[docs]
def CoppyDcard(self, modeTE, modeTM):
'''
Parameters
----------
modeTE : str
The TE mode that will be copy to dCard to do the overlap analysis.
modeTM : str
The TM mode that will be copy to dCard to do the overlap analysis.
Returns
-------
None.
'''
self.lum.copydcard(str(modeTE), 'TE')
self.lum.copydcard(str(modeTM), 'TM')
[docs]
def Overlap(self):
'''
Returns
-------
TEModes : dict
Dictionary of TE - Modes.
dictDataTE : dict
Dictionary of TE - Modes E field values
SweepDictTE : dict
Dictionary of TE - Modes:
1) overlap Procentage
2) TE polarization fraction number
3) TE polarization fraction
4) loss
5) Ex
6) Ey
7) Ez
TMModes : dict
Dictionary of TM - Modes..
dictDataTM : dict
Dictionary of TM - Modes E field values.
SweepDictTM : dict
Dictionary of TM - Modes:
1) overlap Procentage
2) TE polarization fraction number
3) TE polarization fraction
4) loss
5) Ex
6) Ey
7) Ez
'''
numModes = self.lum.nummodes()
TEModes = {}
dictDataTE = {}
SweepDictTE = {}
TMModes = {}
dictDataTM = {}
SweepDictTM = {}
for i in range(1, int(numModes + 1)):
if self.lum.overlap("TE", 'mode' + str(i))[0] > 0.80:
SweepDictTE['mode ' + str(i) + ' overlap Procentage'] = self.lum.overlap("TE", 'mode' + str(i))[0] * 100
SweepDictTE['mode ' + str(i) + ' TE polarization fraction num'] = self.lum.getdata(
'FDE::data::mode' + str(i), 'TE polarization fraction')
TEModes['mode ' + str(i) + ' effective index num'] = self.lum.getdata('FDE::data::mode' + str(i),
'neff')
SweepDictTE['mode ' + str(i) + ' effective index num'] = self.lum.getdata('FDE::data::mode' + str(i),
'neff')
SweepDictTE['mode ' + str(i) + ' loss'] = self.lum.getdata('FDE::data::mode' + str(i), 'loss') / 100
SweepDictTE['mode ' + str(i) + ' Ex'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ex')
SweepDictTE['mode ' + str(i) + ' Ey'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ey')
SweepDictTE['mode ' + str(i) + ' Ez'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ez')
dictDataTE['mode ' + str(i) + ' z'] = self.lum.getresult('FDE::data::mode' + str(i), 'E')
else:
pass
for i in range(1, int(numModes + 1)):
if self.lum.overlap("TM", 'mode' + str(i))[0] > 0.80:
SweepDictTM['mode ' + str(i) + ' overlap Procentage'] = self.lum.overlap("TM", 'mode' + str(i))[0] * 100
SweepDictTM['mode ' + str(i) + ' TE polarization fraction num'] = self.lum.getdata(
'FDE::data::mode' + str(i), 'TE polarization fraction')
TMModes['mode ' + str(i) + ' effective index num'] = self.lum.getdata('FDE::data::mode' + str(i),
'neff')
SweepDictTM['mode ' + str(i) + ' effective index num'] = self.lum.getdata('FDE::data::mode' + str(i),
'neff')
SweepDictTM['mode ' + str(i) + ' loss'] = self.lum.getdata('FDE::data::mode' + str(i), 'loss') / 100
SweepDictTM['mode ' + str(i) + ' Ex'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ex')
SweepDictTM['mode ' + str(i) + ' Ey'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ey')
SweepDictTM['mode ' + str(i) + ' Ez'] = self.lum.getdata('FDE::data::mode' + str(i), 'Ez')
dictDataTM['mode ' + str(i) + ' z'] = self.lum.getresult('FDE::data::mode' + str(i), 'E')
else:
pass
return TEModes, dictDataTE, SweepDictTE, TMModes, dictDataTM, SweepDictTM
[docs]
def Plot3DFDTD(self, Parameters):
MonitorName = Parameters["Monitor Name FDTD"]
Field = Parameters["Field FDTD"]
E = self.lum.getresult(MonitorName, Field)[Field]
Shape = E.shape
if Shape[0] == 1:
Ex = self.lum.getresult(MonitorName, Field)["x"]
Ey = []
Ez = []
for i in range(len(self.lum.getresult(MonitorName, Field)["y"])):
Ey.append(self.lum.getresult(MonitorName, Field)["y"][i][0] * 1e6)
for i in range(len(self.lum.getresult(MonitorName, Field)["z"])):
Ez.append(self.lum.getresult(MonitorName, Field)["z"][i][0] * 1e6)
E_a = abs(E) ** 2
slice_2d = np.sqrt(E_a[0, :, :, 0, 0] ** 2 + E_a[0, :, :, 0, 1] ** 2 + E_a[0, :, :, 0, 2] ** 2)
elif Shape[1] == 1:
Ex = []
Ey = self.lum.getresult(MonitorName, Field)["y"]
Ez = []
for i in range(len(self.lum.getresult(MonitorName, Field)["x"])):
Ex.append(self.lum.getresult(MonitorName, Field)["x"][i][0] * 1e6)
for i in range(len(self.lum.getresult(MonitorName, Field)["z"])):
Ez.append(self.lum.getresult(MonitorName, Field)["z"][i][0] * 1e6)
E_a = abs(E) ** 2
slice_2d = np.sqrt(E_a[:, 0, :, 0, 0] ** 2 + E_a[:, 0, :, 0, 1] ** 2 + E_a[:, 0, :, 0, 2] ** 2)
elif Shape[2] == 1:
Ex = []
Ey = []
Ez = self.lum.getresult(MonitorName, Field)["z"]
for i in range(len(self.lum.getresult(MonitorName, Field)["y"])):
Ey.append(self.lum.getresult(MonitorName, Field)["y"][i][0] * 1e6)
for i in range(len(self.lum.getresult(MonitorName, Field)["x"])):
Ex.append(self.lum.getresult(MonitorName, Field)["x"][i][0] * 1e6)
E_a = abs(E) ** 2
slice_2d = np.sqrt(E_a[:, :, 0, 0, 0] ** 2 + E_a[:, :, 0, 0, 1] ** 2 + E_a[:, :, 0, 0, 2] ** 2)
else:
raise ValueError("This function can print only 2D fields and no 3D fields")
# Calc Intensity and murge the 2D monitor data to one vector for printing
# # Rotate the image by 90 degrees counterclockwise
# slice_2d_rotated = np.rot90(slice_2d, k=3)
# # Define the y and z axis ranges
# z_range = np.linspace(-2.68e-6, 50e-6, slice_2d_rotated.shape[0]) * 1e6 # Convert to micrometers
# y_range = np.linspace(-5e-6, 5e-6, slice_2d_rotated.shape[1]) * 1e6 # Convert to micrometers
# Create the colormap plot
fig = plt.figure(figsize=(10, 5))
plt.pcolormesh( slice_2d, cmap='jet')
# plt.pcolormesh(y_range, z_range, slice_2d_rotated, cmap='jet')
plt.colorbar(label='$|'+ Field + '|^2$ Intensity')
# plt.xlabel('Width / $\mu m$')
# plt.ylabel('$|E|^2$')
# plt.title('Lense Thickness = 2.5 $\mu m$')
if "Save Image" in list(Parameters.keys()):
# Save the plot as SVG
plt.savefig(Parameters["Save Image"] + '.svg', format='svg', bbox_inches='tight')
plt.savefig(Parameters["Save Image"] + '.png', format='png', bbox_inches='tight')
else:
pass
plt.show()
return fig
[docs]
def Plot3DFDE(self, Parameters):
MonitorName = Parameters["Mode FDE"]
Field = Parameters["Field FDE"]
Mode = "TM_LNOI_1x0,5um"
Ex = abs(self.lum.getdata("FDE::data::" + MonitorName, Field + "x")) ** 2
Ey = abs(self.lum.getdata("FDE::data::" + MonitorName, Field + "y")) ** 2
Ez = abs(self.lum.getdata("FDE::data::" + MonitorName, Field + "z")) ** 2
y = self.lum.getdata("FDE::data::" + MonitorName, "y")[:, 0] * 1e6
z = self.lum.getdata("FDE::data::" + MonitorName, "z")[:, 0] * 1e6
slice_2d = np.sqrt(Ex[0, :, :, ] ** 2 + Ey[0, :, :] ** 2 + Ez[0, :, :] ** 2)
# Rotate the image by 90 degrees counterclockwise
slice_2d_rotated = np.rot90(slice_2d, k=3)
# Create the colormap plot
fig = plt.figure(figsize=(10, 5))
# plt.pcolormesh(z_range, y_range, slice_2d, cmap='viridis')
plt.pcolormesh(y, z, slice_2d_rotated[:, :, 0], cmap='jet')
plt.colorbar(label='$|' + Field + '|^2$')
if "Save Image" in list(Parameters.keys()):
# Save the plot as SVG
plt.savefig(Parameters["Save Image"] + '.svg', format='svg', bbox_inches='tight')
plt.savefig(Parameters["Save Image"] + '.png', format='png', bbox_inches='tight')
else:
pass
plt.show()
return fig
[docs]
class Charge(Constructor):
def __init__(self, file, Mode, MaterialLib=None):
'''
Parameters
----------
file : str
Path to lumerical lumapi.py.
Mode : str
Can only be set to CHARGE. Later on it will be integrated to the rest of the solvers
MaterialLib : str, optional
If there is an material library providet it can be imported with passig MateriaLib to the path of where the Material lib is save in the system . The default is None.
Raises
------
ValueError
DESCRIPTION.
'''
self.file = file
self.Mode = Mode
self.MaterialLib = MaterialLib
self.Materials_Opt = []
self.Materials_Electric = []
# Check Python Version !!! No import imp module after python 3.11
PyVersion = sys.version
try:
if PyVersion.split(" ")[0] > "3.11.0":
import importlib.util
import importlib.machinery
def load_source(modname, filename):
loader = importlib.machinery.SourceFileLoader(modname, filename)
spec = importlib.util.spec_from_file_location(modname, filename, loader=loader)
module = importlib.util.module_from_spec(spec)
# The module is always executed and not cached in sys.modules.
# Uncomment the following line to cache the module.
# sys.modules[module.__name__] = module
loader.exec_module(module)
return module
self.lumpai = load_source('lumapi', self.file)
else:
import imp
self.lumpai = imp.load_source('lumapi', self.file)
except ValueError as e:
print(f"ValueError encountered: {e}")
except ImportError as e:
print(f"ImportError encountered: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
self.Mode = Mode
self.Struct = None
self.SolverInfo = {}
if self.Mode == "CHARGE":
self.CHARGE()
self.SolverInfo["Solver Used"] = "CHARGE"
if self.MaterialLib is None:
pass
else:
self.lum.importmaterialdb(self.MaterialLib)
else:
raise ValueError(
"Non Valid Solver was choosen. Please pass on one of the two supported solvers ['FDTD' or 'EME']")
[docs]
def CHARGE(self):
'''
Calls the CHARGE Solver.
Returns
-------
None.
'''
self.lum = self.lumpai.DEVICE()
print('Lumerical CHARGE API is started')
# Close Programm
[docs]
def Close(self):
'''
Returns
-------
Close Lumerical GUI
'''
self.lum.close()
print('Lumerical API is closed')
# Remove the object and solver region
[docs]
def removeObject(self):
'''
Switch from simulation to layout mode.
Remove all objects.
Returns
-------
None.
'''
self.lum.switchtolayout()
self.lum.deleteall()
file = self.file
[docs]
def Material(self, Material_Data):
'''
Parameters
----------
Material_Data : dictionary
Material Optical or Electrical data.
Separate Materials into Electrical and Optical in List Material_Data["Electrical"] = ["mat1", "mat2"..]
and optical Material_Data["Optical"] = ["omat1", "omat2", ...]
Returns
-------
Materials_Added : list
List of added materials to the simulation.
'''
# Separate Materials into Electrical and Optical in List Material_Data["Electrical"] = ["mat1", "mat2"..]
# and optical Material_Data["Optical"] = ["omat1", "omat2", ...]
Electrical_Materials = []
Optical_Materials = []
Materials_Added = []
for i in Material_Data["Electrical"]:
Electrical_Materials.append(i)
for j in Material_Data["Optical"]:
Optical_Materials.append(j)
# Check for Items that are common for the optical and electrical Material
Common_Materials = [item for item in Electrical_Materials if item in Optical_Materials]
Electrical_Materials = [item for item in Electrical_Materials if item not in Common_Materials]
Optical_Materials = [item for item in Optical_Materials if item not in Common_Materials]
if Common_Materials:
# Add all Electrical Materials to materials folder
for i in range(len(Common_Materials)):
self.lum.addmodelmaterial()
self.lum.set("name", Common_Materials[i])
self.lum.addmaterialproperties("CT", Common_Materials[i]) # importing from electrical material database
self.lum.select("materials::" + Common_Materials[i])
self.lum.addmaterialproperties("HT", Common_Materials[i]) # importing from thermal material database
self.lum.select("materials::" + Common_Materials[i])
self.lum.addmaterialproperties("EM", Common_Materials[i])
# Add all Electrical Materials to materials folder
Materials_Added = Materials_Added + Common_Materials
for i in range(len(Electrical_Materials)):
self.lum.addmodelmaterial()
self.lum.set("name", Electrical_Materials[i])
self.lum.addmaterialproperties("CT",
Electrical_Materials[i]) # importing from electrical material database
self.lum.select("materials::" + Electrical_Materials[i])
self.lum.addmaterialproperties("HT",
Electrical_Materials[i]) # importing from thermal material database
self.lum.select("materials::" + Electrical_Materials[i])
for _ in Optical_Materials:
if Electrical_Materials[i].split(" ")[0] == _.split(" ")[0]:
Optical_Materials.remove(_)
self.lum.select("materials::" + Electrical_Materials[i])
self.lum.addmaterialproperties("EM", _)
else:
pass
Materials_Added = Materials_Added + Electrical_Materials
# Add all Optical Materials to materials folder
for i in range(len(Optical_Materials)):
if Optical_Materials[i].split(" ")[0]:
self.lum.select("materials::LiNbO3 semiconductor - X/Y cut (Lithium Niobate)")
self.lum.addemmaterialproperty("Dielectric")
self.lum.set("name",Optical_Materials[i])
self.lum.set("refractive index",2.21)
else:
self.lum.addmodelmaterial()
self.lum.set("name", Optical_Materials[i])
self.lum.addmaterialproperties("EM", Optical_Materials[i])
Materials_Added = Materials_Added + Optical_Materials
else:
# Add all Electrical Materials to materials folder
for i in range(len(Electrical_Materials)):
self.lum.addmodelmaterial()
self.lum.set("name", Electrical_Materials[i])
self.lum.addmaterialproperties("CT",
Electrical_Materials[i]) # importing from electrical material database
self.lum.select("materials::" + Electrical_Materials[i])
self.lum.addmaterialproperties("HT",
Electrical_Materials[i]) # importing from thermal material database
self.lum.select("materials::" + Electrical_Materials[i])
# for _ in Optical_Materials:
# if Electrical_Materials[i].split(" ")[0] == _.split(" ")[0]:
# Optical_Materials.remove(_)
# self.lum.select("materials::" + Electrical_Materials[i])
# self.lum.addmaterialproperties("EM", _)
# else:
# pass
Materials_Added = Materials_Added + Electrical_Materials
# Add all Optical Materials to materials folder
for i in range(len(Optical_Materials)):
if Optical_Materials[i].split(" ")[0]:
self.lum.select("materials::LiNbO3 semiconductor - X/Y cut (Lithium Niobate)")
self.lum.addemmaterialproperty("Dielectric")
self.lum.set("name",Optical_Materials[i])
self.lum.set("refractive index",2.21)
else:
self.lum.addmodelmaterial()
self.lum.set("name", Optical_Materials[i])
self.lum.addmaterialproperties("EM", Optical_Materials[i])
Materials_Added = Materials_Added + Optical_Materials
return Materials_Added
[docs]
def MZM(self, Parameters):
'''
Parameters
----------
Parameters : dictionary
Dictionary with all the parameters needt for the MZM creation
Parameters['Substrate Height'] : int/float
Substrate Height
Parameters["Optical"] : dictionary of str
Optical Materials Dataset
Parameters["Electrical"] : dictionary of str
Electrical Materials Dataset
Parameters['angle'] : int/float
Side angle of the Waveguife
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters['WG Length'] : int/float
Waveguide lenght. This determin the structure length as well
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
Returns
-------
None.
'''
# Define Materials
Substrate_Height = Parameters['Substrate Height']
Optical_Material = Parameters["Optical"]
Electrical_Material = Parameters["Electrical"]
angle = Parameters['angle']
Slab_Height = Parameters['Slab Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
WG_Length = Parameters['WG Length']
Metal_GND_Width = Parameters["GND Electrodes Width"]
Metal_Sig_Width = Parameters["Signal Electrodes Width"]
Metal_Height = Parameters["Electrodes Height"]
Gap = Parameters["Gap"]
# Becouse of Material Properties x and y directions will be swaped !!!
# Add materials to Simulation enviroment
Materials_Dict = {}
Materials_Dict["Electrical"] = Electrical_Material
Materials_Dict["Optical"] = Optical_Material
self.Material(Materials_Dict)
# Add LiNBo3 and Vacuum optical properties to materia
self.lum.select("materials::LiNbO3 semiconductor - X/Y cut (Lithium Niobate)")
self.lum.addemmaterialproperty("Dielectric")
self.lum.set("name","LiNBo3 Optical")
self.lum.set("refractive index",2.21)
self.lum.select("materials::LiNbO3 semiconductor - X/Y cut (Lithium Niobate)")
self.lum.set("color",np.array([[1], [0], [0]]))
self.lum.select("materials::Au (Gold) - CRC")
self.lum.set("color", np.array([[1], [1], [0]]))
self.lum.select("materials::SiO2 (Glass) - Sze")
self.lum.set("color", np.array([[0.537], [0.812], [0.941]]))
self.lum.select("materials::Air")
self.lum.addmaterialproperties("EM", "Vacuum")
# Material definition
if "Air" in Electrical_Material:
Electrical_Material.remove("Air")
MaterialElectrodes = Electrical_Material[0]
else:
MaterialElectrodes = Electrical_Material[0]
if "Si (Silicon)" in Electrical_Material:
MaterialSub = Electrical_Material[2]
MaterialClad = Electrical_Material[2]
MaterialSlab = Electrical_Material[1]
MaterialWG = MaterialSlab
else:
MaterialSub = Electrical_Material[2]
MaterialClad = Electrical_Material[2]
MaterialSlab = Electrical_Material[1]
MaterialWG = MaterialSlab
# Device Lenght
MZM_Leght = WG_Length
MZM_Width = WG_Width * 2 + Metal_GND_Width + Metal_Sig_Width + Gap * 2 + 2e-6
# Triangle EQ for MMI Width
x = abs(WG_Height / (np.cos((angle) * np.pi / 180))) # in Radians
extention = np.sqrt(x ** 2 - WG_Height ** 2)
WG_W = WG_Width + 2 * extention
WG_Width_top = WG_W
# Set offsets for the Optcal Waveguides
WG_Y_Pos = Metal_Sig_Width / 2 + Gap + WG_Width / 2
Metal_Y_Pos = WG_Y_Pos + Gap + WG_Width / 2 + Metal_GND_Width / 2 # Metal_GND_Width + Gap*2 + WG_Width
# creating the LN Handle
self.lum.addrect()
self.lum.set("name", "LN_Handle")
# self.lum.set("x", 0)
# self.lum.set("x span", MZM_Width + 8e-6)
self.lum.set("x min", -Metal_Sig_Width/2 - 1e-6)
self.lum.set("x max", Metal_Y_Pos + Metal_GND_Width/2 + 1e-6)
self.lum.set("z", -Substrate_Height / 2 - (9 / 2) * 1e-6)
self.lum.set("z span", 9e-6)
self.lum.set("y", 0)
self.lum.set("y span", MZM_Leght)
self.lum.set("material", MaterialSlab)
self.lum.set("preserve surfaces",1)
# creating the substrate
self.lum.addrect()
self.lum.set("name", "Substrate")
# self.lum.set("x", 0)
# self.lum.set("x span", MZM_Width + 8e-6)
self.lum.set("x min", -Metal_Sig_Width/2 - 1e-6)
self.lum.set("x max", Metal_Y_Pos + Metal_GND_Width/2 + 1e-6)
self.lum.set("z", 0)
self.lum.set("z span", Substrate_Height)
self.lum.set("y", 0)
self.lum.set("y span", MZM_Leght)
self.lum.set("material", MaterialSub)
self.lum.set("preserve surfaces",1)
# self.lum.set("color",[1; 1; 0; 0])
# Position Thin Film and Waveguides
if Slab_Height == 0:
z_Offset = Substrate_Height / 2
Point4 = [Metal_Sig_Width/2 + Gap , z_Offset]
Point3 = [Metal_Sig_Width/2 + Gap + extention , z_Offset + WG_Height]
Point2 = [Metal_Sig_Width/2 + Gap + extention + WG_Width , z_Offset + WG_Height]
Point1 = [Metal_Sig_Width/2 + Gap + 2*extention + WG_Width , z_Offset]
vtx = np.array([Point1, Point2, Point3, Point4])
self.lum.putv('vertices', vtx)
else:
# creating the thin film
self.lum.select("Substrate")
zmax = self.lum.get("z max")
z_Offset = zmax + Slab_Height
Point4 = [Metal_Sig_Width/2 + Gap , z_Offset ]
Point3 = [Metal_Sig_Width/2 + Gap + extention , z_Offset + WG_Height]
Point2 = [Metal_Sig_Width/2 + Gap + extention + WG_Width , z_Offset + WG_Height]
Point1 = [Metal_Sig_Width/2 + Gap + 2*extention + WG_Width , z_Offset]
Point5 = [-Metal_Sig_Width/2 - 1e-6 , z_Offset]
Point6 = [-Metal_Sig_Width/2 - 1e-6 , zmax]
Point7 = [Metal_Y_Pos + Metal_GND_Width/2 + 1e-6 , zmax]
Point8 = [Metal_Y_Pos + Metal_GND_Width/2 + 1e-6 , z_Offset]
vtx = np.array([ Point1, Point2, Point3, Point4, Point5, Point6, Point7, Point8])
self.lum.putv('vertices', vtx)
# Cfreate Polynome
self.lum.addpoly()
self.lum.set("name", "Waveguide_Right")
self.lum.set("z", 0)
self.lum.set("z span", WG_Length) # Z is length of the Waveguide
self.lum.set("material", MaterialWG)
self.lum.set("preserve surfaces",1)
self.lum.set("first axis", "x")
self.lum.set("rotation 1", 90)
self.lum.set("vertices",vtx)
self.lum.set("x", 0) # Waveguide x Position
# Add Electrodes
self.lum.addrect()
self.lum.set("name", "Sig")
self.lum.set("x", 0)
self.lum.set("x span", Metal_Sig_Width)
self.lum.set("y", 0)
self.lum.set("y span", MZM_Leght)
self.lum.set("z min", z_Offset)
self.lum.set("z max", z_Offset + Metal_Height)
self.lum.set("material", MaterialElectrodes)
self.lum.set("preserve surfaces",1)
self.lum.addrect()
self.lum.set("name", "Ground_R")
self.lum.set("x", Metal_Y_Pos)
self.lum.set("x span", Metal_GND_Width)
self.lum.set("y", 0)
self.lum.set("y span", MZM_Leght)
self.lum.set("z min", z_Offset)
self.lum.set("z max", z_Offset + Metal_Height)
self.lum.set("material", MaterialElectrodes)
self.lum.set("preserve surfaces",1)
[docs]
def Set_SimulationRegion(self, Parameters):
'''
Parameters
----------
Parameters : Dictionary of Parameters
Dictionary with all the parameters needt for the MZM simulation region. The Simulation region is define
only on the halft of the structure sice the MZM is symetrical component.
Parameters['Substrate Height'] : int/float
Substrate Heigh
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
Returns
-------
None.
'''
# Define Parameters
Substrate_Height = Parameters['Substrate Height']
Slab_Height = Parameters['Slab Height']
WG_Width = Parameters['WG Width']
Metal_GND_Width = Parameters["GND Electrodes Width"]
Metal_Sig_Width = Parameters["Signal Electrodes Width"]
Metal_Height = Parameters["Electrodes Height"]
Gap = Parameters["Gap"]
Metal_Y_Pos = Metal_GND_Width + Gap * 2 + WG_Width
MZM_Width = WG_Width * 2 + 2 * Metal_GND_Width + Metal_Sig_Width + Gap * 4
# self.lum.select("geometry::Sig")
# x_min = self.lum.get("x min")
# self.lum.select("geometry::Ground_R")
# x_max = self.lum.get("x max")
self.lum.select("geometry::Sig")
x_min = self.lum.get("x max") - 2e-6
self.lum.select("geometry::Ground_R")
x_max = self.lum.get("x min") + 2e-6
# self.lum.select("geometry::Waveguide_Right")
# x = self.lum.get("x")
# self.lum.select("geometry::Ground_R")
# x_span = self.lum.get("x span")
# self.lum.select("geometry::Slab")
# z_min = self.lum.get("z max")
self.lum.select("simulation region")
self.lum.set("dimension", "2D Y-Normal")
# self.lum.addmodelmaterial()
# self.lum.set("name", "Air")
# self.lum.addmaterialproperties("CT", "Air") # importing from electrical material database
# self.lum.addmaterialproperties("HT", "Air") # importing from thermal material database)
self.lum.set("background material", "Air")
self.lum.set("y", 0)
# self.lum.set("x", x)
# self.lum.set("x span", x_span/2)
self.lum.set("x min", x_min - 0.5e-6)
self.lum.set("x max", x_max + 0.5e-6)
# self.lum.set("y",0)
# self.lum.set("y span", MZM_Width + 1e-6)
self.lum.set("z min", 0)
self.lum.set("z max", Substrate_Height + Slab_Height + 2 * Metal_Height)
# self.lum.set("z", z_min + WG_Height/2)
# self.lum.set("z span", 2 * Metal_Height)
[docs]
def MZM_ChargeSolver(self, Parameters):
"""
This function will create the CHARGE solver putting all the needed parameters and setting the simulation
region monitor.
Args: Parameters: dict
Dictionary with all the parameters needt for the MZM FEEM Solver region. The Simulation region is define
only on the halft of the structure since the FEEM region is the simulation region too.
Parameters['Substrate Height']: int/float
Height of the Substrate
Parameters['Slab Height']: int/float
Slab Height
Parameters['WG Height']: int/float
Waveguide hight. You need to give this value but for the moment it will not effect anything
Parameters['WG Width']: int/float
Width of the Waveguide
Parameters["GND Electrodes Width"]: int/float
Ground Electrode width
Parameters["Signal Electrodes Width"]: int/float
Signal Electrode width
Parameters["Electrodes Height"]: int/float
Height of the Ground and Singla electrodes
Parameters["Gap"]: int/float
Gap between the electrode and Waveguide.
Returns
-------
None.
"""
# Parameters
Substrate_Height = Parameters['Substrate Height']
Slab_Height = Parameters['Slab Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
Metal_GND_Width = Parameters["GND Electrodes Width"]
Metal_Sig_Width = Parameters["Signal Electrodes Width"]
Metal_Height = Parameters["Electrodes Height"]
Gap = Parameters["Gap"]
MZM_Width = WG_Width * 2 + 2 * Metal_GND_Width + Metal_Sig_Width + Gap * 4
Metal_Y_Pos = Metal_GND_Width + Gap * 2 + WG_Width
# Set simulation region
self.Set_SimulationRegion(Parameters)
# Set charge Solver
self.lum.addchargesolver()
self.lum.set("min edge length", 0.04e-6) # TODO
# TODO Results Tab syntax
# Set boundary
self.lum.select("CHARGE::boundary conditions")
# self.lum.addelectricalcontact()
# self.lum.set("name", "Ground_L")
# self.lum.set("surface type", "domain")
# self.lum.set("domain", 1)
# self.lum.set("name", "Ground_L")
# self.lum.set("sweep type", "single")
# self.lum.set("voltage", 0)
# self.lum.set("surface type", "solid")
# self.lum.set("solid", "Ground_R")
# self.lum.set("outer surface only", 0)
self.lum.addelectricalcontact()
self.lum.set("name", "Signal")
self.lum.set("sweep type", "range")
self.lum.set("range start", 0)
self.lum.set("range stop", 5)
self.lum.set("range interval", 0.5)
self.lum.set("range num points", 11)
self.lum.set("surface type", "solid")
self.lum.set("solid", "Sig")
self.lum.set("outer surface only", 1)
self.lum.addelectricalcontact()
self.lum.set("name", "Ground_R")
self.lum.set("sweep type", "single")
self.lum.set("voltage", 0)
self.lum.set("surface type", "solid")
self.lum.set("solid", "Ground_R")
self.lum.set("outer surface only", 0)
# Set monitor
self.lum.addefieldmonitor()
self.lum.set("name", "CHARGE_Field_Monitor")
self.lum.set("monitor type", "2D y-normal")
self.lum.set("y", 0)
# self.lum.set("y",0)
# self.lum.set("y span", MZM_Width + 1e-6)
self.lum.set("x min", 0 - Metal_Sig_Width / 2 - 1e-6)
self.lum.set("x max", Metal_Y_Pos + Metal_GND_Width / 2 + 1e-6)
self.lum.set("z min", -Substrate_Height / 2)
self.lum.set("z max", Substrate_Height + Slab_Height + Metal_Height / 2)
# Add fine mesh
self.lum.addmesh()
self.lum.set("name", "CHARGE_Volume_Mesh")
self.lum.set("geometry type", "volume")
self.lum.set("volume type", "solid")
self.lum.set("volume solid", "Waveguide_Right")
self.lum.set("max edge length", 0.01e-6) # TODO need to be checked and made variable
[docs]
def MZM_FEEMSolver(self, Parameters):
'''
Parameters
----------
Parameters : dict
Dictionary with all the parameters needt for the MZM FEEM Solver region. The Simulation region is define
only on the halft of the structure since the FEEM region is the simulation region too.
Parameters['Substrate Height'] : int/float
Substrate Heigh
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
Parameters["Wavelength"] : int/float
Simulation wavelength
Returns
-------
None.
'''
# Parameters
Substrate_Height = Parameters['Substrate Height']
Slab_Height = Parameters['Slab Height']
WG_Height = Parameters['WG Height']
WG_Width = Parameters['WG Width']
Metal_GND_Width = Parameters["GND Electrodes Width"]
Metal_Sig_Width = Parameters["Signal Electrodes Width"]
Metal_Height = Parameters["Electrodes Height"]
Gap = Parameters["Gap"]
Wavelength = Parameters["Wavelength"]
MZM_Width = WG_Width * 2 + 2 * Metal_GND_Width + Metal_Sig_Width + Gap * 4
# Set Solver
self.lum.addfeemsolver()
self.lum.set("edges per wavelength", 4) # TODO Check for Converchange need to be separate parameter
self.lum.set("polynomial order", 2) # TODO Check what is this parameter for
self.lum.set("display frequency or wavelength", "wavelength")
self.lum.set("wavelength", Wavelength)
self.lum.set("use max index", 0)
self.lum.set("n", 2.02) # TODO Check for what exactly supposable under limit of n_eff
# Set Boundary
self.lum.addpec()
self.lum.set("surface type", "simulation region")
self.lum.set("x min", 1)
self.lum.set("x max", 1)
self.lum.set("y min", 1)
self.lum.set("y max", 1)
self.lum.set("z min", 1)
self.lum.set("z max", 1)
self.lum.addpml()
self.lum.set("sigma", 5) # TODO Check for what exactly
# Set monitor
self.lum.addimportnk()
self.lum.set("name", "nk import")
self.lum.set("volume type", "solid")
self.lum.set("volume solid", "Waveguide_Right")
# self.lum.addimportnk()
# self.lum.set("name", "nk import Slab")
# self.lum.set("volume type", "solid")
# self.lum.set("volume solid", "Slab")
[docs]
def StartCHARGESolver(self):
'''
This function will first save the simulation into the folder where the
this script is saved. After saving the script an Mesh of the structure will
be performed and the mesh will be locked for further simulation. After this
the CHARGE solver will be started.
Returns
-------
None.
'''
#Save file
self.lum.save('SimRun1')
# Set mesh lock
self.lum.select("CHARGE")
self.lum.mesh("CHARGE")
self.lum.set("mesh lock", 1)
# Run CHARGE
self.lum.run("CHARGE")
[docs]
def StartFEEMSolver(self):
'''
This function will first save the simulation into the folder where the
this script is saved. After saving the script an Mesh of the structure will
be performed and the mesh will be locked for further simulation. After this
the FEEM solver will be started.
Returns
-------
None.
'''
#Svae file
self.lum.save("SimRun1")
# Set mesh lock
self.lum.select("FEEM")
self.lum.mesh("FEEM")
self.lum.set("mesh lock", 1)
# Run FEEM
self.lum.run("FEEM")
[docs]
def ResultsCHARGE(self, Parameters_CHARGE):
'''
Parameters
----------
Parameters : dict
Dictionary with all the parameters needt for the MZM CHARGE Results analysis. This function will plot
the changes in the n_EO so that the user can see it.
Parameters_CHARGE['min Polarization Fraction']: int/float
This is the minimal Polarisation Fraction of the mode that need to be tracked in the waveguide.
The user can first performe an FDE Analysis and extract the Polarisation Fraction and then give it in
this function, so that in case of multimodal waveguide the right mode will be tracked and analysed.
Returns
-------
electro : dict
Simulation results from the monitor of CHARGE. This function will create an Lumerical window into Charge solver.
The user can use the window to see tha changes in EO index depending on the voltage apply on the electrodes.
The function will return the monitor Data, so that the user can use it for some post precessing.
'''
# Lead mode neff for FEEM
Min_Polarization_Fraction = Parameters_CHARGE['min Polarization Fraction']
# Lithium Niobate telecom permitivity
eps_o = 2.21 ** 2
eps_e = 2.14 ** 2
self.lum.putv('eps_o',eps_o)
self.lum.putv('eps_e',eps_e)
# Lithium Niobate non;linear coefficents
r_13 = 9.6e-12
r_33 = 30.9e-12
electro = self.lum.getresult("CHARGE::CHARGE_Field_Monitor", "electrostatics")
self.lum.eval('electro = getresult("CHARGE::CHARGE_Field_Monitor","electrostatics");')
# Get electrostatic results
E = np.squeeze(electro["E"])
Volt = electro["V_Signal"]
self.lum.eval('Volt = electro.V_Signal;')
self.lum.eval('E = pinch(electro.E);')
# Intialize perturbation matrices same size as E
dts = E.shape
n_EO = np.zeros(dts)
dn = np.zeros(dts)
# self.lum.eval('electro = getresult("CHARGE::CHARGE_Field_Monitor","electrostatics");')
# self.lum.eval('E = pinch(electro.E);')
# self.lum.eval('Volt = electro.V_Signal;')
# self.lum.eval('dts = size(E);')
# self.lum.eval('n_EO = matrix(dts(1),dts(2),dts(3));')
# self.lum.eval('dn = matrix(dts(1),dts(2),dts(3));')
# Vector components work well with unstructured datasets, need to do this for voltage and wavelength
for vv in range(len(Volt)):
# Spatial index data (diagonal permittivity)
eps_unperturbed = np.array([eps_e, eps_o, eps_o])[np.newaxis, :] # Shape: (1, 3)
# Pockels effect (perturbation term)
deps_inv = np.array([r_33 * E[:, vv, 0], r_13 * E[:, vv, 0], r_13 * E[:, vv, 0]]).T # Shape: (dts[0], 3)
# Compute modified index using element-wise inversion (instead of np.linalg.inv)
n_EO[:, vv, :] = np.sqrt(1 / (1 / eps_unperturbed + deps_inv))
n_EO[:, ::-1, :] # This one is here becouse the values ware somehow rotated from back to front
dn = np.copy(n_EO)
self.lum.putv('dn',n_EO)
self.lum.putv('n_EO',n_EO)
self.lum.eval('dn(:,:,1) = n_EO(:,:,1) - sqrt(eps_e);')
self.lum.eval('dn(:,:,2:3) = n_EO (:,:,2:3) - sqrt(eps_o);')
dn[:, :, 0] = n_EO[:, :, 0] - np.sqrt(eps_e)
dn[:, :, 1:3] = n_EO[:, :, 1:3] - np.sqrt(eps_o)
# Add dn and n_EO to dataset and visualize
self.lum.eval("electro.addattribute('n_EO',n_EO);")
self.lum.eval("electro.addattribute('dn',dn);")
electro['n_EO'] = n_EO ## total index
electro['dn'] = dn ## total index
electro['Min Polarization Fraction'] = Min_Polarization_Fraction
self.lum.visualize(electro)
return electro
[docs]
def ResultsFEEM(self, CHARGE_Data):
"""
This function will performe an FEEM sweep simulation to extract the neff changes
from the waveguide. The changing neff is due to the changing voltage and electric
field power over the waveguide.
Parameters
----------
Parameters : dict
Dictionary with all the parameters needt for the MZM FEEM Results analysis. This function will plot
the L_pi vs Volt, L_pi*Volt vs Volt, and delta_neff vs Volt
CHARGE_Data is an dictionary that can be taken directly from the function ResultsCHARGE()
and give to this function. All the needed Parameters for the FEEM solver are there.
If the user wanna give the Parameters manuel you can:
CHARGE_Data["V_Signal"]: int/float array
Array of the sweep voltage from the CHARGE Solver
CHARGE_Data['min Polarization Fraction']: int/float
This is the minimal Polarisation Fraction of the mode that need to be tracked in the waveguide.
The user can first performe an FDE Analysis and extract the Polarisation Fraction and then give it in
this function, so that in case of multimodal waveguide the right mode will be tracked and analysed.
Returns:
FeemResults: dictionary with all the results from the FEEM solver to plot them or use them in other function
FeemResults["lambda"]: int/float
Operation Wavelength
FeemResults["Volt"]: int/float
Array of voltages used in CHARGE solver and FEEM for the analysis
FeemResults["L_pi"]: int/float
Array with L_pi values
FeemResults["neff_TE"]: int/float
Array with neff TE Mode values from the FEEM solver
FeemResults["neff"]: int/float
Array with neff values from the FEEM solver
FeemResults["dneff"]: int/float
Array of delta_neff Values for the L_Pi Calculation: L_pi = lambda/ (2*RE{neff[No Voltage over Electrodes]] - neff[Voltage over Electrodes]})
"""
# Switch to layout
self.lum.switchtolayout()
# Get wavelength
lambda_ = self.lum.getnamed("FEEM", "wavelength")
self.lum.eval("tmp = electro.elements;")
elements = self.lum.getv("tmp")
self.lum.putv("lambda", lambda_)
self.lum.putv("Volt", CHARGE_Data["V_Signal"])
Volt = CHARGE_Data["V_Signal"]
self.lum.eval("neff_TE = matrix(length(Volt));")
neff_TE = np.zeros(len(Volt))
Min_Polarization_Fraction = CHARGE_Data['Min Polarization Fraction']
# # Create dataset
# nkmaterial = self.lum.unstructureddataset("nk import WG",
# CHARGE_Data["x"],
# CHARGE_Data["y"],
# CHARGE_Data["z"],
# elements)
# self.lum.putv("nkmaterial", nkmaterial)
# self.lum.eval('nkmaterial.addparameter("lambda", lambda) ;')
# self.lum.eval('nkmaterial.addparameter("Voltage", Volt) ;')
# self.lum.eval('nkmaterial.addattribute("nk",n_EO);')
# # Import dataset
# self.lum.setnamed("FEEM::nk import WG", "enabled", True)
# self.lum.select("FEEM::nk import WG")
# self.lum.eval('importdataset(nkmaterial);')
### Create new unstructured dataset based on CHARGE data grid at this wavelength
self.lum.eval('nkmaterial = unstructureddataset("nk import",electro.x,electro.y,electro.z,electro.elements);')
self.lum.eval('nkmaterial.addparameter("lambda", lambda);')
self.lum.eval('nkmaterial.addparameter("Voltage", Volt);')
self.lum.eval('nkmaterial.addattribute("nk",n_EO);')
### Import, the nk dataset and apply to waveguide solid
self.lum.eval('setnamed("FEEM::nk import","enabled",true);')
self.lum.eval('select("FEEM::nk import");')
self.lum.eval('importdataset(nkmaterial);')
self.lum.select("FEEM::nk import")
self.lum.set("volume type","solid")
self.lum.set("volume solid","Waveguide_Right")
self.lum.set("selected attribute","nk")
# Set mesh lock
self.lum.select("FEEM")
self.lum.mesh("FEEM")
self.lum.set("mesh lock", 1)
# Run FEEM
self.lum.run("FEEM")
### Find Fundamental TE Mode effective index
TE_pol_frac = self.lum.getresult("FEEM", "modeproperties.TE polarization fraction")["TE polarization fraction"]
# Extract effective index
neff = self.lum.getresult("FEEM", "modeproperties.neff")["neff"]
mde_num = np.where(TE_pol_frac > Min_Polarization_Fraction)[0]
neff_TE[0] = abs(neff[mde_num[0]][0])
# Rotate the n_EO vertically to have an index that make sence
n_EO = CHARGE_Data['n_EO']
n_EO = n_EO[:, ::-1, :]
self.lum.putv('n_EO',n_EO)
self.lum.eval("electro.addattribute('n_EO',n_EO);")
for vv in range(1, len(Volt)): # Python uses 0-based indexing, so start from 1
self.lum.switchtolayout()
self.lum.setnamed("FEEM::nk import", "Voltage_index", vv)
self.lum.run("FEEM")
# Find Fundamental TE Mode
TE_pol_frac = self.lum.getresult("FEEM", "modeproperties.TE polarization fraction")["TE polarization fraction"]
neff = self.lum.getresult("FEEM", "modeproperties.neff")["neff"]
# Find mode indices where TE polarization fraction is greater than 0.95
mde_num = np.where(TE_pol_frac > Min_Polarization_Fraction)[0] # Extract indices where condition is met
# Assign the first qualifying neff value to neff_TE[vv]
if len(mde_num) > 0: # Ensure at least one mode satisfies the condition
neff_TE[vv] = abs(neff[mde_num[0]][0])
# Compute dneff, L_pi, and alpha_dB
dneff = neff_TE - neff_TE[0]
L_pi = lambda_ / (2 * np.real(dneff))
# alpha_dB = -0.20 * np.log10(np.exp(-2 * np.pi * np.imag(neff_TE) / lambda_))
# Plot Effective Index vs Voltage
plt.figure()
plt.plot(Volt, np.real(neff_TE), linewidth=3)
plt.xlabel("Voltage (V)")
plt.ylabel("neff (Fundamental TE Mode)")
plt.title("Effective Index vs Voltage")
plt.legend(["Effective Index"])
plt.grid()
plt.show()
# Plot Modulator Performance - L_pi vs Voltage
plt.figure()
plt.plot(Volt, abs(L_pi) * 100, linewidth=3)
plt.xlabel("Voltage [V]")
plt.ylabel("L_pi [cm]")
plt.title("Modulator Performance")
plt.ylim(0, 15)
plt.grid()
plt.show()
# Plot Modulator Performance - V_piL vs Voltage
plt.figure()
plt.plot(Volt, (np.array(Volt[:,0], dtype=float) * np.array(abs(L_pi))) * 100, linewidth=3)
plt.xlabel("Voltage [V]")
plt.ylabel("V_piL [V-cm]")
plt.title("Modulator Performance")
#plt.ylim(0, 4) # Equivalent to setplot("y max",4); setplot("y min",0);
plt.grid()
plt.show()
# # Plot Modulator Performance - Loss vs Voltage
# plt.figure()
# plt.plot(Volt, alpha_dB, linewidth=3)
# plt.xlabel("Voltage [V]")
# plt.ylabel("Loss [dB/cm]")
# plt.title("Modulator Performance")
# plt.ylim(3, 7) # Equivalent to setplot("y max",7); setplot("y min",3);
# plt.show()
# Collect results in one dictionary
FeemResults = {}
FeemResults["lambda"] = lambda_
FeemResults["Volt"] = Volt
FeemResults["L_pi"] = L_pi
FeemResults["neff_TE"] = neff_TE
FeemResults["neff"] = neff
FeemResults["dneff"] = dneff
return FeemResults
[docs]
class HelpSubject:
[docs]
@classmethod
def Help_Objects(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for Lumerical Structure #
# You can ask for help for one of the following structures #
# #
# 1) Waveguide - used for FDE simulation only! #
# 2) MMI2x1 - can be used in EME and FDTD solvers #
# 3) MMI2x2 - can be used in EME and FDTD solvers #
# 4) InverseTaper - can be used in EME and FDTD solvers #
# 5) Directional Coupler - can be used in EME and FDTD solvers #
# 6) StraightWaveguide - can be used in EME and FDTD solvers #
# 7) WDM (Wavelength Division Multiplexing, angeled MMI) -onyl #
# available for EME Solver #
# 8) BendWaveguide - onyl available for FDTD Solver #
# 9) ArcWaveguide - onyl available for FDTD Solver #
# 10) GratingCoupler - onyl available for FDTD Solver #
# 11) RingGratingCoupler - onyl available for FDTD Solver #
# 12) MZM - only available fro CHARGE Solver #
# #
# To print information about the structure you desire please #
# use obj.Help({"Objects": Number}). For Example #
# obj.Help({"Objects": 1}) will give you information about #
# Structure Waveguide #
# ============================================================================= #
""")
[docs]
def Structures(self, Number):
if Number == 1:
print("""
Call Waveguide with -> obj.Waveguide(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int7float
Height of the substrate.
Parameters['WG Length']: int/float
Length of the Waveguide.
Parameters['WG Height'] : int/float
Height of the Waveguide
Parameters['WG Width'] : int/float
Width of the Waveguide
Parameters['angle'] : int
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height.
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 6:
print("""
Call StraightWaveguide with -> obj.StraightWaveguide(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int float
Substrate Height
Parameters['WG Height'] : int/float
Height of the Waveguide
Parameters['WG Width'] : int/float
Width of the Waveguide
Parameters['WG Length'] : int/float
Length of the Waveguide
Parameters['Taper'] : boolen
If Taper == False, only Waveguiedes will be constructed.
If Taper == True, only an single Taper will be constructed
Parameters['Taper Length'] : int/float
Taper Length
Parameters['Taper Width'] : int/float
Taper backside width, the frontside width is the waveguide width
Parameters['angle'] : int
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height.
Parameters['Material'] : list
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["Wavelength"] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
Bending angle of the Waveguide. Set it to 0 to simulate the straight waveguide.
If Waveguide Angle is different then 0, then the straight waveguide will be tilted
at the choosen degrees.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Taper Type is selected the user need to provide additional information:
TaperWidthF = Parameters['PWB Taper Width Front']
TaperWidthB = Parameters['PWB Taper Width Back']
TaperHightB = Parameters['PWB Taper Hight Back']
TaperHightF = Parameters['PWB Taper Hight Front']
TaperLength_PWB = Parameters['PWB Taper Length']
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 8:
print("""
Call BendWaveguide with -> obj.BendWaveguide(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate Height
Parameters['WG Height'] : int/float
Height of the Waveguide (Etching depth)
Parameters['WG Width'] : int/float
Top width of the Waveguide.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. It is calculated WG_angle = 90 - angle.
For angle = 90 , a perfect rect is created!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["Wavelength"] : int/float
Wavelength
Parameters["x span"] : int/float
Length of the S Curve. Span of the object in x direction.
Parameters["y span"] : int/float
Height of the curve. Span (difference between the input and output ports) of the S-curve in y direction.
Parameters["poles"] : boolen
If Parameters["poles"] = True an Bezier Curbe will be made.
if Parameters["poles"] = False an Cosinus Curve = y_span*(cos((pi/(2*x_span))*t)^2) will be made. Where
t is in the range of 0 - y_span
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 9:
print("""
Call ArcWaveguide with -> obj.ArcWaveguide(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height
Parameters['WG Height'] : int/float
Waveguide height
Parameters['WG Width'] : int/float
Waveguide width
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Material'] : list
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters["Wavelength"] : int/float
Wavelength
Parameters["S_Band Radius"] : int/float
Radius of the Circle in um
Parameters['Arc deg'] : int
Can be 90 or 180 for 1/4 of a cirle or 1/2 of a circle.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 2:
print("""
Call MMI2x1 with -> obj.MMI2x1(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Offset of the input waveguide.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Taper'] : boolen
Taper can be set to be True ot False.
if Taper == False - No Taper used
if Taper == True - Taper placed
Parameters['Taper Length'] : int/float
If Taper == True, then this will set the Tapers length. If Taper == False
this will be ignored and some random value can be given.
Parameters['Taper Width'] : int/float
If Taper == True, then this will set the Tapers width. If Taper == False
this will be ignored and some random value can be given.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 3:
print("""
Call MMI2x2 with -> obj.MMI2x2(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Taper'] : boolen
Taper can be set to be True ot False.
if Taper == False - No Taper used
if Taper == True - Taper placed
Parameters['Taper Length'] : int/float
If Taper == True, then this will set the Tapers length. If Taper == False
this will be ignored and some random value can be given.
Parameters['Taper Width'] : int/float
If Taper == True, then this will set the Tapers width. If Taper == False
this will be ignored and some random value can be given.
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 7:
print("""
Call WDM with -> obj.WDM(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Angle Thetha'] :
Input and output angle of the waveguide. This is only temporally
Parameters['Taper Width'] : int/float
Backside width of the Taper, frontside width is the waveguide width
Parameters['Taper Length'] : int/float
Length of the Taper.
Parameters['Taper'] : boolen
If Taper == False, no Taper will be placed with the Waveguides.
If Taper == True, tapers will be placed with the waveguides
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 4:
print("""
Call InverseTaper with -> obj.InverseTaper(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 2 materials!
For this structure 3 materials will be needed!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width. Also in this function and ONLY in this function this will be the
ibverse Taper width!!!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Taper Length'] : int/float
Inverse Taper Length
Parameters['Taper Width'] : int/float
Front Width of the inverse Taper!! In this Function and ONLY in this function, the
Parameters['Taper Width'] is the frond width of the inverse Taper!
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 5:
print("""
Call DirectionalCoupler with -> obj.DirectionalCoupler(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 3 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material', 'Photonic Wire Bonding'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut', 'Si (Silicon) - Palik'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['Substrate Width'] : int/float
Substrate Width.
Parameters['DC Length'] : int/float
Length of the directional coupler
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Position Offset'] : int/float
Offset between the waveguides. The minimum distance between Waveguides is 1 um
becouse of manufactering restrictions in the University.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Cladding"] : anything, optional
This function will check if you have set Parameters["Cladding"] to anaything, for example "Parameters["Cladding"]=1"
and if so it will put cladding over your structure. If the user didnt give the "Cladding" as dictionary key no cladding
will be set.
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 10:
print("""
Call GratingCoupler with -> obj.GratingCoupler(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material GC'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 3 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material', 'Photonic Wire Bonding'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut', 'Si (Silicon) - Palik'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["Etch Depth GC"]: int/float
How deep, taken from the Parameters["Hight GC"] will be the etchin depth of the gratings
Parameters["Duty Cycle"]: int/float
Duty cycle of the gratings. For example Parameters["Duty Cycle"] = 0.39 will result in 39% Duty Cycle
Parameters["Pitch GC"]: int/float
Pitch of the Grating Coupler. For Example Parameters["Pitch GC"] = 0.6e-6 will result in 6um Etch Space + Rib Space = 0.6um.
Parameters["Input LengthGC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect! In this case is used only when Parameters["Taper"] = True
Parameters["Taper"] : boolen
You can create an input Taper to your Grating Coupler structure
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width. Also in this function and ONLY in this function this will be the
ibverse Taper width!!!
Parameters['Slab Height'] : int/float
Slab height
Parameters['Taper Length'] : int/float
Taper Length
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Parameters["SMF Theta"]: int/float
Tilting Angle of the Single Mode Fiber to the Grating Coupler. Normaly we choose Parameters["SMF Theta"] = 15
Parameters["SMF Z Span"]: int/float
Lenght/Span of the Single Mode Fiber
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 11:
print("""
Call RingGratingCoupler with -> obj.RingGratingCoupler(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Material GC'] : list of str
List of Materials. The list should be with names (str) of a valid Lumerical materials.
Check the names in Lumerical Materials viewer.
The List of materials must contain at least 3 materials!
Parameters['Material'] = ['Cladding/Substrat', 'Object Material', 'Photonic Wire Bonding'].
For Example: Parameters['Material'] = ["SiO2 (Glass) - Palik", 'LiNbO3_20deg_X cut', 'Si (Silicon) - Palik'].
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
Parameters["Etch Depth GC"]: int/float
How deep, taken from the Parameters["Hight GC"] will be the etchin depth of the gratings
Parameters["Duty Cycle"]: int/float
Duty cycle of the gratings. For example Parameters["Duty Cycle"] = 0.39 will result in 39% Duty Cycle
Parameters["Pitch GC"]: int/float
Pitch of the Grating Coupler. For Example Parameters["Pitch GC"] = 0.6e-6 will result in 6um Etch Space + Rib Space = 0.6um.
Parameters["Input LengthGC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters["SMF Core Index"]
Single Mode Fiber Core Index
Parameters["SMF Cladding Index"]
Single Mode Fiber Cladding Index
Parameters["SMF Theta"]: int/float
Tilting Angle of the Single Mode Fiber to the Grating Coupler. Normaly we choose Parameters["SMF Theta"] = 15
Parameters["SMF Z Span"]: int/float
Lenght/Span of the Single Mode Fiber
-----------------------------------------------------------------------------------------------------------
""")
elif Number == 12:
print("""
Call StraightWaveguide with -> obj.MZM(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate Height
Parameters["Optical"] : dictionary of str
Optical Materials Dataset
Parameters["Electrical"] : dictionary of str
Electrical Materials Dataset
Parameters['angle'] : int/float
Side angle of the Waveguife
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters['WG Length'] : int/float
Waveguide lenght. This determin the structure length as well
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
-----------------------------------------------------------------------------------------------------------
""")
[docs]
def Help_Solvers(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for Lumerical Structure Solver. You #
# can ask for help for one of the following structural solvers #
# #
# 1) Waveguide FDE Solver #
# 2) MMI2x1 EME Solver #
# 3) MMI2x2 EME Solver #
# 4) InverseTaper EME Solver #
# 5) Directional Coupler EME Solver #
# 6) StraightWaveguide EME Solver #
# 7) WDM (Wavelength Division Multiplexing, angeled MMI) -EME #
# Solver #
# 8) MMI2x1 FDTD Solver #
# 9) MMI2x2 FDTD Solver #
# 10) InverseTaper FDTD Solver #
# 11) Directional Coupler FDTD Solver #
# 12) StraightWaveguide FDTD Solver #
# 13) BendWaveguide FDTD Solver #
# 14) ArcWaveguide FDTD Solver #
# 15) GratingCoupler FDTD Solver #
# 16) RingGratingCoupler FDTD Solver #
# 17) MZM CHARGE Solver #
# 18) MZM FEEM Solver #
# #
# To print information about the solver you desire please #
# use obj.Help({"Solvers"}: Number). For Example #
# obj.Help({"Solvers": 1}) will give you information about #
# FDE Solver Parameters for Waveguide Structure #
# ============================================================================= #
""")
[docs]
def Solvers(self, NumberSolver):
if NumberSolver == 1:
print("""
Call Waveguide FDE Solver with -> obj.Solver("Waveguide", "FDE", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height']: int/float
Substrate height
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 2:
print("""
Call MMI2x1 EME Solver with -> obj.Solver("MMI2x1", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['angle'] : int/float
Angle of the Waveguide Walls. it is calculated WG_angle = 90 - angle.
For anfle = 90 we get a perfect rect!
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Input waveguide/taper offset.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 3:
print("""
Call MMI2x2 EME Solver with -> obj.Solver("MMI2x2", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['y res'] : int/float
Mesh cell sizes.
Parameters['z res'] : int/float
Mesh cell size.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 4:
print("""
Call InverseTaper EME Solver with -> obj.Solver("InverseTaper", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters['y res'] : int/float
Mesh y-Axis
Parameters['z res'] : int/float
Mesh z-Axis
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 5:
print("""
Call InverseTaper EME Solver with -> obj.Solver("DirectionalCoupler", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : float/int
Height of the Substrate
Parameters['Substrate Width'] : float/int
Width of the MMI
Parameters['DC Length'] : float/int
Length of the Directional coupler
Parameters['WG Height'] : float/int
Height of the Waveguide
Parameters['WG Width'] : float/int
Waveguide Width
Parameters['Position Offset'] : float/int
Positional offser of the waveguides. If posOffset the two Waveguides
will be offset of the middle position (y = 0) by the half of there
Width. In this case they will not overlap if the Offset is 0.
Parameters['y res'] : float/int
Mesh resolution for the y-Axis
Parameters['z res'] : float/int
Mesh resolution for the z Axis
Parameters['Slab Height'] : float/int
Slab height.
Parameters['Wavelength'] : float/int
Wavelength
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 6:
print("""
Call StraightWaveguide EME Solver with -> obj.Solver("StraightWaveguide", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Length'] : int/float
Waveguide Length
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters["Taper"] : boolen
If Taper == False, only straight Waveguide will be simulated,
If Taper == True an Taper will be simulated
Parameters['Taper Width'] : int/float
Taper backside Width. Taper Fronside width is the width of the Waveguide
Parameters['Taper Length'] : int/float
Taper Length
Parameters['y res']: int/float
EME Mesh resolutio,
Parameters['z res']: int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
This Parameter will set the theta ratation angle of the port. It can be 90 or 180.
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Parameters["Taper Type"] is given, themn the user need to set couple more parameters:
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 7:
print("""
Call Wavelength Division Multiplexing EME Solver with -> obj.Solver("WDM", "EME", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['MMI Width'] : int/float
Width of the MMI.
Parameters['MMI Length'] : int/float
Length of the MMI.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Length'] : int/float
Waveguide length.
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Taper Width'] : int/float
Taper backside width, frontside width is the waveguide width.
Parameters['Taper Length'] : int/float
Taper Length
Parameters['y res'] : int/float
Mesh y-Axis
Parameters['z res'] : int/float
Mesh z-Axis
Parameters['Slab Height'] : int/float
Slab Height.
Parameters['Wavelength'] : int/float
Wavelength
Parameters['Angle Thetha'] : boolen
Angle for the input and output waveguides
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 8:
print("""
Call MMI2x1 FDTD Solver with -> obj.Solver("MMI2x1", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters['Offset Input'] : int/float
Input waveguide/taper offset.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['x res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Offset Output"] : anything, optional
This function will allow the user to move the outputs in oposite direction. Please dont use it since is there only
becouse the maschine of our physic departmant had some proiblems with the LNOI objects design.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 9:
print("""
Call MMI2x2 FDTD Solver with -> obj.Solver("MMI2x2", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Height of the slab.
Parameters['MMI Width'] : int/float
Width of MMI
Parameters['MMI Length'] : int/float
Length of MMI
Parameters['WG Height'] : int/float
Heigth of waveguide
Parameters['WG Width'] : int/float
Width og waveguide
Parameters['WG Length'] : int/float
Length of waveguide
Parameters['Position Offset'] : int/float
Offset between the waveguides. If Taper == True then this become the offset
betweent he tapers wider sides. Waveguide and Tapers cannot be placed ourside
the MMI structure. The minimum distance between Taper and Waveguide is 1 um
becouse of manufactering restrictions in the University.
Parameters["Taper"]: boolen
Add Taper to the structure on the input and output waveguids
Parameters['Taper Length']: int/float
Lenght of the Taper in Parameters["Taper"] = True
Parameters['x res'] : int/float
Mesh cell sizes.
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 10:
print("""
Call InverseTaper FDTD Solver with -> obj.Solver("InverseTaper", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height' : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['Slab Height'] : int/float
Slab height
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
Parameters["SMF Core Diameter"] : int/float
Single Mode Fiber core Diameter
Parameters["SMF Cladding Diameter"] : int/float
Single Mode Fiber Cladding Diameter
Parameters['x res'] : int/float
Mesh x-Axis
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 11:
print("""
Call Directional FDTD Solver with -> obj.Solver("DirectionalCoupler", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : float/int
Height of the Substrate
Parameters['Substrate Width'] : float/int
Width of the MMI
Parameters['DC Length'] : float/int
Length of the Directional coupler
Parameters['WG Height'] : float/int
Height of the Waveguide
Parameters['WG Width'] : float/int
Waveguide Width
Parameters['Position Offset'] : float/int
Positional offser of the waveguides. If posOffset the two Waveguides
will be offset of the middle position (y = 0) by the half of there
Width. In this case they will not overlap if the Offset is 0.
Parameters['x res'] : float/int
Mesh resolution for the x-Axis
Parameters['Slab Height'] : float/int
Slab height.
Parameters['Wavelength'] : float/int
Wavelength
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 12:
print("""
Call StraightWaveguide FDTD Solver with -> obj.Solver("StraightWaveguide", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Length'] : int/float
Waveguide Length
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters["Taper"] : boolen
If Taper == False, only straight Waveguide will be simulated,
If Taper == True an Taper will be simulated
Parameters['Taper Width'] : int/float
Taper backside Width. Taper Fronside width is the width of the Waveguide
Parameters['Taper Length'] : int/float
Taper Length
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["Waveguide Angle"] : int/float
This Parameter will set the theta ratation angle of the port. It can be 90 or 180.
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Taper Type"] : anything, optional
This function will check if you have set Parameters["Taper Type"] to anaything, for example "Parameters["Taper Type"]=1"
and if so it will design an Inverse Taper Structure with no Cladding. Here the option "Cladding" is not active and will be ignored.
If the user didnt give the "Taper Type" as dictionary key, then an normal taper structure will be simulated.
If Parameters["Taper Type"] is given, themn the user need to set couple more parameters:
Parameters['PWB Taper Width Back'] : int/float
Photonic Wirebonding (PWB) Width back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Hight Back'] : int/float
Photonic Wire Bonding Height back side (to the Photonic Wire Bonding)
Parameters['PWB Taper Width Front'] : int/float
Photonic Wirebonding (PWB) Width front side (to the photonic waveguide)
Parameters['PWB Taper Hight Front'] : int/float
Photonic Wire Bonding Height front side (to the photonic waveguide)
Parameters['PWB Taper Length'] : int/float
Length of the Photonic Wire Bonding Taper
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 13:
print("""
Call BendWaveguide FDTD Solver with -> obj.Solver("BendWaveguide", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["x span"] : int/float
Length of the S-Bend Waveguide
Parameters["y span"] : int/float
Width of the S-Bend Waveguide
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 14:
print("""
Call ArcWaveguide FDTD Solver with -> obj.Solver("ArcWaveguide", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters['WG Height'] : int/float
Waveguide hight. Also the height of the MMI section
Parameters['WG Width'] : int/float
Waveguide width.
Parameters['x res'] : int/float
EME Mesh resolutio,
Parameters['Slab Height'] : int/float
Height of the slab.
Parameters['Wavelength'] : int/float
Wavelength
Parameters["S_Band Radius"] : int/float
S-Bend Radius in um.
Parameters['Arc deg'] : int/float
Arc define the Arc of the curve. It can be 90 or 180 degrees only.
This two will define an 1/4 of a circle or 1/2 of a circle.
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"]: list of int/floats
Parameters["Port Span"] = [Span of Port in x direction, Span of Port in y direction, Span of Port in z direction]
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 15:
print("""
Call GratingCoupler FDTD Solver with -> obj.Solver("GratingCoupler", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Input Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters['Taper'] : boolen
Add Taper to structure
Parameters['Taper Length'] : int/float
Length of the Taper
Parameters['Wavelength'] : int/float
Wavelength
Parameters['x res'] : int/float
Mesh x-Axis
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 16:
print("""
Call RingGratingCoupler FDTD Solver with -> obj.Solver("RingGratingCoupler", "FDTD", Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate height.
Parameters["Length GC"]: int/float
Lenght of the Grating Coupler Area
Parameters["Input Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place before the Grating Coupler region will start.
Parameters["Output Length GC"]: int/float
An squere Waveguide with the same WG Height as the Grating coupler place after the Grating Coupler region to finish the structure.
Parameters["Width GC"]: int/float
Widht of the Grating Coupler Area
Parameters["Hight GC"]: int/float
Hight of the Grating Coupler Material
Parameters["GC Radius"]: int/float
Radius of the Ring Grating Coupler in um. For Example "Parameters["GC Radius"] = 25e-6"
Parameters['Taper Length'] : int/float
Length of the input Taper
Parameters['Wavelength'] : int/float
Wavelength
Parameters['x res'] : int/float
Mesh x-Axis
Parameters["Mode"] : str
Mode to choose from ("fundamental TE mode", "fundamental TM mode", "fundamental mode")
Parameters["Port Span"] : list of floats/ints
List of x,y and z span of the Ports. For this simulation only y and z parametes will be taken.
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 17:
print("""
Call Wavelength Division Multiplexing EME Solver with -> obj.MZM_ChargeSolver(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate Heigh
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
Parameters["Wavelength"] : int/float
Simulation wavelength
-----------------------------------------------------------------------------------------------------------
""")
elif NumberSolver == 18:
print("""
Call Wavelength Division Multiplexing EME Solver with -> obj.MZM_FEEMSolver(Parameters)
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
Parameters['Substrate Height'] : int/float
Substrate Heigh
Parameters['Slab Height'] : Slab Height
Height of the Material slab. It can be set to 0 if no Slab is presented
Parameters['WG Height'] : int/float
Waveguide Height
Parameters['WG Width'] : int/float
Waveguide Width. Here the Top Waveguide width is considered
Parameters["GND Electrodes Width"] : int/float
Ground Electrode width
Parameters["Signal Electrodes Width"] : int/float
Signal electrode Width
Parameters["Electrodes Height"] : int/float
Height of the Metal electrodes
Parameters["Gap"] : int/float
Gap between the waveguide and the electrodes. The Gab is set from bottom Wg corner to electrodes.
Parameters["Wavelength"] : int/float
Simulation wavelength
-----------------------------------------------------------------------------------------------------------
""")
[docs]
def Help_StartSimulation(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for Lumerical Simulation Run. You #
# can ask for help for one of the following simulations #
# #
# 1) Save and start FDE Simulation #
# 2) Save and start EME Simulation #
# 3) Save and start FDTD Simulation #
# #
# To print information about how to start an simulation in the #
# choosen Solver. With obj.Help({"Start Simulation"}, Number) an #
# save of your simulation will be made in your current working #
# directory. Afterwards the Simulation will be started.For #
# Example obj.Help({"Start Simulation": 1}) will start the FDE #
# simulation. #
# ============================================================================= #
""")
[docs]
def StartSolver(self, NumberStart):
if NumberStart == 1:
print("""
Start FDE Simulation -> obj.StartFDESimulation()
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
None
-----------------------------------------------------------------------------------------------------------
""")
elif NumberStart == 2:
print("""
Start EME Simulation -> obj.StartEMESimulation()
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
None
-----------------------------------------------------------------------------------------------------------
""")
elif NumberStart == 3:
print("""
Start FDTD Simulation -> obj.StartFDTDSimulation()
Dictionary Parameters:
-----------------------------------------------------------------------------------------------------------
None
-----------------------------------------------------------------------------------------------------------
""")
[docs]
def Help_Results(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for Lumerical Result Extraction. #
# Youcan ask for help for one of the following Result Extraction #
# options. #
# #
# 1) Extract FDE Simulation Results #
# 2) How to set Overlap Anaylsis with FDE Solver #
# 3) Extract EME Simulation Results #
# 3) Extract FDTD Simulation Results #
# #
# To print information about how to extract results from the #
# simulation after Solver is done. With obj.Help({"Results"}, #
# Number) the simulated results will be extracted from the #
# simulation workspace and transfered to python IDE. #
# For Example obj.Help({"Results": 1}) will extract the FDE #
# data into 4 dictionaries. #
# ============================================================================= #
""")
[docs]
def Help_LoadingBar(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for the loading bar function #
# Here you can find a short discription on how to use it. #
# #
# If the user want to keep track of the simulation state an #
# laoding bar is imported in this library. The loading bar is #
# not part of the class, so you dont need to call it like an #
# class. #
# #
# loadingBar(count, total) #
# count - is your loop variable #
# total - is the lenght of the variables that will be sweeped #
# #
# Example python code of loading bar: #
# #
# >from Constructor import loadingBar #
# > #
# >for i in range(10): #
# > loadingBar(i, 10) #
# #
# ============================================================================= #
""")
[docs]
def Help_RemoveObject(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for how to remove simulation #
# from Lumrical #
# #
# To Remove an Object simply use: #
# #
# >obj.removeObject() #
# #
# This will switch Lumerical from Simulation to #
# design and delete all the Structures, Monitors and Solver #
# created by the user. #
# #
# ============================================================================= #
""")
[docs]
def Help_LogFile(self):
print("""
# ============================================================================= #
# Welcome to the Help Menu for creating simulation log File #
# from Lumrical #
# #
# When you load the Logfile function from the Constructor Library#
# the user can keep track with the simulation and parameters #
# used. To use the log File you need to give an list with #
# two dictionarays. The first can be the dictionary with #
# Parameters given for the simulation like "Tapper Width", #
# "WG Width"... The secound dictionary can be called direct #
# after performing an analysis with obj.ReturnLogInfo(). This #
# will extract the object that you simulate and the solver that #
# you used. An text file will be created in the given Path. The #
# name of the File will be "Lumerical_Simulation_Log_15_10_52" #
# #
# An example can be seen here: #
# #
# >from Constructor import Logfile #
# >Path = "C:/Downloads/" #
# >solverInfo = obj.ReturnLogInfo() #
# >data = [solverInfo, Parameters] #
# >Logfile(data, Path) #
# #
# ============================================================================= #
""")
[docs]
def Help():
print("""
To start the Python Lumerical Constructor Library you need first to:
from Constructor import Constructor
from Constructor import loadingBar
# Call the Constructor Object
obj = Constructor(file, Lumerical Solver Type)
# or
obj = Constructor(file, MaterialPath, Lumerical Solver Type)
# file = Path to lumapi.py for example -> "lumerical/2022-r2.4/api/python/lumapi.py"
# MaterialPath = Path to Material Library for example -> "/Desktop/LNOI_Env/LiNbO3.mdf"-> THIS IS OPTIONAL
# Lumerical Solver Type = Solver Type can be EME or FDTD for example -> "EME"
# To Close Lumerical simply use
obj.Close()
# For Further Help after colling the Constructor Object please use:
obj.Help()
""")
# obj.Help('Objects') -> Shows how to build an photonic element
# obj.Help('Solvers') -> Shows hot to build an FDE, EME or FDTD Solver
# obj.Help('Start Simulation') -> Commands to start simulation
# obj.Help('Results') -> Shows how to extract resuslts form FDE, EME and FDTD Simulations
# obj.Help('Loading Bar') -> Shows how to use the Loading Bar Function
# obj.Help('Remove Object') -> Romove object function
# obj.Help('Log File') -> Generate an log file from the simulation
[docs]
def Logfile(data, path):
"""
Parameters
----------
data : list
List of dictionarys with all the data for the simulation (obj.ReturnLogInfo()) and the parameters (Parameters dictionary) given to the simulation.
Returns
-------
None.
"""
from datetime import datetime
# datetime object containing current date and time
now = datetime.now()
# dd/mm/YY H:M:S
dt_string = now.strftime("%d/%m/%Y %H:%M:%S")
name = "Lumerical_Simulation_Log_"+(dt_string.split(" ")[1]).replace(":","_")
if "EME" or "FDTD" in list(data[0].keys()):
SimulationData = data[0]
Parameters = data[1]
else:
Parameters = data[0]
SimulationData = data[1]
ParamKeys = list(Parameters.keys())
SimulationDataKeys = list(SimulationData.keys())
with open(path + name + '.txt','w') as data:
data.write("""
==========================================================================================================================================================
""" + "\n")
data.write(f"This is an Simulation Log file from the Lumerical Simulation done on {dt_string}" +"\n")
data.write("""
==========================================================================================================================================================
""" + "\n")
for keys in range(len(Parameters.keys())):
data.write(ParamKeys[keys] + " : " + str(Parameters[ParamKeys[keys]]) + "\n")
data.write("""
==========================================================================================================================================================
""" + "\n")
data.write(f"Simulated Abject and used Solver" +"\n")
data.write("""
==========================================================================================================================================================
""" + "\n")
for keys in range(len(SimulationData.keys())):
data.write(SimulationDataKeys[keys] + " : " + str(SimulationData[SimulationDataKeys[keys]]) + "\n")