Skip to content

violations

Utilities to fix DRC violations.

🇵🇾func:~fix_spacing_tiled uses 🇵🇾func:~kdb.Region.space_check to detect minimum space violations and then applies a fix.

RegionOperator

Bases: TileOutputReceiver

Region collector. Just getst the tile and inserts it into the target cell.

Source code in src/kfactory/utils/violations.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
class RegionOperator(kdb.TileOutputReceiver):
    """Region collector. Just getst the tile and inserts it into the target cell."""

    def __init__(self) -> None:
        """Initialization."""
        self.region = kdb.Region()

    def put(
        self,
        ix: int,
        iy: int,
        tile: kdb.Box,
        region: kdb.Region,
        dbu: float,
        clip: bool,
    ) -> None:
        """Tiling Processor output call.

        Args:
            ix: x-axis index of tile.
            iy: y_axis index of tile.
            tile: The bounding box of the tile.
            region: The target object of the :py:class:~`klayout.db.TilingProcessor`
            dbu: dbu used by the processor.
            clip: Whether the target was clipped to the tile or not.
        """
        self.region.insert(region)

__init__

__init__() -> None

Initialization.

Source code in src/kfactory/utils/violations.py
440
441
442
def __init__(self) -> None:
    """Initialization."""
    self.region = kdb.Region()

put

put(ix: int, iy: int, tile: kdb.Box, region: kdb.Region, dbu: float, clip: bool) -> None

Tiling Processor output call.

Parameters:

Name Type Description Default
ix int

x-axis index of tile.

required
iy int

y_axis index of tile.

required
tile Box

The bounding box of the tile.

required
region Region

The target object of the 🇵🇾class:~klayout.db.TilingProcessor

required
dbu float

dbu used by the processor.

required
clip bool

Whether the target was clipped to the tile or not.

required
Source code in src/kfactory/utils/violations.py
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
def put(
    self,
    ix: int,
    iy: int,
    tile: kdb.Box,
    region: kdb.Region,
    dbu: float,
    clip: bool,
) -> None:
    """Tiling Processor output call.

    Args:
        ix: x-axis index of tile.
        iy: y_axis index of tile.
        tile: The bounding box of the tile.
        region: The target object of the :py:class:~`klayout.db.TilingProcessor`
        dbu: dbu used by the processor.
        clip: Whether the target was clipped to the tile or not.
    """
    self.region.insert(region)

fix_spacing_minkowski_tiled

fix_spacing_minkowski_tiled(c: KCell, min_space: int, ref: kdb.LayerInfo | kdb.Region, n_threads: int | None = None, tile_size: tuple[float, float] | None = None, overlap: int = 1, smooth: int | None = None) -> kdb.Region

Fix min space issues by using a dilation & erosion with a box.

Parameters:

Name Type Description Default
c KCell

Input cell

required
min_space int

Minimum space rule [dbu]

required
ref LayerInfo | Region

Input layer index or region

required
n_threads int | None

on how many threads to run the check simultaneously

None
tile_size tuple[float, float] | None

tuple determining the size of each sub tile (in um), should be big compared to the violation size

None
overlap int

how many times bigger to make the tile border in relation to the violation size. Smaller than 1 can lead to errors

1
smooth int | None

Apply smoothening (simplifying) at the end if > 0

None

Returns:

Type Description
Region

kdb.Region: Region containing the fixes for the violations

Source code in src/kfactory/utils/violations.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def fix_spacing_minkowski_tiled(
    c: KCell,
    min_space: int,
    ref: kdb.LayerInfo | kdb.Region,
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    overlap: int = 1,
    smooth: int | None = None,
) -> kdb.Region:
    """Fix min space issues by using a dilation & erosion with a box.

    Args:
        c: Input cell
        min_space: Minimum space rule [dbu]
        ref: Input layer index or region
        n_threads: on how many threads to run the check simultaneously
        tile_size: tuple determining the size of each sub tile (in um), should be big
            compared to the violation size
        overlap: how many times bigger to make the tile border in relation to the
            violation size. Smaller than 1 can lead to errors
        smooth: Apply smoothening (simplifying) at the end if > 0

    Returns:
        kdb.Region: Region containing the fixes for the violations
    """
    tp = kdb.TilingProcessor()
    tp.frame = c.dbbox()  # type: ignore[misc]
    tp.dbu = c.kcl.dbu
    tp.threads = n_threads or config.n_threads

    min_tile_size_rec = 10 * min_space * tp.dbu

    if tile_size is None:
        tile_size = (min_tile_size_rec * 2, min_tile_size_rec * 2)

    tp.tile_border(min_space * overlap * tp.dbu, min_space * overlap * tp.dbu)

    tp.tile_size(*tile_size)
    if isinstance(ref, kdb.LayerInfo):
        tp.input("main_layer", c.kcl.layout, c.cell_index(), c.kcl.find_layer(ref))
    else:
        tp.input("main_layer", ref)

    operator = RegionOperator()
    tp.output("target", operator)
    if smooth is None:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_space});"
            f"var shape = Box.new({min_space},{min_space});"
            "var reg = main_layer.minkowski_sum(shape); reg.merge();"
            "reg = tile_reg - (tile_reg - reg).minkowski_sum(shape);"
            "_output(target, reg & _tile, true);"
        )
    else:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_space});"
            f"var shape = Box.new({min_space},{min_space});"
            "var reg = main_layer.minkowski_sum(shape); reg.merge();"
            "reg = tile_reg - (tile_reg - reg).minkowski_sum(shape);"
            f"reg.smooth({smooth});"
            "_output(target, reg & _tile, true);"
        )

    tp.queue(queue_str)
    logger.debug("String queued for {}:  {}", c.name, queue_str)

    c.kcl.start_changes()
    logger.info("Starting minkowski on {}", c.name)
    tp.execute(f"Minkowski {c.name}")
    c.kcl.end_changes()

    return operator.region

fix_spacing_sizing_tiled

fix_spacing_sizing_tiled(c: KCell, min_space: int, layer: kdb.LayerInfo, n_threads: int | None = None, tile_size: tuple[float, float] | None = None, overlap: int = 2) -> kdb.Region

Fix min space issues by using a dilation & erosion.

Parameters:

Name Type Description Default
c KCell

Input cell

required
min_space int

Minimum space rule [dbu]

required
layer LayerInfo

Input layer

required
n_threads int | None

on how many threads to run the check simultaneously

None
tile_size tuple[float, float] | None

tuple determining the size of each sub tile (in um), should be big compared to the violation size

None
overlap int

how many times bigger to make the tile border in relation to the violation size. Smaller than 1 can lead to errors

2

Returns:

Type Description
Region

kdb.Region: Region containing the fixes for the violations

Source code in src/kfactory/utils/violations.py
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def fix_spacing_sizing_tiled(
    c: KCell,
    min_space: int,
    layer: kdb.LayerInfo,
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    overlap: int = 2,
) -> kdb.Region:
    """Fix min space issues by using a dilation & erosion.

    Args:
        c: Input cell
        min_space: Minimum space rule [dbu]
        layer: Input layer
        n_threads: on how many threads to run the check simultaneously
        tile_size: tuple determining the size of each sub tile (in um), should be big
            compared to the violation size
        overlap: how many times bigger to make the tile border in relation to the
            violation size. Smaller than 1 can lead to errors

    Returns:
        kdb.Region: Region containing the fixes for the violations

    """
    tp = kdb.TilingProcessor()
    if tile_size is None:
        size = min_space * 20 * c.kcl.dbu
        tile_size = (size, size)
    li = c.kcl.find_layer(layer)
    tp.frame = c.kcl.to_um(c.bbox(li))  # type: ignore[misc]
    tp.dbu = c.kcl.dbu
    tp.tile_size(*tile_size)  # tile size in um
    tp.tile_border(min_space * overlap * tp.dbu, min_space * overlap * tp.dbu)
    tp.input("reg", c.kcl.layout, c.cell_index(), li)
    tp.threads = n_threads or config.n_threads

    fix_reg = kdb.Region()

    tp.output("fix_reg", fix_reg)

    queue_str = (
        "var tile_reg=reg & (_tile & _frame);"
        + f"reg = tile_reg.sized({min_space}).sized({-min_space});"
        + "_output(fix_reg, reg)"
    )

    tp.queue(queue_str)

    c.kcl.start_changes()
    tp.execute("Min Space Fix")
    c.kcl.end_changes()

    return fix_reg

fix_spacing_tiled

fix_spacing_tiled(c: KCell, min_space: int, layer: kdb.LayerInfo, metrics: kdb.Metrics = kdb.Metrics.Euclidian, ignore_angle: float = 80, size_space_check: int = 5, n_threads: int = 4, tile_size: tuple[float, float] | None = None, overlap: float = 3, smooth_factor: float = 0.05) -> kdb.Region
fix_spacing_tiled(c: KCell, min_space: int, layer: kdb.LayerInfo, metrics: kdb.Metrics = kdb.Metrics.Euclidian, ignore_angle: float = 80, size_space_check: int = 5, n_threads: int = 4, tile_size: tuple[float, float] | None = None, overlap: float = 3, *, smooth_absolute: int) -> kdb.Region
fix_spacing_tiled(c: KCell, min_space: int, layer: kdb.LayerInfo, metrics: kdb.Metrics = kdb.Metrics.Euclidian, ignore_angle: float = 80, size_space_check: int = 5, n_threads: int | None = None, tile_size: tuple[float, float] | None = None, overlap: float = 3, smooth_factor: float = 0.05, smooth_absolute: int | None = None, smooth_keep_hv: bool = True) -> kdb.Region

Fix minimum space violations.

Fix min space issues by running a drc check on the input region and merging it with the affcted polygons.

Parameters:

Name Type Description Default
c KCell

Input cell

required
min_space int

Minimum space rule [dbu]

required
layer LayerInfo

Input layer index

required
metrics Metrics

The metrics to use to determine the violation edges

Euclidian
ignore_angle float

ignore angles greater or equal to this angle

80
size_space_check int

Sizing in dbu of the offending edges towards the polygons

5
n_threads int | None

on how many threads to run the check simultaneously

None
tile_size tuple[float, float] | None

tuple determining the size of each sub tile (in um), should be big compared to the violation size

None
overlap float

how many times bigger to make the tile border in relation to the violation size. Smaller than 1 can lead to errors

3
smooth_factor float

how big to smooth the resulting region in relation to the violation. 1 == the violation size. Set to 0 to disable

0.05
smooth_absolute int | None

If set will overwrite smooth with an an absolute value, not relative to the violation size. If set, this will disable smooth_factor. [dbu]

None
smooth_keep_hv bool

Keep horizontal and vertical vertices when smoothing.

True

Returns:

Name Type Description
fix Region

Region containing the fixes for the violations

Source code in src/kfactory/utils/violations.py
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def fix_spacing_tiled(
    c: KCell,
    min_space: int,
    layer: kdb.LayerInfo,
    metrics: kdb.Metrics = kdb.Metrics.Euclidian,
    ignore_angle: float = 80,
    size_space_check: int = 5,
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    overlap: float = 3,
    smooth_factor: float = 0.05,
    smooth_absolute: int | None = None,
    smooth_keep_hv: bool = True,
) -> kdb.Region:
    """Fix minimum space violations.

    Fix min space issues by running a drc check on the input region and merging
    it with the affcted polygons.

    Args:
        c: Input cell
        min_space: Minimum space rule [dbu]
        layer: Input layer index
        metrics: The metrics to use to determine the violation edges
        ignore_angle: ignore angles greater or equal to this angle
        size_space_check: Sizing in dbu of the offending edges towards the polygons
        n_threads: on how many threads to run the check simultaneously
        tile_size: tuple determining the size of each sub tile (in um), should be big
            compared to the violation size
        overlap: how many times bigger to make the tile border in relation to the
            violation size. Smaller than 1 can lead to errors
        smooth_factor: how big to smooth the resulting region in relation to the
            violation. 1 == the violation size. Set to 0 to disable
        smooth_absolute: If set will overwrite smooth with an an absolute value, not
            relative to the violation size. If set, this will disable smooth_factor.
            [dbu]
        smooth_keep_hv: Keep horizontal and vertical vertices when smoothing.

    Returns:
        fix: Region containing the fixes for the violations

    """
    if tile_size is None:
        min(25 * min_space, 250)
        tile_size = (30 * min_space * c.kcl.dbu, 30 * min_space * c.kcl.dbu)
    li = c.kcl.find_layer(layer)
    tp = kdb.TilingProcessor()
    tp.frame = c.kcl.to_um(c.bbox(li))  # type: ignore[misc]
    tp.dbu = c.kcl.dbu
    tp.tile_size(*tile_size)  # tile size in um
    tp.tile_border(min_space * overlap * tp.dbu, min_space * overlap * tp.dbu)
    tp.input("reg", c.kcl.layout, c.cell_index(), li)
    tp.threads = n_threads or config.n_threads

    fix_reg = RegionOperator()
    tp.output("fix_reg", fix_reg)

    if smooth_factor != 0 or smooth_absolute:
        keep = "true" if smooth_keep_hv else "false"
        smooth = (
            min(int(smooth_factor * min_space), 1)
            if not smooth_absolute
            else smooth_absolute
        )
        queue_str = (
            f"var sc = reg.space_check({min_space},"
            f" false, Metrics.{metrics.to_s()},"
            f" {ignore_angle}); "
            "var edges = sc.edges(); edges.merge(); "
            f"var r_int = (edges.extended(0, 0, 0, {size_space_check}, true)"
            " + sc.polygons()); r_int.merge();"
            " r_int.insert(reg.interacting(sc.polygons())); "
            f"r_int.merge(); r_int.smooth({smooth}, {keep}); "
            f"_output(fix_reg, r_int)"
        )
    else:
        queue_str = (
            f"var sc = reg.space_check({min_space},"
            f" false, Metrics.{metrics.to_s()},"
            f" {ignore_angle});"
            "var edges = sc.edges(); edges.merge();"
            f"var r_int = (edges.extended(0, 0, 0, {size_space_check}, true)"
            " + sc.polygons()); r_int.merge();"
            " r_int.insert(reg.interacting(sc.polygons()));"
            "r_int.merge(); _output(fix_reg, r_int)"
        )

    tp.queue(queue_str)

    c.kcl.start_changes()
    tp.execute("Min Space Fix")
    c.kcl.end_changes()

    return fix_reg.region

fix_width_and_spacing_minkowski_tiled

fix_width_and_spacing_minkowski_tiled(c: KCell, min_space: int, min_width: int, ref: kdb.LayerInfo | kdb.Region, n_threads: int | None = None, tile_size: tuple[float, float] | None = None, overlap: int = 1, smooth: int | None = None) -> kdb.Region

Fix min space and width issues by using a dilation & erosion with a box.

The algorithm will dilate by min_space, erode by min_width + min_space, and finally dilate by min_width

Parameters:

Name Type Description Default
c KCell

Input cell

required
min_space int

Minimum space rule [dbu]

required
min_width int

Minimum width rule [dbu]

required
ref LayerInfo | Region

Input layer index or region

required
n_threads int | None

on how many threads to run the check simultaneously

None
tile_size tuple[float, float] | None

tuple determining the size of each sub tile (in um), should be big compared to the violation size

None
overlap int

how many times bigger to make the tile border in relation to the violation size. Smaller than 1 can lead to errors (overlap*min_space)

1
smooth int | None

Apply smoothening (simplifying) at the end if > 0

None

Returns:

Type Description
Region

kdb.Region: Region containing the fixes for the violations

Source code in src/kfactory/utils/violations.py
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
def fix_width_and_spacing_minkowski_tiled(
    c: KCell,
    min_space: int,
    min_width: int,
    ref: kdb.LayerInfo | kdb.Region,
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    overlap: int = 1,
    smooth: int | None = None,
) -> kdb.Region:
    """Fix min space and width issues by using a dilation & erosion with a box.

    The algorithm will dilate by min_space, erode by min_width + min_space, and
    finally dilate by min_width

    Args:
        c: Input cell
        min_space: Minimum space rule [dbu]
        min_width: Minimum width rule [dbu]
        ref: Input layer index or region
        n_threads: on how many threads to run the check simultaneously
        tile_size: tuple determining the size of each sub tile (in um), should be big
            compared to the violation size
        overlap: how many times bigger to make the tile border in relation to the
            violation size. Smaller than 1 can lead to errors (overlap*min_space)
        smooth: Apply smoothening (simplifying) at the end if > 0

    Returns:
        kdb.Region: Region containing the fixes for the violations
    """
    tp = kdb.TilingProcessor()
    tp.frame = c.dbbox()  # type: ignore[misc]
    tp.dbu = c.kcl.dbu
    tp.threads = n_threads or config.n_threads

    min_tile_size_rec = 10 * min_space * tp.dbu

    if tile_size is None:
        tile_size = (min_tile_size_rec * 2, min_tile_size_rec * 2)

    border = min_space * tp.dbu * overlap
    tp.tile_border(border, border)

    tp.tile_size(*tile_size)
    if isinstance(ref, kdb.LayerInfo):
        tp.input("main_layer", c.kcl.layout, c.cell_index(), c.kcl.find_layer(ref))
    else:
        tp.input("main_layer", ref)

    shrink = min_space + min_width

    operator = RegionOperator()
    tp.output("target", operator)
    if smooth is None:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_space});"
            f"var space_shape = Box.new({min_space},{min_space});"
            f"var shrink_shape = Box.new({shrink},{shrink});"
            f"var width_shape = Box.new({min_width},{min_width});"
            "var reg = main_layer.minkowski_sum(space_shape); reg.merge();"
            "reg = tile_reg - (tile_reg - reg).minkowski_sum(shrink_shape);"
            "reg = reg.minkowski_sum(width_shape);"
            "_output(target, reg & _tile, true);"
        )
    else:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_space});"
            f"var space_shape = Box.new({min_space},{min_space});"
            f"var shrink_shape = Box.new({shrink},{shrink});"
            f"var width_shape = Box.new({min_width},{min_width});"
            "var reg = main_layer.minkowski_sum(space_shape); reg.merge();"
            "reg = tile_reg - (tile_reg - reg).minkowski_sum(shrink_shape);"
            "reg = reg.minkowski_sum(width_shape);"
            f"reg.smooth({smooth});"
            "_output(target, reg & _tile, true);"
        )

    tp.queue(queue_str)
    logger.debug("String queued for {}:  {}", c.name, queue_str)

    c.kcl.start_changes()
    logger.info("Starting minkowski on {}", c.name)
    tp.execute(f"Minkowski {c.name}")
    c.kcl.end_changes()

    return operator.region

fix_width_minkowski_tiled

fix_width_minkowski_tiled(c: KCell, min_width: int, ref: kdb.LayerInfo | kdb.Region, n_threads: int | None = None, tile_size: tuple[float, float] | None = None, overlap: int = 1, smooth: int | None = None) -> kdb.Region

Fix min space issues by using a dilation & erosion with a box.

Parameters:

Name Type Description Default
c KCell

Input cell

required
min_width int

Minimum width rule [dbu]

required
ref LayerInfo | Region

Input layer index or region

required
n_threads int | None

on how many threads to run the check simultaneously

None
tile_size tuple[float, float] | None

tuple determining the size of each sub tile (in um), should be big compared to the violation size

None
overlap int

how many times bigger to make the tile border in relation to the violation size. Smaller than 1 can lead to errors

1
smooth int | None

Apply smoothening (simplifying) at the end if > 0

None

Returns:

Type Description
Region

kdb.Region: Region containing the fixes for the violations

Source code in src/kfactory/utils/violations.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
def fix_width_minkowski_tiled(
    c: KCell,
    min_width: int,
    ref: kdb.LayerInfo | kdb.Region,
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    overlap: int = 1,
    smooth: int | None = None,
) -> kdb.Region:
    """Fix min space issues by using a dilation & erosion with a box.

    Args:
        c: Input cell
        min_width: Minimum width rule [dbu]
        ref: Input layer index or region
        n_threads: on how many threads to run the check simultaneously
        tile_size: tuple determining the size of each sub tile (in um), should be big
            compared to the violation size
        overlap: how many times bigger to make the tile border in relation to the
            violation size. Smaller than 1 can lead to errors
        smooth: Apply smoothening (simplifying) at the end if > 0

    Returns:
        kdb.Region: Region containing the fixes for the violations
    """
    tp = kdb.TilingProcessor()
    tp.frame = c.dbbox()  # type: ignore[misc]
    tp.dbu = c.kcl.dbu
    tp.threads = n_threads or config.n_threads

    min_tile_size_rec = 10 * min_width * tp.dbu

    if tile_size is None:
        tile_size = (min_tile_size_rec * 2, min_tile_size_rec * 2)

    tp.tile_border(min_width * overlap * tp.dbu, min_width * overlap * tp.dbu)

    tp.tile_size(*tile_size)
    if isinstance(ref, kdb.LayerInfo):
        tp.input("main_layer", c.kcl.layout, c.cell_index(), c.kcl.find_layer(ref))
    else:
        tp.input("main_layer", ref)

    operator = RegionOperator()
    tp.output("target", operator)
    if smooth is None:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_width});"
            f"var shape = Box.new({min_width},{min_width});"
            "var reg = tile_reg - (tile_reg - main_layer).minkowski_sum(shape);"
            "reg = reg.minkowski_sum(shape); reg.merge();"
            "_output(target, reg & _tile, true);"
        )
    else:
        queue_str = (
            f"var tile_reg = (_tile & _frame).sized({min_width});"
            f"var shape = Box.new({min_width},{min_width});"
            "var reg = tile_reg - (tile_reg - main_layer).minkowski_sum(shape);"
            "reg = reg.minkowski_sum(shape); reg.merge();"
            f"reg.smooth({smooth});"
            "_output(target, reg & _tile, true);"
        )

    tp.queue(queue_str)
    logger.debug("String queued for {}:  {}", c.name, queue_str)

    c.kcl.start_changes()
    logger.info("Starting minkowski on {}", c.name)
    tp.execute(f"Minkowski {c.name}")
    c.kcl.end_changes()

    return operator.region