prefab 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefab/__init__.py +5 -1
- prefab/compare.py +22 -20
- prefab/device.py +167 -105
- prefab/geometry.py +21 -15
- prefab/models.py +31 -1
- prefab/read.py +45 -22
- prefab/shapes.py +765 -0
- {prefab-1.0.2.dist-info → prefab-1.0.4.dist-info}/METADATA +7 -5
- prefab-1.0.4.dist-info/RECORD +12 -0
- prefab-1.0.2.dist-info/RECORD +0 -11
- {prefab-1.0.2.dist-info → prefab-1.0.4.dist-info}/WHEEL +0 -0
- {prefab-1.0.2.dist-info → prefab-1.0.4.dist-info}/licenses/LICENSE +0 -0
prefab/__init__.py
CHANGED
|
@@ -5,7 +5,9 @@ Usage:
|
|
|
5
5
|
import prefab as pf
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
__version__ = "1.0.4"
|
|
9
|
+
|
|
10
|
+
from . import compare, geometry, read, shapes
|
|
9
11
|
from .device import BufferSpec, Device
|
|
10
12
|
from .models import models
|
|
11
13
|
|
|
@@ -14,6 +16,8 @@ __all__ = [
|
|
|
14
16
|
"BufferSpec",
|
|
15
17
|
"geometry",
|
|
16
18
|
"read",
|
|
19
|
+
"shapes",
|
|
17
20
|
"compare",
|
|
18
21
|
"models",
|
|
22
|
+
"__version__",
|
|
19
23
|
]
|
prefab/compare.py
CHANGED
|
@@ -7,40 +7,40 @@ from .device import Device
|
|
|
7
7
|
|
|
8
8
|
def mean_squared_error(device_a: Device, device_b: Device) -> float:
|
|
9
9
|
"""
|
|
10
|
-
Calculate the mean squared error (MSE) between two non-binarized devices.
|
|
10
|
+
Calculate the mean squared error (MSE) between two non-binarized devices. A lower
|
|
11
|
+
value indicates more similarity.
|
|
11
12
|
|
|
12
13
|
Parameters
|
|
13
14
|
----------
|
|
14
15
|
device_a : Device
|
|
15
|
-
The first device.
|
|
16
|
+
The first device (non-binarized).
|
|
16
17
|
device_b : Device
|
|
17
|
-
The second device.
|
|
18
|
+
The second device (non-binarized).
|
|
18
19
|
|
|
19
20
|
Returns
|
|
20
21
|
-------
|
|
21
22
|
float
|
|
22
|
-
The mean squared error between two devices.
|
|
23
|
-
similarity.
|
|
23
|
+
The mean squared error between two devices.
|
|
24
24
|
"""
|
|
25
25
|
return np.mean((device_a.device_array - device_b.device_array) ** 2)
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def intersection_over_union(device_a: Device, device_b: Device) -> float:
|
|
29
29
|
"""
|
|
30
|
-
Calculates the Intersection over Union (IoU) between two binary devices.
|
|
30
|
+
Calculates the Intersection over Union (IoU) between two binary devices. A value
|
|
31
|
+
closer to 1 indicates more similarity (more overlap).
|
|
31
32
|
|
|
32
33
|
Parameters
|
|
33
34
|
----------
|
|
34
35
|
device_a : Device
|
|
35
|
-
The first device.
|
|
36
|
+
The first device (binarized).
|
|
36
37
|
device_b : Device
|
|
37
|
-
The second device.
|
|
38
|
+
The second device (binarized).
|
|
38
39
|
|
|
39
40
|
Returns
|
|
40
41
|
-------
|
|
41
42
|
float
|
|
42
|
-
The Intersection over Union between two devices.
|
|
43
|
-
more similarity (more overlap).
|
|
43
|
+
The Intersection over Union between two devices.
|
|
44
44
|
"""
|
|
45
45
|
return np.sum(
|
|
46
46
|
np.logical_and(device_a.device_array, device_b.device_array)
|
|
@@ -49,40 +49,42 @@ def intersection_over_union(device_a: Device, device_b: Device) -> float:
|
|
|
49
49
|
|
|
50
50
|
def hamming_distance(device_a: Device, device_b: Device) -> int:
|
|
51
51
|
"""
|
|
52
|
-
Calculates the Hamming distance between two binary devices.
|
|
52
|
+
Calculates the Hamming distance between two binary devices. A lower value indicates
|
|
53
|
+
more similarity. The Hamming distance is calculated as the number of positions at
|
|
54
|
+
which the corresponding pixels are different.
|
|
53
55
|
|
|
54
56
|
Parameters
|
|
55
57
|
----------
|
|
56
58
|
device_a : Device
|
|
57
|
-
The first device.
|
|
59
|
+
The first device (binarized).
|
|
58
60
|
device_b : Device
|
|
59
|
-
The second device.
|
|
61
|
+
The second device (binarized).
|
|
60
62
|
|
|
61
63
|
Returns
|
|
62
64
|
-------
|
|
63
65
|
int
|
|
64
|
-
The Hamming distance between two devices.
|
|
65
|
-
similarity.
|
|
66
|
+
The Hamming distance between two devices.
|
|
66
67
|
"""
|
|
67
68
|
return np.sum(device_a.device_array != device_b.device_array)
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
def dice_coefficient(device_a: Device, device_b: Device) -> float:
|
|
71
72
|
"""
|
|
72
|
-
Calculates the Dice coefficient between two binary devices.
|
|
73
|
+
Calculates the Dice coefficient between two binary devices. A value closer to 1
|
|
74
|
+
indicates more similarity. The Dice coefficient is calculated as twice the number of
|
|
75
|
+
pixels in common divided by the total number of pixels in the two devices.
|
|
73
76
|
|
|
74
77
|
Parameters
|
|
75
78
|
----------
|
|
76
79
|
device_a : Device
|
|
77
|
-
The first device.
|
|
80
|
+
The first device (binarized).
|
|
78
81
|
device_b : Device
|
|
79
|
-
The second device.
|
|
82
|
+
The second device (binarized).
|
|
80
83
|
|
|
81
84
|
Returns
|
|
82
85
|
-------
|
|
83
86
|
float
|
|
84
|
-
The Dice coefficient between two devices.
|
|
85
|
-
similarity.
|
|
87
|
+
The Dice coefficient between two devices.
|
|
86
88
|
"""
|
|
87
89
|
intersection = 2.0 * np.sum(
|
|
88
90
|
np.logical_and(device_a.device_array, device_b.device_array)
|
prefab/device.py
CHANGED
|
@@ -40,14 +40,16 @@ class BufferSpec(BaseModel):
|
|
|
40
40
|
('top', 'bottom', 'left', 'right'), where 'constant' is used for isolated
|
|
41
41
|
structures and 'edge' is utilized for preserving the edge, such as for waveguide
|
|
42
42
|
connections.
|
|
43
|
-
thickness : conint(gt=0)
|
|
44
|
-
|
|
43
|
+
thickness : dict[str, conint(gt=0)]
|
|
44
|
+
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.
|
|
45
46
|
|
|
46
47
|
Raises
|
|
47
48
|
------
|
|
48
49
|
ValueError
|
|
49
50
|
If any of the modes specified in the 'mode' dictionary are not one of the
|
|
50
|
-
allowed values ('constant', 'edge'). Or if the thickness
|
|
51
|
+
allowed values ('constant', 'edge'). Or if any of the thickness values are not
|
|
52
|
+
greater than 0.
|
|
51
53
|
|
|
52
54
|
Example
|
|
53
55
|
-------
|
|
@@ -60,7 +62,12 @@ class BufferSpec(BaseModel):
|
|
|
60
62
|
"left": "constant",
|
|
61
63
|
"right": "edge",
|
|
62
64
|
},
|
|
63
|
-
thickness=
|
|
65
|
+
thickness={
|
|
66
|
+
"top": 150,
|
|
67
|
+
"bottom": 100,
|
|
68
|
+
"left": 200,
|
|
69
|
+
"right": 250,
|
|
70
|
+
},
|
|
64
71
|
)
|
|
65
72
|
"""
|
|
66
73
|
|
|
@@ -72,7 +79,14 @@ class BufferSpec(BaseModel):
|
|
|
72
79
|
"right": "constant",
|
|
73
80
|
}
|
|
74
81
|
)
|
|
75
|
-
thickness: conint(gt=0) =
|
|
82
|
+
thickness: dict[str, conint(gt=0)] = Field(
|
|
83
|
+
default_factory=lambda: {
|
|
84
|
+
"top": 128,
|
|
85
|
+
"bottom": 128,
|
|
86
|
+
"left": 128,
|
|
87
|
+
"right": 128,
|
|
88
|
+
}
|
|
89
|
+
)
|
|
76
90
|
|
|
77
91
|
@validator("mode", pre=True)
|
|
78
92
|
def check_mode(cls, v):
|
|
@@ -146,28 +160,26 @@ class Device(BaseModel):
|
|
|
146
160
|
|
|
147
161
|
self.device_array = np.pad(
|
|
148
162
|
self.device_array,
|
|
149
|
-
pad_width=((buffer_thickness, 0), (0, 0)),
|
|
163
|
+
pad_width=((buffer_thickness["top"], 0), (0, 0)),
|
|
150
164
|
mode=buffer_mode["top"],
|
|
151
165
|
)
|
|
152
166
|
self.device_array = np.pad(
|
|
153
167
|
self.device_array,
|
|
154
|
-
pad_width=((0, buffer_thickness), (0, 0)),
|
|
168
|
+
pad_width=((0, buffer_thickness["bottom"]), (0, 0)),
|
|
155
169
|
mode=buffer_mode["bottom"],
|
|
156
170
|
)
|
|
157
171
|
self.device_array = np.pad(
|
|
158
172
|
self.device_array,
|
|
159
|
-
pad_width=((0, 0), (buffer_thickness, 0)),
|
|
173
|
+
pad_width=((0, 0), (buffer_thickness["left"], 0)),
|
|
160
174
|
mode=buffer_mode["left"],
|
|
161
175
|
)
|
|
162
176
|
self.device_array = np.pad(
|
|
163
177
|
self.device_array,
|
|
164
|
-
pad_width=((0, 0), (0, buffer_thickness)),
|
|
178
|
+
pad_width=((0, 0), (0, buffer_thickness["right"])),
|
|
165
179
|
mode=buffer_mode["right"],
|
|
166
180
|
)
|
|
167
181
|
|
|
168
|
-
self.device_array = np.expand_dims(
|
|
169
|
-
self.device_array.astype(np.float32), axis=-1
|
|
170
|
-
)
|
|
182
|
+
self.device_array = np.expand_dims(self.device_array, axis=-1)
|
|
171
183
|
|
|
172
184
|
@root_validator(pre=True)
|
|
173
185
|
def check_device_array(cls, values):
|
|
@@ -212,6 +224,7 @@ class Device(BaseModel):
|
|
|
212
224
|
model: Model,
|
|
213
225
|
model_type: str,
|
|
214
226
|
binarize: bool,
|
|
227
|
+
gpu: bool = False,
|
|
215
228
|
) -> "Device":
|
|
216
229
|
try:
|
|
217
230
|
with open(os.path.expanduser("~/.prefab.toml")) as file:
|
|
@@ -233,6 +246,7 @@ class Device(BaseModel):
|
|
|
233
246
|
"Signup/login and generate a new token.\n"
|
|
234
247
|
"See https://www.prefabphotonics.com/docs/guides/quickstart."
|
|
235
248
|
) from None
|
|
249
|
+
|
|
236
250
|
headers = {
|
|
237
251
|
"Authorization": f"Bearer {access_token}",
|
|
238
252
|
"X-Refresh-Token": refresh_token,
|
|
@@ -246,84 +260,99 @@ class Device(BaseModel):
|
|
|
246
260
|
}
|
|
247
261
|
json_data = json.dumps(predict_data)
|
|
248
262
|
|
|
249
|
-
endpoint_url =
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
)
|
|
254
|
-
response.raise_for_status()
|
|
255
|
-
event_type = None
|
|
256
|
-
model_descriptions = {"p": "Prediction", "c": "Correction", "s": "SEMulate"}
|
|
257
|
-
progress_bar = tqdm(
|
|
258
|
-
total=100,
|
|
259
|
-
desc=f"{model_descriptions[model_type]}",
|
|
260
|
-
unit="%",
|
|
261
|
-
colour="green",
|
|
262
|
-
bar_format="{l_bar}{bar:30}{r_bar}{bar:-10b}",
|
|
263
|
-
)
|
|
263
|
+
endpoint_url = (
|
|
264
|
+
"https://prefab-photonics--predict-gpu-v1.modal.run"
|
|
265
|
+
if gpu
|
|
266
|
+
else "https://prefab-photonics--predict-v1.modal.run"
|
|
267
|
+
)
|
|
264
268
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
results.append(decoded_image)
|
|
269
|
+
try:
|
|
270
|
+
with requests.post(
|
|
271
|
+
endpoint_url, data=json_data, headers=headers, stream=True
|
|
272
|
+
) as response:
|
|
273
|
+
response.raise_for_status()
|
|
274
|
+
event_type = None
|
|
275
|
+
model_descriptions = {
|
|
276
|
+
"p": "Prediction",
|
|
277
|
+
"c": "Correction",
|
|
278
|
+
"s": "SEMulate",
|
|
279
|
+
}
|
|
280
|
+
progress_bar = tqdm(
|
|
281
|
+
total=100,
|
|
282
|
+
desc=f"{model_descriptions[model_type]}",
|
|
283
|
+
unit="%",
|
|
284
|
+
colour="green",
|
|
285
|
+
bar_format="{l_bar}{bar:30}{r_bar}{bar:-10b}",
|
|
286
|
+
)
|
|
284
287
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
288
|
+
for line in response.iter_lines():
|
|
289
|
+
if line:
|
|
290
|
+
decoded_line = line.decode("utf-8").strip()
|
|
291
|
+
if decoded_line.startswith("event:"):
|
|
292
|
+
event_type = decoded_line.split(":")[1].strip()
|
|
293
|
+
elif decoded_line.startswith("data:"):
|
|
294
|
+
try:
|
|
295
|
+
data_content = json.loads(
|
|
296
|
+
decoded_line.split("data: ")[1]
|
|
297
|
+
)
|
|
298
|
+
if event_type == "progress":
|
|
299
|
+
progress = round(100 * data_content["progress"])
|
|
300
|
+
progress_bar.update(progress - progress_bar.n)
|
|
301
|
+
elif event_type == "result":
|
|
302
|
+
results = []
|
|
303
|
+
for key in sorted(data_content.keys()):
|
|
304
|
+
if key.startswith("result"):
|
|
305
|
+
decoded_image = self._decode_array(
|
|
306
|
+
data_content[key]
|
|
307
|
+
)
|
|
308
|
+
results.append(decoded_image)
|
|
309
|
+
|
|
310
|
+
if results:
|
|
311
|
+
prediction = np.stack(results, axis=-1)
|
|
312
|
+
if binarize:
|
|
313
|
+
prediction = geometry.binarize_hard(
|
|
314
|
+
prediction
|
|
315
|
+
)
|
|
316
|
+
progress_bar.close()
|
|
317
|
+
return prediction
|
|
318
|
+
elif event_type == "end":
|
|
319
|
+
print("Stream ended.")
|
|
289
320
|
progress_bar.close()
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
elif event_type == "auth":
|
|
296
|
-
if "new_refresh_token" in data_content["auth"]:
|
|
297
|
-
prefab_file_path = os.path.expanduser(
|
|
298
|
-
"~/.prefab.toml"
|
|
299
|
-
)
|
|
300
|
-
with open(
|
|
301
|
-
prefab_file_path, "w", encoding="utf-8"
|
|
302
|
-
) as toml_file:
|
|
303
|
-
toml.dump(
|
|
304
|
-
{
|
|
305
|
-
"access_token": data_content["auth"][
|
|
306
|
-
"new_access_token"
|
|
307
|
-
],
|
|
308
|
-
"refresh_token": data_content["auth"][
|
|
309
|
-
"new_refresh_token"
|
|
310
|
-
],
|
|
311
|
-
},
|
|
312
|
-
toml_file,
|
|
321
|
+
break
|
|
322
|
+
elif event_type == "auth":
|
|
323
|
+
if "new_refresh_token" in data_content["auth"]:
|
|
324
|
+
prefab_file_path = os.path.expanduser(
|
|
325
|
+
"~/.prefab.toml"
|
|
313
326
|
)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
327
|
+
with open(
|
|
328
|
+
prefab_file_path, "w", encoding="utf-8"
|
|
329
|
+
) as toml_file:
|
|
330
|
+
toml.dump(
|
|
331
|
+
{
|
|
332
|
+
"access_token": data_content[
|
|
333
|
+
"auth"
|
|
334
|
+
]["new_access_token"],
|
|
335
|
+
"refresh_token": data_content[
|
|
336
|
+
"auth"
|
|
337
|
+
]["new_refresh_token"],
|
|
338
|
+
},
|
|
339
|
+
toml_file,
|
|
340
|
+
)
|
|
341
|
+
elif event_type == "error":
|
|
342
|
+
raise ValueError(f"{data_content['error']}")
|
|
343
|
+
except json.JSONDecodeError:
|
|
344
|
+
raise ValueError(
|
|
345
|
+
"Failed to decode JSON:",
|
|
346
|
+
decoded_line.split("data: ")[1],
|
|
347
|
+
) from None
|
|
348
|
+
except requests.RequestException as e:
|
|
349
|
+
raise RuntimeError(f"Request failed: {e}") from e
|
|
322
350
|
|
|
323
351
|
def predict(
|
|
324
352
|
self,
|
|
325
353
|
model: Model,
|
|
326
354
|
binarize: bool = False,
|
|
355
|
+
gpu: bool = False,
|
|
327
356
|
) -> "Device":
|
|
328
357
|
"""
|
|
329
358
|
Predict the nanofabrication outcome of the device using a specified model.
|
|
@@ -345,6 +374,10 @@ class Device(BaseModel):
|
|
|
345
374
|
If True, the predicted device geometry will be binarized using a threshold
|
|
346
375
|
method. This is useful for converting probabilistic predictions into binary
|
|
347
376
|
geometries. Defaults to False.
|
|
377
|
+
gpu : bool, optional
|
|
378
|
+
If True, the prediction will be performed on a GPU. Defaults to False.
|
|
379
|
+
Note: The GPU option has more overhead and will take longer for small
|
|
380
|
+
devices, but will be faster for larger devices.
|
|
348
381
|
|
|
349
382
|
Returns
|
|
350
383
|
-------
|
|
@@ -361,6 +394,7 @@ class Device(BaseModel):
|
|
|
361
394
|
model=model,
|
|
362
395
|
model_type="p",
|
|
363
396
|
binarize=binarize,
|
|
397
|
+
gpu=gpu,
|
|
364
398
|
)
|
|
365
399
|
return self.model_copy(update={"device_array": prediction_array})
|
|
366
400
|
|
|
@@ -368,6 +402,7 @@ class Device(BaseModel):
|
|
|
368
402
|
self,
|
|
369
403
|
model: Model,
|
|
370
404
|
binarize: bool = True,
|
|
405
|
+
gpu: bool = False,
|
|
371
406
|
) -> "Device":
|
|
372
407
|
"""
|
|
373
408
|
Correct the nanofabrication outcome of the device using a specified model.
|
|
@@ -391,6 +426,10 @@ class Device(BaseModel):
|
|
|
391
426
|
If True, the corrected device geometry will be binarized using a threshold
|
|
392
427
|
method. This is useful for converting probabilistic corrections into binary
|
|
393
428
|
geometries. Defaults to True.
|
|
429
|
+
gpu : bool, optional
|
|
430
|
+
If True, the prediction will be performed on a GPU. Defaults to False.
|
|
431
|
+
Note: The GPU option has more overhead and will take longer for small
|
|
432
|
+
devices, but will be faster for larger devices.
|
|
394
433
|
|
|
395
434
|
Returns
|
|
396
435
|
-------
|
|
@@ -407,12 +446,14 @@ class Device(BaseModel):
|
|
|
407
446
|
model=model,
|
|
408
447
|
model_type="c",
|
|
409
448
|
binarize=binarize,
|
|
449
|
+
gpu=gpu,
|
|
410
450
|
)
|
|
411
451
|
return self.model_copy(update={"device_array": correction_array})
|
|
412
452
|
|
|
413
453
|
def semulate(
|
|
414
454
|
self,
|
|
415
455
|
model: Model,
|
|
456
|
+
gpu: bool = False,
|
|
416
457
|
) -> "Device":
|
|
417
458
|
"""
|
|
418
459
|
Simulate the appearance of the device as if viewed under a scanning electron
|
|
@@ -432,6 +473,10 @@ class Device(BaseModel):
|
|
|
432
473
|
in `models.py`. Each model is associated with a version and dataset that
|
|
433
474
|
detail its creation and the data it was trained on, ensuring the SEMulation
|
|
434
475
|
is tailored to specific fabrication parameters.
|
|
476
|
+
gpu : bool, optional
|
|
477
|
+
If True, the prediction will be performed on a GPU. Defaults to False.
|
|
478
|
+
Note: The GPU option has more overhead and will take longer for small
|
|
479
|
+
devices, but will be faster for larger devices.
|
|
435
480
|
|
|
436
481
|
Returns
|
|
437
482
|
-------
|
|
@@ -443,6 +488,7 @@ class Device(BaseModel):
|
|
|
443
488
|
model=model,
|
|
444
489
|
model_type="s",
|
|
445
490
|
binarize=False,
|
|
491
|
+
gpu=gpu,
|
|
446
492
|
)
|
|
447
493
|
return self.model_copy(update={"device_array": semulated_array})
|
|
448
494
|
|
|
@@ -464,10 +510,12 @@ class Device(BaseModel):
|
|
|
464
510
|
buffer_thickness = self.buffer_spec.thickness
|
|
465
511
|
buffer_mode = self.buffer_spec.mode
|
|
466
512
|
|
|
467
|
-
crop_top = buffer_thickness if buffer_mode["top"] == "edge" else 0
|
|
468
|
-
crop_bottom =
|
|
469
|
-
|
|
470
|
-
|
|
513
|
+
crop_top = buffer_thickness["top"] if buffer_mode["top"] == "edge" else 0
|
|
514
|
+
crop_bottom = (
|
|
515
|
+
buffer_thickness["bottom"] if buffer_mode["bottom"] == "edge" else 0
|
|
516
|
+
)
|
|
517
|
+
crop_left = buffer_thickness["left"] if buffer_mode["left"] == "edge" else 0
|
|
518
|
+
crop_right = buffer_thickness["right"] if buffer_mode["right"] == "edge" else 0
|
|
471
519
|
|
|
472
520
|
ndarray = device_array[
|
|
473
521
|
crop_top : device_array.shape[0] - crop_bottom,
|
|
@@ -629,7 +677,7 @@ class Device(BaseModel):
|
|
|
629
677
|
bounds: Optional[tuple[tuple[int, int], tuple[int, int]]],
|
|
630
678
|
ax: Optional[Axes],
|
|
631
679
|
**kwargs,
|
|
632
|
-
) -> Axes:
|
|
680
|
+
) -> tuple[plt.cm.ScalarMappable, Axes]:
|
|
633
681
|
if ax is None:
|
|
634
682
|
_, ax = plt.subplots()
|
|
635
683
|
ax.set_ylabel("y (nm)")
|
|
@@ -673,6 +721,13 @@ class Device(BaseModel):
|
|
|
673
721
|
if show_buffer:
|
|
674
722
|
self._add_buffer_visualization(ax)
|
|
675
723
|
|
|
724
|
+
# # Adjust colorbar font size if a colorbar is added
|
|
725
|
+
# if "cmap" in kwargs:
|
|
726
|
+
# cbar = plt.colorbar(mappable, ax=ax)
|
|
727
|
+
# cbar.ax.tick_params(labelsize=14)
|
|
728
|
+
# if "label" in kwargs:
|
|
729
|
+
# cbar.set_label(kwargs["label"], fontsize=16)
|
|
730
|
+
|
|
676
731
|
return mappable, ax
|
|
677
732
|
|
|
678
733
|
def plot(
|
|
@@ -937,9 +992,9 @@ class Device(BaseModel):
|
|
|
937
992
|
buffer_hatch = "/"
|
|
938
993
|
|
|
939
994
|
mid_rect = Rectangle(
|
|
940
|
-
(buffer_thickness, buffer_thickness),
|
|
941
|
-
plot_array.shape[1] -
|
|
942
|
-
plot_array.shape[0] -
|
|
995
|
+
(buffer_thickness["left"], buffer_thickness["top"]),
|
|
996
|
+
plot_array.shape[1] - buffer_thickness["left"] - buffer_thickness["right"],
|
|
997
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
943
998
|
facecolor="none",
|
|
944
999
|
edgecolor="black",
|
|
945
1000
|
linewidth=1,
|
|
@@ -949,25 +1004,25 @@ class Device(BaseModel):
|
|
|
949
1004
|
top_rect = Rectangle(
|
|
950
1005
|
(0, 0),
|
|
951
1006
|
plot_array.shape[1],
|
|
952
|
-
buffer_thickness,
|
|
1007
|
+
buffer_thickness["top"],
|
|
953
1008
|
facecolor=buffer_fill,
|
|
954
1009
|
hatch=buffer_hatch,
|
|
955
1010
|
)
|
|
956
1011
|
ax.add_patch(top_rect)
|
|
957
1012
|
|
|
958
1013
|
bottom_rect = Rectangle(
|
|
959
|
-
(0, plot_array.shape[0] - buffer_thickness),
|
|
1014
|
+
(0, plot_array.shape[0] - buffer_thickness["bottom"]),
|
|
960
1015
|
plot_array.shape[1],
|
|
961
|
-
buffer_thickness,
|
|
1016
|
+
buffer_thickness["bottom"],
|
|
962
1017
|
facecolor=buffer_fill,
|
|
963
1018
|
hatch=buffer_hatch,
|
|
964
1019
|
)
|
|
965
1020
|
ax.add_patch(bottom_rect)
|
|
966
1021
|
|
|
967
1022
|
left_rect = Rectangle(
|
|
968
|
-
(0, buffer_thickness),
|
|
969
|
-
buffer_thickness,
|
|
970
|
-
plot_array.shape[0] -
|
|
1023
|
+
(0, buffer_thickness["top"]),
|
|
1024
|
+
buffer_thickness["left"],
|
|
1025
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
971
1026
|
facecolor=buffer_fill,
|
|
972
1027
|
hatch=buffer_hatch,
|
|
973
1028
|
)
|
|
@@ -975,11 +1030,11 @@ class Device(BaseModel):
|
|
|
975
1030
|
|
|
976
1031
|
right_rect = Rectangle(
|
|
977
1032
|
(
|
|
978
|
-
plot_array.shape[1] - buffer_thickness,
|
|
979
|
-
buffer_thickness,
|
|
1033
|
+
plot_array.shape[1] - buffer_thickness["right"],
|
|
1034
|
+
buffer_thickness["top"],
|
|
980
1035
|
),
|
|
981
|
-
buffer_thickness,
|
|
982
|
-
plot_array.shape[0] -
|
|
1036
|
+
buffer_thickness["right"],
|
|
1037
|
+
plot_array.shape[0] - buffer_thickness["top"] - buffer_thickness["bottom"],
|
|
983
1038
|
facecolor=buffer_fill,
|
|
984
1039
|
hatch=buffer_hatch,
|
|
985
1040
|
)
|
|
@@ -1017,7 +1072,9 @@ class Device(BaseModel):
|
|
|
1017
1072
|
binarized_device_array = geometry.binarize(
|
|
1018
1073
|
device_array=self.device_array, eta=eta, beta=beta
|
|
1019
1074
|
)
|
|
1020
|
-
return self.model_copy(
|
|
1075
|
+
return self.model_copy(
|
|
1076
|
+
update={"device_array": binarized_device_array.astype(np.uint8)}
|
|
1077
|
+
)
|
|
1021
1078
|
|
|
1022
1079
|
def binarize_hard(self, eta: float = 0.5) -> "Device":
|
|
1023
1080
|
"""
|
|
@@ -1038,12 +1095,14 @@ class Device(BaseModel):
|
|
|
1038
1095
|
binarized_device_array = geometry.binarize_hard(
|
|
1039
1096
|
device_array=self.device_array, eta=eta
|
|
1040
1097
|
)
|
|
1041
|
-
return self.model_copy(
|
|
1098
|
+
return self.model_copy(
|
|
1099
|
+
update={"device_array": binarized_device_array.astype(np.uint8)}
|
|
1100
|
+
)
|
|
1042
1101
|
|
|
1043
1102
|
def binarize_monte_carlo(
|
|
1044
1103
|
self,
|
|
1045
1104
|
threshold_noise_std: float = 2.0,
|
|
1046
|
-
threshold_blur_std: float =
|
|
1105
|
+
threshold_blur_std: float = 8.0,
|
|
1047
1106
|
) -> "Device":
|
|
1048
1107
|
"""
|
|
1049
1108
|
Binarize the device geometry using a Monte Carlo approach with Gaussian
|
|
@@ -1106,9 +1165,10 @@ class Device(BaseModel):
|
|
|
1106
1165
|
|
|
1107
1166
|
Parameters
|
|
1108
1167
|
----------
|
|
1109
|
-
buffer_thickness :
|
|
1110
|
-
|
|
1111
|
-
|
|
1168
|
+
buffer_thickness : dict, optional
|
|
1169
|
+
A dictionary specifying the thickness of the buffer to leave around the
|
|
1170
|
+
non-zero elements of the array. Should contain keys 'top', 'bottom', 'left',
|
|
1171
|
+
'right'. Defaults to None, which means no buffer is added.
|
|
1112
1172
|
|
|
1113
1173
|
Returns
|
|
1114
1174
|
-------
|
|
@@ -1233,3 +1293,5 @@ class Device(BaseModel):
|
|
|
1233
1293
|
with higher values indicating greater uncertainty.
|
|
1234
1294
|
"""
|
|
1235
1295
|
return 1 - 2 * np.abs(0.5 - self.device_array)
|
|
1296
|
+
|
|
1297
|
+
return 1 - 2 * np.abs(0.5 - self.device_array)
|