-
Notifications
You must be signed in to change notification settings - Fork 187
/
name.py
212 lines (162 loc) · 5.8 KB
/
name.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
"""Define names, clean values for names."""
from __future__ import annotations
import hashlib
import re
from typing import Any
import pydantic
from gdsfactory.config import CONF
@pydantic.validate_call
def get_name_short(name: str, max_name_length=CONF.max_name_length) -> str:
"""Returns a short name."""
if len(name) > max_name_length:
name_hash = hashlib.md5(name.encode()).hexdigest()[:8]
name = f"{name[:(max_name_length - 9)]}_{name_hash}"
return name
def join_first_letters(name: str) -> str:
"""Join the first letter of a name separated with underscores.
taper_length -> TL
"""
return "".join([x[0] for x in name.split("_") if x])
# replace function_name prefix for some components
component_type_to_name = {"phidl": "phidl"}
def get_component_name(component_type: str, *args, **kwargs) -> str:
"""Returns concatenated kwargs Key_Value."""
name = component_type + "_".join([clean_value(a) for a in args])
for k, v in component_type_to_name.items():
name = name.replace(k, v)
if kwargs:
name += f"_{dict2name(**kwargs)}"
return name
def dict2hash(**kwargs) -> str:
ignore_from_name = kwargs.pop("ignore_from_name", [])
h = hashlib.sha256()
for key in sorted(kwargs):
if key not in ignore_from_name:
value = kwargs[key]
value = clean_value(value)
h.update(f"{key}{value}".encode())
return h.hexdigest()
def dict2name(prefix: str = "", **kwargs) -> str:
"""Returns name from a dict."""
ignore_from_name = kwargs.pop("ignore_from_name", [])
kv = []
kwargs = kwargs.copy()
kwargs.pop("layer_to_inclusion", "")
for key in sorted(kwargs):
if key not in ignore_from_name and isinstance(key, str):
value = kwargs[key]
# key = join_first_letters(key).upper()
if value is not None:
kv += [f"{key}{clean_value(value)}"]
label = prefix + "_".join(kv)
return clean_name(label)
def assert_first_letters_are_different(**kwargs):
"""Assert that the first letters for each key are different.
Avoids different args that start with the same first letter getting
the same hash.
"""
first_letters = [join_first_letters(k) for k in kwargs]
if len(set(first_letters)) != len(first_letters):
raise ValueError(
f"Possible name collision! {kwargs.keys()} repeats first letters {first_letters}",
"you can separate your arguments with underscores",
" (delta_length -> DL, delta_width -> DW",
)
def print_first_letters_warning(**kwargs) -> None:
"""Prints kwargs that have same cell."""
first_letters = [join_first_letters(k) for k in kwargs]
if len(set(first_letters)) != len(first_letters):
print(
f"Possible name collision! {kwargs.keys()} "
f"repeats first letters {first_letters}"
"you can separate your arguments with underscores"
" (delta_length -> DL, delta_width -> DW"
)
def clean_name(
name: str,
remove_dots: bool = False,
allowed_characters: list[str] | None = None,
) -> str:
"""Return a string with correct characters for a cell name.
By default, the characters [a-zA-Z0-9] are allowed.
Args:
name (str): The name to clean.
remove_dots (bool, optional): Whether to remove dots from the name. Defaults to False.
allowed_characters (list[str], optional): List of additional allowed characters. Defaults to an empty list.
Returns:
str: The cleaned name.
"""
# Default allowed characters, including underscore
allowed = r"a-zA-Z0-9_"
allowed_characters = allowed_characters or []
# Add additional allowed characters
for char in allowed_characters:
allowed += re.escape(char)
# Pattern for characters to be replaced
pattern = f"[^{allowed}]"
# Replacements map
replace_map = {
" ": "_",
"!": "",
"?": "",
"#": "_",
"%": "_",
"(": "",
")": "",
"*": "_",
",": "_",
"-": "m",
".": "p",
"/": "_",
":": "_",
"=": "",
"@": "_",
"[": "",
"]": "",
"{": "",
"}": "",
"$": "",
}
if remove_dots:
replace_map["."] = ""
# Replace characters using the replace_map
def replace_match(match):
return replace_map.get(match.group(0), "")
return re.sub(pattern, replace_match, name)
def clean_value(value: Any) -> str:
from gdsfactory.serialization import clean_value_json
return str(clean_value_json(value))
# def testclean_value_json() -> None:
# assert clean_value(0.5) == "500n"
# assert clean_value(5) == "5"
# assert clean_value(5.0) == "5"
# assert clean_value(11.001) == "11p001"
def test_clean_name() -> None:
assert clean_name("wg(:_=_2852") == "wg___2852"
if __name__ == "__main__":
# testclean_value_json()
import gdsfactory as gf
# print(clean_value(gf.components.straight))
# c = gf.components.straight(polarization="TMeraer")
# print(c.settings["polarization"])
# print(clean_value(11.001))
c = gf.components.straight(length=10)
# print(c.name)
# print(c)
# c.show(show_ports=True)
# print(clean_name("Waveguidenol1_(:_=_2852"))
# print(clean_value(1.2))
# print(clean_value(0.2))
# print(clean_value([1, [2.4324324, 3]]))
# print(clean_value([1, 2.4324324, 3]))
# print(clean_value((0.001, 24)))
# print(clean_value({"a": 1, "b": 2}))
# import gdsfactory as gf
# d = {
# "X": gf.components.crossing45(port_spacing=40.0),
# "-": gf.components.compensation_path(
# crossing45=gf.components.crossing45(port_spacing=40.0)
# ),
# }
# d2 = clean_value(d)
# print(d2)