camfr_guided_modes

ipkiss3.all.device_sim.camfr_guided_modes(material_stack, wavelengths, pml_thickness=0.0)

Calculates the neff for guided modes for a material stack using the camfr mode solver.

Parameters:
material_stack: MaterialStack object

The stack of ipkiss materials to calculate the modes for.

wavelengths: array_like

An array of wavelengths to calculate the modes for.

pml_thickness: float

Thickness of the complex Perfectly Matched Layer to use.

Returns:
guided_modes: dict

Returns a dictionary that maps the mode names onto a numpy array that contains the wavelength and the corresponding mode effective index.

Examples

import si_fab.all as pdk
from ipkiss.technology import get_technology
TECH = get_technology()
import ipkiss3.all as i3
import numpy as np
import matplotlib.pyplot as plt
from pysics.basics.material.material_stack import MaterialStack

# we define a MaterialStack
mstack = MaterialStack(
    name="150nm Si",
    materials_heights=[
       (TECH.MATERIALS.SILICON_OXIDE, 0.500),
       (TECH.MATERIALS.SILICON, 0.150),
       (TECH.MATERIALS.AIR, 1.070)
   ]
)

# use the MaterialStack method to plot it
mstack.visualize()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-1.png
wavelengths = np.linspace(1.54, 1.56, 10)
neffs = i3.device_sim.camfr_guided_modes(mstack, wavelengths)

# Now we plot the zeroth-order mode:
TE0 = neffs['TE0']

# TEO is 'structured numpy array', this means it has fields
# in this case it has a wavelength and n_eff field.

wls = TE0['wavelength']
# n_eff is a complex number
neffs = TE0['n_eff'].real

plt.plot(wls, neffs)
plt.show()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-2.png
# If we make the core higher
# we should see higher order modes appear
mstack = MaterialStack(
    name="300nm Si",
    materials_heights=[
       (TECH.MATERIALS.SILICON_OXIDE, 0.500),
       (TECH.MATERIALS.SILICON, 0.300),
       (TECH.MATERIALS.AIR, 1.070)
   ]
)

mstack.visualize()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-3.png
results = i3.device_sim.camfr_guided_modes(mstack, wavelengths)

for mode_name, neffs in results.items():
    plt.plot(neffs['wavelength'], neffs['n_eff'].real, label=mode_name)
plt.legend(results.keys())
plt.show()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-4.png

Sometimes you’ll want to write/load the data to/from a file. To allow reading it from a different application or just to avoid running the simulation multiple times.

# With Python's builtin pickle module it's easy to write
# and load the data to/from a data file.

import si_fab.all as pdk
from ipkiss.technology import get_technology
TECH = get_technology()
from pysics.basics.material.material_stack import MaterialStack
import ipkiss3.all as i3
import numpy as np

mstack = MaterialStack(
    name="300nm Si",
    materials_heights=[
        (TECH.MATERIALS.SILICON_OXIDE, 0.500),
        (TECH.MATERIALS.SILICON, 0.300),
        (TECH.MATERIALS.AIR, 1.070)
    ]
)

wavelengths = np.linspace(1.54, 1.56, 10)
results = i3.device_sim.camfr_guided_modes(mstack, wavelengths)

# to write to the file
import pickle

with open('out.dat', 'wb') as out:
    pickle.dump(results, out)

fin = open('out.dat', 'rb')
results_new = pickle.load(fin)
fin.close()

# in case you want to export to a textual format, e.g a csv file
# you can use np.savetxt, though this requires a bit more work:

def savetocsv(filename, results):
    results_items = results.items()
    data = np.hstack([val for _, val in results_items])
    dtype = [
        ('mode_name', 'S10'),
        ('wavelength', np.float64),
        ('neff_real', np.float64),
        ('neff_imag', np.float64)
    ]

    results_np = np.zeros(
        len(data),
        dtype=dtype
    )

    offset = 0
    for mode_name, result in results_items:
        results_np['mode_name'][offset:offset + len(result)] = mode_name
        offset += len(result)

    results_np['wavelength'] = data['wavelength']
    results_np['neff_real'] = data['n_eff'].real
    results_np['neff_imag'] = data['n_eff'].imag

    np.savetxt(filename, results_np, delimiter=',', fmt=['%s', '%.18e', '%.18e', '%.18e'])

savetocsv('guided_modes.csv', results)

# to load the csv data again, you can use np.loadtxt:

def loadfromcsv(filename):
    # we assume the same order as used in the savetocsv
    dtype = [
        ('mode_name', 'S10'),
        ('wavelength', np.float64),
        ('neff_real', np.float64),
        ('neff_imag', np.float64)
    ]

    raw_data = np.loadtxt(filename, dtype=dtype, delimiter=',')
    mode_names = np.unique(raw_data['mode_name']).tolist()
    results = {}

    for mode_name in mode_names:
        mask = raw_data['mode_name'] == mode_name
        mode_data = raw_data[mask][['wavelength', 'neff_real', 'neff_imag']]
        results[mode_name] = np.rec.fromarrays(
            [mode_data['wavelength'], mode_data['neff_real'] + 1j * mode_data['neff_imag']],
            names='wavelength, n_eff'
        )

    return results

loaded_results = loadfromcsv('guided_modes.csv')

# Note that loadtxt filters out lines starting with '#'.
# You can use this to add comments to your data:

with open('guided_modes_comments.csv', 'w') as csv_out:
    csv_out.writelines([
        '# This file contains n_eff data for our material stack\n',
        '# mode_name, wavelength, neff_real, neff_imag\n'
    ])

    savetocsv(csv_out, results)

# comments will be filtered out:
loaded_results = loadfromcsv('guided_modes_comments.csv')

A MaterialStack might require preprocessing before calculating the modes. The code snippet below illustrates how you can define a function that clip a material stack between a z_min and z_max value.

import si_fab.all as pdk
from ipkiss.technology import get_technology
TECH = get_technology()
from pysics.basics.material.material_stack import MaterialStack
import ipkiss3.all as i3
import numpy as np

mstack = MaterialStack(
    name="300nm Si",
    materials_heights=[
       (TECH.MATERIALS.SILICON_OXIDE, 0.500),
       (TECH.MATERIALS.SILICON, 0.300),
       (TECH.MATERIALS.AIR, 1.070)
   ]
)

mstack.visualize()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-6.png
mstack_b = mstack.clip_copy(z_min=0.1, z_max=1.0)
mstack_b.visualize()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-7.png

Using negative values for z_min will make the material stack grow. If you use a z_max value higher than the top of the material stack, it will grow as well.

mstack_c = mstack_b.clip_copy(z_min=-1, z_max=2.0)
mstack_c.visualize()
../../../../_images/ipkiss3-all-device_sim-camfr_guided_modes-8.png