Skip to content

Download notebook (.ipynb)

DBU vs µm — coordinate systems

kfactory inherits two parallel coordinate systems from KLayout:

System Unit Type KLayout prefix
DBU — database units 1 nm (by default) int no prefix / I
µm — micrometres 1 µm float D

Every geometry class exists in both flavours:

DBU (integer) µm (float)
kdb.Box kdb.DBox
kdb.Polygon kdb.DPolygon
kdb.Point kdb.DPoint
kdb.Vector kdb.DVector
kdb.Trans kdb.DTrans
kdb.Edge kdb.DEdge

The relationship is fixed: 1 µm = 1 000 DBU (because kf.kcl.dbu = 0.001 µm). This page shows how to use both, convert between them, and choose the right one.

import kfactory as kf


class LAYER(kf.LayerInfos):
    WG: kf.kdb.LayerInfo = kf.kdb.LayerInfo(1, 0)
    WGEX: kf.kdb.LayerInfo = kf.kdb.LayerInfo(2, 0)
    SLAB: kf.kdb.LayerInfo = kf.kdb.LayerInfo(3, 0)


L = LAYER()
kf.kcl.infos = L

The dbu constant

kf.kcl.dbu is the size of one DBU expressed in µm. The default is 0.001, meaning 1 DBU = 0.001 µm = 1 nm.

print(f"dbu = {kf.kcl.dbu} µm/DBU")
print(f"1 µm = {kf.kcl.to_dbu(1.0)} DBU")
print(f"1000 DBU = {kf.kcl.to_um(1000)} µm")
dbu = 0.001 µm/DBU
1 µm = 1000 DBU
1000 DBU = 1.0 µm

Geometry objects

Box vs DBox

# DBU: integer coordinates, 1 unit = 1 nm
box_dbu = kf.kdb.Box(2000, 1000)  # 2 µm × 1 µm
print(f"Box (DBU):  {box_dbu}")

# µm: float coordinates
box_um = kf.kdb.DBox(2.0, 1.0)  # same size
print(f"DBox (µm): {box_um}")
Box (DBU):  (-1000,-500;1000,500)
DBox (µm): (-1,-0.5;1,0.5)

Both represent the same physical area. KLayout centres boxes at the origin by default, so the corners are (±half_width, ±half_height).

Converting between DBU and µm

# DBU → µm
box_um_from_dbu = box_dbu.to_dtype(kf.kcl.dbu)
print(f"Box → DBox: {box_um_from_dbu}")

# µm → DBU
box_dbu_from_um = box_um.to_itype(kf.kcl.dbu)
print(f"DBox → Box: {box_dbu_from_um}")
Box → DBox: (-1,-0.5;1,0.5)
DBox → Box: (-1000,-500;1000,500)

Trans vs DTrans

Trans / DTrans encode a rotation + optional mirror + translation. The displacement part uses DBU integers / µm floats respectively.

# DBU: displacement in integer DBU
t_dbu = kf.kdb.Trans(0, False, 5000, 0)  # 5 µm to the right
print(f"Trans (DBU):  {t_dbu}")

# µm: displacement in float µm
t_um = kf.kdb.DTrans(0, False, 5.0, 0.0)
print(f"DTrans (µm): {t_um}")

# Convert
print(f"Trans → DTrans: {t_dbu.to_dtype(kf.kcl.dbu)}")
print(f"DTrans → Trans: {t_um.to_itype(kf.kcl.dbu)}")
Trans (DBU):  r0 5000,0
DTrans (µm): r0 5,0
Trans → DTrans: r0 5,0
DTrans → Trans: r0 5000,0

Cell bounding boxes

KCell exposes bounding boxes in both systems:

Method Returns
cell.bbox() kdb.Box in DBU
cell.dbbox() kdb.DBox in µm
li = kf.kcl.find_layer(L.WG)

example = kf.KCell("example_bbox")
example.shapes(li).insert(kf.kdb.Box(4000, 2000))  # 4 µm × 2 µm

print(f"bbox  (DBU): {example.bbox()}")  # integer coords
print(f"dbbox (µm):  {example.dbbox()}")  # float coords
bbox  (DBU): (-2000,-1000;2000,1000)
dbbox (µm):  (-2,-1;2,1)

Ports: width vs dwidth

Port width is stored internally in DBU (int). The .dwidth property converts on-the-fly to µm:

p = kf.Port(
    name="o1",
    trans=kf.kdb.Trans(0, False, 2000, 0),
    width=500,  # 500 DBU = 0.5 µm
    layer=li,
    port_type="optical",
)
print(f"Port width  (DBU): {p.width}")  # 500
print(f"Port dwidth (µm):  {p.dwidth}")  # 0.5
Port width  (DBU): 500
Port dwidth (µm):  0.5

Shapes: inserting DBU and µm geometry

cell.shapes(layer_index).insert(...) accepts both DBU and µm objects directly — KLayout converts D-prefixed shapes automatically using the layout's dbu.

mixed = kf.KCell("shapes_mixed")

# Insert a DBU box
mixed.shapes(li).insert(kf.kdb.Box(3000, 500))

# Insert a µm DPolygon — automatically converted
dpoly = kf.kdb.DPolygon(kf.kdb.DBox(1.0, 0.25))
mixed.shapes(li).insert(dpoly)

print(f"Shapes count: {mixed.shapes(li).size()}")
print(f"Total bbox (DBU): {mixed.bbox()}")
print(f"Total dbbox (µm): {mixed.dbbox()}")
Shapes count: 2
Total bbox (DBU): (-1500,-250;1500,250)
Total dbbox (µm): (-1.5,-0.25;1.5,0.25)

Choosing the right system

Use DBU (int) when: - Writing internal cell logic — integer arithmetic avoids floating-point drift. - Defining port width and trans — the API stores these as integers. - Doing boolean operations (Region) — Region always works in DBU.

Use µm (float) when: - Accepting user-facing parameters in a factory function — width=0.5 reads more naturally than width=500. - Reading back coordinates for display or export. - Constructing paths or references from physical dimensions.

A common idiom is to accept µm at the function boundary and convert immediately:

@kf.cell
def waveguide(width: float = 0.5, length: float = 10.0) -> kf.KCell:
    """Simple waveguide with µm parameters converted to DBU internally."""
    c = kf.KCell()
    w = kf.kcl.to_dbu(width)  # → int DBU
    ll = kf.kcl.to_dbu(length)  # → int DBU

    c.shapes(li).insert(kf.kdb.Box(ll, w))
    c.add_port(
        port=kf.Port(
            name="o1",
            trans=kf.kdb.Trans(2, False, -ll // 2, 0),
            width=w,
            layer=li,
            port_type="optical",
        )
    )
    c.add_port(
        port=kf.Port(
            name="o2",
            trans=kf.kdb.Trans(0, False, ll // 2, 0),
            width=w,
            layer=li,
            port_type="optical",
        )
    )
    return c


wg = waveguide(width=0.5, length=10.0)
print(f"bbox  (DBU): {wg.bbox()}")
print(f"dbbox (µm):  {wg.dbbox()}")
print(f"Port o1 dwidth: {wg.ports['o1'].dwidth} µm")
bbox  (DBU): (-5000,-250;5000,250)
dbbox (µm):  (-5,-0.25;5,0.25)
Port o1 dwidth: 0.5 µm

Quick-reference table

Task DBU expression µm expression
Rectangle kdb.Box(w, h) kdb.DBox(w, h)
Point kdb.Point(x, y) kdb.DPoint(x, y)
Translation kdb.Trans(rot, mir, x, y) kdb.DTrans(rot, mir, x, y)
Cell bbox cell.bbox() cell.dbbox()
Port width port.width port.dwidth
Convert DBU→µm kf.kcl.to_um(n)
Convert µm→DBU kf.kcl.to_dbu(x)
Shape convert dshape.to_itype(kf.kcl.dbu) shape.to_dtype(kf.kcl.dbu)

See Also

Topic Where
KCell and DKCell side-by-side Core Concepts: KCell
Port width and position in DBU Core Concepts: Ports
Factory functions and their unit conventions Components: Factory Functions
FAQ — when to use DBU vs µm How-To: FAQ