DRC Fixing Utilities
Design Rule Check (DRC) violations occur when shapes on a layer violate foundry-specified constraints — most commonly:
| Violation type | Description |
|---|---|
| Min space | Two polygons on the same layer are closer than the allowed gap |
| Min width | A polygon is narrower than the process can reliably fabricate |
kfactory provides tiled utilities that detect and automatically repair these
violations by merging nearby shapes. All fixers live in kf.utils:
| Function | Strategy | Best for |
|---|---|---|
kf.utils.fix_spacing_tiled |
Space-check + merge | General min-space cleanup |
kf.utils.fix_spacing_minkowski_tiled |
Minkowski dilation/erosion | Smoother results on complex geometry |
Both work by splitting the cell into rectangular tiles, processing each tile in
parallel, and collecting the corrected geometry into a new kdb.Region.
This keeps memory usage constant regardless of layout size.
Setup
import kfactory as kf
class LAYER(kf.LayerInfos):
WG: kf.kdb.LayerInfo = kf.kdb.LayerInfo(1, 0)
WG_FIXED: kf.kdb.LayerInfo = kf.kdb.LayerInfo(2, 0)
WG_FIXED_MK: kf.kdb.LayerInfo = kf.kdb.LayerInfo(3, 0)
FLOORPLAN: kf.kdb.LayerInfo = kf.kdb.LayerInfo(10, 0)
L = LAYER()
kf.kcl.infos = L
1 · Minimum-Space Violations
Creating a layout with violations
We place a grid of ellipses that are intentionally too close together. In a real PDK the minimum space rule might be 200 nm (200 dbu at 1 nm/dbu). Here we use 1 µm (1000 dbu) as the minimum spacing so the violation is obvious.
MIN_SPACE_DBU = 1000 # 1 µm minimum space rule
c = kf.KCell(name="drc_demo")
# Place ellipses in a grid — adjacent columns are only 200 nm apart (violates 1 µm rule)
for row in range(4):
for col in range(5):
ellipse = kf.kdb.Polygon.ellipse(kf.kdb.Box(8000, 12000), 64)
# Offset each column by 9000 dbu — less than 8000 + 1000 spacing → violation
c.shapes(c.kcl.layer(L.WG)).insert(
ellipse.transformed(kf.kdb.Trans(col * 9000, row * 14000))
)
c
Applying fix_spacing_tiled
fix_spacing_tiled detects pairs of polygons that are closer than min_space and
merges them. The function returns a kdb.Region with the corrected geometry.
Key parameters:
- min_space — minimum allowed gap in dbu
- metrics — Euclidian (default) or Square; controls how distance is measured
- n_threads — parallelism; None uses kfactory's default (CPU count)
- tile_size — tile dimensions in µm; auto-selected if None
fixed_region = kf.utils.fix_spacing_tiled(
c,
MIN_SPACE_DBU,
L.WG,
metrics=kf.kdb.Metrics.Euclidian,
)
# Insert corrected geometry onto WG_FIXED layer so we can compare
c.shapes(c.kcl.layer(L.WG_FIXED)).insert(fixed_region)
c
What happened: polygons that were too close were merged into a single polygon.
The WG layer (layer 1) still has the original violating shapes; WG_FIXED
(layer 2) contains the DRC-clean result.
Applying fix_spacing_minkowski_tiled
The Minkowski-sum approach dilates each polygon by min_space/2, merges overlapping
shapes, then erodes by the same amount. This produces smoother, more rounded outlines
compared to the space-check approach — useful when device performance is sensitive to
sharp corners.
The optional smooth parameter controls how aggressively corners are simplified
after the fix (value in dbu).
fixed_mk_region = kf.utils.fix_spacing_minkowski_tiled(
c,
MIN_SPACE_DBU,
L.WG,
smooth=20,
)
c.shapes(c.kcl.layer(L.WG_FIXED_MK)).insert(fixed_mk_region)
c
[32m2026-05-12 10:04:29.945[0m | [1mINFO [0m | [36mkfactory.utils.violations[0m:[36mfix_spacing_minkowski_tiled[0m:[36m272[0m - [1mStarting minkowski on drc_demo[0m
2 · Choosing the Right Fixer
| Scenario | Recommended fixer |
|---|---|
| Rectilinear (Manhattan) geometry | fix_spacing_tiled — faster, exact edges |
| Curved / free-form geometry | fix_spacing_minkowski_tiled — smoother result |
| Very large layouts (>10 mm²) | Either; both tile automatically |
3 · Workflow Pattern
A typical post-layout DRC fix workflow:
import kfactory as kf
from kfactory.utils.violations import fix_spacing_tiled
# 1. Load or create your cell
kcl = kf.KCLayout("my_pdk")
cell = kcl["TOP"]
# 2. Run the fixer (returns a Region, does not modify in-place)
clean = kf.utils.fix_spacing_tiled(cell, min_space=200, layer=LAYER.WG)
# 3. Replace the original geometry with the fixed version
li = kcl.layer(LAYER.WG)
cell.clear(li)
cell.shapes(li).insert(clean)
# 4. Write out
kcl.write("fixed.gds")
Note:
fix_spacing_tiledandfix_spacing_minkowski_tiledreturn akdb.Region— they do not modify the input cell in-place. Insert the result where you need it, replacing or supplementing the original layer.
4 · Performance Notes
Both fixers use KLayout's TilingProcessor internally. Tile size is auto-selected
as max(25 × min_space_µm, 250 µm) but can be tuned:
# Larger tiles → fewer tiles, more memory per tile
kf.utils.fix_spacing_tiled(c, 200, LAYER.WG, tile_size=(500, 500))
# Smaller tiles → lower peak memory, more parallelism benefit
kf.utils.fix_spacing_tiled(c, 200, LAYER.WG, tile_size=(50, 50))
Thread count defaults to kf.config.n_threads (≈ logical CPU count).
Override with n_threads=N when running in a resource-constrained environment.
See Also
| Topic | Where |
|---|---|
| Boolean / region operations | Core Concepts: Geometry |
| Tile-based fill | Utilities: Fill |
| Cell-level enclosures | Enclosures: KCell Enclosure |
| Layout regression testing | Utilities: Difftest |