prefab 1.1.0__py3-none-any.whl → 1.1.2__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 CHANGED
@@ -5,7 +5,7 @@ Usage:
5
5
  import prefab as pf
6
6
  """
7
7
 
8
- __version__ = "1.1.0"
8
+ __version__ = "1.1.2"
9
9
 
10
10
  from . import compare, geometry, read, shapes
11
11
  from .device import BufferSpec, Device
prefab/compare.py CHANGED
@@ -1,5 +1,7 @@
1
1
  """Provides functions to measure the similarity between devices."""
2
2
 
3
+ import warnings
4
+
3
5
  import numpy as np
4
6
 
5
7
  from .device import Device
@@ -42,6 +44,11 @@ def intersection_over_union(device_a: Device, device_b: Device) -> float:
42
44
  float
43
45
  The Intersection over Union between two devices.
44
46
  """
47
+ if not device_a.is_binary or not device_b.is_binary:
48
+ warnings.warn(
49
+ "One or both devices are not binarized.", UserWarning, stacklevel=2
50
+ )
51
+
45
52
  return np.sum(
46
53
  np.logical_and(device_a.device_array, device_b.device_array)
47
54
  ) / np.sum(np.logical_or(device_a.device_array, device_b.device_array))
@@ -65,6 +72,11 @@ def hamming_distance(device_a: Device, device_b: Device) -> int:
65
72
  int
66
73
  The Hamming distance between two devices.
67
74
  """
75
+ if not device_a.is_binary or not device_b.is_binary:
76
+ warnings.warn(
77
+ "One or both devices are not binarized.", UserWarning, stacklevel=2
78
+ )
79
+
68
80
  return np.sum(device_a.device_array != device_b.device_array)
69
81
 
70
82
 
@@ -86,6 +98,11 @@ def dice_coefficient(device_a: Device, device_b: Device) -> float:
86
98
  float
87
99
  The Dice coefficient between two devices.
88
100
  """
101
+ if not device_a.is_binary or not device_b.is_binary:
102
+ warnings.warn(
103
+ "One or both devices are not binarized.", UserWarning, stacklevel=2
104
+ )
105
+
89
106
  intersection = 2.0 * np.sum(
90
107
  np.logical_and(device_a.device_array, device_b.device_array)
91
108
  )
prefab/device.py CHANGED
@@ -18,7 +18,6 @@ from PIL import Image
18
18
  from pydantic import BaseModel, Field, conint, root_validator, validator
19
19
  from scipy.ndimage import distance_transform_edt
20
20
  from skimage import measure
21
- from skimage.morphology import closing, disk, opening, square
22
21
  from tqdm import tqdm
23
22
 
24
23
  from . import compare, geometry
@@ -193,6 +192,7 @@ class Device(BaseModel):
193
192
  raise ValueError("device_array must be a 2D array.")
194
193
  return values
195
194
 
195
+ @property
196
196
  def is_binary(self) -> bool:
197
197
  """
198
198
  Check if the device geometry is binary.
@@ -493,6 +493,7 @@ class Device(BaseModel):
493
493
  binarize=False,
494
494
  gpu=gpu,
495
495
  )
496
+ semulated_array += np.random.normal(0, 0.03, semulated_array.shape)
496
497
  return self.model_copy(update={"device_array": semulated_array})
497
498
 
498
499
  def to_ndarray(self) -> np.ndarray:
@@ -695,8 +696,8 @@ class Device(BaseModel):
695
696
  "try `pip install gdsfactory`."
696
697
  ) from None
697
698
 
698
- device_array = np.rot90(self.device_array, k=-1)
699
- return gf.read.from_np(device_array[:, :, 0], nm_per_pixel=1)
699
+ device_array = np.rot90(self.to_ndarray(), k=-1)
700
+ return gf.read.from_np(device_array, nm_per_pixel=1)
700
701
 
701
702
  def to_tidy3d(
702
703
  self,
@@ -808,6 +809,9 @@ class Device(BaseModel):
808
809
  raise ValueError("Thickness must be a positive integer.")
809
810
 
810
811
  layered_array = self.to_3d(thickness_nm)
812
+ layered_array = np.pad(
813
+ layered_array, ((0, 0), (0, 0), (10, 10)), mode="constant"
814
+ )
811
815
  verts, faces, _, _ = measure.marching_cubes(layered_array, level=0.5)
812
816
  cube = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
813
817
  for i, f in enumerate(faces):
@@ -1468,16 +1472,11 @@ class Device(BaseModel):
1468
1472
  ValueError
1469
1473
  If an invalid structuring element type is specified.
1470
1474
  """
1471
- if strel == "disk":
1472
- structuring_element = disk(radius=min_feature_size / 2)
1473
- elif strel == "square":
1474
- structuring_element = square(width=min_feature_size)
1475
- else:
1476
- raise ValueError(f"Invalid structuring element: {strel}")
1477
-
1478
- modified_geometry = closing(self.device_array[:, :, 0], structuring_element)
1479
- modified_geometry = opening(modified_geometry, structuring_element)
1480
- modified_geometry = np.expand_dims(modified_geometry, axis=-1)
1475
+ modified_geometry = geometry.enforce_feature_size(
1476
+ device_array=self.device_array,
1477
+ min_feature_size=min_feature_size,
1478
+ strel=strel,
1479
+ )
1481
1480
  return self.model_copy(update={"device_array": modified_geometry})
1482
1481
 
1483
1482
  def check_feature_size(self, min_feature_size: int, strel: str = "disk"):
prefab/geometry.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import cv2
4
4
  import numpy as np
5
+ from skimage.morphology import closing, disk, opening, square
5
6
 
6
7
 
7
8
  def normalize(device_array: np.ndarray) -> np.ndarray:
@@ -248,7 +249,6 @@ def rotate(device_array: np.ndarray, angle: float) -> np.ndarray:
248
249
  ),
249
250
  axis=-1,
250
251
  )
251
- return np.expand_dims(rotated_device_array, axis=-1)
252
252
 
253
253
 
254
254
  def erode(device_array: np.ndarray, kernel_size: int) -> np.ndarray:
@@ -306,3 +306,45 @@ def flatten(device_array: np.ndarray) -> np.ndarray:
306
306
  The flattened array with values scaled between 0 and 1.
307
307
  """
308
308
  return normalize(np.sum(device_array, axis=-1, keepdims=True))
309
+
310
+
311
+ def enforce_feature_size(
312
+ device_array: np.ndarray, min_feature_size: int, strel: str = "disk"
313
+ ) -> np.ndarray:
314
+ """
315
+ Enforce a minimum feature size on the device geometry.
316
+
317
+ This function applies morphological operations to ensure that all features in the
318
+ device geometry are at least the specified minimum size. It uses either a disk
319
+ or square structuring element for the operations.
320
+
321
+ Parameters
322
+ ----------
323
+ device_array : np.ndarray
324
+ The input array representing the device geometry.
325
+ min_feature_size : int
326
+ The minimum feature size to enforce, in nanometers.
327
+ strel : str, optional
328
+ The type of structuring element to use. Can be either "disk" or "square".
329
+ Defaults to "disk".
330
+
331
+ Returns
332
+ -------
333
+ np.ndarray
334
+ The modified device array with enforced feature size.
335
+
336
+ Raises
337
+ ------
338
+ ValueError
339
+ If an invalid structuring element type is specified.
340
+ """
341
+ if strel == "disk":
342
+ structuring_element = disk(radius=min_feature_size / 2)
343
+ elif strel == "square":
344
+ structuring_element = square(width=min_feature_size)
345
+ else:
346
+ raise ValueError(f"Invalid structuring element: {strel}")
347
+
348
+ modified_geometry = closing(device_array[:, :, 0], structuring_element)
349
+ modified_geometry = opening(modified_geometry, structuring_element)
350
+ return np.expand_dims(modified_geometry, axis=-1)
prefab/read.py CHANGED
@@ -244,7 +244,7 @@ def from_gdsfactory(
244
244
  If the gdsfactory package is not installed.
245
245
  """
246
246
  try:
247
- import gdsfactory as gf
247
+ import gdsfactory as gf # noqa: F401
248
248
  except ImportError:
249
249
  raise ImportError(
250
250
  "The gdsfactory package is required to use this function; "
@@ -352,6 +352,10 @@ def get_sem_resolution(sem_path: str, sem_resolution_key: str) -> float:
352
352
  Extracts the resolution of a scanning electron microscope (SEM) image from its
353
353
  metadata.
354
354
 
355
+ Note:
356
+ -----
357
+ This function is used internally and may not be useful for most users.
358
+
355
359
  Parameters
356
360
  ----------
357
361
  sem_path : str
prefab/shapes.py CHANGED
@@ -6,7 +6,7 @@ from skimage.draw import polygon
6
6
  from .device import Device
7
7
 
8
8
 
9
- def rectangle(width: int = 200, height: int = 100, **kwargs) -> Device:
9
+ def rectangle(width: int = 200, height: int = None, **kwargs) -> Device:
10
10
  """
11
11
  Create a Device object with a rectangular shape.
12
12
 
@@ -15,7 +15,7 @@ def rectangle(width: int = 200, height: int = 100, **kwargs) -> Device:
15
15
  width : int, optional
16
16
  The width of the rectangle. Defaults to 200.
17
17
  height : int, optional
18
- The height of the rectangle. Defaults to 100.
18
+ The height of the rectangle. Defaults to the value of width.
19
19
  **kwargs : dict
20
20
  Additional keyword arguments to be passed to the Device constructor.
21
21
 
@@ -24,40 +24,57 @@ def rectangle(width: int = 200, height: int = 100, **kwargs) -> Device:
24
24
  Device
25
25
  A Device object containing the rectangular shape.
26
26
  """
27
+ if height is None:
28
+ height = width
27
29
  rectangle = np.zeros((height, width))
28
30
  rectangle[:, :] = 1
29
31
  return Device(device_array=rectangle, **kwargs)
30
32
 
31
33
 
32
- def box(width: int = 200, **kwargs) -> Device:
34
+ def window(
35
+ width: int = 200, height: int = None, border_width: int = 60, **kwargs
36
+ ) -> Device:
33
37
  """
34
- Create a Device object with a square box shape.
38
+ Create a Device object with a window shape (hollow square).
35
39
 
36
40
  Parameters
37
41
  ----------
38
42
  width : int, optional
39
- The width and height of the square box. Defaults to 200.
43
+ The overall width of the window. Defaults to 200.
44
+ height : int, optional
45
+ The overall height of the window. Defaults to the value of width.
46
+ border_width : int, optional
47
+ The width of the window border. Defaults to 60.
40
48
  **kwargs : dict
41
49
  Additional keyword arguments to be passed to the Device constructor.
42
50
 
43
51
  Returns
44
52
  -------
45
53
  Device
46
- A Device object containing the square box shape.
54
+ A Device object containing the window shape.
47
55
  """
48
- box = np.zeros((width, width))
49
- box[:, :] = 1
50
- return Device(device_array=box, **kwargs)
56
+ if height is None:
57
+ height = width
58
+ window = np.zeros((height, width))
59
+ window[:border_width, :] = 1
60
+ window[-border_width:, :] = 1
61
+ window[:, :border_width] = 1
62
+ window[:, -border_width:] = 1
63
+ return Device(device_array=window, **kwargs)
51
64
 
52
65
 
53
- def cross(width: int = 200, arm_width: int = 60, **kwargs) -> Device:
66
+ def cross(
67
+ width: int = 200, height: int = None, arm_width: int = 60, **kwargs
68
+ ) -> Device:
54
69
  """
55
70
  Create a Device object with a cross shape.
56
71
 
57
72
  Parameters
58
73
  ----------
59
74
  width : int, optional
60
- The overall width and height of the cross. Defaults to 200.
75
+ The overall width of the cross. Defaults to 200.
76
+ height : int, optional
77
+ The overall height of the cross. Defaults to the value of width.
61
78
  arm_width : int, optional
62
79
  The width of the cross arms. Defaults to 60.
63
80
  **kwargs : dict
@@ -68,22 +85,29 @@ def cross(width: int = 200, arm_width: int = 60, **kwargs) -> Device:
68
85
  Device
69
86
  A Device object containing the cross shape.
70
87
  """
71
- cross = np.zeros((width, width))
72
- center = width // 2
88
+ if height is None:
89
+ height = width
90
+ cross = np.zeros((height, width))
91
+ center_x = width // 2
92
+ center_y = height // 2
73
93
  half_arm_width = arm_width // 2
74
- cross[center - half_arm_width : center + half_arm_width + 1, :] = 1
75
- cross[:, center - half_arm_width : center + half_arm_width + 1] = 1
94
+ cross[center_y - half_arm_width : center_y + half_arm_width + 1, :] = 1
95
+ cross[:, center_x - half_arm_width : center_x + half_arm_width + 1] = 1
76
96
  return Device(device_array=cross, **kwargs)
77
97
 
78
98
 
79
- def target(width: int = 200, arm_width: int = 60, **kwargs) -> Device:
99
+ def target(
100
+ width: int = 200, height: int = None, arm_width: int = 60, **kwargs
101
+ ) -> Device:
80
102
  """
81
103
  Create a Device object with a target shape (cross with center removed).
82
104
 
83
105
  Parameters
84
106
  ----------
85
107
  width : int, optional
86
- The overall width and height of the target. Defaults to 200.
108
+ The overall width of the target. Defaults to 200.
109
+ height : int, optional
110
+ The overall height of the target. Defaults to the value of width.
87
111
  arm_width : int, optional
88
112
  The width of the target arms. Defaults to 60.
89
113
  **kwargs : dict
@@ -94,45 +118,22 @@ def target(width: int = 200, arm_width: int = 60, **kwargs) -> Device:
94
118
  Device
95
119
  A Device object containing the target shape.
96
120
  """
97
- target = np.zeros((width, width))
98
- center = width // 2
121
+ if height is None:
122
+ height = width
123
+ target = np.zeros((height, width))
124
+ center_x = width // 2
125
+ center_y = height // 2
99
126
  half_arm_width = arm_width // 2
100
- target[center - half_arm_width : center + half_arm_width + 1, :] = 1
101
- target[:, center - half_arm_width : center + half_arm_width + 1] = 1
127
+ target[center_y - half_arm_width : center_y + half_arm_width + 1, :] = 1
128
+ target[:, center_x - half_arm_width : center_x + half_arm_width + 1] = 1
102
129
  target[
103
- center - half_arm_width : center + half_arm_width + 1,
104
- center - half_arm_width : center + half_arm_width + 1,
130
+ center_y - half_arm_width : center_y + half_arm_width + 1,
131
+ center_x - half_arm_width : center_x + half_arm_width + 1,
105
132
  ] = 0
106
133
  return Device(device_array=target, **kwargs)
107
134
 
108
135
 
109
- def window(width: int = 200, border_width: int = 60, **kwargs) -> Device:
110
- """
111
- Create a Device object with a window shape (hollow square).
112
-
113
- Parameters
114
- ----------
115
- width : int, optional
116
- The overall width and height of the window. Defaults to 200.
117
- border_width : int, optional
118
- The width of the window border. Defaults to 60.
119
- **kwargs : dict
120
- Additional keyword arguments to be passed to the Device constructor.
121
-
122
- Returns
123
- -------
124
- Device
125
- A Device object containing the window shape.
126
- """
127
- window = np.zeros((width, width))
128
- window[:border_width, :] = 1
129
- window[-border_width:, :] = 1
130
- window[:, :border_width] = 1
131
- window[:, -border_width:] = 1
132
- return Device(device_array=window, **kwargs)
133
-
134
-
135
- def ellipse(width: int = 200, height: int = 100, **kwargs) -> Device:
136
+ def disk(width: int = 200, height: int = None, **kwargs) -> Device:
136
137
  """
137
138
  Create a Device object with an elliptical shape.
138
139
 
@@ -141,7 +142,7 @@ def ellipse(width: int = 200, height: int = 100, **kwargs) -> Device:
141
142
  width : int, optional
142
143
  The width of the ellipse. Defaults to 200.
143
144
  height : int, optional
144
- The height of the ellipse. Defaults to 100.
145
+ The height of the ellipse. Defaults to the value of width.
145
146
  **kwargs : dict
146
147
  Additional keyword arguments to be passed to the Device constructor.
147
148
 
@@ -150,38 +151,54 @@ def ellipse(width: int = 200, height: int = 100, **kwargs) -> Device:
150
151
  Device
151
152
  A Device object containing the elliptical shape.
152
153
  """
153
- y, x = np.ogrid[-height // 2 : height // 2, -width // 2 : width // 2]
154
- mask = (x**2 / (width // 2) ** 2) + (y**2 / (height // 2) ** 2) <= 1
154
+ if height is None:
155
+ height = width
156
+ radius_x = width // 2
157
+ radius_y = height // 2
158
+ y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
159
+ mask = (x**2 / radius_x**2) + (y**2 / radius_y**2) <= 1
155
160
  ellipse = np.zeros((height, width))
156
161
  ellipse[mask] = 1
157
162
  return Device(device_array=ellipse, **kwargs)
158
163
 
159
164
 
160
- def circle(width: int = 200, **kwargs) -> Device:
165
+ def ring(
166
+ width: int = 200, height: int = None, border_width: int = 60, **kwargs
167
+ ) -> Device:
161
168
  """
162
- Create a Device object with a circular shape.
169
+ Create a Device object with a ring shape.
163
170
 
164
171
  Parameters
165
172
  ----------
166
173
  width : int, optional
167
- The width and height of the circle. Defaults to 200.
174
+ The overall width of the ring. Defaults to 200.
175
+ height : int, optional
176
+ The overall height of the ring. Defaults to the value of width.
177
+ border_width : int, optional
178
+ The width of the ring border. Defaults to 60.
168
179
  **kwargs : dict
169
180
  Additional keyword arguments to be passed to the Device constructor.
170
181
 
171
182
  Returns
172
183
  -------
173
184
  Device
174
- A Device object containing the circular shape.
185
+ A Device object containing the ring shape.
175
186
  """
176
- radius = width // 2
177
- y, x = np.ogrid[-radius:radius, -radius:radius]
178
- mask = x**2 + y**2 <= radius**2
179
- circle = np.zeros((width, width))
180
- circle[mask] = 1
181
- return Device(device_array=circle, **kwargs)
187
+ if height is None:
188
+ height = width
189
+ radius_x = width // 2
190
+ radius_y = height // 2
191
+ inner_radius_x = radius_x - border_width
192
+ inner_radius_y = radius_y - border_width
193
+ y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
194
+ outer_mask = x**2 / radius_x**2 + y**2 / radius_y**2 <= 1
195
+ inner_mask = x**2 / inner_radius_x**2 + y**2 / inner_radius_y**2 <= 1
196
+ ring = np.zeros((height, width))
197
+ ring[outer_mask & ~inner_mask] = 1
198
+ return Device(device_array=ring, **kwargs)
182
199
 
183
200
 
184
- def circle_wavy(
201
+ def disk_wavy(
185
202
  width: int = 200, wave_amplitude: float = 10, wave_frequency: float = 10, **kwargs
186
203
  ) -> Device:
187
204
  """
@@ -215,14 +232,18 @@ def circle_wavy(
215
232
  return Device(device_array=circle_wavy, **kwargs)
216
233
 
217
234
 
218
- def pie(width: int = 200, arc_angle: float = 270, **kwargs) -> Device:
235
+ def pie(
236
+ width: int = 200, height: int = None, arc_angle: float = 270, **kwargs
237
+ ) -> Device:
219
238
  """
220
239
  Create a Device object with a pie shape.
221
240
 
222
241
  Parameters
223
242
  ----------
224
243
  width : int, optional
225
- The width and height of the pie. Defaults to 200.
244
+ The width of the pie. Defaults to 200.
245
+ height : int, optional
246
+ The height of the pie. Defaults to the value of width.
226
247
  arc_angle : float, optional
227
248
  The angle of the pie slice in degrees. Defaults to 270.
228
249
  **kwargs : dict
@@ -233,12 +254,15 @@ def pie(width: int = 200, arc_angle: float = 270, **kwargs) -> Device:
233
254
  Device
234
255
  A Device object containing the pie shape.
235
256
  """
236
- radius = width // 2
237
- y, x = np.ogrid[-radius:radius, -radius:radius]
257
+ if height is None:
258
+ height = width
259
+ radius_x = width // 2
260
+ radius_y = height // 2
261
+ y, x = np.ogrid[-radius_y:radius_y, -radius_x:radius_x]
238
262
  angle = np.arctan2(y, x) * 180 / np.pi
239
263
  angle = (angle + 360) % 360
240
- mask = (x**2 + y**2 <= radius**2) & (angle <= arc_angle)
241
- pie = np.zeros((width, width))
264
+ mask = (x**2 / radius_x**2 + y**2 / radius_y**2 <= 1) & (angle <= arc_angle)
265
+ pie = np.zeros((height, width))
242
266
  pie[mask] = 1
243
267
  return Device(device_array=pie, **kwargs)
244
268
 
@@ -350,36 +374,6 @@ def poly(width: int = 200, num_points: int = 5, **kwargs) -> Device:
350
374
  return Device(device_array=poly, **kwargs)
351
375
 
352
376
 
353
- def ring(width: int = 200, border_width: int = 60, **kwargs) -> Device:
354
- """
355
- Create a Device object with a ring shape.
356
-
357
- Parameters
358
- ----------
359
- width : int, optional
360
- The overall width and height of the ring. Defaults to 200.
361
- border_width : int, optional
362
- The width of the ring border. Defaults to 60.
363
- **kwargs : dict
364
- Additional keyword arguments to be passed to the Device constructor.
365
-
366
- Returns
367
- -------
368
- Device
369
- A Device object containing the ring shape.
370
- """
371
- radius_outer = width // 2
372
- radius_inner = radius_outer - border_width
373
- y, x = np.ogrid[-radius_outer:radius_outer, -radius_outer:radius_outer]
374
- distance_from_center = np.sqrt(x**2 + y**2)
375
- mask = (distance_from_center <= radius_outer) & (
376
- distance_from_center >= radius_inner
377
- )
378
- ring = np.zeros((width, width))
379
- ring[mask] = 1
380
- return Device(device_array=ring, **kwargs)
381
-
382
-
383
377
  def radial_grating(
384
378
  width: int = 200, grating_skew: int = 0, num_gratings: int = 6, **kwargs
385
379
  ) -> Device:
@@ -465,7 +459,8 @@ def offset_grating(
465
459
 
466
460
 
467
461
  def L_grating(
468
- height: int = 200,
462
+ width: int = 200,
463
+ height: int = None,
469
464
  pitch: int = 100,
470
465
  duty_cycle: float = 0.5,
471
466
  **kwargs,
@@ -475,8 +470,10 @@ def L_grating(
475
470
 
476
471
  Parameters
477
472
  ----------
473
+ width : int, optional
474
+ The width of the L-grating. Defaults to 200.
478
475
  height : int, optional
479
- The height and width of the L-grating. Defaults to 200.
476
+ The height of the L-grating. Defaults to the value of width.
480
477
  pitch : int, optional
481
478
  The pitch (period) of the L-shapes. Defaults to 100.
482
479
  duty_cycle : float, optional
@@ -489,8 +486,10 @@ def L_grating(
489
486
  Device
490
487
  A Device object containing the L-shaped grating pattern.
491
488
  """
492
- L_grating = np.zeros((height, height))
493
- num_L_shapes = height // pitch
489
+ if height is None:
490
+ height = width
491
+ L_grating = np.zeros((height, width))
492
+ num_L_shapes = min(height, width) // pitch
494
493
  L_width = int(pitch * duty_cycle)
495
494
  for i in range(num_L_shapes):
496
495
  start = i * pitch
@@ -499,11 +498,11 @@ def L_grating(
499
498
  return Device(device_array=L_grating, **kwargs)
500
499
 
501
500
 
502
- def circles(
503
- rows: int = 5, cols: int = 5, radius: int = 30, spacing: int = 60, **kwargs
501
+ def disks(
502
+ rows: int = 5, cols: int = 5, disk_radius: int = 30, spacing: int = 60, **kwargs
504
503
  ) -> Device:
505
504
  """
506
- Create a Device object with a grid of uniform circles.
505
+ Create a Device object with a grid of uniform disks.
507
506
 
508
507
  Parameters
509
508
  ----------
@@ -511,39 +510,39 @@ def circles(
511
510
  The number of rows in the grid. Defaults to 5.
512
511
  cols : int, optional
513
512
  The number of columns in the grid. Defaults to 5.
514
- radius : int, optional
515
- The radius of each circle. Defaults to 30.
513
+ disk_radius : int, optional
514
+ The radius of each disk. Defaults to 30.
516
515
  spacing : int, optional
517
- The spacing between circle centers. Defaults to 60.
516
+ The spacing between disk centers. Defaults to 60.
518
517
  **kwargs : dict
519
518
  Additional keyword arguments to be passed to the Device constructor.
520
519
 
521
520
  Returns
522
521
  -------
523
522
  Device
524
- A Device object containing a grid of circles.
523
+ A Device object containing a grid of disks.
525
524
  """
526
- grid_height = rows * (2 * radius + spacing) - spacing
527
- grid_width = cols * (2 * radius + spacing) - spacing
528
- circles = np.zeros((grid_height, grid_width))
529
- y, x = np.ogrid[-radius:radius, -radius:radius]
530
- mask = x**2 + y**2 <= radius**2
525
+ grid_height = rows * (2 * disk_radius + spacing) - spacing
526
+ grid_width = cols * (2 * disk_radius + spacing) - spacing
527
+ disks = np.zeros((grid_height, grid_width))
528
+ y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
529
+ mask = x**2 + y**2 <= disk_radius**2
531
530
  for row in range(rows):
532
531
  for col in range(cols):
533
- center_y = row * (2 * radius + spacing) + radius
534
- center_x = col * (2 * radius + spacing) + radius
535
- circles[
536
- center_y - radius : center_y + radius,
537
- center_x - radius : center_x + radius,
532
+ center_y = row * (2 * disk_radius + spacing) + disk_radius
533
+ center_x = col * (2 * disk_radius + spacing) + disk_radius
534
+ disks[
535
+ center_y - disk_radius : center_y + disk_radius,
536
+ center_x - disk_radius : center_x + disk_radius,
538
537
  ][mask] = 1
539
- return Device(device_array=circles, **kwargs)
538
+ return Device(device_array=disks, **kwargs)
540
539
 
541
540
 
542
- def circles_offset(
543
- rows: int = 5, cols: int = 5, radius: int = 30, spacing: int = 30, **kwargs
541
+ def disks_offset(
542
+ rows: int = 5, cols: int = 5, disk_radius: int = 30, spacing: int = 30, **kwargs
544
543
  ) -> Device:
545
544
  """
546
- Create a Device object with an offset grid of circles.
545
+ Create a Device object with an offset grid of disks.
547
546
 
548
547
  Parameters
549
548
  ----------
@@ -551,48 +550,50 @@ def circles_offset(
551
550
  The number of rows in the grid. Defaults to 5.
552
551
  cols : int, optional
553
552
  The number of columns in the grid. Defaults to 5.
554
- radius : int, optional
555
- The radius of each circle. Defaults to 30.
553
+ disk_radius : int, optional
554
+ The radius of each disk. Defaults to 30.
556
555
  spacing : int, optional
557
- The spacing between circle centers. Defaults to 30.
556
+ The spacing between disk centers. Defaults to 30.
558
557
  **kwargs : dict
559
558
  Additional keyword arguments to be passed to the Device constructor.
560
559
 
561
560
  Returns
562
561
  -------
563
562
  Device
564
- A Device object containing an offset grid of circles.
563
+ A Device object containing an offset grid of disks.
565
564
  """
566
- grid_height = rows * (2 * radius + spacing) - spacing
567
- grid_width = cols * (2 * radius + spacing) - spacing + (radius + spacing // 2)
568
- circles_offset = np.zeros((grid_height, grid_width))
569
- y, x = np.ogrid[-radius:radius, -radius:radius]
570
- mask = x**2 + y**2 <= radius**2
565
+ grid_height = rows * (2 * disk_radius + spacing) - spacing
566
+ grid_width = (
567
+ cols * (2 * disk_radius + spacing) - spacing + (disk_radius + spacing // 2)
568
+ )
569
+ disks_offset = np.zeros((grid_height, grid_width))
570
+ y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
571
+ mask = x**2 + y**2 <= disk_radius**2
571
572
  for row in range(rows):
572
573
  for col in range(cols):
573
- center_y = row * (2 * radius + spacing) + radius
574
+ center_y = row * (2 * disk_radius + spacing) + disk_radius
574
575
  center_x = (
575
- col * (2 * radius + spacing)
576
- + radius
577
- + (radius + spacing // 2 if row % 2 == 1 else 0)
576
+ col * (2 * disk_radius + spacing)
577
+ + disk_radius
578
+ + (disk_radius + spacing // 2 if row % 2 == 1 else 0)
578
579
  )
579
- circles_offset[
580
- center_y - radius : center_y + radius,
581
- center_x - radius : center_x + radius,
580
+ disks_offset[
581
+ center_y - disk_radius : center_y + disk_radius,
582
+ center_x - disk_radius : center_x + disk_radius,
582
583
  ][mask] = 1
583
- return Device(device_array=circles_offset, **kwargs)
584
+ return Device(device_array=disks_offset, **kwargs)
584
585
 
585
586
 
586
- def circles_varying(
587
+ def disks_varying(
587
588
  rows: int = 5,
588
589
  cols: int = 5,
589
- min_radius: int = 10,
590
- max_radius: int = 30,
590
+ min_disk_radius: int = 10,
591
+ max_disk_radius: int = 30,
591
592
  spacing: int = 30,
592
593
  **kwargs,
593
594
  ) -> Device:
594
595
  """
595
- Create a Device object with a grid of circles with varying radii.
596
+ Create a Device object with a grid of disks with varying radii.
596
597
 
597
598
  Parameters
598
599
  ----------
@@ -600,40 +601,42 @@ def circles_varying(
600
601
  The number of rows in the grid. Defaults to 5.
601
602
  cols : int, optional
602
603
  The number of columns in the grid. Defaults to 5.
603
- min_radius : int, optional
604
- The minimum radius of the circles. Defaults to 10.
605
- max_radius : int, optional
606
- The maximum radius of the circles. Defaults to 30.
604
+ min_disk_radius : int, optional
605
+ The minimum radius of the disks. Defaults to 10.
606
+ max_disk_radius : int, optional
607
+ The maximum radius of the disks. Defaults to 30.
607
608
  spacing : int, optional
608
- The spacing between circle centers. Defaults to 30.
609
+ The spacing between disk centers. Defaults to 30.
609
610
  **kwargs : dict
610
611
  Additional keyword arguments to be passed to the Device constructor.
611
612
 
612
613
  Returns
613
614
  -------
614
615
  Device
615
- A Device object containing a grid of circles with varying radii.
616
+ A Device object containing a grid of disks with varying radii.
616
617
  """
617
- grid_height = rows * (2 * max_radius + spacing) - spacing
618
- grid_width = cols * (2 * max_radius + spacing) - spacing
619
- circles_varying = np.zeros((grid_height, grid_width))
620
- radius_range = np.linspace(min_radius, max_radius, rows * cols).reshape(rows, cols)
618
+ grid_height = rows * (2 * max_disk_radius + spacing) - spacing
619
+ grid_width = cols * (2 * max_disk_radius + spacing) - spacing
620
+ disks_varying = np.zeros((grid_height, grid_width))
621
+ radius_range = np.linspace(min_disk_radius, max_disk_radius, rows * cols).reshape(
622
+ rows, cols
623
+ )
621
624
  for row in range(rows):
622
625
  for col in range(cols):
623
- radius = int(radius_range[row, col])
624
- y, x = np.ogrid[-radius:radius, -radius:radius]
625
- mask = x**2 + y**2 <= radius**2
626
- center_y = row * (2 * max_radius + spacing) + max_radius
627
- center_x = col * (2 * max_radius + spacing) + max_radius
628
- circles_varying[
629
- center_y - radius : center_y + radius,
630
- center_x - radius : center_x + radius,
626
+ disk_radius = int(radius_range[row, col])
627
+ y, x = np.ogrid[-disk_radius:disk_radius, -disk_radius:disk_radius]
628
+ mask = x**2 + y**2 <= disk_radius**2
629
+ center_y = row * (2 * max_disk_radius + spacing) + max_disk_radius
630
+ center_x = col * (2 * max_disk_radius + spacing) + max_disk_radius
631
+ disks_varying[
632
+ center_y - disk_radius : center_y + disk_radius,
633
+ center_x - disk_radius : center_x + disk_radius,
631
634
  ][mask] = 1
632
- return Device(device_array=circles_varying, **kwargs)
635
+ return Device(device_array=disks_varying, **kwargs)
633
636
 
634
637
 
635
638
  def holes(
636
- rows: int = 5, cols: int = 5, radius: int = 30, spacing: int = 30, **kwargs
639
+ rows: int = 5, cols: int = 5, hole_radius: int = 30, spacing: int = 30, **kwargs
637
640
  ) -> Device:
638
641
  """
639
642
  Create a Device object with a grid of uniform circular holes.
@@ -644,7 +647,7 @@ def holes(
644
647
  The number of rows in the grid. Defaults to 5.
645
648
  cols : int, optional
646
649
  The number of columns in the grid. Defaults to 5.
647
- radius : int, optional
650
+ hole_radius : int, optional
648
651
  The radius of each hole. Defaults to 30.
649
652
  spacing : int, optional
650
653
  The spacing between hole centers. Defaults to 30.
@@ -656,24 +659,24 @@ def holes(
656
659
  Device
657
660
  A Device object containing a grid of circular holes.
658
661
  """
659
- grid_height = rows * (2 * radius + spacing) - spacing
660
- grid_width = cols * (2 * radius + spacing) - spacing
662
+ grid_height = rows * (2 * hole_radius + spacing) - spacing
663
+ grid_width = cols * (2 * hole_radius + spacing) - spacing
661
664
  holes = np.ones((grid_height, grid_width))
662
- y, x = np.ogrid[-radius:radius, -radius:radius]
663
- mask = x**2 + y**2 <= radius**2
665
+ y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
666
+ mask = x**2 + y**2 <= hole_radius**2
664
667
  for row in range(rows):
665
668
  for col in range(cols):
666
- center_y = row * (2 * radius + spacing) + radius
667
- center_x = col * (2 * radius + spacing) + radius
669
+ center_y = row * (2 * hole_radius + spacing) + hole_radius
670
+ center_x = col * (2 * hole_radius + spacing) + hole_radius
668
671
  holes[
669
- center_y - radius : center_y + radius,
670
- center_x - radius : center_x + radius,
672
+ center_y - hole_radius : center_y + hole_radius,
673
+ center_x - hole_radius : center_x + hole_radius,
671
674
  ][mask] = 0
672
675
  return Device(device_array=holes, **kwargs)
673
676
 
674
677
 
675
678
  def holes_offset(
676
- rows: int = 5, cols: int = 5, radius: int = 30, spacing: int = 30, **kwargs
679
+ rows: int = 5, cols: int = 5, hole_radius: int = 30, spacing: int = 30, **kwargs
677
680
  ) -> Device:
678
681
  """
679
682
  Create a Device object with an offset grid of circular holes.
@@ -684,7 +687,7 @@ def holes_offset(
684
687
  The number of rows in the grid. Defaults to 5.
685
688
  cols : int, optional
686
689
  The number of columns in the grid. Defaults to 5.
687
- radius : int, optional
690
+ hole_radius : int, optional
688
691
  The radius of each hole. Defaults to 30.
689
692
  spacing : int, optional
690
693
  The spacing between hole centers. Defaults to 30.
@@ -696,22 +699,24 @@ def holes_offset(
696
699
  Device
697
700
  A Device object containing an offset grid of circular holes.
698
701
  """
699
- grid_height = rows * (2 * radius + spacing) - spacing
700
- grid_width = cols * (2 * radius + spacing) - spacing + (radius + spacing // 2)
702
+ grid_height = rows * (2 * hole_radius + spacing) - spacing
703
+ grid_width = (
704
+ cols * (2 * hole_radius + spacing) - spacing + (hole_radius + spacing // 2)
705
+ )
701
706
  holes_offset = np.ones((grid_height, grid_width))
702
- y, x = np.ogrid[-radius:radius, -radius:radius]
703
- mask = x**2 + y**2 <= radius**2
707
+ y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
708
+ mask = x**2 + y**2 <= hole_radius**2
704
709
  for row in range(rows):
705
710
  for col in range(cols):
706
- center_y = row * (2 * radius + spacing) + radius
711
+ center_y = row * (2 * hole_radius + spacing) + hole_radius
707
712
  center_x = (
708
- col * (2 * radius + spacing)
709
- + radius
710
- + (radius + spacing // 2 if row % 2 == 1 else 0)
713
+ col * (2 * hole_radius + spacing)
714
+ + hole_radius
715
+ + (hole_radius + spacing // 2 if row % 2 == 1 else 0)
711
716
  )
712
717
  holes_offset[
713
- center_y - radius : center_y + radius,
714
- center_x - radius : center_x + radius,
718
+ center_y - hole_radius : center_y + hole_radius,
719
+ center_x - hole_radius : center_x + hole_radius,
715
720
  ][mask] = 0
716
721
  return Device(device_array=holes_offset, **kwargs)
717
722
 
@@ -719,8 +724,8 @@ def holes_offset(
719
724
  def holes_varying(
720
725
  rows: int = 5,
721
726
  cols: int = 5,
722
- min_radius: int = 10,
723
- max_radius: int = 30,
727
+ min_hole_radius: int = 10,
728
+ max_hole_radius: int = 30,
724
729
  spacing: int = 30,
725
730
  **kwargs,
726
731
  ) -> Device:
@@ -733,9 +738,9 @@ def holes_varying(
733
738
  The number of rows in the grid. Defaults to 5.
734
739
  cols : int, optional
735
740
  The number of columns in the grid. Defaults to 5.
736
- min_radius : int, optional
741
+ min_hole_radius : int, optional
737
742
  The minimum radius of the holes. Defaults to 10.
738
- max_radius : int, optional
743
+ max_hole_radius : int, optional
739
744
  The maximum radius of the holes. Defaults to 30.
740
745
  spacing : int, optional
741
746
  The spacing between hole centers. Defaults to 30.
@@ -747,19 +752,21 @@ def holes_varying(
747
752
  Device
748
753
  A Device object containing a grid of circular holes with varying radii.
749
754
  """
750
- grid_height = rows * (2 * max_radius + spacing) - spacing
751
- grid_width = cols * (2 * max_radius + spacing) - spacing
755
+ grid_height = rows * (2 * max_hole_radius + spacing) - spacing
756
+ grid_width = cols * (2 * max_hole_radius + spacing) - spacing
752
757
  holes_varying = np.ones((grid_height, grid_width))
753
- radius_range = np.linspace(min_radius, max_radius, rows * cols).reshape(rows, cols)
758
+ radius_range = np.linspace(min_hole_radius, max_hole_radius, rows * cols).reshape(
759
+ rows, cols
760
+ )
754
761
  for row in range(rows):
755
762
  for col in range(cols):
756
- radius = int(radius_range[row, col])
757
- y, x = np.ogrid[-radius:radius, -radius:radius]
758
- mask = x**2 + y**2 <= radius**2
759
- center_y = row * (2 * max_radius + spacing) + max_radius
760
- center_x = col * (2 * max_radius + spacing) + max_radius
763
+ hole_radius = int(radius_range[row, col])
764
+ y, x = np.ogrid[-hole_radius:hole_radius, -hole_radius:hole_radius]
765
+ mask = x**2 + y**2 <= hole_radius**2
766
+ center_y = row * (2 * max_hole_radius + spacing) + max_hole_radius
767
+ center_x = col * (2 * max_hole_radius + spacing) + max_hole_radius
761
768
  holes_varying[
762
- center_y - radius : center_y + radius,
763
- center_x - radius : center_x + radius,
769
+ center_y - hole_radius : center_y + hole_radius,
770
+ center_x - hole_radius : center_x + hole_radius,
764
771
  ][mask] = 0
765
772
  return Device(device_array=holes_varying, **kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: prefab
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: Artificial nanofabrication of integrated photonic circuits using deep learning
5
5
  Project-URL: Homepage, https://prefabphotonics.com
6
6
  Project-URL: Repository, https://github.com/PreFab-Photonics/PreFab
@@ -516,7 +516,7 @@ Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGP
516
516
  Classifier: Operating System :: OS Independent
517
517
  Classifier: Programming Language :: Python :: 3
518
518
  Requires-Python: >=3.9
519
- Requires-Dist: gdstk
519
+ Requires-Dist: gdstk>=0.9.55
520
520
  Requires-Dist: matplotlib
521
521
  Requires-Dist: numpy
522
522
  Requires-Dist: opencv-python-headless
@@ -533,23 +533,23 @@ Description-Content-Type: text/markdown
533
533
 
534
534
  ![PreFab logo](https://github.com/PreFab-Photonics/PreFab/blob/main/docs/assets/logo.png?raw=true)
535
535
 
536
- PreFab leverages **deep learning** to model fabrication-induced structural variations in integrated photonic devices. Through this _virtual nanofabrication environment_, we uncover valuable insights into nanofabrication processes and enhance device design accuracy.
536
+ PreFab is a _virtual nanofabrication environment_ that leverages **deep learning** and **computer vision** to predict and correct for structural variations in integrated photonic devices during nanofabrication.
537
537
 
538
538
  ## Prediction
539
539
 
540
- PreFab accurately predicts process-induced structural alterations such as corner rounding, washing away of small lines and islands, and filling of narrow holes in planar photonic devices. This enables designers to quickly prototype expected performance and rectify designs prior to nanofabrication.
540
+ PreFab predicts process-induced structural variations, including corner rounding, loss of small lines and islands, filling of narrow holes and channels, sidewall angle deviations, and stochastic effects. This allows designers to rapidly prototype and evaluate expected performance pre-fabrication.
541
541
 
542
542
  ![Example of PreFab prediction](https://github.com/PreFab-Photonics/PreFab/blob/main/docs/assets/promo_p.png?raw=true)
543
543
 
544
544
  ## Correction
545
545
 
546
- PreFab automates corrections to device designs, ensuring the fabricated outcome aligns with the original design. This results in reduced structural variation and performance disparity from simulation to experiment.
546
+ PreFab corrects device designs to ensure that the fabricated outcome closely matches the intended specifications. This minimizes structural variations and reduces performance discrepancies between simulations and actual experiments.
547
547
 
548
548
  ![Example of PreFab correction](https://github.com/PreFab-Photonics/PreFab/blob/main/docs/assets/promo_c.png?raw=true)
549
549
 
550
550
  ## Models
551
551
 
552
- PreFab accommodates unique _predictor_ and _corrector_ models for each photonic foundry, regularly updated based on recent fabrication data. Current models include (see full list on [`docs/models.md`](https://github.com/PreFab-Photonics/PreFab/blob/main/docs/models.md)):
552
+ Each photonic nanofabrication process requires unique models, which are regularly updated with the latest data. The current models include (see the full list in [`docs/models.md`](https://github.com/PreFab-Photonics/PreFab/blob/main/docs/models.md)):
553
553
 
554
554
  | Foundry | Process | Latest Version | Latest Dataset | Model Name |
555
555
  | ------- | ------- | ----------------- | ---------------- | ----------- |
@@ -557,7 +557,7 @@ PreFab accommodates unique _predictor_ and _corrector_ models for each photonic
557
557
  | ANT | SiN | ANF1 (May 6 2024) | d1 (Jan 31 2024) | ANT_SiN_ANF1_d1 |
558
558
  | Generic | DUV-SOI | ANF1 (May 6 2024) | d0 (Jul 30 2024) | generic_DUV_SOI_ANF1_d0 |
559
559
 
560
- > _New models and foundries are to be regularly added. Usage may change. For additional foundry and process models, feel free to contact us._
560
+ > _New models are to be regularly added. Usage may change. For additional foundry and process models, feel free to [contact us](mailto:hi@prefabphotonics.com) or raise an issue._
561
561
 
562
562
  ## Installation
563
563
 
@@ -581,7 +581,7 @@ pip install -e .
581
581
 
582
582
  Before you can make PreFab requests, you will need to [create an account](https://www.prefabphotonics.com/login).
583
583
 
584
- To link your account, you will need a token. You can do this by running the following command in your terminal. This will open a browser window where you can log in and authenticate your token.
584
+ To link your account, you will need an token. You can do this by running the following command in your terminal. This will open a browser window where you can log in and authenticate your token.
585
585
 
586
586
  ```sh
587
587
  python3 -m prefab setup
@@ -593,11 +593,11 @@ Visit [`/docs/examples`](https://github.com/PreFab-Photonics/PreFab/tree/main/do
593
593
 
594
594
  ## Performance and Usage
595
595
 
596
- PreFab models are served via a serverless cloud platform. Please note:
596
+ PreFab models are hosted on a [serverless cloud platform](https://modal.com/). Please keep in mind:
597
597
 
598
- - 🐢 CPU inference may result in slower performance. Future updates will introduce GPU inference.
599
- - 🥶 The first prediction may take longer due to cold start server loading. Subsequent predictions will be faster.
600
- - 😊 Be considerate of usage. Start small and limit usage during the initial stages. Thank you!
598
+ - 🐢 Default CPU inference may be slower.
599
+ - 🥶 The first prediction using optional GPU inference may take longer due to cold start server loading. Subsequent predictions will be faster.
600
+ - 😊 Please be considerate of usage. Start with small tasks and limit usage during the initial stages. Thank you!
601
601
 
602
602
  ## License
603
603
 
@@ -0,0 +1,12 @@
1
+ prefab/__init__.py,sha256=fbGTDuVA76ol5cM6fXbPAZMB0BCT2zmHD7Pgr9I-zV0,401
2
+ prefab/__main__.py,sha256=aAgt1WXa44k1nJqsiSD3uAfNeGpwtjWqMUYCHN5_Qrw,2759
3
+ prefab/compare.py,sha256=2MKUT7N2A639tUGCnJHpfF9MmS-v3oARDkTqHbWJ9OM,3239
4
+ prefab/device.py,sha256=dpx4dDspruImy__bp3X11y_s-CBJZr6dKpxn8wGtmSA,58314
5
+ prefab/geometry.py,sha256=0sa6ietUWZGkxOnUPUzD3q2QpFuOpWkSANoopGpPd6s,11035
6
+ prefab/models.py,sha256=UMzYZzKouroxlwkXCMKIYozmQCMhNhvt8kQrZmwmZB4,3671
7
+ prefab/read.py,sha256=MuF-cugFQ7MWBJ8DOvQuwktIk0fJ8PXBeLye0ydrB8o,14734
8
+ prefab/shapes.py,sha256=2qaqyNzu5WG3wVdk4oQzeNXmhwXRHcPnRZlgRrM4MoA,25576
9
+ prefab-1.1.2.dist-info/METADATA,sha256=wZOFUncUh7ZmZRoJ8AxH_SfpKwd2KdxDnVvmTyiLSR0,34824
10
+ prefab-1.1.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
11
+ prefab-1.1.2.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
12
+ prefab-1.1.2.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- prefab/__init__.py,sha256=Tx4t6ODZeL-V-ZPuQXjEHHtwb7rjqCb2ApLFDniQ5fA,401
2
- prefab/__main__.py,sha256=aAgt1WXa44k1nJqsiSD3uAfNeGpwtjWqMUYCHN5_Qrw,2759
3
- prefab/compare.py,sha256=AfLJ69DTqvt0mkMqNCTdn2KWKoclt_0htGVXvarEhkY,2709
4
- prefab/device.py,sha256=lklENpvqROifkEgNCT4BwU9xnsLt1nwdLksh5VDgpKU,58507
5
- prefab/geometry.py,sha256=pXsVeu4Ycnq60bG6WqFNoUWnyiNStIaYQgbMIrEyndM,9614
6
- prefab/models.py,sha256=UMzYZzKouroxlwkXCMKIYozmQCMhNhvt8kQrZmwmZB4,3671
7
- prefab/read.py,sha256=Rzj9GGrszsKWdY8GxtmpfYcA4nsiuxgwSdEDVvfHN80,14624
8
- prefab/shapes.py,sha256=Hc6dc2Y5Wmb2mZAxhdjBNEJF7C7tF2o460HNvcqcQdo,24856
9
- prefab-1.1.0.dist-info/METADATA,sha256=tHahGccx-TKDbeo2ZMKTjC_JYPWesvovFyUuTONF_YA,34817
10
- prefab-1.1.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
11
- prefab-1.1.0.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
12
- prefab-1.1.0.dist-info/RECORD,,
File without changes