prefab 1.1.5__py3-none-any.whl → 1.1.6__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/device.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Provides the Device class for representing photonic devices."""
2
2
 
3
- from typing import TYPE_CHECKING, Optional
3
+ from typing import TYPE_CHECKING, Literal, Optional
4
4
 
5
5
  import cv2
6
6
  import gdstk
@@ -9,7 +9,7 @@ import numpy as np
9
9
  from matplotlib.axes import Axes
10
10
  from matplotlib.patches import Rectangle
11
11
  from PIL import Image
12
- from pydantic import BaseModel, Field, root_validator, validator
12
+ from pydantic import BaseModel, Field, model_validator, validator
13
13
  from scipy.ndimage import distance_transform_edt
14
14
  from skimage import measure
15
15
 
@@ -37,19 +37,21 @@ class BufferSpec(BaseModel):
37
37
  ----------
38
38
  mode : dict[str, str]
39
39
  A dictionary that defines the buffer mode for each side of the device
40
- ('top', 'bottom', 'left', 'right'), where 'constant' is used for isolated
41
- structures and 'edge' is utilized for preserving the edge, such as for waveguide
42
- connections.
40
+ ('top', 'bottom', 'left', 'right'), where:
41
+ - 'constant' is used for isolated structures
42
+ - 'edge' is utilized for preserving the edge, such as for waveguide connections
43
+ - 'none' for no buffer on that side
43
44
  thickness : dict[str, int]
44
45
  A dictionary that defines the thickness of the buffer zone for each side of the
45
- device ('top', 'bottom', 'left', 'right'). Each value must be greater than 0.
46
+ device ('top', 'bottom', 'left', 'right'). Each value must be greater than or
47
+ equal to 0.
46
48
 
47
49
  Raises
48
50
  ------
49
51
  ValueError
50
52
  If any of the modes specified in the 'mode' dictionary are not one of the
51
- allowed values ('constant', 'edge'). Or if any of the thickness values are not
52
- greater than 0.
53
+ allowed values ('constant', 'edge', 'none'). Or if any of the thickness values
54
+ are negative.
53
55
 
54
56
  Example
55
57
  -------
@@ -58,20 +60,20 @@ class BufferSpec(BaseModel):
58
60
  buffer_spec = pf.BufferSpec(
59
61
  mode={
60
62
  "top": "constant",
61
- "bottom": "edge",
63
+ "bottom": "none",
62
64
  "left": "constant",
63
65
  "right": "edge",
64
66
  },
65
67
  thickness={
66
68
  "top": 150,
67
- "bottom": 100,
69
+ "bottom": 0,
68
70
  "left": 200,
69
71
  "right": 250,
70
72
  },
71
73
  )
72
74
  """
73
75
 
74
- mode: dict[str, str] = Field(
76
+ mode: dict[str, Literal["constant", "edge", "none"]] = Field(
75
77
  default_factory=lambda: {
76
78
  "top": "constant",
77
79
  "bottom": "constant",
@@ -90,17 +92,32 @@ class BufferSpec(BaseModel):
90
92
 
91
93
  @validator("mode", pre=True)
92
94
  def check_mode(cls, v):
93
- allowed_modes = ["constant", "edge"]
95
+ allowed_modes = ["constant", "edge", "none"]
94
96
  if not all(mode in allowed_modes for mode in v.values()):
95
- raise ValueError(f"Buffer mode must be one of {allowed_modes}, got '{v}'")
97
+ raise ValueError(f"Buffer mode must be one of {allowed_modes}, got '{v}'.")
96
98
  return v
97
99
 
98
100
  @validator("thickness")
99
101
  def check_thickness(cls, v):
100
- if not all(t > 0 for t in v.values()):
101
- raise ValueError("All thickness values must be greater than 0")
102
+ if not all(t >= 0 for t in v.values()):
103
+ raise ValueError("All thickness values must be greater than or equal to 0.")
102
104
  return v
103
105
 
106
+ @model_validator(mode="after")
107
+ def check_none_thickness(cls, values):
108
+ mode = values.mode
109
+ thickness = values.thickness
110
+ for side in mode:
111
+ if mode[side] == "none" and thickness[side] != 0:
112
+ raise ValueError(
113
+ f"Thickness must be 0 when mode is 'none' for {side} side"
114
+ )
115
+ if mode[side] != "none" and thickness[side] == 0:
116
+ raise ValueError(
117
+ f"Mode must be 'none' when thickness is 0 for {side} side"
118
+ )
119
+ return values
120
+
104
121
 
105
122
  class Device(BaseModel):
106
123
  device_array: np.ndarray = Field(...)
@@ -122,11 +139,11 @@ class Device(BaseModel):
122
139
 
123
140
  This class is designed to encapsulate the geometric representation of a photonic
124
141
  device, facilitating operations such as padding, normalization, binarization,
125
- ternarization, trimming, and blurring. These operations are useful for preparing
126
- the device design for prediction or correction. Additionally, the class provides
127
- methods for exporting the device representation to various formats, including
128
- ndarray, image files, and GDSII files, supporting a range of analysis and
129
- fabrication workflows.
142
+ erosion/dilation, trimming, and blurring. These operations are useful for
143
+ preparingthe device design for prediction or correction. Additionally, the class
144
+ providesmethods for exporting the device representation to various formats,
145
+ includingndarray, image files, and GDSII files, supporting a range of analysis
146
+ and fabrication workflows.
130
147
 
131
148
  Parameters
132
149
  ----------
@@ -134,7 +151,7 @@ class Device(BaseModel):
134
151
  A 2D array representing the planar geometry of the device. This array
135
152
  undergoes various transformations to predict or correct the nanofabrication
136
153
  process.
137
- buffer_spec : BufferSpec, optional
154
+ buffer_spec : Optional[BufferSpec]
138
155
  Defines the parameters for adding a buffer zone around the device geometry.
139
156
  This buffer zone is needed for providing surrounding context for prediction
140
157
  or correction and for ensuring seamless integration with the surrounding
@@ -164,30 +181,37 @@ class Device(BaseModel):
164
181
  buffer_thickness = self.buffer_spec.thickness
165
182
  buffer_mode = self.buffer_spec.mode
166
183
 
167
- self.device_array = np.pad(
168
- self.device_array,
169
- pad_width=((buffer_thickness["top"], 0), (0, 0)),
170
- mode=buffer_mode["top"],
171
- )
172
- self.device_array = np.pad(
173
- self.device_array,
174
- pad_width=((0, buffer_thickness["bottom"]), (0, 0)),
175
- mode=buffer_mode["bottom"],
176
- )
177
- self.device_array = np.pad(
178
- self.device_array,
179
- pad_width=((0, 0), (buffer_thickness["left"], 0)),
180
- mode=buffer_mode["left"],
181
- )
182
- self.device_array = np.pad(
183
- self.device_array,
184
- pad_width=((0, 0), (0, buffer_thickness["right"])),
185
- mode=buffer_mode["right"],
186
- )
184
+ if buffer_mode["top"] != "none":
185
+ self.device_array = np.pad(
186
+ self.device_array,
187
+ pad_width=((buffer_thickness["top"], 0), (0, 0)),
188
+ mode=buffer_mode["top"],
189
+ )
190
+
191
+ if buffer_mode["bottom"] != "none":
192
+ self.device_array = np.pad(
193
+ self.device_array,
194
+ pad_width=((0, buffer_thickness["bottom"]), (0, 0)),
195
+ mode=buffer_mode["bottom"],
196
+ )
197
+
198
+ if buffer_mode["left"] != "none":
199
+ self.device_array = np.pad(
200
+ self.device_array,
201
+ pad_width=((0, 0), (buffer_thickness["left"], 0)),
202
+ mode=buffer_mode["left"],
203
+ )
204
+
205
+ if buffer_mode["right"] != "none":
206
+ self.device_array = np.pad(
207
+ self.device_array,
208
+ pad_width=((0, 0), (0, buffer_thickness["right"])),
209
+ mode=buffer_mode["right"],
210
+ )
187
211
 
188
212
  self.device_array = np.expand_dims(self.device_array, axis=-1)
189
213
 
190
- @root_validator(pre=True)
214
+ @model_validator(mode="before")
191
215
  def check_device_array(cls, values):
192
216
  device_array = values.get("device_array")
193
217
  if not isinstance(device_array, np.ndarray):
@@ -236,11 +260,11 @@ class Device(BaseModel):
236
260
  in `models.py`. Each model is associated with a version and dataset that
237
261
  detail its creation and the data it was trained on, ensuring the prediction
238
262
  is tailored to specific fabrication parameters.
239
- binarize : bool, optional
263
+ binarize : bool
240
264
  If True, the predicted device geometry will be binarized using a threshold
241
265
  method. This is useful for converting probabilistic predictions into binary
242
266
  geometries. Defaults to False.
243
- gpu : bool, optional
267
+ gpu : bool
244
268
  If True, the prediction will be performed on a GPU. Defaults to False.
245
269
  Note: The GPU option has more overhead and will take longer for small
246
270
  devices, but will be faster for larger devices.
@@ -252,7 +276,7 @@ class Device(BaseModel):
252
276
 
253
277
  Raises
254
278
  ------
255
- ValueError
279
+ RuntimeError
256
280
  If the prediction service returns an error or if the response from the
257
281
  service cannot be processed correctly.
258
282
  """
@@ -289,11 +313,11 @@ class Device(BaseModel):
289
313
  in `models.py`. Each model is associated with a version and dataset that
290
314
  detail its creation and the data it was trained on, ensuring the correction
291
315
  is tailored to specific fabrication parameters.
292
- binarize : bool, optional
316
+ binarize : bool
293
317
  If True, the corrected device geometry will be binarized using a threshold
294
318
  method. This is useful for converting probabilistic corrections into binary
295
319
  geometries. Defaults to True.
296
- gpu : bool, optional
320
+ gpu : bool
297
321
  If True, the prediction will be performed on a GPU. Defaults to False.
298
322
  Note: The GPU option has more overhead and will take longer for small
299
323
  devices, but will be faster for larger devices.
@@ -305,7 +329,7 @@ class Device(BaseModel):
305
329
 
306
330
  Raises
307
331
  ------
308
- ValueError
332
+ RuntimeError
309
333
  If the correction service returns an error or if the response from the
310
334
  service cannot be processed correctly.
311
335
  """
@@ -341,16 +365,27 @@ class Device(BaseModel):
341
365
  in `models.py`. Each model is associated with a version and dataset that
342
366
  detail its creation and the data it was trained on, ensuring the SEMulation
343
367
  is tailored to specific fabrication parameters.
344
- gpu : bool, optional
368
+ gpu : bool
345
369
  If True, the prediction will be performed on a GPU. Defaults to False.
346
370
  Note: The GPU option has more overhead and will take longer for small
347
371
  devices, but will be faster for larger devices.
348
372
 
373
+ Notes
374
+ -----
375
+ The salt-and-pepper noise is added manually until the model is trained to
376
+ generate this noise (not a big priority).
377
+
349
378
  Returns
350
379
  -------
351
380
  Device
352
381
  A new instance of the Device class with its geometry transformed to simulate
353
382
  an SEM image style.
383
+
384
+ Raises
385
+ ------
386
+ RuntimeError
387
+ If the prediction service returns an error or if the response from the
388
+ service cannot be processed correctly.
354
389
  """
355
390
  semulated_array = predict_array(
356
391
  device_array=self.device_array,
@@ -405,12 +440,12 @@ class Device(BaseModel):
405
440
 
406
441
  Parameters
407
442
  ----------
408
- img_path : str, optional
443
+ img_path : str
409
444
  The path where the image file will be saved. If not specified, the image is
410
445
  saved as "prefab_device.png" in the current directory.
411
446
  """
412
447
  cv2.imwrite(img_path, 255 * self.flatten().to_ndarray())
413
- print(f"Saved Device to '{img_path}'")
448
+ print(f"Saved Device image to '{img_path}'")
414
449
 
415
450
  def to_gds(
416
451
  self,
@@ -429,21 +464,21 @@ class Device(BaseModel):
429
464
 
430
465
  Parameters
431
466
  ----------
432
- gds_path : str, optional
467
+ gds_path : str
433
468
  The path where the GDSII file will be saved. If not specified, the file is
434
469
  saved as "prefab_device.gds" in the current directory.
435
- cell_name : str, optional
470
+ cell_name : str
436
471
  The name of the cell within the GDSII file. If not specified, defaults to
437
472
  "prefab_device".
438
- gds_layer : tuple[int, int], optional
473
+ gds_layer : tuple[int, int]
439
474
  The layer and datatype to use within the GDSII file. Defaults to (1, 0).
440
- contour_approx_mode : int, optional
475
+ contour_approx_mode : int
441
476
  The mode of contour approximation used during the conversion. Defaults to 2,
442
477
  which corresponds to `cv2.CHAIN_APPROX_SIMPLE`, a method that compresses
443
478
  horizontal, vertical, and diagonal segments and leaves only their endpoints.
444
- origin : tuple[float, float], optional
445
- The x and y coordinates of the origin for the GDSII export. Defaults to
446
- (0.0, 0.0).
479
+ origin : tuple[float, float]
480
+ The x and y coordinates of the origin in µm for the GDSII export. Defaults
481
+ to (0.0, 0.0).
447
482
  """
448
483
  gdstk_cell = self.flatten()._device_to_gdstk(
449
484
  cell_name=cell_name,
@@ -473,17 +508,17 @@ class Device(BaseModel):
473
508
 
474
509
  Parameters
475
510
  ----------
476
- cell_name : str, optional
511
+ cell_name : str
477
512
  The name of the cell to be created. Defaults to "prefab_device".
478
- gds_layer : tuple[int, int], optional
513
+ gds_layer : tuple[int, int]
479
514
  The layer and datatype to use within the GDSTK cell. Defaults to (1, 0).
480
- contour_approx_mode : int, optional
515
+ contour_approx_mode : int
481
516
  The mode of contour approximation used during the conversion. Defaults to 2,
482
517
  which corresponds to `cv2.CHAIN_APPROX_SIMPLE`, a method that compresses
483
518
  horizontal, vertical, and diagonal segments and leaves only their endpoints.
484
- origin : tuple[float, float], optional
485
- The x and y coordinates of the origin for the GDSTK cell. Defaults to
486
- (0.0, 0.0).
519
+ origin : tuple[float, float]
520
+ The x and y coordinates of the origin in µm for the GDSTK cell. Defaults
521
+ to (0.0, 0.0).
487
522
 
488
523
  Returns
489
524
  -------
@@ -558,10 +593,12 @@ class Device(BaseModel):
558
593
  center_y_um = center_y_nm / 1000
559
594
 
560
595
  adjusted_polygons = [
561
- [
562
- (x - center_x_um + origin[0], y - center_y_um + origin[1])
563
- for x, y in polygon
564
- ]
596
+ gdstk.Polygon(
597
+ [
598
+ (x - center_x_um + origin[0], y - center_y_um + origin[1])
599
+ for x, y in polygon
600
+ ]
601
+ )
565
602
  for polygon in polygons_to_process
566
603
  ]
567
604
  processed_polygons = gdstk.boolean(
@@ -576,7 +613,7 @@ class Device(BaseModel):
576
613
 
577
614
  return cell
578
615
 
579
- def to_gdsfactory(self) -> "gf.Component": # noqa: F821
616
+ def to_gdsfactory(self) -> "gf.Component":
580
617
  """
581
618
  Convert the device geometry to a gdsfactory Component.
582
619
 
@@ -669,11 +706,11 @@ class Device(BaseModel):
669
706
  """
670
707
  bottom_layer = self.device_array[:, :, 0]
671
708
  top_layer = self.device_array[:, :, -1]
672
- dt_bottom = distance_transform_edt(bottom_layer) - distance_transform_edt(
673
- 1 - bottom_layer
709
+ dt_bottom = np.array(distance_transform_edt(bottom_layer)) - np.array(
710
+ distance_transform_edt(1 - bottom_layer)
674
711
  )
675
- dt_top = distance_transform_edt(top_layer) - distance_transform_edt(
676
- 1 - top_layer
712
+ dt_top = np.array(distance_transform_edt(top_layer)) - np.array(
713
+ distance_transform_edt(1 - top_layer)
677
714
  )
678
715
  weights = np.linspace(0, 1, thickness_nm)
679
716
  layered_array = np.zeros(
@@ -692,7 +729,7 @@ class Device(BaseModel):
692
729
  ----------
693
730
  thickness_nm : int
694
731
  The thickness of the 3D representation in nanometers.
695
- filename : str, optional
732
+ filename : str
696
733
  The name of the STL file to save. Defaults to "prefab_device.stl".
697
734
 
698
735
  Raises
@@ -750,7 +787,6 @@ class Device(BaseModel):
750
787
  plot_array.shape[0] - max_y : plot_array.shape[0] - min_y,
751
788
  min_x:max_x,
752
789
  ]
753
- extent = [min_x, max_x, min_y, max_y]
754
790
 
755
791
  if not np.ma.is_masked(plot_array):
756
792
  max_size = (1000, 1000)
@@ -769,7 +805,12 @@ class Device(BaseModel):
769
805
 
770
806
  mappable = ax.imshow(
771
807
  plot_array,
772
- extent=extent,
808
+ extent=(
809
+ float(min_x),
810
+ float(max_x),
811
+ float(min_y),
812
+ float(max_y),
813
+ ),
773
814
  **kwargs,
774
815
  )
775
816
 
@@ -803,17 +844,17 @@ class Device(BaseModel):
803
844
 
804
845
  Parameters
805
846
  ----------
806
- show_buffer : bool, optional
847
+ show_buffer : bool
807
848
  If True, visualizes the buffer zones around the device. Defaults to True.
808
849
  bounds : Optional[tuple[tuple[int, int], tuple[int, int]]], optional
809
850
  Specifies the bounds for zooming into the device geometry, formatted as
810
851
  ((min_x, min_y), (max_x, max_y)). If 'max_x' or 'max_y' is set to "end", it
811
852
  will be replaced with the corresponding dimension size of the device array.
812
853
  If None, the entire device geometry is visualized.
813
- level : int, optional
854
+ level : int
814
855
  The vertical layer to plot. If None, the device geometry is flattened.
815
856
  Defaults to None.
816
- ax : Optional[Axes], optional
857
+ ax : Optional[Axes]
817
858
  An existing matplotlib Axes object to draw the device geometry on. If
818
859
  None, a new figure and axes will be created. Defaults to None.
819
860
  **kwargs
@@ -858,21 +899,21 @@ class Device(BaseModel):
858
899
 
859
900
  Parameters
860
901
  ----------
861
- linewidth : Optional[int], optional
902
+ linewidth : Optional[int]
862
903
  The width of the contour lines. If None, the linewidth is automatically
863
904
  determined based on the size of the device array. Defaults to None.
864
- show_buffer : bool, optional
905
+ show_buffer : bool
865
906
  If True, the buffer zones around the device will be visualized. By default,
866
907
  it is set to True.
867
- bounds : Optional[tuple[tuple[int, int], tuple[int, int]]], optional
908
+ bounds : Optional[tuple[tuple[int, int], tuple[int, int]]]
868
909
  Specifies the bounds for zooming into the device geometry, formatted as
869
910
  ((min_x, min_y), (max_x, max_y)). If 'max_x' or 'max_y' is set to "end", it
870
911
  will be replaced with the corresponding dimension size of the device array.
871
912
  If None, the entire device geometry is visualized.
872
- level : int, optional
913
+ level : int
873
914
  The vertical layer to plot. If None, the device geometry is flattened.
874
915
  Defaults to None.
875
- ax : Optional[Axes], optional
916
+ ax : Optional[Axes]
876
917
  An existing matplotlib Axes object to draw the device contour on. If None, a
877
918
  new figure and axes will be created. Defaults to None.
878
919
  **kwargs
@@ -934,18 +975,18 @@ class Device(BaseModel):
934
975
 
935
976
  Parameters
936
977
  ----------
937
- show_buffer : bool, optional
978
+ show_buffer : bool
938
979
  If True, the buffer zones around the device will also be visualized. By
939
980
  default, it is set to True.
940
- bounds : Optional[tuple[tuple[int, int], tuple[int, int]]], optional
981
+ bounds : Optional[tuple[tuple[int, int], tuple[int, int]]]
941
982
  Specifies the bounds for zooming into the device geometry, formatted as
942
983
  ((min_x, min_y), (max_x, max_y)). If 'max_x' or 'max_y' is set to "end", it
943
984
  will be replaced with the corresponding dimension size of the device array.
944
985
  If None, the entire device geometry is visualized.
945
- level : int, optional
986
+ level : int
946
987
  The vertical layer to plot. If None, the device geometry is flattened.
947
988
  Defaults to None.
948
- ax : Optional[Axes], optional
989
+ ax : Optional[Axes]
949
990
  An existing matplotlib Axes object to draw the uncertainty visualization on.
950
991
  If None, a new figure and axes will be created. Defaults to None.
951
992
  **kwargs
@@ -997,17 +1038,17 @@ class Device(BaseModel):
997
1038
  ----------
998
1039
  ref_device : Device
999
1040
  The reference device to compare against.
1000
- show_buffer : bool, optional
1041
+ show_buffer : bool
1001
1042
  If True, visualizes the buffer zones around the device. Defaults to True.
1002
- bounds : Optional[tuple[tuple[int, int], tuple[int, int]]], optional
1043
+ bounds : Optional[tuple[tuple[int, int], tuple[int, int]]]
1003
1044
  Specifies the bounds for zooming into the device geometry, formatted as
1004
1045
  ((min_x, min_y), (max_x, max_y)). If 'max_x' or 'max_y' is set to "end", it
1005
1046
  will be replaced with the corresponding dimension size of the device array.
1006
1047
  If None, the entire device geometry is visualized.
1007
- level : int, optional
1048
+ level : int
1008
1049
  The vertical layer to plot. If None, the device geometry is flattened.
1009
1050
  Defaults to None.
1010
- ax : Optional[Axes], optional
1051
+ ax : Optional[Axes]
1011
1052
  An existing matplotlib Axes object to draw the comparison on. If None, a new
1012
1053
  figure and axes will be created. Defaults to None.
1013
1054
  **kwargs
@@ -1113,9 +1154,9 @@ class Device(BaseModel):
1113
1154
 
1114
1155
  Parameters
1115
1156
  ----------
1116
- eta : float, optional
1157
+ eta : float
1117
1158
  The threshold value for binarization. Defaults to 0.5.
1118
- beta : float, optional
1159
+ beta : float
1119
1160
  The scaling factor for the binarization process. A higher value makes the
1120
1161
  transition sharper. Defaults to np.inf, which results in a hard threshold.
1121
1162
 
@@ -1137,15 +1178,15 @@ class Device(BaseModel):
1137
1178
  is generally preferred for most use cases, but it can create numerical artifacts
1138
1179
  for large beta values.
1139
1180
 
1140
- Parameters
1141
- ----------
1142
- eta : float, optional
1143
- The threshold value for binarization. Defaults to 0.5.
1181
+ Parameters
1182
+ ----------
1183
+ eta : float
1184
+ The threshold value for binarization. Defaults to 0.5.
1144
1185
 
1145
- Returns
1146
- -------
1147
- Device
1148
- A new instance of the Device with the threshold-binarized geometry.
1186
+ Returns
1187
+ -------
1188
+ Device
1189
+ A new instance of the Device with the threshold-binarized geometry.
1149
1190
  """
1150
1191
  binarized_device_array = geometry.binarize_hard(
1151
1192
  device_array=self.device_array, eta=eta
@@ -1156,26 +1197,31 @@ class Device(BaseModel):
1156
1197
 
1157
1198
  def binarize_monte_carlo(
1158
1199
  self,
1159
- threshold_noise_std: float = 2.0,
1160
- threshold_blur_std: float = 8.0,
1200
+ noise_magnitude: float = 2.0,
1201
+ blur_radius: float = 8.0,
1161
1202
  ) -> "Device":
1162
1203
  """
1163
- Binarize the device geometry using a Monte Carlo approach with Gaussian
1164
- blurring.
1204
+ Binarize the input ndarray using a dynamic thresholding approach to simulate
1205
+ surfaceroughness.
1165
1206
 
1166
- This method applies a dynamic thresholding technique where the threshold value
1207
+ This function applies a dynamic thresholding technique where the threshold value
1167
1208
  is determined by a base value perturbed by Gaussian-distributed random noise.
1168
- The threshold is then spatially varied across the device array using Gaussian
1169
- blurring, simulating a more realistic scenario where the threshold is not
1209
+ Thethreshold is then spatially varied across the array using Gaussian blurring,
1210
+ simulating a potentially more realistic scenario where the threshold is not
1170
1211
  uniform across the device.
1171
1212
 
1213
+ Notes
1214
+ -----
1215
+ This is a temporary solution, where the defaults are chosen based on what looks
1216
+ good. A better, data-driven approach is needed.
1217
+
1172
1218
  Parameters
1173
1219
  ----------
1174
- threshold_noise_std : float, optional
1220
+ noise_magnitude : float
1175
1221
  The standard deviation of the Gaussian distribution used to generate noise
1176
1222
  for the threshold values. This controls the amount of randomness in the
1177
1223
  threshold. Defaults to 2.0.
1178
- threshold_blur_std : float, optional
1224
+ blur_radius : float
1179
1225
  The standard deviation for the Gaussian kernel used in blurring the
1180
1226
  threshold map. This controls the spatial variation of the threshold across
1181
1227
  the array. Defaults to 9.0.
@@ -1187,8 +1233,8 @@ class Device(BaseModel):
1187
1233
  """
1188
1234
  binarized_device_array = geometry.binarize_monte_carlo(
1189
1235
  device_array=self.device_array,
1190
- threshold_noise_std=threshold_noise_std,
1191
- threshold_blur_std=threshold_blur_std,
1236
+ noise_magnitude=noise_magnitude,
1237
+ blur_radius=blur_radius,
1192
1238
  )
1193
1239
  return self.model_copy(update={"device_array": binarized_device_array})
1194
1240
 
@@ -1199,9 +1245,9 @@ class Device(BaseModel):
1199
1245
 
1200
1246
  Parameters
1201
1247
  ----------
1202
- eta1 : float, optional
1248
+ eta1 : float
1203
1249
  The first threshold value for ternarization. Defaults to 1/3.
1204
- eta2 : float, optional
1250
+ eta2 : float
1205
1251
  The second threshold value for ternarization. Defaults to 2/3.
1206
1252
 
1207
1253
  Returns
@@ -1229,13 +1275,22 @@ class Device(BaseModel):
1229
1275
  )
1230
1276
  return self.model_copy(update={"device_array": trimmed_device_array})
1231
1277
 
1278
+ def pad(self, pad_width: int) -> "Device":
1279
+ """
1280
+ Pad the device geometry with a specified width on all sides.
1281
+ """
1282
+ padded_device_array = geometry.pad(
1283
+ device_array=self.device_array, pad_width=pad_width
1284
+ )
1285
+ return self.model_copy(update={"device_array": padded_device_array})
1286
+
1232
1287
  def blur(self, sigma: float = 1.0) -> "Device":
1233
1288
  """
1234
1289
  Apply Gaussian blur to the device geometry and normalize the result.
1235
1290
 
1236
1291
  Parameters
1237
1292
  ----------
1238
- sigma : float, optional
1293
+ sigma : float
1239
1294
  The standard deviation for the Gaussian kernel. This controls the amount of
1240
1295
  blurring. Defaults to 1.0.
1241
1296
 
@@ -1347,11 +1402,16 @@ class Device(BaseModel):
1347
1402
  device geometry are at least the specified minimum size. It uses either a disk
1348
1403
  or square structuring element for the operations.
1349
1404
 
1405
+ Notes
1406
+ -----
1407
+ This function does not guarantee that the minimum feature size is enforced in
1408
+ all cases. A better process is needed.
1409
+
1350
1410
  Parameters
1351
1411
  ----------
1352
1412
  min_feature_size : int
1353
1413
  The minimum feature size to enforce, in nanometers.
1354
- strel : str, optional
1414
+ strel : str
1355
1415
  The type of structuring element to use. Can be either "disk" or "square".
1356
1416
  Defaults to "disk".
1357
1417
 
@@ -1383,11 +1443,16 @@ class Device(BaseModel):
1383
1443
  between the original and modified geometries, providing a measure of the changes
1384
1444
  introduced by the feature size enforcement.
1385
1445
 
1446
+ Notes
1447
+ -----
1448
+ This is not a design-rule-checking function, but it can be useful for quick
1449
+ checks.
1450
+
1386
1451
  Parameters
1387
1452
  ----------
1388
1453
  min_feature_size : int
1389
1454
  The minimum feature size to enforce, in nanometers.
1390
- strel : str, optional
1455
+ strel : str
1391
1456
  The type of structuring element to use. Can be either "disk" or "square".
1392
1457
  Defaults to "disk".
1393
1458