prefab 1.2.0__py3-none-any.whl → 1.4.0__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 +1 -1
- prefab/__main__.py +29 -24
- prefab/compare.py +49 -61
- prefab/device.py +163 -418
- prefab/geometry.py +102 -136
- prefab/models.py +19 -48
- prefab/predict.py +281 -249
- prefab/py.typed +0 -0
- prefab/read.py +57 -303
- prefab/shapes.py +357 -187
- {prefab-1.2.0.dist-info → prefab-1.4.0.dist-info}/METADATA +21 -35
- prefab-1.4.0.dist-info/RECORD +15 -0
- prefab-1.2.0.dist-info/RECORD +0 -14
- {prefab-1.2.0.dist-info → prefab-1.4.0.dist-info}/WHEEL +0 -0
- {prefab-1.2.0.dist-info → prefab-1.4.0.dist-info}/entry_points.txt +0 -0
- {prefab-1.2.0.dist-info → prefab-1.4.0.dist-info}/licenses/LICENSE +0 -0
prefab/geometry.py
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Functions for manipulating and transforming device geometry arrays.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
This module provides utilities for common geometric operations on numpy arrays
|
|
5
|
+
representing device geometries, including normalization, binarization, trimming,
|
|
6
|
+
padding, blurring, rotation, morphological operations (erosion/dilation), and
|
|
7
|
+
flattening. All functions operate on npt.NDArray[np.float64] arrays.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import cast
|
|
4
11
|
|
|
5
12
|
import cv2
|
|
6
13
|
import numpy as np
|
|
14
|
+
import numpy.typing as npt
|
|
7
15
|
|
|
8
16
|
|
|
9
|
-
def normalize(device_array: np.
|
|
17
|
+
def normalize(device_array: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
|
|
10
18
|
"""
|
|
11
19
|
Normalize the input ndarray to have values between 0 and 1.
|
|
12
20
|
|
|
13
21
|
Parameters
|
|
14
22
|
----------
|
|
15
|
-
device_array : np.
|
|
23
|
+
device_array : npt.NDArray[np.float64]
|
|
16
24
|
The input array to be normalized.
|
|
17
25
|
|
|
18
26
|
Returns
|
|
19
27
|
-------
|
|
20
|
-
np.
|
|
28
|
+
npt.NDArray[np.float64]
|
|
21
29
|
The normalized array with values scaled between 0 and 1.
|
|
22
30
|
"""
|
|
23
31
|
return (device_array - np.min(device_array)) / (
|
|
@@ -26,14 +34,14 @@ def normalize(device_array: np.ndarray) -> np.ndarray:
|
|
|
26
34
|
|
|
27
35
|
|
|
28
36
|
def binarize(
|
|
29
|
-
device_array: np.
|
|
30
|
-
) -> np.
|
|
37
|
+
device_array: npt.NDArray[np.float64], eta: float = 0.5, beta: float = np.inf
|
|
38
|
+
) -> npt.NDArray[np.float64]:
|
|
31
39
|
"""
|
|
32
40
|
Binarize the input ndarray based on a threshold and a scaling factor.
|
|
33
41
|
|
|
34
42
|
Parameters
|
|
35
43
|
----------
|
|
36
|
-
device_array : np.
|
|
44
|
+
device_array : npt.NDArray[np.float64]
|
|
37
45
|
The input array to be binarized.
|
|
38
46
|
eta : float
|
|
39
47
|
The threshold value for binarization. Defaults to 0.5.
|
|
@@ -43,15 +51,19 @@ def binarize(
|
|
|
43
51
|
|
|
44
52
|
Returns
|
|
45
53
|
-------
|
|
46
|
-
np.
|
|
54
|
+
npt.NDArray[np.float64]
|
|
47
55
|
The binarized array with elements scaled to 0 or 1.
|
|
48
56
|
"""
|
|
49
|
-
return (
|
|
50
|
-
|
|
57
|
+
return cast(
|
|
58
|
+
npt.NDArray[np.float64],
|
|
59
|
+
(np.tanh(beta * eta) + np.tanh(beta * (device_array - eta)))
|
|
60
|
+
/ (np.tanh(beta * eta) + np.tanh(beta * (1 - eta))),
|
|
51
61
|
)
|
|
52
62
|
|
|
53
63
|
|
|
54
|
-
def binarize_hard(
|
|
64
|
+
def binarize_hard(
|
|
65
|
+
device_array: npt.NDArray[np.float64], eta: float = 0.5
|
|
66
|
+
) -> npt.NDArray[np.float64]:
|
|
55
67
|
"""
|
|
56
68
|
Apply a hard threshold to binarize the input ndarray. The `binarize` function is
|
|
57
69
|
generally preferred for most use cases, but it can create numerical artifacts for
|
|
@@ -59,46 +71,24 @@ def binarize_hard(device_array: np.ndarray, eta: float = 0.5) -> np.ndarray:
|
|
|
59
71
|
|
|
60
72
|
Parameters
|
|
61
73
|
----------
|
|
62
|
-
device_array : np.
|
|
74
|
+
device_array : npt.NDArray[np.float64]
|
|
63
75
|
The input array to be binarized.
|
|
64
76
|
eta : float
|
|
65
77
|
The threshold value for binarization. Defaults to 0.5.
|
|
66
78
|
|
|
67
79
|
Returns
|
|
68
80
|
-------
|
|
69
|
-
np.
|
|
81
|
+
npt.NDArray[np.float64]
|
|
70
82
|
The binarized array with elements set to 0 or 1 based on the threshold.
|
|
71
83
|
"""
|
|
72
84
|
return np.where(device_array < eta, 0.0, 1.0)
|
|
73
85
|
|
|
74
86
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
Binarize a grayscale scanning electron microscope (SEM) image.
|
|
78
|
-
|
|
79
|
-
This function applies Otsu's method to automatically determine the optimal threshold
|
|
80
|
-
value for binarization of a grayscale SEM image.
|
|
81
|
-
|
|
82
|
-
Parameters
|
|
83
|
-
----------
|
|
84
|
-
sem_array : np.ndarray
|
|
85
|
-
The input SEM image array to be binarized.
|
|
86
|
-
|
|
87
|
-
Returns
|
|
88
|
-
-------
|
|
89
|
-
np.ndarray
|
|
90
|
-
The binarized SEM image array with elements scaled to 0 or 1.
|
|
91
|
-
"""
|
|
92
|
-
return cv2.threshold(
|
|
93
|
-
sem_array.astype("uint8"), 0, 1, cv2.THRESH_BINARY + cv2.THRESH_OTSU
|
|
94
|
-
)[1]
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def binarize_monte_carlo(
|
|
98
|
-
device_array: np.ndarray,
|
|
87
|
+
def binarize_with_roughness(
|
|
88
|
+
device_array: npt.NDArray[np.float64],
|
|
99
89
|
noise_magnitude: float,
|
|
100
90
|
blur_radius: float,
|
|
101
|
-
) -> np.
|
|
91
|
+
) -> npt.NDArray[np.float64]:
|
|
102
92
|
"""
|
|
103
93
|
Binarize the input ndarray using a dynamic thresholding approach to simulate surface
|
|
104
94
|
roughness.
|
|
@@ -116,7 +106,7 @@ def binarize_monte_carlo(
|
|
|
116
106
|
|
|
117
107
|
Parameters
|
|
118
108
|
----------
|
|
119
|
-
device_array : np.
|
|
109
|
+
device_array : npt.NDArray[np.float64]
|
|
120
110
|
The input array to be binarized.
|
|
121
111
|
noise_magnitude : float
|
|
122
112
|
The standard deviation of the Gaussian distribution used to generate noise for
|
|
@@ -127,34 +117,35 @@ def binarize_monte_carlo(
|
|
|
127
117
|
|
|
128
118
|
Returns
|
|
129
119
|
-------
|
|
130
|
-
np.
|
|
120
|
+
npt.NDArray[np.float64]
|
|
131
121
|
The binarized array with elements set to 0 or 1 based on the dynamically
|
|
132
122
|
generated threshold.
|
|
133
123
|
"""
|
|
134
124
|
device_array = np.squeeze(device_array)
|
|
135
|
-
|
|
125
|
+
base_threshold_raw = float(np.random.normal(loc=0.5, scale=0.1))
|
|
126
|
+
base_threshold = max(0.2, min(base_threshold_raw, 0.8))
|
|
136
127
|
threshold_noise = np.random.normal(
|
|
137
128
|
loc=0, scale=noise_magnitude, size=device_array.shape
|
|
138
129
|
)
|
|
139
|
-
spatial_threshold = cv2.GaussianBlur(
|
|
130
|
+
spatial_threshold: npt.NDArray[np.float64] = cv2.GaussianBlur(
|
|
140
131
|
threshold_noise, ksize=(0, 0), sigmaX=blur_radius
|
|
141
|
-
)
|
|
142
|
-
dynamic_threshold = base_threshold + spatial_threshold
|
|
132
|
+
).astype(np.float64)
|
|
133
|
+
dynamic_threshold: npt.NDArray[np.float64] = base_threshold + spatial_threshold
|
|
143
134
|
binarized_array = np.where(device_array < dynamic_threshold, 0.0, 1.0)
|
|
144
135
|
binarized_array = np.expand_dims(binarized_array, axis=-1)
|
|
145
136
|
return binarized_array
|
|
146
137
|
|
|
147
138
|
|
|
148
139
|
def ternarize(
|
|
149
|
-
device_array: np.
|
|
150
|
-
) -> np.
|
|
140
|
+
device_array: npt.NDArray[np.float64], eta1: float = 1 / 3, eta2: float = 2 / 3
|
|
141
|
+
) -> npt.NDArray[np.float64]:
|
|
151
142
|
"""
|
|
152
143
|
Ternarize the input ndarray based on two thresholds. This function is useful for
|
|
153
144
|
flattened devices with angled sidewalls (i.e., three segments).
|
|
154
145
|
|
|
155
146
|
Parameters
|
|
156
147
|
----------
|
|
157
|
-
device_array : np.
|
|
148
|
+
device_array : npt.NDArray[np.float64]
|
|
158
149
|
The input array to be ternarized.
|
|
159
150
|
eta1 : float
|
|
160
151
|
The first threshold value for ternarization. Defaults to 1/3.
|
|
@@ -163,21 +154,22 @@ def ternarize(
|
|
|
163
154
|
|
|
164
155
|
Returns
|
|
165
156
|
-------
|
|
166
|
-
np.
|
|
157
|
+
npt.NDArray[np.float64]
|
|
167
158
|
The ternarized array with elements set to 0, 0.5, or 1 based on the thresholds.
|
|
168
159
|
"""
|
|
169
160
|
return np.where(device_array < eta1, 0.0, np.where(device_array >= eta2, 1.0, 0.5))
|
|
170
161
|
|
|
171
162
|
|
|
172
163
|
def trim(
|
|
173
|
-
device_array: np.
|
|
174
|
-
|
|
164
|
+
device_array: npt.NDArray[np.float64],
|
|
165
|
+
buffer_thickness: dict[str, int] | None = None,
|
|
166
|
+
) -> npt.NDArray[np.float64]:
|
|
175
167
|
"""
|
|
176
168
|
Trim the input ndarray by removing rows and columns that are completely zero.
|
|
177
169
|
|
|
178
170
|
Parameters
|
|
179
171
|
----------
|
|
180
|
-
device_array : np.
|
|
172
|
+
device_array : npt.NDArray[np.float64]
|
|
181
173
|
The input array to be trimmed.
|
|
182
174
|
buffer_thickness : Optional[dict[str, int]]
|
|
183
175
|
A dictionary specifying the thickness of the buffer to leave around the non-zero
|
|
@@ -186,22 +178,28 @@ def trim(
|
|
|
186
178
|
|
|
187
179
|
Returns
|
|
188
180
|
-------
|
|
189
|
-
np.
|
|
181
|
+
npt.NDArray[np.float64]
|
|
190
182
|
The trimmed array, potentially with a buffer around the non-zero elements.
|
|
191
183
|
"""
|
|
192
184
|
if buffer_thickness is None:
|
|
193
185
|
buffer_thickness = {"top": 0, "bottom": 0, "left": 0, "right": 0}
|
|
194
186
|
|
|
195
|
-
|
|
196
|
-
|
|
187
|
+
nonzero_indices = np.nonzero(np.squeeze(device_array))
|
|
188
|
+
nonzero_rows = nonzero_indices[0]
|
|
189
|
+
nonzero_cols = nonzero_indices[1]
|
|
190
|
+
|
|
191
|
+
row_min_val = int(nonzero_rows.min())
|
|
192
|
+
row_max_val = int(nonzero_rows.max())
|
|
193
|
+
col_min_val = int(nonzero_cols.min())
|
|
194
|
+
col_max_val = int(nonzero_cols.max())
|
|
195
|
+
|
|
196
|
+
row_min = max(row_min_val - buffer_thickness.get("top", 0), 0)
|
|
197
197
|
row_max = min(
|
|
198
|
-
|
|
199
|
-
device_array.shape[0],
|
|
198
|
+
row_max_val + buffer_thickness.get("bottom", 0) + 1, device_array.shape[0]
|
|
200
199
|
)
|
|
201
|
-
col_min = max(
|
|
200
|
+
col_min = max(col_min_val - buffer_thickness.get("left", 0), 0)
|
|
202
201
|
col_max = min(
|
|
203
|
-
|
|
204
|
-
device_array.shape[1],
|
|
202
|
+
col_max_val + buffer_thickness.get("right", 0) + 1, device_array.shape[1]
|
|
205
203
|
)
|
|
206
204
|
return device_array[
|
|
207
205
|
row_min:row_max,
|
|
@@ -209,20 +207,22 @@ def trim(
|
|
|
209
207
|
]
|
|
210
208
|
|
|
211
209
|
|
|
212
|
-
def pad(
|
|
210
|
+
def pad(
|
|
211
|
+
device_array: npt.NDArray[np.float64], pad_width: int
|
|
212
|
+
) -> npt.NDArray[np.float64]:
|
|
213
213
|
"""
|
|
214
214
|
Pad the input ndarray uniformly with a specified width on all sides.
|
|
215
215
|
|
|
216
216
|
Parameters
|
|
217
217
|
----------
|
|
218
|
-
device_array : np.
|
|
218
|
+
device_array : npt.NDArray[np.float64]
|
|
219
219
|
The input array to be padded.
|
|
220
220
|
pad_width : int
|
|
221
221
|
The number of pixels to pad on each side.
|
|
222
222
|
|
|
223
223
|
Returns
|
|
224
224
|
-------
|
|
225
|
-
np.
|
|
225
|
+
npt.NDArray[np.float64]
|
|
226
226
|
The padded array.
|
|
227
227
|
"""
|
|
228
228
|
return np.pad(
|
|
@@ -233,13 +233,15 @@ def pad(device_array: np.ndarray, pad_width: int) -> np.ndarray:
|
|
|
233
233
|
)
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
def blur(
|
|
236
|
+
def blur(
|
|
237
|
+
device_array: npt.NDArray[np.float64], sigma: float = 1.0
|
|
238
|
+
) -> npt.NDArray[np.float64]:
|
|
237
239
|
"""
|
|
238
240
|
Apply Gaussian blur to the input ndarray and normalize the result.
|
|
239
241
|
|
|
240
242
|
Parameters
|
|
241
243
|
----------
|
|
242
|
-
device_array : np.
|
|
244
|
+
device_array : npt.NDArray[np.float64]
|
|
243
245
|
The input array to be blurred.
|
|
244
246
|
sigma : float
|
|
245
247
|
The standard deviation for the Gaussian kernel. This controls the amount of
|
|
@@ -247,21 +249,28 @@ def blur(device_array: np.ndarray, sigma: float = 1.0) -> np.ndarray:
|
|
|
247
249
|
|
|
248
250
|
Returns
|
|
249
251
|
-------
|
|
250
|
-
np.
|
|
252
|
+
npt.NDArray[np.float64]
|
|
251
253
|
The blurred and normalized array with values scaled between 0 and 1.
|
|
252
254
|
"""
|
|
253
255
|
return np.expand_dims(
|
|
254
|
-
normalize(
|
|
256
|
+
normalize(
|
|
257
|
+
cv2.GaussianBlur(device_array, ksize=(0, 0), sigmaX=sigma).astype(
|
|
258
|
+
np.float64
|
|
259
|
+
)
|
|
260
|
+
),
|
|
261
|
+
axis=-1,
|
|
255
262
|
)
|
|
256
263
|
|
|
257
264
|
|
|
258
|
-
def rotate(
|
|
265
|
+
def rotate(
|
|
266
|
+
device_array: npt.NDArray[np.float64], angle: float
|
|
267
|
+
) -> npt.NDArray[np.float64]:
|
|
259
268
|
"""
|
|
260
269
|
Rotate the input ndarray by a given angle.
|
|
261
270
|
|
|
262
271
|
Parameters
|
|
263
272
|
----------
|
|
264
|
-
device_array : np.
|
|
273
|
+
device_array : npt.NDArray[np.float64]
|
|
265
274
|
The input array to be rotated.
|
|
266
275
|
angle : float
|
|
267
276
|
The angle of rotation in degrees. Positive values mean counter-clockwise
|
|
@@ -269,7 +278,7 @@ def rotate(device_array: np.ndarray, angle: float) -> np.ndarray:
|
|
|
269
278
|
|
|
270
279
|
Returns
|
|
271
280
|
-------
|
|
272
|
-
np.
|
|
281
|
+
npt.NDArray[np.float64]
|
|
273
282
|
The rotated array.
|
|
274
283
|
"""
|
|
275
284
|
center = (device_array.shape[1] / 2, device_array.shape[0] / 2)
|
|
@@ -279,116 +288,73 @@ def rotate(device_array: np.ndarray, angle: float) -> np.ndarray:
|
|
|
279
288
|
device_array,
|
|
280
289
|
M=rotation_matrix,
|
|
281
290
|
dsize=(device_array.shape[1], device_array.shape[0]),
|
|
282
|
-
),
|
|
291
|
+
).astype(np.float64),
|
|
283
292
|
axis=-1,
|
|
284
293
|
)
|
|
285
294
|
|
|
286
295
|
|
|
287
|
-
def erode(
|
|
296
|
+
def erode(
|
|
297
|
+
device_array: npt.NDArray[np.float64], kernel_size: int
|
|
298
|
+
) -> npt.NDArray[np.float64]:
|
|
288
299
|
"""
|
|
289
300
|
Erode the input ndarray using a specified kernel size and number of iterations.
|
|
290
301
|
|
|
291
302
|
Parameters
|
|
292
303
|
----------
|
|
293
|
-
device_array : np.
|
|
304
|
+
device_array : npt.NDArray[np.float64]
|
|
294
305
|
The input array representing the device geometry to be eroded.
|
|
295
306
|
kernel_size : int
|
|
296
307
|
The size of the kernel used for erosion.
|
|
297
308
|
|
|
298
309
|
Returns
|
|
299
310
|
-------
|
|
300
|
-
np.
|
|
311
|
+
npt.NDArray[np.float64]
|
|
301
312
|
The eroded array.
|
|
302
313
|
"""
|
|
303
314
|
kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8)
|
|
304
|
-
return np.expand_dims(
|
|
315
|
+
return np.expand_dims(
|
|
316
|
+
cv2.erode(device_array, kernel=kernel).astype(np.float64), axis=-1
|
|
317
|
+
)
|
|
305
318
|
|
|
306
319
|
|
|
307
|
-
def dilate(
|
|
320
|
+
def dilate(
|
|
321
|
+
device_array: npt.NDArray[np.float64], kernel_size: int
|
|
322
|
+
) -> npt.NDArray[np.float64]:
|
|
308
323
|
"""
|
|
309
324
|
Dilate the input ndarray using a specified kernel size.
|
|
310
325
|
|
|
311
326
|
Parameters
|
|
312
327
|
----------
|
|
313
|
-
device_array : np.
|
|
328
|
+
device_array : npt.NDArray[np.float64]
|
|
314
329
|
The input array representing the device geometry to be dilated.
|
|
315
330
|
kernel_size : int
|
|
316
331
|
The size of the kernel used for dilation.
|
|
317
332
|
|
|
318
333
|
Returns
|
|
319
334
|
-------
|
|
320
|
-
np.
|
|
335
|
+
npt.NDArray[np.float64]
|
|
321
336
|
The dilated array.
|
|
322
337
|
"""
|
|
323
338
|
kernel = np.ones((kernel_size, kernel_size), dtype=np.uint8)
|
|
324
|
-
return np.expand_dims(
|
|
339
|
+
return np.expand_dims(
|
|
340
|
+
cv2.dilate(device_array, kernel=kernel).astype(np.float64), axis=-1
|
|
341
|
+
)
|
|
325
342
|
|
|
326
343
|
|
|
327
|
-
def flatten(device_array: np.
|
|
344
|
+
def flatten(device_array: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]:
|
|
328
345
|
"""
|
|
329
346
|
Flatten the input ndarray by summing the vertical layers and normalizing the result.
|
|
330
347
|
|
|
331
348
|
Parameters
|
|
332
349
|
----------
|
|
333
|
-
device_array : np.
|
|
350
|
+
device_array : npt.NDArray[np.float64]
|
|
334
351
|
The input array to be flattened.
|
|
335
352
|
|
|
336
353
|
Returns
|
|
337
354
|
-------
|
|
338
|
-
np.
|
|
355
|
+
npt.NDArray[np.float64]
|
|
339
356
|
The flattened array with values scaled between 0 and 1.
|
|
340
357
|
"""
|
|
341
|
-
return normalize(
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def enforce_feature_size(
|
|
345
|
-
device_array: np.ndarray, min_feature_size: int, strel: str = "disk"
|
|
346
|
-
) -> np.ndarray:
|
|
347
|
-
"""
|
|
348
|
-
Enforce a minimum feature size on the device geometry.
|
|
349
|
-
|
|
350
|
-
This function applies morphological operations to ensure that all features in the
|
|
351
|
-
device geometry are at least the specified minimum size. It uses either a disk
|
|
352
|
-
or square structuring element for the operations.
|
|
353
|
-
|
|
354
|
-
Notes
|
|
355
|
-
-----
|
|
356
|
-
This function does not guarantee that the minimum feature size is enforced in all
|
|
357
|
-
cases. A better process is needed.
|
|
358
|
-
|
|
359
|
-
Parameters
|
|
360
|
-
----------
|
|
361
|
-
device_array : np.ndarray
|
|
362
|
-
The input array representing the device geometry.
|
|
363
|
-
min_feature_size : int
|
|
364
|
-
The minimum feature size to enforce, in nanometers.
|
|
365
|
-
strel : str
|
|
366
|
-
The type of structuring element to use. Can be either "disk" or "square".
|
|
367
|
-
Defaults to "disk".
|
|
368
|
-
|
|
369
|
-
Returns
|
|
370
|
-
-------
|
|
371
|
-
np.ndarray
|
|
372
|
-
The modified device array with enforced feature size.
|
|
373
|
-
|
|
374
|
-
Raises
|
|
375
|
-
------
|
|
376
|
-
ValueError
|
|
377
|
-
If an invalid structuring element type is specified.
|
|
378
|
-
"""
|
|
379
|
-
if strel == "disk":
|
|
380
|
-
kernel = cv2.getStructuringElement(
|
|
381
|
-
cv2.MORPH_ELLIPSE, (min_feature_size, min_feature_size)
|
|
382
|
-
)
|
|
383
|
-
elif strel == "square":
|
|
384
|
-
kernel = cv2.getStructuringElement(
|
|
385
|
-
cv2.MORPH_RECT, (min_feature_size, min_feature_size)
|
|
386
|
-
)
|
|
387
|
-
else:
|
|
388
|
-
raise ValueError(f"Invalid structuring element: {strel}")
|
|
389
|
-
|
|
390
|
-
device_array_2d = (device_array[:, :, 0] * 255).astype(np.uint8)
|
|
391
|
-
modified_geometry = cv2.morphologyEx(device_array_2d, cv2.MORPH_CLOSE, kernel)
|
|
392
|
-
modified_geometry = cv2.morphologyEx(modified_geometry, cv2.MORPH_OPEN, kernel)
|
|
393
|
-
|
|
394
|
-
return np.expand_dims(modified_geometry.astype(float) / 255, axis=-1)
|
|
358
|
+
return normalize(
|
|
359
|
+
cast(npt.NDArray[np.float64], np.sum(device_array, axis=-1, keepdims=True))
|
|
360
|
+
)
|
prefab/models.py
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Fabrication process model definitions and configurations.
|
|
3
|
+
|
|
4
|
+
This module defines the data structures for representing nanofabrication processes
|
|
5
|
+
and their associated machine learning models. It includes Pydantic models for
|
|
6
|
+
fabrication specifications (foundry, process) and versioned model configurations
|
|
7
|
+
(dataset, version, release dates). Pre-configured model instances are provided
|
|
8
|
+
for common fabrication processes.
|
|
9
|
+
"""
|
|
2
10
|
|
|
3
11
|
import json
|
|
4
12
|
from datetime import date
|
|
@@ -16,22 +24,10 @@ class Fab(BaseModel):
|
|
|
16
24
|
The name of the foundry where the fabrication process takes place.
|
|
17
25
|
process : str
|
|
18
26
|
The specific process used in the fabrication.
|
|
19
|
-
material : str
|
|
20
|
-
The material used in the fabrication process.
|
|
21
|
-
technology : str
|
|
22
|
-
The technology used in the fabrication process.
|
|
23
|
-
thickness : int
|
|
24
|
-
The thickness of the material used, measured in nanometers.
|
|
25
|
-
has_sidewall : bool
|
|
26
|
-
Indicates whether the fabrication has angled sidewalls.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
29
|
foundry: str
|
|
30
30
|
process: str
|
|
31
|
-
material: str
|
|
32
|
-
technology: str
|
|
33
|
-
thickness: int
|
|
34
|
-
has_sidewall: bool
|
|
35
31
|
|
|
36
32
|
|
|
37
33
|
class Model(BaseModel):
|
|
@@ -67,48 +63,23 @@ class Model(BaseModel):
|
|
|
67
63
|
tag: str
|
|
68
64
|
|
|
69
65
|
def to_json(self):
|
|
70
|
-
return json.dumps(self.
|
|
71
|
-
|
|
66
|
+
return json.dumps(self.model_dump(), default=str)
|
|
72
67
|
|
|
73
|
-
ANT_NanoSOI = Fab(
|
|
74
|
-
foundry="ANT",
|
|
75
|
-
process="NanoSOI",
|
|
76
|
-
material="SOI",
|
|
77
|
-
technology="E-Beam",
|
|
78
|
-
thickness=220,
|
|
79
|
-
has_sidewall=False,
|
|
80
|
-
)
|
|
81
68
|
|
|
82
|
-
|
|
83
|
-
foundry="
|
|
84
|
-
process="
|
|
85
|
-
material="SiN",
|
|
86
|
-
technology="E-Beam",
|
|
87
|
-
thickness=400,
|
|
88
|
-
has_sidewall=True,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
ANT_NanoSOI_ANF1_d10 = Model(
|
|
92
|
-
fab=ANT_NanoSOI,
|
|
93
|
-
version="ANF1",
|
|
94
|
-
version_date=date(2024, 5, 6),
|
|
95
|
-
dataset="d10",
|
|
96
|
-
dataset_date=date(2024, 6, 8),
|
|
97
|
-
tag="",
|
|
69
|
+
Generic = Fab(
|
|
70
|
+
foundry="Generic",
|
|
71
|
+
process="SOI",
|
|
98
72
|
)
|
|
99
73
|
|
|
100
|
-
|
|
101
|
-
fab=
|
|
74
|
+
Generic_SOI_ANF1_d0 = Model(
|
|
75
|
+
fab=Generic,
|
|
102
76
|
version="ANF1",
|
|
103
|
-
version_date=date(
|
|
104
|
-
dataset="
|
|
105
|
-
dataset_date=date(
|
|
77
|
+
version_date=date(2025, 11, 7),
|
|
78
|
+
dataset="d0",
|
|
79
|
+
dataset_date=date(2025, 11, 7),
|
|
106
80
|
tag="",
|
|
107
81
|
)
|
|
108
82
|
|
|
109
83
|
models = dict(
|
|
110
|
-
|
|
111
|
-
ANT_NanoSOI_ANF1_d10=ANT_NanoSOI_ANF1_d10,
|
|
112
|
-
ANT_SiN=ANT_SiN_ANF1_d1,
|
|
113
|
-
ANT_SiN_ANF1_d1=ANT_SiN_ANF1_d1,
|
|
84
|
+
Generic_SOI=Generic_SOI_ANF1_d0,
|
|
114
85
|
)
|