-
Notifications
You must be signed in to change notification settings - Fork 181
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1309 from simbilod/variability2
Sweep Fabrication
- Loading branch information
Showing
6 changed files
with
721 additions
and
16 deletions.
There are no files selected for viewing
342 changes: 342 additions & 0 deletions
342
docs/notebooks/plugins/sax/03_variability_analysis.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,342 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Variability analysis\n", | ||
"\n", | ||
"The same machinery that is used to iterate over component parameters to build models can be used to study the effect of variability on device performance. \n", | ||
"\n", | ||
"## Lithographic parameters\n", | ||
"\n", | ||
"Not all variability can be captured by simply changing the Component or LayerStack input parameters. \n", | ||
"\n", | ||
"`LithoParameter` parameters have a parametrizable `transformation` attribute that can be used 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.\n", | ||
"\n", | ||
"Currently, 4 types of transformation have been coded. Let's create a funky polygon to look at them:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import gdsfactory as gf\n", | ||
"\n", | ||
"c = gf.Component(\"myComponent\")\n", | ||
"poly1a = c.add_polygon(\n", | ||
" [\n", | ||
" [2.8,3],\n", | ||
" [5,3],\n", | ||
" [5,0.8]\n", | ||
" ],\n", | ||
" layer=\"WG\",\n", | ||
")\n", | ||
"poly1b = c.add_polygon(\n", | ||
" [\n", | ||
" [2, 0],\n", | ||
" [2, 2],\n", | ||
" [4, 2],\n", | ||
" [4, 0],\n", | ||
" ],\n", | ||
" layer=\"WG\",\n", | ||
")\n", | ||
"poly1c = c.add_polygon(\n", | ||
" [\n", | ||
" [0, 0.5],\n", | ||
" [0, 1.5],\n", | ||
" [3, 1.5],\n", | ||
" [3, 0.5],\n", | ||
" ],\n", | ||
" layer=\"WG\",\n", | ||
")\n", | ||
"poly2 = c.add_polygon(\n", | ||
" [\n", | ||
" [0, 0],\n", | ||
" [5, 0],\n", | ||
" [5, 3],\n", | ||
" [0, 3],\n", | ||
" ],\n", | ||
" layer=\"SLAB90\",\n", | ||
")\n", | ||
"poly3 = c.add_polygon(\n", | ||
" [\n", | ||
" [2.5, -2],\n", | ||
" [3.5, -2],\n", | ||
" [3.5, -0.1],\n", | ||
" [2.5, -0.1],\n", | ||
" ],\n", | ||
" layer=\"WG\",\n", | ||
")\n", | ||
"c.add_port(name=\"o1\", center=(0,1), width=1, orientation=0, layer=1)\n", | ||
"c.add_port(name=\"o2\", center=(3,-2), width=1, orientation=90, layer=1)\n", | ||
"c.plot() # we want to see the ports too" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Dilation and erosion\n", | ||
"\n", | ||
"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 if they are on the transformed layer:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from gdsfactory.simulation.sax.parameter import LithoParameter\n", | ||
"\n", | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"eroded_c = param.layer_dilation_erosion(c, 0.2)\n", | ||
"eroded_c.plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"eroded_c = param.layer_dilation_erosion(c, -0.3)\n", | ||
"eroded_c.plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"slab90\")\n", | ||
"eroded_c = param.layer_dilation_erosion(c, 0.2)\n", | ||
"eroded_c.plot()" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"### Offsets\n", | ||
"\n", | ||
"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:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"offset_c = param.layer_x_offset(c, 0.5)\n", | ||
"offset_c.plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"offset_c = param.layer_y_offset(c, -0.5)\n", | ||
"offset_c.plot()" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Corner rounding\n", | ||
"\n", | ||
"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." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"smooth_c = param.layer_round_corners(c, 0.1)\n", | ||
"smooth_c.plot()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"param = LithoParameter(layername=\"core\")\n", | ||
"smooth_c = param.layer_round_corners(c, 0.4)\n", | ||
"smooth_c.plot()" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## Corner analysis\n", | ||
"\n", | ||
"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\")`.\n", | ||
"\n", | ||
"## Directional coupler example\n", | ||
"\n", | ||
"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.\n", | ||
"\n", | ||
"Here, we are only interested in variability analysis of the geometry, and so we create a trainable coupler with fixed length and gap:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"import gdsfactory as gf\n", | ||
"from gdsfactory.simulation.sax.parameter import NamedParameter\n", | ||
"from gdsfactory.technology import LayerStack\n", | ||
"from gdsfactory.pdk import _ACTIVE_PDK, get_layer_stack\n", | ||
"\n", | ||
"\n", | ||
"# gdsfactory layerstack\n", | ||
"filtered_layerstack = LayerStack(\n", | ||
" layers={\n", | ||
" k: get_layer_stack().layers[k]\n", | ||
" for k in (\n", | ||
" \"slab90\",\n", | ||
" \"core\",\n", | ||
" \"box\",\n", | ||
" \"clad\",\n", | ||
" )\n", | ||
" }\n", | ||
")\n", | ||
"\n", | ||
"# trainable component function, choosing which parameters to fix and which to consider for the model\n", | ||
"def trainable_coupler(parameters):\n", | ||
" return gf.components.coupler_full(\n", | ||
" coupling_length=10,\n", | ||
" gap=0.3,\n", | ||
" dw=0.0,\n", | ||
" )\n", | ||
"\n", | ||
"c = trainable_coupler({})\n", | ||
"c.plot()" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"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." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from gdsfactory.simulation.sax.meep_FDTD_model import MeepFDTDModel\n", | ||
"\n", | ||
"# Simulation settings\n", | ||
"port_symmetries_coupler = {\n", | ||
" \"o1@0,o1@0\": [\"o2@0,o2@0\", \"o3@0,o3@0\", \"o4@0,o4@0\"],\n", | ||
" \"o2@0,o1@0\": [\"o1@0,o2@0\", \"o3@0,o4@0\", \"o4@0,o3@0\"],\n", | ||
" \"o3@0,o1@0\": [\"o1@0,o3@0\", \"o2@0,o4@0\", \"o4@0,o2@0\"],\n", | ||
" \"o4@0,o1@0\": [\"o1@0,o4@0\", \"o2@0,o3@0\", \"o3@0,o2@0\"],\n", | ||
"}\n", | ||
"\n", | ||
"sim_settings = dict(\n", | ||
" resolution=30,\n", | ||
" xmargin=1.0,\n", | ||
" ymargin=1.0,\n", | ||
" is_3d=False,\n", | ||
" port_source_names=[\"o1\"],\n", | ||
" port_symmetries=port_symmetries_coupler,\n", | ||
" run=True,\n", | ||
" overwrite=False,\n", | ||
" layer_stack=filtered_layerstack,\n", | ||
" z=0.1,\n", | ||
")\n", | ||
"\n", | ||
"\n", | ||
"coupler_model = MeepFDTDModel(trainable_component=trainable_coupler,\n", | ||
" layerstack=filtered_layerstack,\n", | ||
" simulation_settings={\n", | ||
" \"sim_settings\": sim_settings,\n", | ||
" },\n", | ||
" trainable_parameters={\n", | ||
" \"dilation_magnitude\": LithoParameter(\n", | ||
" type=\"layer_dilation_erosion\", layername=\"core\", min_value=-0.05, max_value=0.05, nominal_value=0.0, step=0.05\n", | ||
" ),\n", | ||
" },\n", | ||
" non_trainable_parameters={\n", | ||
" \"wavelength\": NamedParameter(\n", | ||
" min_value=1.54, max_value=1.56, nominal_value=1.55, step=0.01\n", | ||
" ),\n", | ||
" },\n", | ||
" num_modes=1,\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# input_vectors, output_vectors = coupler_model.get_model_input_output(type=\"corners\")" | ||
] | ||
}, | ||
{ | ||
"attachments": {}, | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"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)." | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "gdsfactory", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.10.9" | ||
}, | ||
"vscode": { | ||
"interpreter": { | ||
"hash": "19d269ed95feaaac3f5a2d92915e9fd960bd420d94635eb150e438afb1801d52" | ||
} | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 2 | ||
} |
Oops, something went wrong.