Component level simulation model

Important note: To run the below simulation models in PhotoCAD, upgrades to fnpcell-1.5.1 and sflow-0.8.36 are required

In this section, we will introduce a method for users to write their own S-Matrix for their components. Here we use the simplest waveguide model as an example.

WGModel class

from typing import Any, Sequence, Tuple

from fnpcell import all as fp
import numpy as np
from fnpcell.interfaces import IScatterMatrix
from fnpcell.interfaces import angle_between, distance_between
from gpdk.technology import get_technology


class WGModel(fp.sim.SimModel):
    op_0: fp.IOwnedPort
    op_1: fp.IOwnedPort
    wl0: float = 1.49
    neff: float = 2.4
    ng: float = 4.0
    loss: float = 0.0

    def simulate_scatter(self, wavelengths: Sequence[float]) -> IScatterMatrix:
        wl = np.array(wavelengths)
        wl0 = np.array(self.wl0)
        neff = np.array(self.neff)
        ng = np.array(self.ng)
        loss = np.array(self.loss)
        length = fp.distance_between(self.op_0.position, self.op_1.position)

        dwl = wl - self.wl0
        dneff_dwl = (ng - neff) / wl0
        neff = neff - dwl * dneff_dwl

        mag = 10 ** (-loss * length / 20)
        arg = 2 * np.pi * neff * length / wl

        op_0 = self.op_0
        op_1 = self.op_1

        S = fp.sim.SMatrix()

        S[op_1, op_0] = S[op_0, op_1] = mag, arg

        return S

Script description

  • Import functions and modules

    from typing import Any, Sequence, Tuple
    
    from fnpcell import all as fp
    import numpy as np
    from fnpcell.interfaces import IScatterMatrix
    from fnpcell.interfaces import angle_between, distance_between
    from gpdk.technology import get_technology
    
  • List the ports and parameters that will be implemented in this simulation model.

op_0: fp.IOwnedPort
op_1: fp.IOwnedPort
wl0: float = 1.49
neff: float = 2.4
ng: float = 4.0
loss: float = 0.0
  • Define the S-Matrix by calculating the magnitude and argument relation between two ports.

    fp.sim.SMatrix() is where the magnitude and argument should be assigned.

def simulate_scatter(self, wavelengths: Sequence[float]) -> IScatterMatrix:
    wl = np.array(wavelengths)
    wl0 = np.array(self.wl0)
    neff = np.array(self.neff)
    ng = np.array(self.ng)
    loss = np.array(self.loss)
    length = fp.distance_between(self.op_0.position, self.op_1.position)

    dwl = wl - self.wl0
    dneff_dwl = (ng - neff) / wl0
    neff = neff - dwl * dneff_dwl

    mag = 10 ** (-loss * length / 20)
    arg = 2 * np.pi * neff * length / wl

    op_0 = self.op_0
    op_1 = self.op_1

    S = fp.sim.SMatrix()

    S[op_1, op_0] = S[op_0, op_1] = mag, arg

    return S

Other simulation models

  • Directional Coupler

class DCModel(fp.sim.SimModel):
    op_0: fp.IOwnedPort
    op_1: fp.IOwnedPort
    op_2: fp.IOwnedPort
    op_3: fp.IOwnedPort
    coupling: float = 0.5

    def simulate_scatter(self, wavelengths: Sequence[float]) -> IScatterMatrix:
        coupling = np.array(self.coupling)
        kappa = coupling**0.5
        tau = (1 - coupling) ** 0.5

        op_0, op_1, op_2, op_3 = self.op_0, self.op_1, self.op_2, self.op_3

        S = fp.sim.SMatrix()

        S[op_2, op_1] = S[op_1, op_2] = tau, 0
        S[op_3, op_1] = S[op_1, op_3] = kappa, np.pi / 2
        S[op_2, op_0] = S[op_0, op_2] = kappa, np.pi / 2
        S[op_3, op_0] = S[op_0, op_3] = tau, 0

        return S
  • Grating Coupler

def simulate_scatter(self, wavelengths: Sequence[float]) -> IScatterMatrix:
wl = np.array(wavelengths)
wl0 = np.array(self.wl0)
peak_transmission = np.array(self.peak_transmission)
bandwidth = np.array(self.bandwidth)

sigma = bandwidth / 2.35482

mag = ((peak_transmission ** 0.5) * np.exp(-(wl-wl0) ** 2.0 /( 2.0 * (sigma ** 2.0))))
arg = 2 * np.pi * 2.4 / wl

op_0 = self.op_0
op_1 = self.op_1

S = fp.sim.SMatrix()

S[op_1, op_0] = S[op_0, op_1] = mag, arg

return S