run_script Cookbook¶
run_script executes Python in-process with these names injected: db
(klayout.db), klayout, session (the current LayoutSession, or None), and
LayoutSession. It is the escape hatch for anything the high-level tools don't cover —
boolean layer ops, sizing, hierarchy, connectivity, custom DRC. A script may rebind
session to a new LayoutSession to make it active. stdout is returned.
Warning
run_script runs arbitrary Python locally, in-process. Only run scripts you trust.
All recipes below assume an active layout (new_layout or load_gds first) and use this
helper to pull a flattened Region
for a layer:
ly, top = session.layout, session.top
dbu = ly.dbu
def R(layer, datatype=0):
return db.Region(top.begin_shapes_rec(session.layer(layer, datatype)))
Boolean operations¶
Derive layers with & (AND), | (OR), - (NOT), ^ (XOR):
od, poly = R(3), R(6)
gate = od & poly # transistor gate = OD AND POLY
sd = od - poly # source/drain = OD NOT POLY
top.shapes(session.layer(20, 0)).insert(gate)
print("gate area um^2:", gate.area() * dbu * dbu)
Grow / shrink (sizing)¶
Region.sized(d) oversizes by d database units (negative shrinks). Useful for
enclosure and spacing checks:
grown = R(9).sized(round(0.1 / dbu)) # oversize M1 by 0.1 um
shrunk = R(3).sized(round(-0.05 / dbu)) # undersize OD by 0.05 um
top.shapes(session.layer(21, 0)).insert(grown)
Merge / flatten a layer¶
reg = R(9)
reg.merge() # union overlapping shapes
top.shapes(session.layer(9, 0)).clear() # replace the layer with the merged result
top.shapes(session.layer(9, 0)).insert(reg)
Cell hierarchy and arrays¶
Build a sub-cell once and instance it as an array (much smaller than flat copies):
tile = ly.create_cell("TILE")
tile.shapes(session.layer(9, 0)).insert(db.DBox(0, 0, 0.5, 0.5))
top.insert(db.DCellInstArray(
tile.cell_index(), db.DTrans(db.DVector(5, 0)),
db.DVector(1, 0), db.DVector(0, 1), 3, 3)) # 3x3 array at 1 um pitch
Custom DRC with violation markers¶
Draw spacing/width violations onto a marker layer so they're visible in the viewer (this is exactly how the Editing & DRC screenshot is made):
mk = session.layer(200, 0)
for ep in R(9).space_check(round(0.2 / dbu)).each(): # M1 spacing < 0.2 um
top.shapes(mk).insert(ep.bbox().enlarged(round(0.05 / dbu)))
Region also offers width_check, separation_check, enclosing_check, overlap_check
— the same primitives the drc_check tool wraps.
Copy or move shapes between layers¶
dst = top.shapes(session.layer(30, 0))
for sh in top.shapes(session.layer(9, 0)).each():
dst.insert(sh) # copy M1 -> 30/0
# top.shapes(session.layer(9, 0)).clear() # ...uncomment to move instead
Measure area and counts¶
for layer, dt in [(3, 0), (6, 0), (9, 0)]:
reg = R(layer, dt); reg.merge()
print(f"{layer}/{dt}: {reg.count()} polys, {reg.area() * dbu * dbu:.3f} um^2")
Start a fresh layout from a script¶
Rebind session to make a new layout active, then keep using the high-level tools:
session = LayoutSession.create("CHIP", 0.001)
session.top.shapes(session.layer(1, 0)).insert(db.DBox(0, 0, 10, 10))
For connectivity (opens/shorts) and netlist extraction, see KLayout's
LayoutToNetlist.