Multi - KCLayout / PDK¶
You can also use multiple KCLayout objects as PDKs or Libraries of KCells and parametric KCell-Functions
Use multiple KCLayout objects as PDKs/Libraries¶
KCLayouts can act as PDKs. They can be seamlessly instantiated into each other
In [1]:
Copied!
import kfactory as kf
class LayerInfos(kf.LayerInfos):
WG: kf.kdb.LayerInfo = kf.kdb.LayerInfo(1,0)
WGEX: kf.kdb.LayerInfo = kf.kdb.LayerInfo(2,0) # WG Exclude
CLAD: kf.kdb.LayerInfo = kf.kdb.LayerInfo(4,0) # cladding
FLOORPLAN: kf.kdb.LayerInfo = kf.kdb.LayerInfo(10,0)
# Make the layout object aware of the new layers:
LAYER = LayerInfos()
kf.kcl.infos = LAYER
kcl_default = kf.kcl
import kfactory as kf
class LayerInfos(kf.LayerInfos):
WG: kf.kdb.LayerInfo = kf.kdb.LayerInfo(1,0)
WGEX: kf.kdb.LayerInfo = kf.kdb.LayerInfo(2,0) # WG Exclude
CLAD: kf.kdb.LayerInfo = kf.kdb.LayerInfo(4,0) # cladding
FLOORPLAN: kf.kdb.LayerInfo = kf.kdb.LayerInfo(10,0)
# Make the layout object aware of the new layers:
LAYER = LayerInfos()
kf.kcl.infos = LAYER
kcl_default = kf.kcl
Empty default KCLayout
In [2]:
Copied!
kcl_default.kcells
kcl_default.kcells
Out[2]:
{}
In [3]:
Copied!
# Create a default straight waveguide in the default KCLayout with dbu==0.001 (1nm grid)
s_default = kf.cells.straight.straight(
width=1, length=10, layer=LAYER.WG
)
# Create a default straight waveguide in the default KCLayout with dbu==0.001 (1nm grid)
s_default = kf.cells.straight.straight(
width=1, length=10, layer=LAYER.WG
)
In [4]:
Copied!
# There is now a a KCell in the KCLayout
kcl_default.kcells
# There is now a a KCell in the KCLayout
kcl_default.kcells
Out[4]:
{0: straight_W1000_L10000_LWG_ENone: ports ['o1', 'o2'], 0 instances}
In [5]:
Copied!
# Control the dbu is still 1nm
kcl_default.dbu
# Control the dbu is still 1nm
kcl_default.dbu
Out[5]:
0.001
In [6]:
Copied!
# Create a new KCLayout to simulate pdk (could be package with e.g. `from test_pdk import kcl as pdk` or similar)
kcl2 = kf.KCLayout("TEST_PDK", infos=LayerInfos)
# Set the dbu to 0.005 (5nm)
kcl2.dbu = 0.005
kcl2.layout.dbu
# Create a new KCLayout to simulate pdk (could be package with e.g. `from test_pdk import kcl as pdk` or similar)
kcl2 = kf.KCLayout("TEST_PDK", infos=LayerInfos)
# Set the dbu to 0.005 (5nm)
kcl2.dbu = 0.005
kcl2.layout.dbu
Out[6]:
0.005
In [7]:
Copied!
# Since it's a new KCLayout, it's empty
kcl2
# Since it's a new KCLayout, it's empty
kcl2
Out[7]:
KCLayout(name='TEST_PDK', layout=<klayout.dbcore.Layout object at 0x7f3e0c0ee990>, layer_enclosures=LayerEnclosureModel(root={}), cross_sections={}, enclosure=KCellEnclosure(enclosures=LayerEnclosureCollection(enclosures=[])), library=<klayout.dbcore.Library object at 0x7f3e0c0ee530>, factories={}, virtual_factories={}, kcells={}, layers=<aenum 'LAYER'>, infos=LayerInfos(WG=WG (1/0), WGEX=WGEX (2/0), CLAD=CLAD (4/0), FLOORPLAN=FLOORPLAN (10/0)), layer_stack=LayerStack(layers={}), netlist_layer_mapping={}, sparameters_path=None, interconnect_cml_path=None, constants=Constants(), rename_function=<function rename_clockwise_multi at 0x7f3e247c6e80>, info=Info(), settings=KCellSettings(version='0.21.7', klayout_version='0.29.8', meta_format='v3'), future_cell_name=None, decorators=<kfactory.decorators.Decorators object at 0x7f3e1e915fd0>)
In [8]:
Copied!
# Create an parametric KCell-Function for straights on the new pdk
sf2 = kf.factories.straight.straight_dbu_factory(kcl=kcl2)
# Create an parametric KCell-Function for straights on the new pdk
sf2 = kf.factories.straight.straight_dbu_factory(kcl=kcl2)
In [9]:
Copied!
# Make an instance with
s2 = kcl2.factories["straight"](length=10000, width=200, layer=LAYER.WG)
s2.settings
# Make an instance with
s2 = kcl2.factories["straight"](length=10000, width=200, layer=LAYER.WG)
s2.settings
Out[9]:
KCellSettings(width=200, length=10000, layer=WG (1/0), enclosure=None)
In [10]:
Copied!
# The default kcl's straight uses 1nm grid and is therefore 1000dbu (1um) high and 10000dbu (10um) wide
print(f"{s_default.bbox().height()=}")
print(f"{s_default.dbbox().height()=}")
print(f"{s_default.bbox().width()=}")
print(f"{s_default.dbbox().width()=}")
# The test pdk uses a 5nm grid, so it will be 200dbu (1um) high and 10000dbu (50um) wide
print(f"{s2.bbox().height()=}")
print(f"{s2.dbbox().height()=}")
print(f"{s2.bbox().width()=}")
print(f"{s2.dbbox().width()=}")
# The default kcl's straight uses 1nm grid and is therefore 1000dbu (1um) high and 10000dbu (10um) wide
print(f"{s_default.bbox().height()=}")
print(f"{s_default.dbbox().height()=}")
print(f"{s_default.bbox().width()=}")
print(f"{s_default.dbbox().width()=}")
# The test pdk uses a 5nm grid, so it will be 200dbu (1um) high and 10000dbu (50um) wide
print(f"{s2.bbox().height()=}")
print(f"{s2.dbbox().height()=}")
print(f"{s2.bbox().width()=}")
print(f"{s2.dbbox().width()=}")
s_default.bbox().height()=1000 s_default.dbbox().height()=1.0 s_default.bbox().width()=10000 s_default.dbbox().width()=10.0 s2.bbox().height()=200 s2.dbbox().height()=1.0 s2.bbox().width()=10000 s2.dbbox().width()=50.0
In [11]:
Copied!
# The ports of the default kcl also have different width dbu dimensions, but are the same in um
s_default.ports.print()
s2.ports.print()
# But in um they are the same
s_default.ports.print(unit="um")
s2.ports.print(unit="um")
# The ports of the default kcl also have different width dbu dimensions, but are the same in um
s_default.ports.print()
s2.ports.print()
# But in um they are the same
s_default.ports.print(unit="um")
s2.ports.print(unit="um")
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━┓ ┃ Name ┃ Width ┃ Layer ┃ X ┃ Y ┃ Angle ┃ Mirror ┃ Info ┃ ┡━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━┩ │ o1 │ 1_000 │ WG (1/0) │ 0 │ 0 │ 2 │ False │ {} │ ├──────┼───────┼──────────┼────────┼───┼───────┼────────┼──────┤ │ o2 │ 1_000 │ WG (1/0) │ 10_000 │ 0 │ 0 │ False │ {} │ └──────┴───────┴──────────┴────────┴───┴───────┴────────┴──────┘
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━┓ ┃ Name ┃ Width ┃ Layer ┃ X ┃ Y ┃ Angle ┃ Mirror ┃ Info ┃ ┡━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━┩ │ o1 │ 200 │ WG (1/0) │ 0 │ 0 │ 2 │ False │ {} │ ├──────┼───────┼──────────┼────────┼───┼───────┼────────┼──────┤ │ o2 │ 200 │ WG (1/0) │ 10_000 │ 0 │ 0 │ False │ {} │ └──────┴───────┴──────────┴────────┴───┴───────┴────────┴──────┘
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━┓ ┃ Name ┃ Width ┃ Layer ┃ X ┃ Y ┃ Angle ┃ Mirror ┃ Info ┃ ┡━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━┩ │ o1 │ 1.0 │ WG (1/0) │ 0.0 │ 0.0 │ 180.0 │ False │ {} │ ├──────┼───────┼──────────┼──────┼─────┼───────┼────────┼──────┤ │ o2 │ 1.0 │ WG (1/0) │ 10.0 │ 0.0 │ 0.0 │ False │ {} │ └──────┴───────┴──────────┴──────┴─────┴───────┴────────┴──────┘
┏━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━━━┳━━━━━━┓ ┃ Name ┃ Width ┃ Layer ┃ X ┃ Y ┃ Angle ┃ Mirror ┃ Info ┃ ┡━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━━━╇━━━━━━┩ │ o1 │ 1.0 │ WG (1/0) │ 0.0 │ 0.0 │ 180.0 │ False │ {} │ ├──────┼───────┼──────────┼──────┼─────┼───────┼────────┼──────┤ │ o2 │ 1.0 │ WG (1/0) │ 50.0 │ 0.0 │ 0.0 │ False │ {} │ └──────┴───────┴──────────┴──────┴─────┴───────┴────────┴──────┘
In [12]:
Copied!
# Both can be instantiated into the same KCell
c = kcl_default.kcell()
si_d = c << s_default
si_2 = c << s2
# Both can be instantiated into the same KCell
c = kcl_default.kcell()
si_d = c << s_default
si_2 = c << s2
In [13]:
Copied!
si_2.connect("o1", si_d, "o2")
si_2.connect("o1", si_d, "o2")
In [14]:
Copied!
c
c