prefab 1.0.2__py3-none-any.whl → 1.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefab/__init__.py +5 -1
- prefab/compare.py +22 -20
- prefab/device.py +62 -36
- prefab/geometry.py +21 -15
- prefab/models.py +31 -1
- prefab/read.py +45 -22
- prefab/shapes.py +765 -0
- {prefab-1.0.2.dist-info → prefab-1.0.3.dist-info}/METADATA +7 -5
- prefab-1.0.3.dist-info/RECORD +12 -0
- prefab-1.0.2.dist-info/RECORD +0 -11
- {prefab-1.0.2.dist-info → prefab-1.0.3.dist-info}/WHEEL +0 -0
- {prefab-1.0.2.dist-info → prefab-1.0.3.dist-info}/licenses/LICENSE +0 -0
prefab/__init__.py
CHANGED
|
@@ -5,7 +5,9 @@ Usage:
|
|
|
5
5
|
import prefab as pf
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
__version__ = "1.0.3"
|
|
9
|
+
|
|
10
|
+
from . import compare, geometry, read, shapes
|
|
9
11
|
from .device import BufferSpec, Device
|
|
10
12
|
from .models import models
|
|
11
13
|
|
|
@@ -14,6 +16,8 @@ __all__ = [
|
|
|
14
16
|
"BufferSpec",
|
|
15
17
|
"geometry",
|
|
16
18
|
"read",
|
|
19
|
+
"shapes",
|
|
17
20
|
"compare",
|
|
18
21
|
"models",
|
|
22
|
+
"__version__",
|
|
19
23
|
]
|
prefab/compare.py
CHANGED
|
@@ -7,40 +7,40 @@ from .device import Device
|
|
|
7
7
|
|
|
8
8
|
def mean_squared_error(device_a: Device, device_b: Device) -> float:
|
|
9
9
|
"""
|
|
10
|
-
Calculate the mean squared error (MSE) between two non-binarized devices.
|
|
10
|
+
Calculate the mean squared error (MSE) between two non-binarized devices. A lower
|
|
11
|
+
value indicates more similarity.
|
|
11
12
|
|
|
12
13
|
Parameters
|
|
13
14
|
----------
|
|
14
15
|
device_a : Device
|
|
15
|
-
The first device.
|
|
16
|
+
The first device (non-binarized).
|
|
16
17
|
device_b : Device
|
|
17
|
-
The second device.
|
|
18
|
+
The second device (non-binarized).
|
|
18
19
|
|
|
19
20
|
Returns
|
|
20
21
|
-------
|
|
21
22
|
float
|
|
22
|
-
The mean squared error between two devices.
|
|
23
|
-
similarity.
|
|
23
|
+
The mean squared error between two devices.
|
|
24
24
|
"""
|
|
25
25
|
return np.mean((device_a.device_array - device_b.device_array) ** 2)
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def intersection_over_union(device_a: Device, device_b: Device) -> float:
|
|
29
29
|
"""
|
|
30
|
-
Calculates the Intersection over Union (IoU) between two binary devices.
|
|
30
|
+
Calculates the Intersection over Union (IoU) between two binary devices. A value
|
|
31
|
+
closer to 1 indicates more similarity (more overlap).
|
|
31
32
|
|
|
32
33
|
Parameters
|
|
33
34
|
----------
|
|
34
35
|
device_a : Device
|
|
35
|
-
The first device.
|
|
36
|
+
The first device (binarized).
|
|
36
37
|
device_b : Device
|
|
37
|
-
The second device.
|
|
38
|
+
The second device (binarized).
|
|
38
39
|
|
|
39
40
|
Returns
|
|
40
41
|
-------
|
|
41
42
|
float
|
|
42
|
-
The Intersection over Union between two devices.
|
|
43
|
-
more similarity (more overlap).
|
|
43
|
+
The Intersection over Union between two devices.
|
|
44
44
|
"""
|
|
45
45
|
return np.sum(
|
|
46
46
|
np.logical_and(device_a.device_array, device_b.device_array)
|
|
@@ -49,40 +49,42 @@ def intersection_over_union(device_a: Device, device_b: Device) -> float:
|
|
|
49
49
|
|
|
50
50
|
def hamming_distance(device_a: Device, device_b: Device) -> int:
|
|
51
51
|
"""
|
|
52
|
-
Calculates the Hamming distance between two binary devices.
|
|
52
|
+
Calculates the Hamming distance between two binary devices. A lower value indicates
|
|
53
|
+
more similarity. The Hamming distance is calculated as the number of positions at
|
|
54
|
+
which the corresponding pixels are different.
|
|
53
55
|
|
|
54
56
|
Parameters
|
|
55
57
|
----------
|
|
56
58
|
device_a : Device
|
|
57
|
-
The first device.
|
|
59
|
+
The first device (binarized).
|
|
58
60
|
device_b : Device
|
|
59
|
-
The second device.
|
|
61
|
+
The second device (binarized).
|
|
60
62
|
|
|
61
63
|
Returns
|
|
62
64
|
-------
|
|
63
65
|
int
|
|
64
|
-
The Hamming distance between two devices.
|
|
65
|
-
similarity.
|
|
66
|
+
The Hamming distance between two devices.
|
|
66
67
|
"""
|
|
67
68
|
return np.sum(device_a.device_array != device_b.device_array)
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def dice_coefficient(device_a: Device, device_b: Device) -> float:
|
|
71
72
|
"""
|
|
72
|
-
Calculates the Dice coefficient between two binary devices.
|
|
73
|
+
Calculates the Dice coefficient between two binary devices. A value closer to 1
|
|
74
|
+
indicates more similarity. The Dice coefficient is calculated as twice the number of
|
|
75
|
+
pixels in common divided by the total number of pixels in the two devices.
|
|
73
76
|
|
|
74
77
|
Parameters
|
|
75
78
|
----------
|
|
76
79
|
device_a : Device
|
|
77
|
-
The first device.
|
|
80
|
+
The first device (binarized).
|
|
78
81
|
device_b : Device
|
|
79
|
-
The second device.
|
|
82
|
+
The second device (binarized).
|
|
80
83
|
|
|
81
84
|
Returns
|
|
82
85
|
-------
|
|
83
86
|
float
|
|
84
|
-
The Dice coefficient between two devices.
|
|
85
|
-
similarity.
|
|
87
|
+
The Dice coefficient between two devices.
|
|
86
88
|
"""
|
|
87
89
|
intersection = 2.0 * np.sum(
|
|
88
90
|
np.logical_and(device_a.device_array, device_b.device_array)
|
prefab/device.py
CHANGED
|
@@ -40,14 +40,16 @@ class BufferSpec(BaseModel):
|
|
|
40
40
|
('top', 'bottom', 'left', 'right'), where 'constant' is used for isolated
|
|
41
41
|
structures and 'edge' is utilized for preserving the edge, such as for waveguide
|
|
42
42
|
connections.
|
|
43
|
-
thickness : conint(gt=0)
|
|
44
|
-
|
|
43
|
+
thickness : dict[str, conint(gt=0)]
|
|
44
|
+
A dictionary that defines the thickness of the buffer zone for each side of the
|
|
45
|
+
device ('top', 'bottom', 'left', 'right'). Each value must be greater than 0.
|
|
45
46
|
|
|
46
47
|
Raises
|
|
47
48
|
------
|
|
48
49
|
ValueError
|
|
49
50
|
If any of the modes specified in the 'mode' dictionary are not one of the
|
|
50
|
-
allowed values ('constant', 'edge'). Or if the thickness
|
|
51
|
+
allowed values ('constant', 'edge'). Or if any of the thickness values are not
|
|
52
|
+
greater than 0.
|
|
51
53
|
|
|
52
54
|
Example
|
|
53
55
|
-------
|
|
@@ -60,7 +62,12 @@ class BufferSpec(BaseModel):
|
|
|
60
62
|
"left": "constant",
|
|
61
63
|
"right": "edge",
|
|
62
64
|
},
|
|
63
|
-
thickness=
|
|
65
|
+
thickness={
|
|
66
|
+
"top": 150,
|
|
67
|
+
"bottom": 100,
|
|
68
|
+
"left": 200,
|
|
69
|
+
"right": 250,
|
|
70
|
+
},
|
|
64
71
|
)
|
|
65
72
|
"""
|
|
66
73
|
|
|
@@ -72,7 +79,14 @@ class BufferSpec(BaseModel):
|
|
|
72
79
|
"right": "constant",
|
|
73
80
|
}
|
|
74
81
|
)
|
|
75
|
-
thickness: conint(gt=0) =
|
|
82
|
+
thickness: dict[str, conint(gt=0)] = Field(
|
|
83
|
+
default_factory=lambda: {
|
|
84
|
+
"top": 128,
|
|
85
|
+
"bottom": 128,
|
|
86
|
+
"left": 128,
|
|
87
|
+
"right": 128,
|
|
88
|
+
}
|
|
89
|
+
)
|
|
76
90
|
|
|
77
91
|
@validator("mode", pre=True)
|
|
78
92
|
def check_mode(cls, v):
|
|
@@ -146,28 +160,26 @@ class Device(BaseModel):
|
|
|
146
160
|
|
|
147
161
|
self.device_array = np.pad(
|
|
148
162
|
self.device_array,
|
|
149
|
-
pad_width=((buffer_thickness, 0), (0, 0)),
|
|
163
|
+
pad_width=((buffer_thickness["top"], 0), (0, 0)),
|
|
150
164
|
mode=buffer_mode["top"],
|
|
151
165
|
)
|
|
152
166
|
self.device_array = np.pad(
|
|
153
167
|
self.device_array,
|
|
154
|
-
pad_width=((0, buffer_thickness), (0, 0)),
|
|
168
|
+
pad_width=((0, buffer_thickness["bottom"]), (0, 0)),
|
|
155
169
|
mode=buffer_mode["bottom"],
|
|
156
170
|
)
|
|
157
171
|
self.device_array = np.pad(
|
|
158
172
|
self.device_array,
|
|
159
|
-
pad_width=((0, 0), (buffer_thickness, 0)),
|
|
173
|
+
pad_width=((0, 0), (buffer_thickness["left"], 0)),
|
|
160
174
|
mode=buffer_mode["left"],
|
|
161
175
|
)
|
|
162
176
|
self.device_array = np.pad(
|
|
163
177
|
self.device_array,
|
|
164
|
-
pad_width=((0, 0), (0, buffer_thickness)),
|
|
178
|
+
pad_width=((0, 0), (0, buffer_thickness["right"])),
|
|
165
179
|
mode=buffer_mode["right"],
|
|
166
180
|
)
|
|
167
181
|
|
|
168
|
-
self.device_array = np.expand_dims(
|
|
169
|
-
self.device_array.astype(np.float32), axis=-1
|
|
170
|
-
)
|
|
182
|
+
self.device_array = np.expand_dims(self.device_array, axis=-1)
|
|
171
183
|
|
|
172
184
|
@root_validator(pre=True)
|
|
173
185
|
def check_device_array(cls, values):
|
|
@@ -464,10 +476,12 @@ class Device(BaseModel):
|
|
|
464
476
|
buffer_thickness = self.buffer_spec.thickness
|
|
465
477
|
buffer_mode = self.buffer_spec.mode
|
|
466
478
|
|
|
467
|
-
crop_top = buffer_thickness if buffer_mode["top"] == "edge" else 0
|
|
468
|
-
crop_bottom =
|
|
469
|
-
|
|
470
|
-
|
|
479
|
+
crop_top = buffer_thickness["top"] if buffer_mode["top"] == "edge" else 0
|
|
480
|
+
crop_bottom = (
|
|
481
|
+
buffer_thickness["bottom"] if buffer_mode["bottom"] == "edge" else 0
|
|
482
|
+
)
|
|
483
|
+
crop_left = buffer_thickness["left"] if buffer_mode["left"] == "edge" else 0
|
|
484
|
+
crop_right = buffer_thickness["right"] if buffer_mode["right"] == "edge" else 0
|
|
471
485
|
|
|
472
486
|
ndarray = device_array[
|
|
473
487
|
crop_top : device_array.shape[0] - crop_bottom,
|
|
@@ -629,7 +643,7 @@ class Device(BaseModel):
|
|
|
629
643
|
bounds: Optional[tuple[tuple[int, int], tuple[int, int]]],
|
|
630
644
|
ax: Optional[Axes],
|
|
631
645
|
**kwargs,
|
|
632
|
-
) -> Axes:
|
|
646
|
+
) -> tuple[plt.cm.ScalarMappable, Axes]:
|
|
633
647
|
if ax is None:
|
|
634
648
|
_, ax = plt.subplots()
|
|
635
649
|
ax.set_ylabel("y (nm)")
|
|
@@ -673,6 +687,13 @@ class Device(BaseModel):
|
|
|
673
687
|
if show_buffer:
|
|
674
688
|
self._add_buffer_visualization(ax)
|
|
675
689
|
|
|
690
|
+
# # Adjust colorbar font size if a colorbar is added
|
|
691
|
+
# if "cmap" in kwargs:
|
|
692
|
+
# cbar = plt.colorbar(mappable, ax=ax)
|
|
693
|
+
# cbar.ax.tick_params(labelsize=14)
|
|
694
|
+
# if "label" in kwargs:
|
|
695
|
+
# cbar.set_label(kwargs["label"], fontsize=16)
|
|
696
|
+
|
|
676
697
|
return mappable, ax
|
|
677
698
|
|
|
678
699
|
def plot(
|
|
@@ -937,9 +958,9 @@ class Device(BaseModel):
|
|
|
937
958
|
buffer_hatch = "/"
|
|
938
959
|
|
|
939
960
|
mid_rect = Rectangle(
|
|
940
|
-
(buffer_thickness, buffer_thickness),
|
|
941
|
-
plot_array.shape[1] -
|
|
942
|
-
plot_array.shape[0] -
|
|
961
|
+
(buffer_thickness["left"], buffer_thickness["top"]),
|
|
962
|
+
plot_array.shape[1] - buffer_thickness["left"] - buffer_thickness["right"],
|
|
963
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
943
964
|
facecolor="none",
|
|
944
965
|
edgecolor="black",
|
|
945
966
|
linewidth=1,
|
|
@@ -949,25 +970,25 @@ class Device(BaseModel):
|
|
|
949
970
|
top_rect = Rectangle(
|
|
950
971
|
(0, 0),
|
|
951
972
|
plot_array.shape[1],
|
|
952
|
-
buffer_thickness,
|
|
973
|
+
buffer_thickness["top"],
|
|
953
974
|
facecolor=buffer_fill,
|
|
954
975
|
hatch=buffer_hatch,
|
|
955
976
|
)
|
|
956
977
|
ax.add_patch(top_rect)
|
|
957
978
|
|
|
958
979
|
bottom_rect = Rectangle(
|
|
959
|
-
(0, plot_array.shape[0] - buffer_thickness),
|
|
980
|
+
(0, plot_array.shape[0] - buffer_thickness["bottom"]),
|
|
960
981
|
plot_array.shape[1],
|
|
961
|
-
buffer_thickness,
|
|
982
|
+
buffer_thickness["bottom"],
|
|
962
983
|
facecolor=buffer_fill,
|
|
963
984
|
hatch=buffer_hatch,
|
|
964
985
|
)
|
|
965
986
|
ax.add_patch(bottom_rect)
|
|
966
987
|
|
|
967
988
|
left_rect = Rectangle(
|
|
968
|
-
(0, buffer_thickness),
|
|
969
|
-
buffer_thickness,
|
|
970
|
-
plot_array.shape[0] -
|
|
989
|
+
(0, buffer_thickness["top"]),
|
|
990
|
+
buffer_thickness["left"],
|
|
991
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
971
992
|
facecolor=buffer_fill,
|
|
972
993
|
hatch=buffer_hatch,
|
|
973
994
|
)
|
|
@@ -975,11 +996,11 @@ class Device(BaseModel):
|
|
|
975
996
|
|
|
976
997
|
right_rect = Rectangle(
|
|
977
998
|
(
|
|
978
|
-
plot_array.shape[1] - buffer_thickness,
|
|
979
|
-
buffer_thickness,
|
|
999
|
+
plot_array.shape[1] - buffer_thickness["right"],
|
|
1000
|
+
buffer_thickness["top"],
|
|
980
1001
|
),
|
|
981
|
-
buffer_thickness,
|
|
982
|
-
plot_array.shape[0] -
|
|
1002
|
+
buffer_thickness["right"],
|
|
1003
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
983
1004
|
facecolor=buffer_fill,
|
|
984
1005
|
hatch=buffer_hatch,
|
|
985
1006
|
)
|
|
@@ -1017,7 +1038,9 @@ class Device(BaseModel):
|
|
|
1017
1038
|
binarized_device_array = geometry.binarize(
|
|
1018
1039
|
device_array=self.device_array, eta=eta, beta=beta
|
|
1019
1040
|
)
|
|
1020
|
-
return self.model_copy(
|
|
1041
|
+
return self.model_copy(
|
|
1042
|
+
update={"device_array": binarized_device_array.astype(np.uint8)}
|
|
1043
|
+
)
|
|
1021
1044
|
|
|
1022
1045
|
def binarize_hard(self, eta: float = 0.5) -> "Device":
|
|
1023
1046
|
"""
|
|
@@ -1038,12 +1061,14 @@ class Device(BaseModel):
|
|
|
1038
1061
|
binarized_device_array = geometry.binarize_hard(
|
|
1039
1062
|
device_array=self.device_array, eta=eta
|
|
1040
1063
|
)
|
|
1041
|
-
return self.model_copy(
|
|
1064
|
+
return self.model_copy(
|
|
1065
|
+
update={"device_array": binarized_device_array.astype(np.uint8)}
|
|
1066
|
+
)
|
|
1042
1067
|
|
|
1043
1068
|
def binarize_monte_carlo(
|
|
1044
1069
|
self,
|
|
1045
1070
|
threshold_noise_std: float = 2.0,
|
|
1046
|
-
threshold_blur_std: float =
|
|
1071
|
+
threshold_blur_std: float = 8.0,
|
|
1047
1072
|
) -> "Device":
|
|
1048
1073
|
"""
|
|
1049
1074
|
Binarize the device geometry using a Monte Carlo approach with Gaussian
|
|
@@ -1106,9 +1131,10 @@ class Device(BaseModel):
|
|
|
1106
1131
|
|
|
1107
1132
|
Parameters
|
|
1108
1133
|
----------
|
|
1109
|
-
buffer_thickness :
|
|
1110
|
-
|
|
1111
|
-
|
|
1134
|
+
buffer_thickness : dict, optional
|
|
1135
|
+
A dictionary specifying the thickness of the buffer to leave around the
|
|
1136
|
+
non-zero elements of the array. Should contain keys 'top', 'bottom', 'left',
|
|
1137
|
+
'right'. Defaults to None, which means no buffer is added.
|
|
1112
1138
|
|
|
1113
1139
|
Returns
|
|
1114
1140
|
-------
|
prefab/geometry.py
CHANGED
|
@@ -124,7 +124,7 @@ def binarize_monte_carlo(
|
|
|
124
124
|
generated threshold.
|
|
125
125
|
"""
|
|
126
126
|
device_array = np.squeeze(device_array)
|
|
127
|
-
base_threshold = np.
|
|
127
|
+
base_threshold = np.random.normal(loc=0.5, scale=0.1)
|
|
128
128
|
threshold_noise = np.random.normal(
|
|
129
129
|
loc=0, scale=threshold_noise_std, size=device_array.shape
|
|
130
130
|
)
|
|
@@ -161,7 +161,7 @@ def ternarize(
|
|
|
161
161
|
return np.where(device_array < eta1, 0.0, np.where(device_array >= eta2, 1.0, 0.5))
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def trim(device_array: np.ndarray, buffer_thickness:
|
|
164
|
+
def trim(device_array: np.ndarray, buffer_thickness: dict = None) -> np.ndarray:
|
|
165
165
|
"""
|
|
166
166
|
Trim the input ndarray by removing rows and columns that are completely zero.
|
|
167
167
|
|
|
@@ -169,25 +169,28 @@ def trim(device_array: np.ndarray, buffer_thickness: int = 0) -> np.ndarray:
|
|
|
169
169
|
----------
|
|
170
170
|
device_array : np.ndarray
|
|
171
171
|
The input array to be trimmed.
|
|
172
|
-
buffer_thickness :
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
buffer_thickness : dict, optional
|
|
173
|
+
A dictionary specifying the thickness of the buffer to leave around the non-zero
|
|
174
|
+
elements of the array. Should contain keys 'top', 'bottom', 'left', 'right'.
|
|
175
|
+
Defaults to None, which means no buffer is added.
|
|
175
176
|
|
|
176
177
|
Returns
|
|
177
178
|
-------
|
|
178
179
|
np.ndarray
|
|
179
180
|
The trimmed array, potentially with a buffer around the non-zero elements.
|
|
180
181
|
"""
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
if buffer_thickness is None:
|
|
183
|
+
buffer_thickness = {"top": 0, "bottom": 0, "left": 0, "right": 0}
|
|
184
|
+
|
|
185
|
+
nonzero_rows, nonzero_cols = np.nonzero(np.squeeze(device_array))
|
|
186
|
+
row_min = max(nonzero_rows.min() - buffer_thickness.get("top", 0), 0)
|
|
184
187
|
row_max = min(
|
|
185
|
-
nonzero_rows.max() + buffer_thickness + 1,
|
|
188
|
+
nonzero_rows.max() + buffer_thickness.get("bottom", 0) + 1,
|
|
186
189
|
device_array.shape[0],
|
|
187
190
|
)
|
|
188
|
-
col_min = max(nonzero_cols.min() - buffer_thickness, 0)
|
|
191
|
+
col_min = max(nonzero_cols.min() - buffer_thickness.get("left", 0), 0)
|
|
189
192
|
col_max = min(
|
|
190
|
-
nonzero_cols.max() + buffer_thickness + 1,
|
|
193
|
+
nonzero_cols.max() + buffer_thickness.get("right", 0) + 1,
|
|
191
194
|
device_array.shape[1],
|
|
192
195
|
)
|
|
193
196
|
return device_array[
|
|
@@ -237,10 +240,13 @@ def rotate(device_array: np.ndarray, angle: float) -> np.ndarray:
|
|
|
237
240
|
"""
|
|
238
241
|
center = (device_array.shape[1] / 2, device_array.shape[0] / 2)
|
|
239
242
|
rotation_matrix = cv2.getRotationMatrix2D(center=center, angle=angle, scale=1)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
243
|
+
return np.expand_dims(
|
|
244
|
+
cv2.warpAffine(
|
|
245
|
+
device_array,
|
|
246
|
+
M=rotation_matrix,
|
|
247
|
+
dsize=(device_array.shape[1], device_array.shape[0]),
|
|
248
|
+
),
|
|
249
|
+
axis=-1,
|
|
244
250
|
)
|
|
245
251
|
return np.expand_dims(rotated_device_array, axis=-1)
|
|
246
252
|
|
prefab/models.py
CHANGED
|
@@ -97,6 +97,24 @@ generic_DUV_SOI = Fab(
|
|
|
97
97
|
has_sidewall=True,
|
|
98
98
|
)
|
|
99
99
|
|
|
100
|
+
ANT_NanoSOI_ANF0_d8 = Model(
|
|
101
|
+
fab=ANT_NanoSOI,
|
|
102
|
+
version="ANF0",
|
|
103
|
+
version_date=date(2024, 1, 1),
|
|
104
|
+
dataset="d8",
|
|
105
|
+
dataset_date=date(2024, 1, 1),
|
|
106
|
+
tag="",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
ANT_NanoSOI_ANF1_d8 = Model(
|
|
110
|
+
fab=ANT_NanoSOI,
|
|
111
|
+
version="ANF1",
|
|
112
|
+
version_date=date(2024, 5, 6),
|
|
113
|
+
dataset="d8",
|
|
114
|
+
dataset_date=date(2024, 1, 1),
|
|
115
|
+
tag="",
|
|
116
|
+
)
|
|
117
|
+
|
|
100
118
|
ANT_NanoSOI_ANF1_d9 = Model(
|
|
101
119
|
fab=ANT_NanoSOI,
|
|
102
120
|
version="ANF1",
|
|
@@ -106,6 +124,15 @@ ANT_NanoSOI_ANF1_d9 = Model(
|
|
|
106
124
|
tag="",
|
|
107
125
|
)
|
|
108
126
|
|
|
127
|
+
ANT_NanoSOI_ANF1_d10 = Model(
|
|
128
|
+
fab=ANT_NanoSOI,
|
|
129
|
+
version="ANF1",
|
|
130
|
+
version_date=date(2024, 5, 6),
|
|
131
|
+
dataset="d10",
|
|
132
|
+
dataset_date=date(2024, 6, 8),
|
|
133
|
+
tag="",
|
|
134
|
+
)
|
|
135
|
+
|
|
109
136
|
ANT_SiN_ANF1_d1 = Model(
|
|
110
137
|
fab=ANT_SiN,
|
|
111
138
|
version="ANF1",
|
|
@@ -125,8 +152,11 @@ generic_DUV_SOI_ANF1_d0 = Model(
|
|
|
125
152
|
)
|
|
126
153
|
|
|
127
154
|
models = dict(
|
|
128
|
-
ANT_NanoSOI=
|
|
155
|
+
ANT_NanoSOI=ANT_NanoSOI_ANF1_d10,
|
|
156
|
+
ANT_NanoSOI_ANF0_d8=ANT_NanoSOI_ANF0_d8,
|
|
157
|
+
ANT_NanoSOI_ANF1_d8=ANT_NanoSOI_ANF1_d8,
|
|
129
158
|
ANT_NanoSOI_ANF1_d9=ANT_NanoSOI_ANF1_d9,
|
|
159
|
+
ANT_NanoSOI_ANF1_d10=ANT_NanoSOI_ANF1_d10,
|
|
130
160
|
ANT_SiN=ANT_SiN_ANF1_d1,
|
|
131
161
|
ANT_SiN_ANF1_d1=ANT_SiN_ANF1_d1,
|
|
132
162
|
generic_DUV_SOI=generic_DUV_SOI_ANF1_d0,
|
prefab/read.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Provides functions to create
|
|
1
|
+
"""Provides functions to create a Device from various data sources."""
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ from .device import Device
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def from_ndarray(
|
|
14
|
-
ndarray: np.ndarray, resolution:
|
|
14
|
+
ndarray: np.ndarray, resolution: float = 1.0, binarize: bool = True, **kwargs
|
|
15
15
|
) -> Device:
|
|
16
16
|
"""
|
|
17
17
|
Create a Device from an ndarray.
|
|
@@ -20,13 +20,13 @@ def from_ndarray(
|
|
|
20
20
|
----------
|
|
21
21
|
ndarray : np.ndarray
|
|
22
22
|
The input array representing the device layout.
|
|
23
|
-
resolution :
|
|
24
|
-
The resolution of the ndarray in nanometers per pixel, defaulting to 1 nm per
|
|
23
|
+
resolution : float, optional
|
|
24
|
+
The resolution of the ndarray in nanometers per pixel, defaulting to 1.0 nm per
|
|
25
25
|
pixel. If specified, the input array will be resized based on this resolution to
|
|
26
26
|
match the desired physical size.
|
|
27
27
|
binarize : bool, optional
|
|
28
28
|
If True, the input array will be binarized (converted to binary values) before
|
|
29
|
-
conversion to a Device object. This is useful for processing grayscale
|
|
29
|
+
conversion to a Device object. This is useful for processing grayscale arrays
|
|
30
30
|
into binary masks. Defaults to True.
|
|
31
31
|
**kwargs
|
|
32
32
|
Additional keyword arguments to be passed to the Device constructor.
|
|
@@ -38,7 +38,10 @@ def from_ndarray(
|
|
|
38
38
|
binarization.
|
|
39
39
|
"""
|
|
40
40
|
device_array = ndarray
|
|
41
|
-
|
|
41
|
+
if resolution != 1.0:
|
|
42
|
+
device_array = cv2.resize(
|
|
43
|
+
device_array, dsize=(0, 0), fx=resolution, fy=resolution
|
|
44
|
+
)
|
|
42
45
|
if binarize:
|
|
43
46
|
device_array = geometry.binarize_hard(device_array)
|
|
44
47
|
return Device(device_array=device_array, **kwargs)
|
|
@@ -55,12 +58,11 @@ def from_img(
|
|
|
55
58
|
img_path : str
|
|
56
59
|
The path to the image file to be converted into a Device object.
|
|
57
60
|
img_width_nm : int, optional
|
|
58
|
-
The
|
|
59
|
-
|
|
60
|
-
performed.
|
|
61
|
+
The width of the image in nanometers. If specified, the Device will be resized
|
|
62
|
+
to this width while maintaining aspect ratio. If None, no resizing is performed.
|
|
61
63
|
binarize : bool, optional
|
|
62
64
|
If True, the image will be binarized (converted to binary values) before
|
|
63
|
-
conversion to a Device object. This is useful for
|
|
65
|
+
conversion to a Device object. This is useful for processing grayscale images
|
|
64
66
|
into binary masks. Defaults to True.
|
|
65
67
|
**kwargs
|
|
66
68
|
Additional keyword arguments to be passed to the Device constructor.
|
|
@@ -73,8 +75,10 @@ def from_img(
|
|
|
73
75
|
"""
|
|
74
76
|
device_array = cv2.imread(img_path, flags=cv2.IMREAD_GRAYSCALE) / 255
|
|
75
77
|
if img_width_nm is not None:
|
|
76
|
-
|
|
77
|
-
device_array = cv2.resize(
|
|
78
|
+
resolution = img_width_nm / device_array.shape[1]
|
|
79
|
+
device_array = cv2.resize(
|
|
80
|
+
device_array, dsize=(0, 0), fx=resolution, fy=resolution
|
|
81
|
+
)
|
|
78
82
|
if binarize:
|
|
79
83
|
device_array = geometry.binarize_hard(device_array)
|
|
80
84
|
return Device(device_array=device_array, **kwargs)
|
|
@@ -134,7 +138,8 @@ def from_gdstk(
|
|
|
134
138
|
gdstk_cell : gdstk.Cell
|
|
135
139
|
The gdstk.Cell object to be converted into a Device object.
|
|
136
140
|
gds_layer : tuple[int, int], optional
|
|
137
|
-
A tuple specifying the layer and datatype to be used. Defaults to
|
|
141
|
+
A tuple specifying the layer and datatype to be used from the cell. Defaults to
|
|
142
|
+
(1, 0).
|
|
138
143
|
bounds : tuple[tuple[int, int], tuple[int, int]], optional
|
|
139
144
|
A tuple specifying the bounds for cropping the cell before conversion, formatted
|
|
140
145
|
as ((min_x, min_y), (max_x, max_y)), in units of the GDS file. If None, the
|
|
@@ -159,6 +164,24 @@ def _gdstk_to_device_array(
|
|
|
159
164
|
gds_layer: tuple[int, int] = (1, 0),
|
|
160
165
|
bounds: tuple[tuple[int, int], tuple[int, int]] = None,
|
|
161
166
|
) -> np.ndarray:
|
|
167
|
+
"""
|
|
168
|
+
Convert a gdstk.Cell to a device array.
|
|
169
|
+
|
|
170
|
+
Parameters
|
|
171
|
+
----------
|
|
172
|
+
gdstk_cell : gdstk.Cell
|
|
173
|
+
The gdstk.Cell object to be converted.
|
|
174
|
+
gds_layer : tuple[int, int], optional
|
|
175
|
+
The layer and datatype to be used from the cell. Defaults to (1, 0).
|
|
176
|
+
bounds : tuple[tuple[int, int], tuple[int, int]], optional
|
|
177
|
+
Bounds for cropping the cell, formatted as ((min_x, min_y), (max_x, max_y)).
|
|
178
|
+
If None, the entire cell is used.
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
np.ndarray
|
|
183
|
+
The resulting device array.
|
|
184
|
+
"""
|
|
162
185
|
polygons = gdstk_cell.get_polygons(layer=gds_layer[0], datatype=gds_layer[1])
|
|
163
186
|
if bounds:
|
|
164
187
|
polygons = gdstk.slice(
|
|
@@ -184,12 +207,12 @@ def _gdstk_to_device_array(
|
|
|
184
207
|
]
|
|
185
208
|
for vertex in polygon.points
|
|
186
209
|
],
|
|
187
|
-
dtype=np.int32,
|
|
188
210
|
)
|
|
189
211
|
for polygon in polygons
|
|
190
212
|
]
|
|
191
213
|
device_array = np.zeros(
|
|
192
|
-
(int(bounds[1][1] - bounds[0][1]), int(bounds[1][0] - bounds[0][0]))
|
|
214
|
+
(int(bounds[1][1] - bounds[0][1]), int(bounds[1][0] - bounds[0][0])),
|
|
215
|
+
dtype=np.uint8,
|
|
193
216
|
)
|
|
194
217
|
cv2.fillPoly(img=device_array, pts=contours, color=(1, 1, 1))
|
|
195
218
|
device_array = np.flipud(device_array)
|
|
@@ -200,7 +223,7 @@ def from_sem(
|
|
|
200
223
|
sem_path: str,
|
|
201
224
|
sem_resolution: float = None,
|
|
202
225
|
sem_resolution_key: str = None,
|
|
203
|
-
binarize: bool =
|
|
226
|
+
binarize: bool = False,
|
|
204
227
|
bounds: tuple[tuple[int, int], tuple[int, int]] = None,
|
|
205
228
|
**kwargs,
|
|
206
229
|
) -> Device:
|
|
@@ -220,7 +243,7 @@ def from_sem(
|
|
|
220
243
|
binarize : bool, optional
|
|
221
244
|
If True, the SEM image will be binarized (converted to binary values) before
|
|
222
245
|
conversion to a Device object. This is needed for processing grayscale images
|
|
223
|
-
into binary masks. Defaults to
|
|
246
|
+
into binary masks. Defaults to False.
|
|
224
247
|
bounds : tuple[tuple[int, int], tuple[int, int]], optional
|
|
225
248
|
A tuple specifying the bounds for cropping the image before conversion,
|
|
226
249
|
formatted as ((min_x, min_y), (max_x, max_y)). If None, the entire image is
|
|
@@ -244,13 +267,13 @@ def from_sem(
|
|
|
244
267
|
raise ValueError("Either sem_resolution or resolution_key must be provided.")
|
|
245
268
|
|
|
246
269
|
device_array = cv2.imread(sem_path, flags=cv2.IMREAD_GRAYSCALE)
|
|
247
|
-
|
|
248
|
-
device_array =
|
|
249
|
-
|
|
250
|
-
)
|
|
270
|
+
device_array = cv2.resize(
|
|
271
|
+
device_array, dsize=(0, 0), fx=sem_resolution, fy=sem_resolution
|
|
272
|
+
)
|
|
251
273
|
if bounds is not None:
|
|
252
274
|
device_array = device_array[
|
|
253
|
-
-bounds[1][1] :
|
|
275
|
+
device_array.shape[0] - bounds[1][1] : device_array.shape[0] - bounds[0][1],
|
|
276
|
+
bounds[0][0] : bounds[1][0],
|
|
254
277
|
]
|
|
255
278
|
if binarize:
|
|
256
279
|
device_array = geometry.binarize_sem(device_array)
|