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/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.clip(np.random.normal(loc=0.5, scale=0.5 / 2), 0.4, 0.6)
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: int = 0) -> np.ndarray:
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 : int, optional
173
- The thickness of the buffer to leave around the non-zero elements of the array.
174
- Defaults to 0, which means no buffer is added.
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
- flattened_device_array = np.squeeze(flatten(device_array))
182
- nonzero_rows, nonzero_cols = np.nonzero(flattened_device_array)
183
- row_min = max(nonzero_rows.min() - buffer_thickness, 0)
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
- rotated_device_array = cv2.warpAffine(
241
- flatten(device_array),
242
- M=rotation_matrix,
243
- dsize=(device_array.shape[1], device_array.shape[0]),
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=ANT_NanoSOI_ANF1_d9,
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 Devices from various data sources."""
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: int = 1, binarize: bool = True, **kwargs
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 : int, optional
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 images
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
- device_array = cv2.resize(device_array, dsize=(0, 0), fx=resolution, fy=resolution)
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 desired width of the device in nanometers. If specified, the image will be
59
- resized to this width while maintaining aspect ratio. If None, no resizing is
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 converting grayscale images
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
- scale = img_width_nm / device_array.shape[1]
77
- device_array = cv2.resize(device_array, dsize=(0, 0), fx=scale, fy=scale)
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 (1, 0).
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 = True,
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 True.
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
- if sem_resolution is not None:
248
- device_array = cv2.resize(
249
- device_array, dsize=(0, 0), fx=sem_resolution, fy=sem_resolution
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] : -bounds[0][1], bounds[0][0] : bounds[1][0]
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)