Skip to content

fill

ill Utilities.

Filling empty regions in KCells with filling cells.

FillOperator

Bases: TileOutputReceiver

Output Receiver of the TilingProcessor.

Source code in src/kfactory/utils/fill.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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
class FillOperator(kdb.TileOutputReceiver):
    """Output Receiver of the TilingProcessor."""

    def __init__(
        self,
        kcl: KCLayout,
        top_cell: KCell,
        fill_cell_index: int,
        fc_bbox: kdb.Box,
        row_step: kdb.Vector,
        column_step: kdb.Vector,
        fill_margin: kdb.Vector = kdb.Vector(0, 0),
        remaining_polygons: kdb.Region | None = None,
        multi: bool = False,
    ) -> None:
        """Initialize the receiver."""
        self.kcl = kcl
        self.top_cell = top_cell
        self.fill_cell_index = fill_cell_index
        self.fc_bbox = fc_bbox
        self.row_step = row_step
        self.column_step = column_step
        self.fill_margin = fill_margin
        self.remaining_polygons = remaining_polygons
        self.multi = multi

    def put(
        self,
        ix: int,
        iy: int,
        tile: kdb.Box,
        region: kdb.Region,
        dbu: float,
        clip: bool,
    ) -> None:
        """Called by the TilingProcessor."""
        if self.multi:
            self.top_cell.fill_region_multi(
                region=region,
                fill_cell_index=self.fill_cell_index,
                fc_bbox=self.fc_bbox,
                row_step=self.row_step,
                column_step=self.column_step,
                remaining_parts=None,
                fill_margin=kdb.Vector(
                    self.row_step.x - self.fc_bbox.width(),
                    self.column_step.y - self.fc_bbox.height(),
                ),
                remaining_polygons=None,
                glue_box=tile,
            )
        else:
            self.top_cell.fill_region(
                region=region,
                fill_cell_index=self.fill_cell_index,
                fc_bbox=self.fc_bbox,
                row_step=self.row_step,
                column_step=self.column_step,
                origin=self.top_cell.bbox().p1,
                remaining_parts=None,
                fill_margin=self.fill_margin,
                remaining_polygons=None,
                glue_box=tile,
            )

__init__

__init__(kcl: KCLayout, top_cell: KCell, fill_cell_index: int, fc_bbox: kdb.Box, row_step: kdb.Vector, column_step: kdb.Vector, fill_margin: kdb.Vector = kdb.Vector(0, 0), remaining_polygons: kdb.Region | None = None, multi: bool = False) -> None

Initialize the receiver.

Source code in src/kfactory/utils/fill.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def __init__(
    self,
    kcl: KCLayout,
    top_cell: KCell,
    fill_cell_index: int,
    fc_bbox: kdb.Box,
    row_step: kdb.Vector,
    column_step: kdb.Vector,
    fill_margin: kdb.Vector = kdb.Vector(0, 0),
    remaining_polygons: kdb.Region | None = None,
    multi: bool = False,
) -> None:
    """Initialize the receiver."""
    self.kcl = kcl
    self.top_cell = top_cell
    self.fill_cell_index = fill_cell_index
    self.fc_bbox = fc_bbox
    self.row_step = row_step
    self.column_step = column_step
    self.fill_margin = fill_margin
    self.remaining_polygons = remaining_polygons
    self.multi = multi

put

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

Called by the TilingProcessor.

Source code in src/kfactory/utils/fill.py
41
42
43
44
45
46
47
48
49
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
def put(
    self,
    ix: int,
    iy: int,
    tile: kdb.Box,
    region: kdb.Region,
    dbu: float,
    clip: bool,
) -> None:
    """Called by the TilingProcessor."""
    if self.multi:
        self.top_cell.fill_region_multi(
            region=region,
            fill_cell_index=self.fill_cell_index,
            fc_bbox=self.fc_bbox,
            row_step=self.row_step,
            column_step=self.column_step,
            remaining_parts=None,
            fill_margin=kdb.Vector(
                self.row_step.x - self.fc_bbox.width(),
                self.column_step.y - self.fc_bbox.height(),
            ),
            remaining_polygons=None,
            glue_box=tile,
        )
    else:
        self.top_cell.fill_region(
            region=region,
            fill_cell_index=self.fill_cell_index,
            fc_bbox=self.fc_bbox,
            row_step=self.row_step,
            column_step=self.column_step,
            origin=self.top_cell.bbox().p1,
            remaining_parts=None,
            fill_margin=self.fill_margin,
            remaining_polygons=None,
            glue_box=tile,
        )

fill_tiled

fill_tiled(c: KCell, fill_cell: KCell, fill_layers: Iterable[tuple[kdb.LayerInfo, int]] = [], fill_regions: Iterable[tuple[kdb.Region, int]] = [], exclude_layers: Iterable[tuple[kdb.LayerInfo, int]] = [], exclude_regions: Iterable[tuple[kdb.Region, int]] = [], n_threads: int | None = None, tile_size: tuple[float, float] | None = None, row_step: kdb.DVector | None = None, col_step: kdb.DVector | None = None, x_space: float = 0, y_space: float = 0, tile_border: tuple[float, float] = (20, 20), multi: bool = False) -> None

Fill a KCell.

Parameters:

Name Type Description Default
c KCell

Target cell.

required
fill_cell KCell

The cell used as a cell to fill the regions.

required
fill_layers Iterable[tuple[LayerInfo, int]]

Tuples of layer and keepout w.r.t. the regions.

[]
fill_regions Iterable[tuple[Region, int]]

Specific regions to fill. Also tuples like the layers.

[]
exclude_layers Iterable[tuple[LayerInfo, int]]

Layers to ignore. Tuples like the fill layers

[]
exclude_regions Iterable[tuple[Region, int]]

Specific regions to ignore. Tuples like the fill layers.

[]
n_threads int | None

Max number of threads used. Defaults to number of cores of the machine.

None
tile_size tuple[float, float] | None

Size of the tiles in um.

None
row_step DVector | None

DVector for steping to the next instance position in the row. x-coordinate must be >= 0.

None
col_step DVector | None

DVector for steping to the next instance position in the column. y-coordinate must be >= 0.

None
x_space float

Spacing between the fill cell bounding boxes in x-direction.

0
y_space float

Spacing between the fill cell bounding boxes in y-direction.

0
tile_border tuple[float, float]

The tile border to consider for excludes

(20, 20)
multi bool

Use the region_fill_multi strategy instead of single fill.

False
Source code in src/kfactory/utils/fill.py
 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
144
145
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
199
200
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
def fill_tiled(
    c: KCell,
    fill_cell: KCell,
    fill_layers: Iterable[tuple[kdb.LayerInfo, int]] = [],
    fill_regions: Iterable[tuple[kdb.Region, int]] = [],
    exclude_layers: Iterable[tuple[kdb.LayerInfo, int]] = [],
    exclude_regions: Iterable[tuple[kdb.Region, int]] = [],
    n_threads: int | None = None,
    tile_size: tuple[float, float] | None = None,
    row_step: kdb.DVector | None = None,
    col_step: kdb.DVector | None = None,
    x_space: float = 0,
    y_space: float = 0,
    tile_border: tuple[float, float] = (20, 20),
    multi: bool = False,
) -> None:
    """Fill a [KCell][kfactory.kcell.KCell].

    Args:
        c: Target cell.
        fill_cell: The cell used as a cell to fill the regions.
        fill_layers: Tuples of layer and keepout w.r.t. the regions.
        fill_regions: Specific regions to fill. Also tuples like the layers.
        exclude_layers: Layers to ignore. Tuples like the fill layers
        exclude_regions: Specific regions to ignore. Tuples like the fill layers.
        n_threads: Max number of threads used. Defaults to number of cores of the
            machine.
        tile_size: Size of the tiles in um.
        row_step: DVector for steping to the next instance position in the row.
            x-coordinate must be >= 0.
        col_step: DVector for steping to the next instance position in the column.
            y-coordinate must be >= 0.
        x_space: Spacing between the fill cell bounding boxes in x-direction.
        y_space: Spacing between the fill cell bounding boxes in y-direction.
        tile_border: The tile border to consider for excludes
        multi: Use the region_fill_multi strategy instead of single fill.
    """
    if n_threads is None:
        n_threads = config.n_threads
    tp = kdb.TilingProcessor()
    tp.frame = c.bbox().to_dtype(c.kcl.dbu)  # type: ignore
    tp.dbu = c.kcl.dbu
    tp.threads = n_threads

    if tile_size is None:
        tile_size = (
            100 * (fill_cell.dbbox().width() + x_space),
            100 * (fill_cell.dbbox().height() + y_space),
        )
    tp.tile_size(*tile_size)
    tp.tile_border(*tile_border)

    layer_names: list[str] = []
    for _layer, _ in fill_layers:
        layer_name = (
            f"layer{_layer.name}"
            if _layer.is_named()
            else f"layer_{_layer.layer}_{_layer.datatype}"
        )
        tp.input(layer_name, c.kcl.layout, c.cell_index(), _layer)
        layer_names.append(layer_name)

    region_names: list[str] = []
    for i, (r, _) in enumerate(fill_regions):
        region_name = f"region{i}"
        tp.input(region_name, r)
        region_names.append(region_name)

    exlayer_names: list[str] = []
    for _layer, _ in exclude_layers:
        layer_name = (
            f"layer{_layer.name}"
            if _layer.is_named()
            else f"layer_{_layer.layer}_{_layer.datatype}"
        )
        tp.input(layer_name, c.kcl.layout, c.cell_index(), _layer)
        exlayer_names.append(layer_name)

    exregion_names: list[str] = []
    for i, (r, _) in enumerate(exclude_regions):
        region_name = f"region{i}"
        tp.input(region_name, r)
        exregion_names.append(region_name)

    if row_step is None:
        _row_step = kdb.Vector(fill_cell.bbox().width() + int(x_space / c.kcl.dbu), 0)
    else:
        _row_step = c.kcl.to_dbu(row_step)
    if col_step is None:
        _col_step = kdb.Vector(0, fill_cell.bbox().height() + int(y_space / c.kcl.dbu))
    else:
        _col_step = c.kcl.to_dbu(col_step)
    fc_bbox = fill_cell.bbox()
    tp.output(
        "to_fill",
        FillOperator(
            c.kcl,
            c,
            fill_cell.cell_index(),
            fc_bbox=fc_bbox,
            row_step=_row_step,
            column_step=_col_step,
        ),
    )

    if layer_names or region_names:
        exlayers = " + ".join(
            [
                layer_name + f".sized({int(size / c.kcl.dbu)})" if size else layer_name
                for layer_name, (_, size) in zip(exlayer_names, exclude_layers)
            ]
        )
        exregions = " + ".join(
            [
                region_name + f".sized({int(size / c.kcl.dbu)})"
                if size
                else region_name
                for region_name, (_, size) in zip(exregion_names, exclude_regions)
            ]
        )
        layers = " + ".join(
            [
                layer_name + f".sized({int(size / c.kcl.dbu)})" if size else layer_name
                for layer_name, (_, size) in zip(layer_names, fill_layers)
            ]
        )
        regions = " + ".join(
            [
                region_name + f".sized({int(size / c.kcl.dbu)})"
                if size
                else region_name
                for region_name, (_, size) in zip(region_names, fill_regions)
            ]
        )

        if exlayer_names or exregion_names:
            queue_str = (
                "var fill= "
                + (
                    " + ".join([layers, regions])
                    if regions and layers
                    else regions + layers
                )
                + "; var exclude = "
                + (
                    " + ".join([exlayers, exregions])
                    if exregions and exlayers
                    else exregions + exlayers
                )
                + "; var fill_region = _tile.minkowski_sum(Box.new("
                f"0,0,{fc_bbox.width() - 1},{fc_bbox.height() - 1}))"
                " & _frame & fill - exclude; _output(to_fill, fill_region)"
            )
        else:
            queue_str = (
                "var fill= "
                + (
                    " + ".join([layers, regions])
                    if regions and layers
                    else regions + layers
                )
                + "; var fill_region = _tile & _frame & fill;"
                " _output(to_fill, fill_region)"
            )
        tp.queue(queue_str)
        c.kcl.start_changes()
        try:
            logger.debug(
                "Filling {} with {}", c.kcl.future_cell_name or c.name, fill_cell.name
            )
            logger.debug("fill string: '{}'", queue_str)
            tp.execute(f"Fill {c.name}")
            logger.info("done with filling {}", c.name)
        finally:
            c.kcl.end_changes()