Skip to content

Layer map

layer_map

Utilities to go lyp <-> yaml.

LayerGroupModel pydantic-model

Bases: BaseModel

A group of layers.

Fields:

Source code in kfactory/technology/layer_map.py
77
78
79
80
81
class LayerGroupModel(BaseModel):
    """A group of layers."""

    name: str
    members: list[LayerPropertiesModel | LayerGroupModel]

LayerPropertiesModel pydantic-model

Bases: BaseModel

A leaf node in the layer properties.

Fields:

  • name (str)
  • layer (tuple[int, int])
  • frame_color (Color | None)
  • fill_color (Color | None)
  • dither_pattern (int)
  • line_style (int)
  • visible (bool)
  • width (int)
  • xfill (bool)
  • layer_to_name (bool)
  • transparent (bool)
  • valid (bool)

Validators:

Source code in kfactory/technology/layer_map.py
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
class LayerPropertiesModel(BaseModel):
    """A leaf node in the layer properties."""

    name: str
    layer: tuple[int, int]
    frame_color: Color | None = None
    fill_color: Color | None = None

    dither_pattern: int = 1
    line_style: int = 1
    visible: bool = True
    width: int = 1
    xfill: bool = False
    layer_to_name: bool = True
    transparent: bool = False
    valid: bool = True

    @model_validator(mode="before")
    @classmethod
    def color_to_frame_fill(cls, data: dict[str, Any]) -> dict[str, Any]:
        """Convert a color string to a frame fill."""
        if "color" in data:
            if "fill_color" not in data:
                data["fill_color"] = data["color"]
            if "frame_color" not in data:
                data["frame_color"] = data["color"]
            del data["color"]
        return data

    @field_validator("dither_pattern", mode="before")
    @classmethod
    def dither_to_index(cls, v: str | int) -> int:
        """Convert string to the index with the dict dither2index."""
        if isinstance(v, str):
            return dither2index[v]
        return v

    @field_validator("line_style", mode="before")
    @classmethod
    def line_to_index(cls, v: str | int) -> int:
        """Convert string to the index with the dict dither2index."""
        if isinstance(v, str):
            return line2index[v]
        return v

    @field_serializer("dither_pattern")
    @staticmethod
    def dither_to_json(value: int) -> str:
        """Convert dither int to string key on json dump."""
        return index2dither[value]

    @field_serializer("line_style")
    @staticmethod
    def line_to_json(value: int) -> str:
        """Convert dither int to string key on json dump."""
        return index2line[value]

color_to_frame_fill pydantic-validator

color_to_frame_fill(data: dict[str, Any]) -> dict[str, Any]

Convert a color string to a frame fill.

Source code in kfactory/technology/layer_map.py
36
37
38
39
40
41
42
43
44
45
46
@model_validator(mode="before")
@classmethod
def color_to_frame_fill(cls, data: dict[str, Any]) -> dict[str, Any]:
    """Convert a color string to a frame fill."""
    if "color" in data:
        if "fill_color" not in data:
            data["fill_color"] = data["color"]
        if "frame_color" not in data:
            data["frame_color"] = data["color"]
        del data["color"]
    return data

dither_to_index pydantic-validator

dither_to_index(v: str | int) -> int

Convert string to the index with the dict dither2index.

Source code in kfactory/technology/layer_map.py
48
49
50
51
52
53
54
@field_validator("dither_pattern", mode="before")
@classmethod
def dither_to_index(cls, v: str | int) -> int:
    """Convert string to the index with the dict dither2index."""
    if isinstance(v, str):
        return dither2index[v]
    return v

dither_to_json staticmethod

dither_to_json(value: int) -> str

Convert dither int to string key on json dump.

Source code in kfactory/technology/layer_map.py
64
65
66
67
68
@field_serializer("dither_pattern")
@staticmethod
def dither_to_json(value: int) -> str:
    """Convert dither int to string key on json dump."""
    return index2dither[value]

line_to_index pydantic-validator

line_to_index(v: str | int) -> int

Convert string to the index with the dict dither2index.

Source code in kfactory/technology/layer_map.py
56
57
58
59
60
61
62
@field_validator("line_style", mode="before")
@classmethod
def line_to_index(cls, v: str | int) -> int:
    """Convert string to the index with the dict dither2index."""
    if isinstance(v, str):
        return line2index[v]
    return v

line_to_json staticmethod

line_to_json(value: int) -> str

Convert dither int to string key on json dump.

Source code in kfactory/technology/layer_map.py
70
71
72
73
74
@field_serializer("line_style")
@staticmethod
def line_to_json(value: int) -> str:
    """Convert dither int to string key on json dump."""
    return index2line[value]

LypModel pydantic-model

Bases: BaseModel

Model for the whole lyp.

Fields:

Source code in kfactory/technology/layer_map.py
84
85
86
87
class LypModel(BaseModel):
    """Model for the whole lyp."""

    layers: list[LayerGroupModel | LayerPropertiesModel]

group2lp

group2lp(lp: LayerGroupModel) -> lay.LayerPropertiesNode

Convert a group model to a LayerPropertiesNode.

Source code in kfactory/technology/layer_map.py
214
215
216
217
218
219
220
221
222
223
224
225
def group2lp(lp: LayerGroupModel) -> lay.LayerPropertiesNode:
    """Convert a group model to a LayerPropertiesNode."""
    kl_lp = lay.LayerPropertiesNode()
    kl_lp.name = lp.name

    for member in lp.members:
        if isinstance(member, LayerPropertiesModel):
            kl_lp.add_child(lp2kl(member))
        else:
            kl_lp.add_child(group2lp(member))

    return kl_lp

kl2group

kl2group(
    iterable: LayerPropertiesIterator,
) -> list[LayerGroupModel | LayerPropertiesModel]

Convert a full LayerPropertiesIterator to a pydantic representation.

Source code in kfactory/technology/layer_map.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
def kl2group(
    iterable: lay.LayerPropertiesIterator,
) -> list[LayerGroupModel | LayerPropertiesModel]:
    """Convert a full LayerPropertiesIterator to a pydantic representation."""
    members: list[LayerGroupModel | LayerPropertiesModel] = []
    while not iterable.at_end():
        lpnr = iterable.current()
        if lpnr.has_children():
            members.append(
                LayerGroupModel(
                    name=lpnr.name, members=kl2group(iterable.first_child())
                )
            )
        else:
            members.append(kl2lp(lpnr))
        iterable.next_sibling(1)
    return members

kl2lp

kl2lp(kl: LayerPropertiesNodeRef) -> LayerPropertiesModel

Convert a KLayout LayerPropertiesNodeRef to a pydantic representation.

Source code in kfactory/technology/layer_map.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def kl2lp(kl: lay.LayerPropertiesNodeRef) -> LayerPropertiesModel:
    """Convert a KLayout LayerPropertiesNodeRef to a pydantic representation."""
    return LayerPropertiesModel(
        name=kl.name.rstrip(f" - {kl.source_layer}/{kl.source_datatype}"),
        layer=(kl.source_layer, kl.source_datatype),
        frame_color=Color(hex(kl.frame_color)) if kl.frame_color else None,
        fill_color=Color(hex(kl.fill_color)) if kl.fill_color else None,
        dither_pattern=index2dither[kl.dither_pattern],  # ty:ignore[invalid-argument-type]
        line_style=index2line.get(kl.line_style, "solid"),  # ty:ignore[invalid-argument-type]
        visible=kl.visible,
        width=kl.width,
        xfill=kl.xfill,
        layer_to_name=kl.name.endswith(f" - {kl.source_layer}/{kl.source_datatype}"),
        transparent=kl.transparent,
        valid=kl.valid,
    )

lp2kl

lp2kl(lp: LayerPropertiesModel) -> lay.LayerPropertiesNode

LayerPropertiesModel to KLayout LayerPropertiesNode.

Source code in kfactory/technology/layer_map.py
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
def lp2kl(lp: LayerPropertiesModel) -> lay.LayerPropertiesNode:
    """LayerPropertiesModel to KLayout LayerPropertiesNode."""
    kl_lp = lay.LayerPropertiesNode()

    kl_lp.name = (
        lp.name + f" - {lp.layer[0]}/{lp.layer[1]}" if lp.layer_to_name else lp.name
    )
    kl_lp.source = f"{lp.layer[0]}/{lp.layer[1]}"
    if lp.frame_color:
        hex_n = lp.frame_color.as_hex()[1:]
        if len(hex_n) < MIN_HEX_THRESHOLD:
            hex_n = "".join(x * 2 for x in hex_n)
        kl_lp.frame_color = int(hex_n, 16)
    if lp.fill_color:
        hex_n = lp.fill_color.as_hex()[1:]
        if len(hex_n) < MIN_HEX_THRESHOLD:
            hex_n = "".join(x * 2 for x in hex_n)
        kl_lp.fill_color = int(hex_n, 16)

    kl_lp.dither_pattern = lp.dither_pattern
    kl_lp.visible = lp.visible
    kl_lp.width = lp.width
    kl_lp.xfill = lp.xfill
    kl_lp.transparent = lp.transparent
    kl_lp.valid = lp.valid
    kl_lp.line_style = lp.line_style

    return kl_lp

lyp_to_lyp_model

lyp_to_lyp_model(inp: Path | str) -> LypModel

Convert a lyp file to a LypModel.

Source code in kfactory/technology/layer_map.py
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def lyp_to_lyp_model(inp: pathlib.Path | str) -> LypModel:
    """Convert a lyp file to a LypModel."""
    f = pathlib.Path(inp).resolve()
    assert f.exists()

    lv = lay.LayoutView()
    lv.load_layer_props(str(f))

    layers_iter = lv.begin_layers()
    layers: list[LayerGroupModel | LayerPropertiesModel] = []

    while not layers_iter.at_end():
        lpnr = layers_iter.current()
        if lpnr.has_children():
            layers.append(
                LayerGroupModel(
                    name=lpnr.name, members=kl2group(layers_iter.first_child())
                )
            )
        else:
            layers.append(kl2lp(lpnr))
        layers_iter.next_sibling(1)

    return LypModel(layers=layers)

lyp_to_yaml

lyp_to_yaml(inp: Path | str, out: Path | str) -> None

Convert a lyp file to a YAML ffile.

Source code in kfactory/technology/layer_map.py
138
139
140
141
142
143
144
def lyp_to_yaml(inp: pathlib.Path | str, out: pathlib.Path | str) -> None:
    """Convert a lyp file to a YAML ffile."""
    yaml = YAML()

    lyp_m = lyp_to_lyp_model(inp)

    yaml.dump(loads(lyp_m.model_dump_json()), pathlib.Path(out))

yaml_to_lyp

yaml_to_lyp(inp: Path | str, out: Path | str) -> None

Convert a YAML file to a lyp file readable by KLayout.

Source code in kfactory/technology/layer_map.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def yaml_to_lyp(inp: pathlib.Path | str, out: pathlib.Path | str) -> None:
    """Convert a YAML file to a lyp file readable by KLayout."""
    f = pathlib.Path(inp)
    assert f.exists()

    yaml = YAML()
    lyp_dict = yaml.load(f)
    lyp_m = LypModel.model_validate(lyp_dict)

    lv = lay.LayoutView()
    layers_iter = lv.end_layers()

    for member in lyp_m.layers:
        if isinstance(member, LayerPropertiesModel):
            lv.insert_layer(layers_iter, lp2kl(member))
            layers_iter.next()
        else:
            lv.insert_layer(layers_iter, group2lp(member))

    lv.save_layer_props(str(out))