Gdsfactory v8 Upgrade#

Gdsfactory v8, based on KFactory, offers enhanced routing functions and additional features from KLayout, including DRC, dummy fill, and connectivity checks.

For those still using gdsfactory v7, it is hosted in gdsfactory/gdsfactory7, along with the documentation.

Benefits of Migrating:#

  • Integrated Data: Ports, information, and settings are stored within the GDS file, eliminating the need for separate files.

  • Improved Booleans: Booleans are more robust with integer-based polygons, removing slivers of less than 1nm.

  • Enhanced Features: More robust booleans, DRC, LVS, and connectivity checks.

  • Active Maintenance: KLayout is more actively maintained with frequent updates than gdstk.

  • Advanced Routing Algorithms: Better routing algorithms for efficient design. Thanks to Kfactory.

  • Grid Alignment: Ports and polygons snap to grid by default, reducing the likelihood of 1nm gaps.

Drawbacks of Migrating:#

  • Potential Errors: As with any code changes, undesired errors may be introduced. It is recommended to have regression tests for all your components before you migrate.

  • Non-Manhattan Placement: Slightly more difficult to define non-manhattan references, routes, or port. Manhattan Components (at 0, 90, 180 and 270) work the same way.

  • Incomplete Functionality: Some features, such as route_path_length_match, are not yet implemented.

Major Differences:#

  • Coordinate System: xmin, xmax, ymin, and ymax are deprecated and will change to Database Units (1nm by default) in gdsfactory9. To set/get them in um, use d (decimal) e.g., Instance.dxmin which is in float.

  • LayerMap: Now an Enum of integers.

  • Routing Functions: New functions do not require starting ports to be in the same orientation and monitor for self-intersections. get_route is now route_single, and get_bundle is now route_bundle.

  • Grid Snapping: All polygon points snap to grid, mitigating 1nm gaps.

Minor Differences:#

  • Replace from gdsfactory.cell import cell with from gdsfactory import cell.

How to Migrate:#

We provide a migration script to assist with migrating your code. Ensure to verify any automatic migrations, and ideally, write regression tests for your code before migrating to ensure accuracy.

gf migrate -i --migration 7to8 <input_folder/file> 
# or 
gf migrate --inplace --migration 7to8 <input_folder/file>

Use dxmin, dcenter, dmove, dsize_info#

This is the biggest difference. By default xmin, ymin, move and size_info will change from um (Decimals) to Database units in the next major gdsfactory release.

import gdsfactory as gf

c = gf.Component()
ref = c << gf.components.bend_euler(radius=5)
c
../_images/ed6f6b8a701936e5e4821c7dcb892346d2faf6009cb3b330400864ce4e855360.png
print(ref.dxmax)
5.25

As you can see you can still use ref.xmax but you will get a deprecation warning.

print(ref.xmax)
5.25
/tmp/ipykernel_2750/398276457.py:1: UserWarning: Getting `bend_euler_R5_A90_P0p5__c7bd5c45_0_0.xmax` in um is deprecated and will change to DataBaseUnits in gdsfactory9. Please use `bend_euler_R5_A90_P0p5__c7bd5c45_0_0.dxmax` instead.
  print(ref.xmax)
print(ref.dsize_info.width)
5.25

Use instance.drotate#

For setting arbitrary rotation angles you can use Instance.drotate(30) to rotate 30deg as an example.

c = gf.Component()
ref = c << gf.components.bend_euler(radius=5)
ref.drotate(45)
c
../_images/e597e454bef12d1dc08aa332aaa49cad8db8d11fa9033cb65187f3c0eea92097.png

Use dmirror#

For mirror with a float number use dmirror instead.

Also notice that instead of a tuple of (x, y) for the mirror points (p1 and p2), you need to use gf.kdb.DPoint.

c = gf.Component()
ref = c << gf.c.mmi1x2()
ref.dmirror(p1=gf.kdb.DPoint(0, 0), p2=gf.kdb.DPoint(0, 1))
c
../_images/3f27580bc9c6b493e2701490d702b3bd6041509089310bb574f73fb8e446b342.png

LayerMap#

In v7 or below, a LayerMap needs to be called


from gdsfactory.technology import LayerMap

class LayerMapFab(LayerMap):
    WG = (1, 0)

LAYER = LayerMapFab()

However in v8 it has a different type and does not need to be called


from gdsfactory.technology import LayerMap

class LayerMapFab(LayerMap):
    WG = (1, 0)

LAYER = LayerMapFab

See below:

from gdsfactory.technology import LayerMap


class LayerMapFab(LayerMap):
    WG = (1, 0)


LAYER = LayerMapFab
type(LAYER)
aenum._enum.EnumType
LAYER.WG
<LayerMapFab.WG: 1>
tuple(LAYER.WG)
(1, 0)
str(LAYER.WG)
'WG'

Component.bbox is now a function#

Both Components and Instances have a bbox and dbbox that are now functions.

c = gf.c.straight(length=10)
c.bbox()
(0,-250;10000,250)
dir(c.dbbox())
['__add__',
 '__and__',
 '__class__',
 '__copy__',
 '__deepcopy__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gsi_id__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__mul__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__rmul__',
 '__setattr__',
 '__setstate__',
 '__sizeof__',
 '__str__',
 '__sub__',
 '__subclasshook__',
 '__weakref__',
 '_const_cast',
 '_create',
 '_destroy',
 '_destroyed',
 '_is_const_object',
 '_manage',
 '_to_const_object',
 '_unmanage',
 'area',
 'assign',
 'bbox',
 'bottom',
 'center',
 'contains',
 'create',
 'destroy',
 'destroyed',
 'dup',
 'empty',
 'enlarge',
 'enlarged',
 'from_ibox',
 'from_s',
 'hash',
 'height',
 'inside',
 'is_const_object',
 'is_point',
 'left',
 'move',
 'moved',
 'new',
 'overlaps',
 'p1',
 'p2',
 'perimeter',
 'right',
 'to_itype',
 'to_s',
 'top',
 'touches',
 'transformed',
 'width',
 'world']
c.dbbox().right
10.0

For the old bbox behavior you can use c.bbox_np which returns the bbox as a numpy array.

c.bbox_np()
array([[ 0.  , -0.25],
       [10.  ,  0.25]])

Component.get_polygons()#

Component.get_polygons() returns all the Polygons and can be slow.

Component.get_polygons_points() is the equivalent to Component.get_polygons() in gdsfactory7 where we return the polygon edges.

import gdsfactory as gf

c = gf.pack([gf.components.seal_ring_segmented()] * 200)[0]
c
../_images/0f6bcbea74b13afec2f9efde4214c6c5ff3859192e28fdb5b8f174bb4cd12680.png
%time
p = c.get_polygons_points()
CPU times: user 7 μs, sys: 4 μs, total: 11 μs
Wall time: 6.68 μs

If you only care about the polygons from one layer you can also only extract those.

%time
p = c.get_polygons_points(layers=("M3",))
CPU times: user 2 μs, sys: 1e+03 ns, total: 3 μs
Wall time: 5.25 μs

ref.get_ports_list#

Reference does not have get_ports_list. You can use

  • ref.ports to get all ports

  • ref.ports.filter(…) to filter all ports with angle, port_type, orientation …

  • gf.port.get_ports_list(b, …) for backwards compatibility.

import gdsfactory as gf

c = gf.Component()
bend = gf.components.bend_euler()
b = c.add_ref(bend)

b.ports.filter(orientation=90)
[Port(name: o2, dwidth: 0.5, trans: r90 *1 10,10, layer: 1, port_type: optical)]
gf.port.get_ports_list(b, orientation=90)
[Port(name: o2, dwidth: 0.5, trans: r90 *1 10,10, layer: 1, port_type: optical)]

Routing functions#

Routing functions NO longer return the route Instances but they place the instances in a Component, so you have to pass a Component as the first argument.

c = gf.Component()
w = gf.components.straight(cross_section="rib")
top = c << w
bot = c << w
bot.dmove((0, -2))

p0 = top.ports["o2"]
p1 = bot.ports["o2"]

r = gf.routing.route_single(
    c,
    p0,
    p1,
    cross_section="rib",
)
c
../_images/86f57485ade197c2c0100880f773f67b8e94918f50d351bc4cb2c170ba6cc18e.png

🚀 The new routing functions allow the starting ports ports1 to have different orientations.

The end ports ports2 still require to have the same orientation.

c = gf.Component()
top = c << gf.components.nxn(north=8, south=0, east=0, west=0)
bot = c << gf.components.nxn(north=2, south=2, east=2, west=2, xsize=10, ysize=10)
top.dmovey(100)

routes = gf.routing.route_bundle(
    c,
    ports1=bot.ports,
    ports2=top.ports,
    radius=5,
    sort_ports=True,
    cross_section="strip",
)
c
../_images/21e6f520ddd489af34e83786b4441d8e4c80d1a4b129447e2d3b5cf063cc67e0.png

Built in connectivity checks#

gdsfactory8 includes connectivity checks to ensure things are properly connected. no

This can help you find any:

  • Gaps between ports.

  • Width mismatches.

  • Unconnected ports.

c = gf.Component()
s = c << gf.components.straight(width=1)

b1 = c << gf.components.bend_euler()
b1.connect("o1", s["o2"], allow_width_mismatch=True)

b2 = c << gf.components.bend_euler(radius=5)
b2.connect("o1", s["o1"], allow_width_mismatch=True)

gc = gf.components.grating_coupler_elliptical_te()
gc1 = c << gc
gc2 = c << gc

gc1.connect("o1", b1.ports["o2"])
gc2.connect("o1", b2.ports["o2"])

c.plot()
../_images/e4130a574b4494fda798356b1c51bbbe9ad9e3fd9fe642e5117e540918beab82.png
lyrdb = c.connectivity_check(port_types=("optical", "electrical"))
filepath = gf.config.home / "errors.lyrdb"
lyrdb.save(filepath)
gf.show(c, lyrdb=filepath)