To import a PDK from GDS files into gdsfactory you need:
Ideally you also get:
GDS files are great for describing geometry thanks to the concept of References, where you store any geometry only once in memory.
For storing device metadata (settings, port locations, port widths, port angles ...) there is no clear standard.
gdsfactory
stores the that metadata in YAML
files, and also has functions to add pins
Component.write_gds()
saves GDSComponent.write_gds_metadata()
save GDS + YAML metadataimport gdsfactory as gf
from gdsfactory.config import PATH
from gdsfactory.generic_tech import get_generic_pdk
from gdsfactory.technology import lyp_to_dataclass
gf.config.rich_output()
c = gf.components.mzi()
c.plot()
You can write GDS files only
gdspath = c.write_gds("extra/mzi.gds")
Or GDS with YAML metadata information (ports, settings, cells ...)
gdspath = c.write_gds("extra/mzi.gds", with_metadata=True)
This created a mzi.yml
file that contains:
c.pprint()
You can read GDS files into gdsfactory thanks to the import_gds
function
import_gds
reads the same GDS file from disk without losing any information
c = gf.import_gds(gdspath, read_metadata=True)
c.plot()
c2 = gf.import_gds(gdspath, read_metadata=True)
c2.plot()
c2.name
c3 = gf.routing.add_fiber_single(c2)
c3.plot()
gdspath = c3.write_gds("extra/pdk.gds", with_metadata=True)
gf.labels.write_labels.write_labels_klayout(gdspath, layer_label=(201, 0))
Sometimes the GDS does not have YAML metadata, therefore you need to figure out the port locations, widths and orientations.
gdsfactory provides you with functions that will add ports to the component by looking for pins shapes on a specific layers (port_markers or pins)
There are different pin standards supported to automatically add ports to components:
Lets add pins, save a GDS and then import it back.
from functools import partial
c = gf.components.straight()
c_with_pins = gf.add_pins.add_pins_container(component=c)
c_with_pins.plot(show_ports=False)
gdspath = c_with_pins.write_gds("extra/wg.gds")
c2 = gf.import_gds(gdspath)
c2.plot()
c2.ports # import_gds does not automatically add the pins
c3 = gf.import_gds(gdspath)
c3 = gf.add_ports.add_ports_from_markers_inside(c3, port_layer="PORT")
c3.plot(show_ports=False)
c3.pprint_ports()
Foundries provide PDKs in different formats and commercial tools.
The easiest way to import a PDK into gdsfactory is to:
With that you can easily create the PDK as as python package.
Thanks to having a gdsfactory PDK as a python package you can:
pip install pdk_fab_a
and easily interface with other toolsTo create a Python package you can start from a customizable template (thanks to cookiecutter)
You can create a python package by running this 2 commands inside a terminal:
pip install cookiecutter
cookiecutter gh:joamatab/python
It will ask you some questions to fill in the template for the python package.
Then you can add the information about the GDS files and the Layers inside that package
print(lyp_to_dataclass(PATH.klayout_lyp))
# lets create a sample PDK (for demo purposes only) using GDSfactory
# if the PDK is in a commercial tool you can also do this. Make sure you save a single pdk.gds
sample_pdk_cells = gf.grid(
[
gf.components.straight,
gf.components.bend_euler,
gf.components.grating_coupler_elliptical,
]
)
sample_pdk_cells.write_gds("extra/pdk.gds")
sample_pdk_cells
sample_pdk_cells.get_dependencies()
# we write the sample PDK into a single GDS file
gf.write_cells.write_cells_recursively(gdspath="extra/pdk.gds", dirpath="extra/gds")
print(gf.write_cells.get_import_gds_script("extra/gds"))
You can also include the code to plot each fix cell in the docstring.
print(gf.write_cells.get_import_gds_script("extra/gds", module="samplepdk.components"))
You can Write the cells to GDS and use the
Ideally you also start transitioning your legacy code Pcells into gdsfactory syntax. It's a great way to learn the gdsfactory way!
Here is some advice:
gdsfactory supports read and write to uPDK YAML definition
Lets write a PDK into uPDK YAML definition and then convert it back to a gdsfactory script.
the uPDK extracts the code from the docstrings.
def evanescent_coupler_sample() -> None:
"""Evanescent coupler example.
Args:
coupler_length: length of coupling (min: 0.0, max: 200.0, um).
"""
pass
from gdsfactory.samples.pdk.fab_c import pdk
yaml_pdk_decription = pdk.to_updk()
print(yaml_pdk_decription)
from gdsfactory.read.from_updk import from_updk
gdsfactory_script = from_updk(yaml_pdk_decription)
print(gdsfactory_script)
You can create a PDK as a python library using a cookiecutter template. For example, you can use this one.
pip install cookiecutter
cookiecutter gh:joamatab/python
Or you can fork the ubcpdk and create new PCell functions that use the correct layers for your foundry. For example.
from pydantic import BaseModel
class LayerMap(BaseModel):
WGCORE = (3, 0)
DEVREC: Layer = (68, 0)
PORT: Layer = (1, 10) # PinRec
PORTE: Layer = (1, 11) # PinRecM
FLOORPLAN: Layer = (99, 0)
TE: Layer = (203, 0)
TM: Layer = (204, 0)
TEXT: Layer = (66, 0)
LABEL_INSTANCE: Layer = (66, 0)
LAYER = LayerMap()