prefab 1.0.2__py3-none-any.whl → 1.0.4__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 +167 -105
- 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.4.dist-info}/METADATA +7 -5
- prefab-1.0.4.dist-info/RECORD +12 -0
- prefab-1.0.2.dist-info/RECORD +0 -11
- {prefab-1.0.2.dist-info → prefab-1.0.4.dist-info}/WHEEL +0 -0
- {prefab-1.0.2.dist-info → prefab-1.0.4.dist-info}/licenses/LICENSE +0 -0
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)
|