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/__init__.py +1 -1
- prefab/__main__.py +14 -11
- prefab/compare.py +16 -1
- prefab/device.py +183 -118
- prefab/geometry.py +66 -22
- prefab/predict.py +51 -21
- prefab/read.py +122 -76
- prefab/shapes.py +82 -81
- {prefab-1.1.5.dist-info → prefab-1.1.6.dist-info}/METADATA +4 -2
- prefab-1.1.6.dist-info/RECORD +13 -0
- {prefab-1.1.5.dist-info → prefab-1.1.6.dist-info}/WHEEL +1 -1
- prefab-1.1.5.dist-info/RECORD +0 -13
- {prefab-1.1.5.dist-info → prefab-1.1.6.dist-info}/licenses/LICENSE +0 -0
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,
|
|
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
|
|
41
|
-
|
|
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
|
|
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
|
|
52
|
-
|
|
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": "
|
|
63
|
+
"bottom": "none",
|
|
62
64
|
"left": "constant",
|
|
63
65
|
"right": "edge",
|
|
64
66
|
},
|
|
65
67
|
thickness={
|
|
66
68
|
"top": 150,
|
|
67
|
-
"bottom":
|
|
69
|
+
"bottom": 0,
|
|
68
70
|
"left": 200,
|
|
69
71
|
"right": 250,
|
|
70
72
|
},
|
|
71
73
|
)
|
|
72
74
|
"""
|
|
73
75
|
|
|
74
|
-
mode: dict[str,
|
|
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
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
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
|
-
|
|
168
|
-
self.device_array
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
@
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
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]
|
|
445
|
-
The x and y coordinates of the origin for the GDSII export. Defaults
|
|
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
|
|
511
|
+
cell_name : str
|
|
477
512
|
The name of the cell to be created. Defaults to "prefab_device".
|
|
478
|
-
gds_layer : tuple[int, int]
|
|
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
|
|
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]
|
|
485
|
-
The x and y coordinates of the origin for the GDSTK cell. Defaults
|
|
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
|
-
|
|
563
|
-
|
|
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":
|
|
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) -
|
|
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) -
|
|
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
|
|
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=
|
|
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
|
|
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
|
|
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]
|
|
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]
|
|
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
|
|
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]]]
|
|
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
|
|
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]
|
|
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
|
|
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]]]
|
|
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
|
|
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]
|
|
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
|
|
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]]]
|
|
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
|
|
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]
|
|
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
|
|
1157
|
+
eta : float
|
|
1117
1158
|
The threshold value for binarization. Defaults to 0.5.
|
|
1118
|
-
beta : float
|
|
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
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1181
|
+
Parameters
|
|
1182
|
+
----------
|
|
1183
|
+
eta : float
|
|
1184
|
+
The threshold value for binarization. Defaults to 0.5.
|
|
1144
1185
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
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
|
-
|
|
1160
|
-
|
|
1200
|
+
noise_magnitude: float = 2.0,
|
|
1201
|
+
blur_radius: float = 8.0,
|
|
1161
1202
|
) -> "Device":
|
|
1162
1203
|
"""
|
|
1163
|
-
Binarize the
|
|
1164
|
-
|
|
1204
|
+
Binarize the input ndarray using a dynamic thresholding approach to simulate
|
|
1205
|
+
surfaceroughness.
|
|
1165
1206
|
|
|
1166
|
-
This
|
|
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
|
-
|
|
1169
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1191
|
-
|
|
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
|
|
1248
|
+
eta1 : float
|
|
1203
1249
|
The first threshold value for ternarization. Defaults to 1/3.
|
|
1204
|
-
eta2 : float
|
|
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
|
|
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
|
|
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
|
|
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
|
|