Import PDK#

Importing a PDK from GDS Files#

For foundry PDKs, we highly recommend using GDSFactory PDKs. See available PDKs here.

If your foundry does not yet have a GDSFactory PDK, you can import a PDK from GDS files into GDSFactory. To do this, you will need:

  • A GDS file containing all the cells you want to import into the PDK (or multiple GDS files, each containing a separate design).

Ideally, you should also obtain:

  • KLayout layer properties files – to define the layers available for custom components. This allows you to create a LayerMap that associates layer names with (GDS_LAYER, GDS_PURPOSE).

  • Layer stack information – including material index, thickness, and the Z positions of each layer.

  • DRC rules – If these are not provided, you can build them using KLayout.

GDS files efficiently describe geometry using References, which store each geometry only once in memory. However, most GDS tools lack a clear standard for storing device metadata such as settings, port locations, port widths, and port angles.

gdsfactory addresses this limitation by storing metadata inside the GDS and provide built-in functions to add pins.

To save a GDS file in gdsfactory, use:

Component.write_gds()

However, if you want to avoid the port and settings metadata you can use:

Component.write_gds(with_metadata=False)
import gdsfactory as gf
from gdsfactory.config import PATH
from gdsfactory.technology import lyp_to_dataclass
c = gf.components.mzi()
c.plot()
../_images/8c8508eac172cf030b0bdce8dd1e457c459dcaa77e68fceb8a043e26f7850dbe.png

You can write GDS files.

gdspath = c.write_gds("extra/mzi.gds")
c.pprint_ports()
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name  width  orientation  layer     center                    port_type ┃
┡━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ o1   │ 0.5   │ 180.0       │ WG (1/0) │ (-10.0, 0.0)             │ optical   │
│ o2   │ 0.5   │ 0.0         │ WG (1/0) │ (81.10000000000001, 0.0) │ optical   │
└──────┴───────┴─────────────┴──────────┴──────────────────────────┴───────────┘

You can import GDS files into gdsfactory thanks to the import_gds function.

import_gds reads the same GDS file from the disk without losing any information.

Gdsfactory stores the settings and the ports as part of the GDS metadata.

c = gf.import_gds(gdspath)
c.pprint_ports()
c.plot()
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name  width  orientation  layer     center                    port_type ┃
┡━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ o1   │ 0.5   │ 180.0       │ WG (1/0) │ (-10.0, 0.0)             │ optical   │
│ o2   │ 0.5   │ 0.0         │ WG (1/0) │ (81.10000000000001, 0.0) │ optical   │
└──────┴───────┴─────────────┴──────────┴──────────────────────────┴───────────┘
../_images/8c8508eac172cf030b0bdce8dd1e457c459dcaa77e68fceb8a043e26f7850dbe.png

Add ports from pins#

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 the shapes of pins on a specific layer (port_markers or pins).

There are different pin standards supported to automatically add ports to components:

  • PINs towards the inside of the port (port at the outer part of the PIN).

  • PINs with half of the pin inside and half outside (port at the center of the PIN).

  • PIN with only labels (no shapes). You have to manually specify the width of the port.

Now let us save a GDS and then import it back.

c = gf.components.straight()
c_with_pins = gf.add_pins.add_pins_container(component=c)
c_with_pins.plot()
../_images/c09aa711174d4e7ef3d237b36b90a2fc9a26f0c6837fc4b05080a5cae7fa422c.png
c_with_pins.ports = []  # Let us rely on the pins to extract the ports.
gdspath = c_with_pins.write_gds("extra/wg.gds")
c2 = gf.import_gds(gdspath)
c2.plot()
../_images/c09aa711174d4e7ef3d237b36b90a2fc9a26f0c6837fc4b05080a5cae7fa422c.png
c2.ports  # Import_gds does not automatically add the pins.
[]
# This line reads a GDSII file from the location specified by the gdspath variable and converts its geometry into a gdsfactory component.
c3 = gf.import_gds(gdspath)

# This is a utility function that adds ports to a component by looking for special marker shapes.
# It searches the component for small shapes (often squares or paths) on the specified pin_layer (in this case, a layer named "PORT").
# It then uses the location, width, and orientation of these markers to create and add proper gdsfactory ports to the component.
c3 = gf.add_ports.add_ports_from_markers_inside(c3, pin_layer="PORT")
c3.plot()
../_images/c09aa711174d4e7ef3d237b36b90a2fc9a26f0c6837fc4b05080a5cae7fa422c.png
c3.pprint_ports()
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name  width  orientation  layer        center       port_type ┃
┡━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ o1   │ 0.5   │ 180.0       │ PORT (1/10) │ (0.0, 0.0)  │ optical   │
│ o2   │ 0.5   │ 0.0         │ PORT (1/10) │ (10.0, 0.0) │ optical   │
└──────┴───────┴─────────────┴─────────────┴─────────────┴───────────┘

Foundries provide PDKs in different formats and commercial tools.

The easiest way to import a PDK into gdsfactory is to:

  1. have each GDS cell import into a separate GDS file.

  2. have one GDS file with all the cells inside.

  3. Have a KLayout layermap. Makes it easier to create the layermap.

With that you can easily create the PDK as as python package.

Thanks to having a gdsfactory PDK as a python package you can:

  • Version control your PDK using GIT to keep track of changes and work on it as a team.

    • Write tests of your pdk components to avoid unwanted changes from one component to another.

    • Ensure you maintain the quality of the PDK with continuous integration checks.

    • Pin the version of gdsfactory, so new updates of gdsfactory will not affect your code.

  • Name your PDK version using semantic versioning. For example patches increase the last number (0.0.1 -> 0.0.2).

  • Install your PDK easily pip install pdk_fab_a and easily access an interface with other tools.

To create a Python package you can start from a customizable template (thanks to cookiecutter).

You can create a python package by running these 2 commands inside a terminal:

pip install cookiecutter
cookiecutter gh:joamatab/python

It will ask you some questions to fill into the template for the python package.

Then you can add the information about the GDS files and the Layers inside that package.

# lyp_to_dataclass(...): This gdsfactory utility function parses the .lyp file and converts its information into the text format of a Python dataclass.
print(lyp_to_dataclass(PATH.klayout_lyp))
from gdsfactory.typings import Layer
from gdsfactory.technology.layer_map import LayerMap


class LayerMapFab(LayerMap):
    CAPACITOR: Layer = (42, 0)
    DEEPTRENCH: Layer = (4, 0)
    DEEP_ETCH: Layer = (3, 6)
    DICING: Layer = (65, 0)
    DRC_EXCLUDE: Layer = (67, 0)
    DRC_MARKER: Layer = (205, 0)
    DevRec: Layer = (68, 0)
    ERROR_PATH: Layer = (1000, 0)
    Errors: Layer = (69, 0)
    FLOORPLAN: Layer = (64, 0)
    FbrTgt: Layer = (81, 0)
    GE: Layer = (5, 0)
    GENPP: Layer = (26, 0)
    GEPPP: Layer = (29, 0)
    LABEL_INSTANCES: Layer = (206, 0)
    LABEL_OPTICAL_IO: Layer = (201, 0)
    LABEL_SETTINGS: Layer = (202, 0)
    Lumerical: Layer = (733, 0)
    M1: Layer = (41, 0)
    M1TILES: Layer = (191, 0)
    M2: Layer = (45, 0)
    M3: Layer = (49, 0)
    METALOPEN: Layer = (46, 0)
    MH: Layer = (47, 0)
    MONITOR: Layer = (101, 0)
    N: Layer = (20, 0)
    NOTILE_M1: Layer = (71, 0)
    NOTILE_M2: Layer = (72, 0)
    NOTILE_M3: Layer = (73, 0)
    NP: Layer = (22, 0)
    NPP: Layer = (24, 0)
    OXIDE_ETCH: Layer = (6, 0)
    PDPP: Layer = (27, 0)
    PP: Layer = (23, 0)
    PPP: Layer = (25, 0)
    P_210: Layer = (21, 0)
    PinRec: Layer = (1, 10)
    PinRecM: Layer = (1, 11)
    SHALLOW_ETCH: Layer = (2, 6)
    SHOW_PORTS: Layer = (1, 12)
    SILICIDE: Layer = (39, 0)
    SIM_REGION: Layer = (100, 0)
    SITILES: Layer = (190, 0)
    SLAB150: Layer = (2, 0)
    SLAB150CLAD: Layer = (2, 9)
    SLAB90: Layer = (3, 0)
    SLAB90CLAD: Layer = (3, 1)
    SOURCE: Layer = (110, 0)
    TE: Layer = (203, 0)
    TM: Layer = (204, 0)
    Text: Layer = (66, 0)
    VIA1: Layer = (44, 0)
    VIA2: Layer = (43, 0)
    VIAC: Layer = (40, 0)
    WAFER: Layer = (999, 0)
    WGCLAD: Layer = (111, 0)
    WGN_Nitride: Layer = (34, 0)
    WGclad_material: Layer = (36, 0)
    Waveguide: Layer = (1, 0)
    XS_BOX: Layer = (300, 0)
    XS_GE: Layer = (315, 0)
    XS_M2: Layer = (399, 0)
    XS_MH: Layer = (306, 0)
    XS_N: Layer = (320, 0)
    XS_NPP: Layer = (321, 0)
    XS_OVERLAY: Layer = (311, 0)
    XS_OXIDE_M1: Layer = (305, 0)
    XS_OXIDE_M2: Layer = (307, 0)
    XS_OXIDE_M3: Layer = (311, 0)
    XS_OXIDE_MH: Layer = (317, 0)
    XS_OXIDE_ML: Layer = (309, 0)
    XS_OX_NIT_CLAD: Layer = (304, 0)
    XS_OX_SI: Layer = (302, 0)
    XS_P: Layer = (330, 0)
    XS_PDPP: Layer = (327, 0)
    XS_PPP: Layer = (331, 0)
    XS_SI: Layer = (301, 0)
    XS_SIN: Layer = (319, 0)
    XS_SIN2: Layer = (305, 0)
    XS_SI_SLAB: Layer = (313, 0)
    XS_VIA1: Layer = (308, 0)
    XS_VIA2: Layer = (310, 0)
    XS_VIAC: Layer = (303, 0)


LAYER = LayerMapFab
# Let us now 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
../_images/ca2a592a694af3c70a6d9705d428fa710bb8dbb2e50e1b4317d6b62d21d748d0.png
gf.clear_cache()

# write_cells_recursively(...): This function recursively goes through the entire hierarchy of the input GDSII file,
# finds every unique cell definition, and writes each one to a new .gds file in the output directory.
# gdspath="extra/pdk.gds": This is the input file. It is a GDSII file that contains a full design or Process Design Kit (PDK),
# which is typically a hierarchical structure of many cells referencing other cells.
# # dirpath="extra/gds": This is the output directory. The function will create this folder if it does not exist.
gf.write_cells.write_cells_recursively(gdspath="extra/pdk.gds", dirpath="extra/gds")
{'grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be': PosixPath('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds/grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be.gds'),
 'straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423': PosixPath('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds/straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423.gds'),
 'bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2': PosixPath('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds/bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2.gds'),
 'Unnamed_21': PosixPath('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds/Unnamed_21.gds')}
print(gf.write_cells.get_import_gds_script("extra/gds"))
from pathlib import Path
from functools import partial
import gdsfactory as gf

add_ports_optical = partial(
    gf.add_ports.add_ports_from_markers_inside, pin_layer=(1, 0), port_layer=(2, 0)
)
add_ports_electrical = partial(
    gf.add_ports.add_ports_from_markers_inside, pin_layer=(41, 0), port_layer=(1, 0)
)
add_ports = gf.compose(add_ports_optical, add_ports_electrical)


gdsdir = Path('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds')

import_gds = partial(gf.import_gds, post_process=add_ports)



@gf.cell
def Unnamed_21()->gf.Component:
    '''Returns Unnamed_21 fixed cell.'''
    return import_gds(gdsdir/'Unnamed_21.gds')




@gf.cell
def bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2()->gf.Component:
    '''Returns bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2 fixed cell.'''
    return import_gds(gdsdir/'bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2.gds')




@gf.cell
def grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be()->gf.Component:
    '''Returns grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be fixed cell.'''
    return import_gds(gdsdir/'grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be.gds')




@gf.cell
def straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423()->gf.Component:
    '''Returns straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423 fixed cell.'''
    return import_gds(gdsdir/'straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423.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"))
from pathlib import Path
from functools import partial
import gdsfactory as gf

add_ports_optical = partial(
    gf.add_ports.add_ports_from_markers_inside, pin_layer=(1, 0), port_layer=(2, 0)
)
add_ports_electrical = partial(
    gf.add_ports.add_ports_from_markers_inside, pin_layer=(41, 0), port_layer=(1, 0)
)
add_ports = gf.compose(add_ports_optical, add_ports_electrical)


gdsdir = Path('/home/runner/work/gdsfactory/gdsfactory/notebooks/extra/gds')

import_gds = partial(gf.import_gds, post_process=add_ports)



@gf.cell
def Unnamed_21()->gf.Component:
    '''Returns Unnamed_21 fixed cell.

    .. plot::
      :include-source:

      import samplepdk

      c = samplepdk.components.Unnamed_21()
      c.plot()
    '''
    return import_gds(gdsdir/'Unnamed_21.gds')




@gf.cell
def bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2()->gf.Component:
    '''Returns bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2 fixed cell.

    .. plot::
      :include-source:

      import samplepdk

      c = samplepdk.components.bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2()
      c.plot()
    '''
    return import_gds(gdsdir/'bend_euler_gdsfactorypcomponentspbendspbend_euler_RNone_387600b2.gds')




@gf.cell
def grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be()->gf.Component:
    '''Returns grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be fixed cell.

    .. plot::
      :include-source:

      import samplepdk

      c = samplepdk.components.grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be()
      c.plot()
    '''
    return import_gds(gdsdir/'grating_coupler_elliptical_gdsfactorypcomponentspgratin_2f9b04be.gds')




@gf.cell
def straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423()->gf.Component:
    '''Returns straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423 fixed cell.

    .. plot::
      :include-source:

      import samplepdk

      c = samplepdk.components.straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423()
      c.plot()
    '''
    return import_gds(gdsdir/'straight_gdsfactorypcomponentspwaveguidespstraight_L10__f2286423.gds')

Import PDK from other python packages#

You can Write the cells into GDS and use the

Ideally you also start transitioning your legacy code Pcells into gdsfactory syntax. It is a great way to learn the gdsfactory way!

Here is some advice:

  • Ask your foundry for the gdsfactory PDK.

  • Leverage the generic pdk cells available in gdsfactory.

  • Write tests for your cells.

  • Break the cells into small reusable functions.

  • Use GIT to track changes.

  • Review your code with your colleagues and other gdsfactory developers to get feedback. This is key to get better at coding in gdsfactory.

  • Get rid of any warnings you see.

Import PDK from YAML uPDK#

gdsfactory supports read and write to uPDK YAML definition

Let us 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

PDK.activate()

# The .to_updk() method converts the PDK's settings into a YAML string that follows the universal PDK (uPDK) standard.
# The uPDK is an open-source format designed to make PDKs interoperable between different design and simulation tools.
yaml_pdk = PDK.to_updk()
print(yaml_pdk)
blocks:
  bend_euler_nc:
    bbox:
    - - -3.0
      - -3.5
    - - 13.5
      - -3.5
    - - 13.5
      - 10.0
    - - -3.0
      - 10.0
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_nc
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 10.0
        - 10.0
        - 90.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  bend_euler_no:
    bbox:
    - - -3.0
      - -3.45
    - - 13.450000000000001
      - -3.45
    - - 13.450000000000001
      - 10.0
    - - -3.0
      - 10.0
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_no
    pins:
      o1:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 10.0
        - 10.0
        - 90.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  bend_euler_sc:
    bbox:
    - - -3.0
      - -3.25
    - - 13.25
      - -3.25
    - - 13.25
      - 10.0
    - - -3.0
      - 10.0
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_sc
    pins:
      o1:
        alias: null
        doc: null
        width: 0.5
        xsection: 71c66a08_500
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.5
        xsection: 71c66a08_500
        xya:
        - 10.0
        - 10.0
        - 90.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  bend_euler_so:
    bbox:
    - - -3.0
      - -3.2
    - - 13.200000000000001
      - -3.2
    - - 13.200000000000001
      - 10.0
    - - -3.0
      - 10.0
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_so
    pins:
      o1:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 10.0
        - 10.0
        - 90.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  gc_sc:
    bbox:
    - - -3.0
      - -14.128
    - - 40.18
      - -14.128
    - - 40.18
      - 14.128
    - - -3.0
      - 14.128
    doc: Canvas where you add polygons, instances and ports.
    parameters: {}
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 10.0
        xsection: 7a93073b_10000
        xya:
        - 16.6
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mmi1x2_nc:
    bbox:
    - - -13.0
      - -4.5
    - - 18.5
      - -4.5
    - - 18.5
      - 4.5
    - - -13.0
      - 4.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_nc
      width_mmi:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 3
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 15.5
        - 0.625
        - 0.0
      o3:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 15.5
        - -0.625
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mmi1x2_no:
    bbox:
    - - -13.0
      - -4.5
    - - 18.5
      - -4.5
    - - 18.5
      - 4.5
    - - -13.0
      - 4.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_no
      width_mmi:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 3
    pins:
      o1:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 15.5
        - 0.625
        - 0.0
      o3:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 15.5
        - -0.625
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mmi1x2_sc:
    bbox:
    - - -13.0
      - -4.5
    - - 18.5
      - -4.5
    - - 18.5
      - 4.5
    - - -13.0
      - 4.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_sc
      width_mmi:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 3
    pins:
      o1:
        alias: null
        doc: null
        width: 0.5
        xsection: 71c66a08_500
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.5
        xsection: 71c66a08_500
        xya:
        - 15.5
        - 0.625
        - 0.0
      o3:
        alias: null
        doc: null
        width: 0.5
        xsection: 71c66a08_500
        xya:
        - 15.5
        - -0.625
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mmi1x2_so:
    bbox:
    - - -13.0
      - -4.5
    - - 18.5
      - -4.5
    - - 18.5
      - 4.5
    - - -13.0
      - 4.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_so
      width_mmi:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 3
    pins:
      o1:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 15.5
        - 0.625
        - 0.0
      o3:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 15.5
        - -0.625
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mzi_nc:
    bbox:
    - - -13.0
      - -31.125
    - - 84.10000000000001
      - -31.125
    - - 84.10000000000001
      - 26.125
    - - -13.0
      - 26.125
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      add_optical_ports_arms:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: false
      auto_rename_ports:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: true
      bend:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: bend_euler_nc
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_nc
      delta_length:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 10
      length_x:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.1
      length_y:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 2
      min_length:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.01
      mirror_bot:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: false
      nbends:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 2
      port1:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o1
      port2:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      port_e0_combiner:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o3
      port_e0_splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o3
      port_e1_combiner:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      port_e1_splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: mmi1x2_nc
      straight:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: straight_nc
      with_splitter:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: true
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 81.10000000000001
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  mzi_no:
    bbox:
    - - -13.0
      - -31.075
    - - 84.10000000000001
      - -31.075
    - - 84.10000000000001
      - 26.075
    - - -13.0
      - 26.075
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      add_optical_ports_arms:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: false
      auto_rename_ports:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: true
      bend:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: bend_euler_no
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_no
      delta_length:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 10
      length_x:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.1
      length_y:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 2
      min_length:
        doc: null
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.01
      mirror_bot:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: false
      nbends:
        doc: null
        max: 0
        min: 0
        type: int
        unit: null
        value: 2
      port1:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o1
      port2:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      port_e0_combiner:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o3
      port_e0_splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o3
      port_e1_combiner:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      port_e1_splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: o2
      splitter:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: mmi1x2_no
      straight:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: straight_no
      with_splitter:
        doc: null
        max: 0
        min: 0
        type: bool
        unit: null
        value: true
    pins:
      o1:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - -10.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 81.10000000000001
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  straight_nc:
    bbox:
    - - -3.0
      - -3.5
    - - 13.0
      - -3.5
    - - 13.0
      - 3.5
    - - -3.0
      - 3.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_nc
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 10.0
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  straight_no:
    bbox:
    - - -3.0
      - -3.45
    - - 13.0
      - -3.45
    - - 13.0
      - 3.45
    - - -3.0
      - 3.45
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_no
    pins:
      o1:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.9
        xsection: 7a93073b_900
        xya:
        - 10.0
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  straight_sc:
    bbox:
    - - -3.0
      - -3.5
    - - 13.0
      - -3.5
    - - 13.0
      - 3.5
    - - -3.0
      - 3.5
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_nc
    pins:
      o1:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 1.0
        xsection: 7a93073b_1000
        xya:
        - 10.0
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
  straight_so:
    bbox:
    - - -3.0
      - -3.2
    - - 13.0
      - -3.2
    - - 13.0
      - 3.2
    - - -3.0
      - 3.2
    doc: Canvas where you add polygons, instances and ports.
    parameters:
      cross_section:
        doc: null
        max: 0
        min: 0
        type: str
        unit: null
        value: strip_so
    pins:
      o1:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 0.0
        - 0.0
        - 180.0
      o2:
        alias: null
        doc: null
        width: 0.4
        xsection: 71c66a08_400
        xya:
        - 10.0
        - 0.0
        - 0.0
    settings:
      Properties:
        doc: ''
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
      info:
        doc: dictionary that includes derived properties, simulation_settings, settings
        max: 0
        min: 0
        type: float
        unit: null
        value: 0.0
header:
  description: fab_c_demopdk
xsections:
  strip_nc:
    width: 1.0
  strip_no:
    width: 0.9
  strip_sc:
    width: 0.5
  strip_so:
    width: 0.4
from gdsfactory.component import GDSDIR_TEMP
from gdsfactory.read.from_updk import from_updk

yamlpath = GDSDIR_TEMP / "pdk.yml"
yamlpath.write_text(yaml_pdk)
gdsfactory_script = from_updk(yamlpath)
print(gdsfactory_script)
import sys
from functools import partial
import gdsfactory as gf
from gdsfactory.get_factories import get_cells
from gdsfactory.add_pins import add_pins_inside2um

cell = gf.cell
layer_bbox = (68, 0)
layer_bbmetal = None
layer_pin_label = None
layer_pin = None
layer_pin_optical = None
layer_pin_electrical = None
layer_label = None

layer_text = (1, 0)
text_function = partial(gf.components.text, layer=layer_text, justify="center", size=2.0)

add_pins = partial(add_pins_inside2um, layer_label=layer_label, layer=layer_pin_optical)
strip_nc = gf.CrossSection(width=1.0)
strip_no = gf.CrossSection(width=0.9)
strip_sc = gf.CrossSection(width=0.5)
strip_so = gf.CrossSection(width=0.4)

cross_sections = dict(strip_nc=strip_nc,strip_no=strip_no,strip_sc=strip_sc,strip_so=strip_so)

@gf.cell
def bend_euler_nc(cross_section:str=strip_nc)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.bend_euler_nc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.5], [13.5, -3.5], [13.5, 10.0], [-3.0, 10.0]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'bend_euler_nc:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_1000', center=(10.0, 10.0), orientation=90.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_1000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def bend_euler_no(cross_section:str=strip_no)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.bend_euler_no()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.45], [13.450000000000001, -3.45], [13.450000000000001, 10.0], [-3.0, 10.0]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'bend_euler_no:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_900', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_900'
    c.add_port(name='o2', cross_section='7a93073b_900', center=(10.0, 10.0), orientation=90.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_900'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def bend_euler_sc(cross_section:str=strip_sc)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.bend_euler_sc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.25], [13.25, -3.25], [13.25, 10.0], [-3.0, 10.0]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'bend_euler_sc:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='71c66a08_500', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '71c66a08_500'
    c.add_port(name='o2', cross_section='71c66a08_500', center=(10.0, 10.0), orientation=90.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '71c66a08_500'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def bend_euler_so(cross_section:str=strip_so)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.bend_euler_so()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.2], [13.200000000000001, -3.2], [13.200000000000001, 10.0], [-3.0, 10.0]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'bend_euler_so:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='71c66a08_400', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '71c66a08_400'
    c.add_port(name='o2', cross_section='71c66a08_400', center=(10.0, 10.0), orientation=90.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '71c66a08_400'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def gc_sc()->gf.Component:
    """Canvas where you add polygons, instances and ports.    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.gc_sc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -14.128], [40.18, -14.128], [40.18, 14.128], [-3.0, 14.128]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'gc_sc'
    c.add_label(text=f'Parameters:\n', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_10000', center=(16.6, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_10000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mmi1x2_nc(cross_section:str=strip_nc, width_mmi:int=3)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
      width_mmi: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mmi1x2_nc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -4.5], [18.5, -4.5], [18.5, 4.5], [-13.0, 4.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mmi1x2_nc:cross_section={cross_section},width_mmi={width_mmi}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}\nwidth_mmi={width_mmi}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_1000', center=(15.5, 0.625), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o3', cross_section='7a93073b_1000', center=(15.5, -0.625), orientation=0.0, port_type='optical')
    c.ports['o3'].info['cross_section'] = '7a93073b_1000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mmi1x2_no(cross_section:str=strip_no, width_mmi:int=3)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
      width_mmi: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mmi1x2_no()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -4.5], [18.5, -4.5], [18.5, 4.5], [-13.0, 4.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mmi1x2_no:cross_section={cross_section},width_mmi={width_mmi}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}\nwidth_mmi={width_mmi}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_900', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_900'
    c.add_port(name='o2', cross_section='7a93073b_900', center=(15.5, 0.625), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_900'
    c.add_port(name='o3', cross_section='7a93073b_900', center=(15.5, -0.625), orientation=0.0, port_type='optical')
    c.ports['o3'].info['cross_section'] = '7a93073b_900'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mmi1x2_sc(cross_section:str=strip_sc, width_mmi:int=3)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
      width_mmi: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mmi1x2_sc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -4.5], [18.5, -4.5], [18.5, 4.5], [-13.0, 4.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mmi1x2_sc:cross_section={cross_section},width_mmi={width_mmi}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}\nwidth_mmi={width_mmi}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='71c66a08_500', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '71c66a08_500'
    c.add_port(name='o2', cross_section='71c66a08_500', center=(15.5, 0.625), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '71c66a08_500'
    c.add_port(name='o3', cross_section='71c66a08_500', center=(15.5, -0.625), orientation=0.0, port_type='optical')
    c.ports['o3'].info['cross_section'] = '71c66a08_500'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mmi1x2_so(cross_section:str=strip_so, width_mmi:int=3)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
      width_mmi: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mmi1x2_so()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -4.5], [18.5, -4.5], [18.5, 4.5], [-13.0, 4.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mmi1x2_so:cross_section={cross_section},width_mmi={width_mmi}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}\nwidth_mmi={width_mmi}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='71c66a08_400', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '71c66a08_400'
    c.add_port(name='o2', cross_section='71c66a08_400', center=(15.5, 0.625), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '71c66a08_400'
    c.add_port(name='o3', cross_section='71c66a08_400', center=(15.5, -0.625), orientation=0.0, port_type='optical')
    c.ports['o3'].info['cross_section'] = '71c66a08_400'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mzi_nc(add_optical_ports_arms:bool=False, auto_rename_ports:bool=True, bend:str=bend_euler_nc, cross_section:str=strip_nc, delta_length:float=10, length_x:float=0.1, length_y:float=2, min_length:float=0.01, mirror_bot:bool=False, nbends:int=2, port1:str=o1, port2:str=o2, port_e0_combiner:str=o3, port_e0_splitter:str=o3, port_e1_combiner:str=o2, port_e1_splitter:str=o2, splitter:str=mmi1x2_nc, straight:str=straight_nc, with_splitter:bool=True)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      add_optical_ports_arms: None (min: 0, max: 0, None).
      auto_rename_ports: None (min: 0, max: 0, None).
      bend: None (min: 0, max: 0, None).
      cross_section: None (min: 0, max: 0, None).
      delta_length: None (min: 0, max: 0, None).
      length_x: None (min: 0, max: 0, None).
      length_y: None (min: 0, max: 0, None).
      min_length: None (min: 0, max: 0, None).
      mirror_bot: None (min: 0, max: 0, None).
      nbends: None (min: 0, max: 0, None).
      port1: None (min: 0, max: 0, None).
      port2: None (min: 0, max: 0, None).
      port_e0_combiner: None (min: 0, max: 0, None).
      port_e0_splitter: None (min: 0, max: 0, None).
      port_e1_combiner: None (min: 0, max: 0, None).
      port_e1_splitter: None (min: 0, max: 0, None).
      splitter: None (min: 0, max: 0, None).
      straight: None (min: 0, max: 0, None).
      with_splitter: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mzi_nc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -31.125], [84.10000000000001, -31.125], [84.10000000000001, 26.125], [-13.0, 26.125]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mzi_nc:add_optical_ports_arms={add_optical_ports_arms},auto_rename_ports={auto_rename_ports},bend={bend},cross_section={cross_section},delta_length={delta_length},length_x={length_x},length_y={length_y},min_length={min_length},mirror_bot={mirror_bot},nbends={nbends},port1={port1},port2={port2},port_e0_combiner={port_e0_combiner},port_e0_splitter={port_e0_splitter},port_e1_combiner={port_e1_combiner},port_e1_splitter={port_e1_splitter},splitter={splitter},straight={straight},with_splitter={with_splitter}'
    c.add_label(text=f'Parameters:\nadd_optical_ports_arms={add_optical_ports_arms}\nauto_rename_ports={auto_rename_ports}\nbend={bend}\ncross_section={cross_section}\ndelta_length={delta_length}\nlength_x={length_x}\nlength_y={length_y}\nmin_length={min_length}\nmirror_bot={mirror_bot}\nnbends={nbends}\nport1={port1}\nport2={port2}\nport_e0_combiner={port_e0_combiner}\nport_e0_splitter={port_e0_splitter}\nport_e1_combiner={port_e1_combiner}\nport_e1_splitter={port_e1_splitter}\nsplitter={splitter}\nstraight={straight}\nwith_splitter={with_splitter}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_1000', center=(81.10000000000001, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_1000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def mzi_no(add_optical_ports_arms:bool=False, auto_rename_ports:bool=True, bend:str=bend_euler_no, cross_section:str=strip_no, delta_length:float=10, length_x:float=0.1, length_y:float=2, min_length:float=0.01, mirror_bot:bool=False, nbends:int=2, port1:str=o1, port2:str=o2, port_e0_combiner:str=o3, port_e0_splitter:str=o3, port_e1_combiner:str=o2, port_e1_splitter:str=o2, splitter:str=mmi1x2_no, straight:str=straight_no, with_splitter:bool=True)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      add_optical_ports_arms: None (min: 0, max: 0, None).
      auto_rename_ports: None (min: 0, max: 0, None).
      bend: None (min: 0, max: 0, None).
      cross_section: None (min: 0, max: 0, None).
      delta_length: None (min: 0, max: 0, None).
      length_x: None (min: 0, max: 0, None).
      length_y: None (min: 0, max: 0, None).
      min_length: None (min: 0, max: 0, None).
      mirror_bot: None (min: 0, max: 0, None).
      nbends: None (min: 0, max: 0, None).
      port1: None (min: 0, max: 0, None).
      port2: None (min: 0, max: 0, None).
      port_e0_combiner: None (min: 0, max: 0, None).
      port_e0_splitter: None (min: 0, max: 0, None).
      port_e1_combiner: None (min: 0, max: 0, None).
      port_e1_splitter: None (min: 0, max: 0, None).
      splitter: None (min: 0, max: 0, None).
      straight: None (min: 0, max: 0, None).
      with_splitter: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.mzi_no()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-13.0, -31.075], [84.10000000000001, -31.075], [84.10000000000001, 26.075], [-13.0, 26.075]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'mzi_no:add_optical_ports_arms={add_optical_ports_arms},auto_rename_ports={auto_rename_ports},bend={bend},cross_section={cross_section},delta_length={delta_length},length_x={length_x},length_y={length_y},min_length={min_length},mirror_bot={mirror_bot},nbends={nbends},port1={port1},port2={port2},port_e0_combiner={port_e0_combiner},port_e0_splitter={port_e0_splitter},port_e1_combiner={port_e1_combiner},port_e1_splitter={port_e1_splitter},splitter={splitter},straight={straight},with_splitter={with_splitter}'
    c.add_label(text=f'Parameters:\nadd_optical_ports_arms={add_optical_ports_arms}\nauto_rename_ports={auto_rename_ports}\nbend={bend}\ncross_section={cross_section}\ndelta_length={delta_length}\nlength_x={length_x}\nlength_y={length_y}\nmin_length={min_length}\nmirror_bot={mirror_bot}\nnbends={nbends}\nport1={port1}\nport2={port2}\nport_e0_combiner={port_e0_combiner}\nport_e0_splitter={port_e0_splitter}\nport_e1_combiner={port_e1_combiner}\nport_e1_splitter={port_e1_splitter}\nsplitter={splitter}\nstraight={straight}\nwith_splitter={with_splitter}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_900', center=(-10.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_900'
    c.add_port(name='o2', cross_section='7a93073b_900', center=(81.10000000000001, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_900'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def straight_nc(cross_section:str=strip_nc)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.straight_nc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.5], [13.0, -3.5], [13.0, 3.5], [-3.0, 3.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'straight_nc:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_1000', center=(10.0, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_1000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def straight_no(cross_section:str=strip_no)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.straight_no()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.45], [13.0, -3.45], [13.0, 3.45], [-3.0, 3.45]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'straight_no:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_900', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_900'
    c.add_port(name='o2', cross_section='7a93073b_900', center=(10.0, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_900'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def straight_sc(cross_section:str=strip_nc)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.straight_sc()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.5], [13.0, -3.5], [13.0, 3.5], [-3.0, 3.5]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'straight_sc:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='7a93073b_1000', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '7a93073b_1000'
    c.add_port(name='o2', cross_section='7a93073b_1000', center=(10.0, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '7a93073b_1000'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c

@gf.cell
def straight_so(cross_section:str=strip_so)->gf.Component:
    """Canvas where you add polygons, instances and ports.

    Args:
      cross_section: None (min: 0, max: 0, None).
    
    .. plot::
      :include-source:

      from pdk import cells

      c = cells.straight_so()
      c.draw_ports()
      c.plot()
    """
    c = gf.Component()
    c.add_polygon([[-3.0, -3.2], [13.0, -3.2], [13.0, 3.2], [-3.0, 3.2]], layer=layer_bbox)
    xc = c.x
    yc = c.y
    name = f'straight_so:cross_section={cross_section}'
    c.add_label(text=f'Parameters:\ncross_section={cross_section}', position=(0,0), layer=layer_label)
    c.add_port(name='o1', cross_section='71c66a08_400', center=(0.0, 0.0), orientation=180.0, port_type='optical')
    c.ports['o1'].info['cross_section'] = '71c66a08_400'
    c.add_port(name='o2', cross_section='71c66a08_400', center=(10.0, 0.0), orientation=0.0, port_type='optical')
    c.ports['o2'].info['cross_section'] = '71c66a08_400'

    c.name = name
    if layer_pin:
        add_pins(c, layer=layer_pin)
    return c



if __name__ == "__main__":
    c = straight_so()
    c.show()

Build your own PDK#

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 gdsfactory.technology import LayerMap


class LayerMap(LayerMap):
    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