Variability analysis#

You can study the effect of variability on device performance using the same methods to iterate component parameters to build models.

Lithographic parameters#

Not all variability can be captured by simply changing the Component or LayerStack input parameters.

LithoParameter parameters have a parametrizable transformation attribute that you can use to modify the Component geometry prior to simulation in more complex ways than simply changing its calling arguments. The parameter has methods that return a temporary component given an initial component and a transformation type.

Here are the transformations we support so far:

  1. Dilation and erosion

  2. Corner rounding

  3. Offsets

  4. Corner rounding

  5. Corner analysis

import gdsfactory as gf

c = gf.Component("myComponent")
poly1a = c.add_polygon(
    [[2.8, 3], [5, 3], [5, 0.8]],
    layer="WG",
)
poly1b = c.add_polygon(
    [
        [2, 0],
        [2, 2],
        [4, 2],
        [4, 0],
    ],
    layer="WG",
)
poly1c = c.add_polygon(
    [
        [0, 0.5],
        [0, 1.5],
        [3, 1.5],
        [3, 0.5],
    ],
    layer="WG",
)
poly2 = c.add_polygon(
    [
        [0, 0],
        [5, 0],
        [5, 3],
        [0, 3],
    ],
    layer="SLAB90",
)
poly3 = c.add_polygon(
    [
        [2.5, -2],
        [3.5, -2],
        [3.5, -0.1],
        [2.5, -0.1],
    ],
    layer="WG",
)
c.add_port(name="o1", center=(0, 1), width=1, orientation=0, layer="WG")
c.add_port(name="o2", center=(3, -2), width=1, orientation=90, layer="SLAB90")
c.plot()
../_images/3b5a84e3d27efa50b993a6f14357c3ad875e19fa1153e031aeb4b0bd81f578c3.png

Dilation and erosion#

A LithoParameter of type = "layer_dilation_erosion" parametrizes a layerwise growing (positive value) or shrinking (negative value) of the geometry. Note that the ports are properly resized when they are on the transformed layer:

from gplugins.sax.parameter import LithoParameter

param = LithoParameter(layername="core")
eroded_c = param.layer_dilation_erosion(c, 0.2)
eroded_c
2024-05-10 10:32:51.127 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_45c349f1'
2024-05-10 10:32:51.129 | WARNING  | gdsfactory.klive:show:49 - UserWarning: Could not connect to klive server. Is klayout open and klive plugin installed?
2024-05-10 10:32:51.130 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_45c349f1'
Unnamed_45c349f1: uid 45c349f1, ports ['o1', 'o2'], references [], 3 polygons
../_images/c61ba74b5fa9542c49b826a29b727c8163e17196ba192f9472d732a1e57b7fb8.png
param = LithoParameter(layername="core")
eroded_c = param.layer_dilation_erosion(c, -0.3)
eroded_c
2024-05-10 10:32:51.313 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_1b96bbd4'
2024-05-10 10:32:51.315 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_1b96bbd4'
Unnamed_1b96bbd4: uid 1b96bbd4, ports ['o1', 'o2'], references [], 4 polygons
../_images/a831258971637b179f256b9238f111561e61e0ac74d296313f437cea928b5d66.png
param = LithoParameter(layername="slab90")
eroded_c = param.layer_dilation_erosion(c, 0.2)
eroded_c
2024-05-10 10:32:51.504 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_28167eb1'
2024-05-10 10:32:51.506 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_28167eb1'
Unnamed_28167eb1: uid 28167eb1, ports ['o1', 'o2'], references [], 5 polygons
../_images/c1afa2302c6a57b29b60a1f3de3327f38226555c15d5d6d3a64960d9f1b7cfd3.png

Offsets#

Lithography can sometimes laterally offset layers w.r.t. to one another. This is captured by layerwise type = "layer_x_offset" and type = "layer_x_offset". Note that ports are also translated:

param = LithoParameter(layername="core")
offset_c = param.layer_x_offset(c, 0.5)
offset_c
2024-05-10 10:32:51.701 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_47a35605'
2024-05-10 10:32:51.703 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_47a35605'
Unnamed_47a35605: uid 47a35605, ports ['o1', 'o2'], references [], 5 polygons
../_images/6d64ebffae0963165570947efb9fc5bf0acc3615378195e78eef69187ee181a7.png
param = LithoParameter(layername="core")
offset_c = param.layer_y_offset(c, -0.5)
offset_c
2024-05-10 10:32:51.860 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_9d17253d'
2024-05-10 10:32:51.863 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_9d17253d'
Unnamed_9d17253d: uid 9d17253d, ports ['o1', 'o2'], references [], 5 polygons
../_images/8c10dc84f60a598fed57810a9384e34d5b6a50e880d06df7798424da270bcd33.png

Corner rounding#

The erosion and dilation above is done with “worst case” sharp corners. An erosion –> dilation –> erosion sequence, accessible with type = "layer_round_corners" can be done to parametrize corner rounding. For ports, here parts of the geometry overlapping with ports are patched to prevent the ports from being off the layer.

param = LithoParameter(layername="core")
smooth_c = param.layer_round_corners(c, 0.1)
smooth_c
2024-05-10 10:32:52.011 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_d80d859b'
2024-05-10 10:32:52.013 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_d80d859b'
Unnamed_d80d859b: uid d80d859b, ports ['o1', 'o2'], references ['extrude_1'], 3 polygons
../_images/1fbc5b4ff41768b092da25f605825458982d409791200d2214890b3968cf0a30.png
param = LithoParameter(layername="core")
smooth_c = param.layer_round_corners(c, 0.4)
smooth_c
2024-05-10 10:32:52.156 | WARNING  | gdsfactory.show:show:47 - UserWarning: Unnamed cells, 1 in 'Unnamed_f26ebc7f'
2024-05-10 10:32:52.158 | WARNING  | gdsfactory.component:plot_klayout:1645 - UserWarning: Unnamed cells, 1 in 'Unnamed_f26ebc7f'
Unnamed_f26ebc7f: uid f26ebc7f, ports ['o1', 'o2'], references ['extrude_1'], 3 polygons
../_images/6961de4034ca4af077a96d402b2a967feaccdb0308f364b15d7ac6e572715887.png

Corner analysis#

For convenience, the model builder can also iterate over only the min, max, and nominal values of all trainable_parameters by using the types=corners instead of the default types=arange argument of Model.get_model_input_output(type="corners").

Directional coupler example#

Consider a directional coupler component which is modeled through a generic MeepFDTDModel. The only difference between this and the FemwellWaveguideModel from last notebook is how the simulation is defined: everything else involving iteration over parameters, multiprocessing, and model fitting, is identical. This makes model building easily extensible to new simulators.

Here, we are only interested in variability analysis of the geometry, and so we create a trainable coupler with fixed length and gap:

import gdsfactory as gf
from gdsfactory.pdk import get_layer_stack
from gdsfactory.technology import LayerStack

from gplugins.sax.parameter import NamedParameter

# gdsfactory layer_stack
filtered_layer_stack = LayerStack(
    layers={
        k: get_layer_stack().layers[k]
        for k in (
            "slab90",
            "core",
            "box",
            "clad",
        )
    }
)


# trainable component function, choosing which parameters to fix and which to consider for the model
def trainable_coupler(parameters):
    return gf.components.coupler_full(
        coupling_length=10,
        gap=0.3,
        dw=0.0,
    )


c = trainable_coupler({})
c.plot()
../_images/e93082311c967a5b3d88abf7c127174c65a8a8843269dd5fb66989eac5753c41.png

When defining the model, we add the LithoParameter erosion_magnitude. For all models, a TransformParameter which if set, will offset the provided component prior to simulation, emulating erosion (when <1), nominal behaviour (when 1) and dilation (when >1). This morphological transformation is currently global; more advanced spatially-correlated filters are an obvious next step.

from gplugins.sax.integrations.meep_FDTD_model import MeepFDTDModel

# Simulation settings
port_symmetries_coupler = {
    "o1@0,o1@0": ["o2@0,o2@0", "o3@0,o3@0", "o4@0,o4@0"],
    "o2@0,o1@0": ["o1@0,o2@0", "o3@0,o4@0", "o4@0,o3@0"],
    "o3@0,o1@0": ["o1@0,o3@0", "o2@0,o4@0", "o4@0,o2@0"],
    "o4@0,o1@0": ["o1@0,o4@0", "o2@0,o3@0", "o3@0,o2@0"],
}

sim_settings = dict(
    resolution=30,
    xmargin=1.0,
    ymargin=1.0,
    is_3d=False,
    port_source_names=["o1"],
    port_symmetries=port_symmetries_coupler,
    run=True,
    overwrite=False,
    layer_stack=filtered_layer_stack,
    z=0.1,
)


coupler_model = MeepFDTDModel(
    trainable_component=trainable_coupler,
    layer_stack=filtered_layer_stack,
    simulation_settings={
        "sim_settings": sim_settings,
    },
    trainable_parameters={
        "dilation_magnitude": LithoParameter(
            type="layer_dilation_erosion",
            layername="core",
            min_value=-0.05,
            max_value=0.05,
            nominal_value=0.0,
            step=0.05,
        ),
    },
    non_trainable_parameters={
        "wavelength": NamedParameter(
            min_value=1.54, max_value=1.56, nominal_value=1.55, step=0.01
        ),
    },
    num_modes=1,
)
Using MPI version 4.1, 1 processes
2024-05-10 10:32:53.019 | INFO     | gplugins.gmeep:<module>:39 - Meep '1.28.0' installed at ['/home/runner/micromamba/lib/python3.11/site-packages/meep']
2024-05-10 10:32:55,781	INFO worker.py:1749 -- Started a local Ray instance.
# input_vectors, output_vectors = coupler_model.get_model_input_output(type="corners")

We can analyze the output vectors as a function of input vectors to study variability (TODO).

Since such a change of morphology can also be approximated with a change in gap and waveguide width of the original component, we can compare the results to a model of the component with these as swept NamedParameters (TODO).