witwin-maxwell 0.3.0__py3-none-win_amd64.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.
- witwin/maxwell/__init__.py +164 -0
- witwin/maxwell/adapters/__init__.py +1 -0
- witwin/maxwell/adapters/gds.py +203 -0
- witwin/maxwell/adapters/tidy3d.py +1669 -0
- witwin/maxwell/adjoint_inputs.py +70 -0
- witwin/maxwell/compiler/__init__.py +11 -0
- witwin/maxwell/compiler/materials.py +1355 -0
- witwin/maxwell/compiler/monitors.py +164 -0
- witwin/maxwell/compiler/sources.py +222 -0
- witwin/maxwell/fdfd/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/bridge.py +180 -0
- witwin/maxwell/fdfd/ams.py +300 -0
- witwin/maxwell/fdfd/krylov.py +280 -0
- witwin/maxwell/fdfd/plotting.py +110 -0
- witwin/maxwell/fdfd/postprocess.py +33 -0
- witwin/maxwell/fdfd/solver.py +1057 -0
- witwin/maxwell/fdtd/__init__.py +4 -0
- witwin/maxwell/fdtd/adjoint/__init__.py +21 -0
- witwin/maxwell/fdtd/adjoint/bridge.py +746 -0
- witwin/maxwell/fdtd/adjoint/capabilities.py +29 -0
- witwin/maxwell/fdtd/adjoint/core.py +3614 -0
- witwin/maxwell/fdtd/adjoint/dispatch.py +684 -0
- witwin/maxwell/fdtd/adjoint/native.py +2561 -0
- witwin/maxwell/fdtd/adjoint/profiler.py +195 -0
- witwin/maxwell/fdtd/adjoint/reverse_common.py +207 -0
- witwin/maxwell/fdtd/adjoint/seeds.py +524 -0
- witwin/maxwell/fdtd/boundary/__init__.py +37 -0
- witwin/maxwell/fdtd/boundary/bloch.py +90 -0
- witwin/maxwell/fdtd/boundary/common.py +41 -0
- witwin/maxwell/fdtd/boundary/cpml.py +527 -0
- witwin/maxwell/fdtd/boundary/runtime.py +209 -0
- witwin/maxwell/fdtd/checkpoint.py +227 -0
- witwin/maxwell/fdtd/coords.py +32 -0
- witwin/maxwell/fdtd/cuda/__init__.py +11 -0
- witwin/maxwell/fdtd/cuda/backend.py +3532 -0
- witwin/maxwell/fdtd/cuda/build.py +292 -0
- witwin/maxwell/fdtd/cuda/extension.cpp +2053 -0
- witwin/maxwell/fdtd/cuda/kernels/adjoint.cu +5290 -0
- witwin/maxwell/fdtd/cuda/kernels/aniso.cu +1704 -0
- witwin/maxwell/fdtd/cuda/kernels/boundary.cu +424 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cu +74 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cuh +82 -0
- witwin/maxwell/fdtd/cuda/kernels/dispersive.cu +782 -0
- witwin/maxwell/fdtd/cuda/kernels/electric.cu +3613 -0
- witwin/maxwell/fdtd/cuda/kernels/magnetic.cu +781 -0
- witwin/maxwell/fdtd/cuda/kernels/observers.cu +347 -0
- witwin/maxwell/fdtd/cuda/kernels/projection.cu +211 -0
- witwin/maxwell/fdtd/cuda/kernels/sources.cu +1482 -0
- witwin/maxwell/fdtd/cuda/kernels/spectral.cu +297 -0
- witwin/maxwell/fdtd/cuda/launch.h +23 -0
- witwin/maxwell/fdtd/cuda/prebuilt/witwin_maxwell_fdtd_cuda.pyd +0 -0
- witwin/maxwell/fdtd/cuda/runtime/__init__.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/dispatcher.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/graph.py +33 -0
- witwin/maxwell/fdtd/cuda/tensors.h +35 -0
- witwin/maxwell/fdtd/dispersion.py +48 -0
- witwin/maxwell/fdtd/evaluation.py +290 -0
- witwin/maxwell/fdtd/excitation/__init__.py +27 -0
- witwin/maxwell/fdtd/excitation/injection.py +1102 -0
- witwin/maxwell/fdtd/excitation/modes.py +1675 -0
- witwin/maxwell/fdtd/excitation/spatial.py +469 -0
- witwin/maxwell/fdtd/excitation/temporal.py +510 -0
- witwin/maxwell/fdtd/excitation/tfsf_apply.py +264 -0
- witwin/maxwell/fdtd/excitation/tfsf_common.py +421 -0
- witwin/maxwell/fdtd/excitation/tfsf_specs.py +490 -0
- witwin/maxwell/fdtd/excitation/tfsf_state.py +778 -0
- witwin/maxwell/fdtd/io.py +31 -0
- witwin/maxwell/fdtd/material_pullback.py +371 -0
- witwin/maxwell/fdtd/meshing/__init__.py +3 -0
- witwin/maxwell/fdtd/meshing/autogrid.py +335 -0
- witwin/maxwell/fdtd/observers.py +1453 -0
- witwin/maxwell/fdtd/plotting.py +121 -0
- witwin/maxwell/fdtd/postprocess.py +205 -0
- witwin/maxwell/fdtd/runtime/__init__.py +119 -0
- witwin/maxwell/fdtd/runtime/initialization.py +236 -0
- witwin/maxwell/fdtd/runtime/materials.py +1563 -0
- witwin/maxwell/fdtd/runtime/module_cache.py +31 -0
- witwin/maxwell/fdtd/runtime/spectral.py +581 -0
- witwin/maxwell/fdtd/runtime/stepping.py +1987 -0
- witwin/maxwell/fdtd/solver.py +502 -0
- witwin/maxwell/geometry/__init__.py +5 -0
- witwin/maxwell/geometry/polyslab.py +299 -0
- witwin/maxwell/materials.py +3 -0
- witwin/maxwell/media.py +1467 -0
- witwin/maxwell/monitors.py +743 -0
- witwin/maxwell/ports.py +113 -0
- witwin/maxwell/postprocess/RCS.py +139 -0
- witwin/maxwell/postprocess/__init__.py +42 -0
- witwin/maxwell/postprocess/diffraction.py +219 -0
- witwin/maxwell/postprocess/directivity.py +152 -0
- witwin/maxwell/postprocess/emission.py +67 -0
- witwin/maxwell/postprocess/modal.py +324 -0
- witwin/maxwell/postprocess/nfft.py +266 -0
- witwin/maxwell/postprocess/scattering_parameters.py +358 -0
- witwin/maxwell/postprocess/stratton_chu.py +1193 -0
- witwin/maxwell/result.py +967 -0
- witwin/maxwell/scene.py +1338 -0
- witwin/maxwell/simulation.py +659 -0
- witwin/maxwell/sources.py +917 -0
- witwin/maxwell/visualization/__init__.py +28 -0
- witwin/maxwell/visualization/interactive.py +484 -0
- witwin/maxwell/visualization/plots.py +568 -0
- witwin_maxwell-0.3.0.dist-info/METADATA +184 -0
- witwin_maxwell-0.3.0.dist-info/RECORD +107 -0
- witwin_maxwell-0.3.0.dist-info/WHEEL +4 -0
- witwin_maxwell-0.3.0.dist-info/licenses/LICENSE +13 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .plots import (
|
|
2
|
+
extract_orthogonal_slice,
|
|
3
|
+
plot_cross_section_panels,
|
|
4
|
+
plot_orthogonal_views,
|
|
5
|
+
plot_slice_image,
|
|
6
|
+
visualize_material_slice,
|
|
7
|
+
visualize_slice,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"build_fdtd_pyvista_grid",
|
|
12
|
+
"extract_orthogonal_slice",
|
|
13
|
+
"plot_cross_section_panels",
|
|
14
|
+
"plot_orthogonal_views",
|
|
15
|
+
"plot_slice_image",
|
|
16
|
+
"show_pyvista_solution",
|
|
17
|
+
"visualize_material_slice",
|
|
18
|
+
"visualize_slice",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def __getattr__(name):
|
|
23
|
+
if name in ("build_fdtd_pyvista_grid", "show_pyvista_solution"):
|
|
24
|
+
from .interactive import build_fdtd_pyvista_grid, show_pyvista_solution
|
|
25
|
+
globals()["build_fdtd_pyvista_grid"] = build_fdtd_pyvista_grid
|
|
26
|
+
globals()["show_pyvista_solution"] = show_pyvista_solution
|
|
27
|
+
return globals()[name]
|
|
28
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
ZINC = {
|
|
5
|
+
"950": "#09090b",
|
|
6
|
+
"900": "#18181b",
|
|
7
|
+
"800": "#27272a",
|
|
8
|
+
"700": "#3f3f46",
|
|
9
|
+
"600": "#52525b",
|
|
10
|
+
"400": "#a1a1aa",
|
|
11
|
+
"200": "#e4e4e7",
|
|
12
|
+
"100": "#f4f4f5",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_fdtd_pyvista_grid(fdtd, freq_solution):
|
|
17
|
+
import pyvista as pv
|
|
18
|
+
|
|
19
|
+
field_mag, _, _, _ = fdtd._interpolate_yee_to_center(freq_solution)
|
|
20
|
+
permittivity = fdtd._get_centered_permittivity().cpu().numpy()
|
|
21
|
+
x0, _, y0, _, z0, _ = fdtd.scene.domain_range
|
|
22
|
+
|
|
23
|
+
image = pv.ImageData()
|
|
24
|
+
image.dimensions = (
|
|
25
|
+
field_mag.shape[0] + 1,
|
|
26
|
+
field_mag.shape[1] + 1,
|
|
27
|
+
field_mag.shape[2] + 1,
|
|
28
|
+
)
|
|
29
|
+
image.origin = (x0, y0, z0)
|
|
30
|
+
image.spacing = (fdtd.dx, fdtd.dy, fdtd.dz)
|
|
31
|
+
image.cell_data["field_mag"] = field_mag.ravel(order="F")
|
|
32
|
+
image.cell_data["permittivity"] = permittivity.ravel(order="F")
|
|
33
|
+
return image.cell_data_to_point_data(pass_cell_data=True)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _iso_value_from_frac(field_max, iso_frac):
|
|
37
|
+
return max(float(field_max) * float(iso_frac), 1e-12)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _build_view_state(grid, iso_frac):
|
|
41
|
+
field_mag = np.asarray(grid["field_mag"])
|
|
42
|
+
permittivity = np.asarray(grid["permittivity"])
|
|
43
|
+
field_min = float(np.nanmin(field_mag))
|
|
44
|
+
field_max = float(np.nanmax(field_mag))
|
|
45
|
+
eps_min = float(np.nanmin(permittivity))
|
|
46
|
+
eps_max = float(np.nanmax(permittivity))
|
|
47
|
+
iso_frac = float(np.clip(iso_frac, 0.05, 0.95))
|
|
48
|
+
dielectric_threshold = float(np.clip(1.05, eps_min, eps_max))
|
|
49
|
+
return {
|
|
50
|
+
"field_min": field_min,
|
|
51
|
+
"field_max": field_max,
|
|
52
|
+
"eps_min": eps_min,
|
|
53
|
+
"eps_max": eps_max,
|
|
54
|
+
"mode": "field",
|
|
55
|
+
"iso_frac": iso_frac,
|
|
56
|
+
"iso_value": _iso_value_from_frac(field_max, iso_frac),
|
|
57
|
+
"dielectric_threshold": dielectric_threshold,
|
|
58
|
+
"volume_opacity": 0.18,
|
|
59
|
+
"show_outline": True,
|
|
60
|
+
"show_slices": True,
|
|
61
|
+
"show_contour": True,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _safe_remove_actor(plotter, actor):
|
|
66
|
+
if actor is None:
|
|
67
|
+
return
|
|
68
|
+
if isinstance(actor, (list, tuple)):
|
|
69
|
+
for item in actor:
|
|
70
|
+
_safe_remove_actor(plotter, item)
|
|
71
|
+
return
|
|
72
|
+
try:
|
|
73
|
+
plotter.remove_actor(actor, render=False)
|
|
74
|
+
except (RuntimeError, TypeError, ValueError):
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _clear_scene_actors(plotter, actors):
|
|
79
|
+
for key, actor in list(actors.items()):
|
|
80
|
+
_safe_remove_actor(plotter, actor)
|
|
81
|
+
actors[key] = None
|
|
82
|
+
if hasattr(plotter, "clear_plane_widgets"):
|
|
83
|
+
plotter.clear_plane_widgets()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _format_status_panel(state):
|
|
87
|
+
mode_label = "Field" if state["mode"] == "field" else "Dielectric"
|
|
88
|
+
return "\n".join(
|
|
89
|
+
[
|
|
90
|
+
"Status",
|
|
91
|
+
f"Mode: {mode_label}",
|
|
92
|
+
f"Field max: {state['field_max']:.3e}",
|
|
93
|
+
f"Eps range: {state['eps_min']:.3f} .. {state['eps_max']:.3f}",
|
|
94
|
+
f"Iso / cutoff: {state['iso_frac']:.2f} / {state['dielectric_threshold']:.3f}",
|
|
95
|
+
]
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _update_status_panel(plotter, state):
|
|
100
|
+
plotter.subplot(0, 1)
|
|
101
|
+
plotter.add_text(
|
|
102
|
+
_format_status_panel(state),
|
|
103
|
+
position=(0.08, 0.015),
|
|
104
|
+
viewport=True,
|
|
105
|
+
font_size=8,
|
|
106
|
+
color=ZINC["100"],
|
|
107
|
+
name="status_panel",
|
|
108
|
+
render=False,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _update_scene_title(plotter, state):
|
|
113
|
+
mode_label = "Field" if state["mode"] == "field" else "Dielectric"
|
|
114
|
+
plotter.subplot(0, 0)
|
|
115
|
+
plotter.add_text(
|
|
116
|
+
f"Scene View: {mode_label}",
|
|
117
|
+
position="upper_left",
|
|
118
|
+
font_size=12,
|
|
119
|
+
color=ZINC["100"],
|
|
120
|
+
name="scene_title",
|
|
121
|
+
render=False,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _add_outline(plotter, grid):
|
|
126
|
+
plotter.subplot(0, 0)
|
|
127
|
+
return plotter.add_mesh(
|
|
128
|
+
grid.outline(),
|
|
129
|
+
color=ZINC["200"],
|
|
130
|
+
line_width=1,
|
|
131
|
+
name="outline",
|
|
132
|
+
render=False,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _add_field_volume(plotter, grid, state):
|
|
137
|
+
plotter.subplot(0, 0)
|
|
138
|
+
return plotter.add_volume(
|
|
139
|
+
grid,
|
|
140
|
+
scalars="field_mag",
|
|
141
|
+
cmap="inferno",
|
|
142
|
+
opacity=state["volume_opacity"],
|
|
143
|
+
shade=False,
|
|
144
|
+
mapper="smart",
|
|
145
|
+
show_scalar_bar=False,
|
|
146
|
+
clim=(state["field_min"], state["field_max"]),
|
|
147
|
+
name="field_volume",
|
|
148
|
+
render=False,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _add_field_contour(plotter, grid, state):
|
|
153
|
+
plotter.subplot(0, 0)
|
|
154
|
+
contour = grid.contour(isosurfaces=[state["iso_value"]], scalars="field_mag")
|
|
155
|
+
if contour.n_cells == 0:
|
|
156
|
+
return None
|
|
157
|
+
return plotter.add_mesh(
|
|
158
|
+
contour,
|
|
159
|
+
scalars="field_mag",
|
|
160
|
+
cmap="inferno",
|
|
161
|
+
opacity=0.9,
|
|
162
|
+
smooth_shading=True,
|
|
163
|
+
show_scalar_bar=False,
|
|
164
|
+
clim=(state["field_min"], state["field_max"]),
|
|
165
|
+
specular=0.2,
|
|
166
|
+
name="field_contour",
|
|
167
|
+
render=False,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _add_field_slices(plotter, grid, state):
|
|
172
|
+
plotter.subplot(0, 0)
|
|
173
|
+
return plotter.add_mesh_slice_orthogonal(
|
|
174
|
+
grid,
|
|
175
|
+
scalars="field_mag",
|
|
176
|
+
cmap="inferno",
|
|
177
|
+
line_width=2,
|
|
178
|
+
widget_color=ZINC["400"],
|
|
179
|
+
show_scalar_bar=False,
|
|
180
|
+
clim=(state["field_min"], state["field_max"]),
|
|
181
|
+
render=False,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _add_dielectric_mesh(plotter, grid, state):
|
|
186
|
+
plotter.subplot(0, 0)
|
|
187
|
+
dielectric = grid.threshold(
|
|
188
|
+
value=state["dielectric_threshold"],
|
|
189
|
+
scalars="permittivity",
|
|
190
|
+
)
|
|
191
|
+
if dielectric.n_cells == 0:
|
|
192
|
+
return None
|
|
193
|
+
return plotter.add_mesh(
|
|
194
|
+
dielectric,
|
|
195
|
+
scalars="permittivity",
|
|
196
|
+
cmap="viridis",
|
|
197
|
+
opacity=0.28,
|
|
198
|
+
smooth_shading=True,
|
|
199
|
+
show_scalar_bar=False,
|
|
200
|
+
clim=(state["eps_min"], state["eps_max"]),
|
|
201
|
+
specular=0.12,
|
|
202
|
+
name="dielectric_mesh",
|
|
203
|
+
render=False,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _add_permittivity_slices(plotter, grid, state):
|
|
208
|
+
plotter.subplot(0, 0)
|
|
209
|
+
return plotter.add_mesh_slice_orthogonal(
|
|
210
|
+
grid,
|
|
211
|
+
scalars="permittivity",
|
|
212
|
+
cmap="viridis",
|
|
213
|
+
line_width=2,
|
|
214
|
+
widget_color=ZINC["400"],
|
|
215
|
+
show_scalar_bar=False,
|
|
216
|
+
clim=(state["eps_min"], state["eps_max"]),
|
|
217
|
+
render=False,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _setup_scene_view(plotter, state):
|
|
222
|
+
plotter.subplot(0, 0)
|
|
223
|
+
plotter.set_background(ZINC["900"], top=ZINC["800"], all_renderers=False)
|
|
224
|
+
plotter.add_axes(line_width=2, color=ZINC["200"])
|
|
225
|
+
plotter.show_grid(color=ZINC["600"])
|
|
226
|
+
plotter.camera_position = "iso"
|
|
227
|
+
_update_scene_title(plotter, state)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _setup_control_panel(plotter, state):
|
|
231
|
+
plotter.subplot(0, 1)
|
|
232
|
+
plotter.set_background(ZINC["800"], top=ZINC["700"], all_renderers=False)
|
|
233
|
+
plotter.add_text(
|
|
234
|
+
"Controls",
|
|
235
|
+
position=(0.08, 0.94),
|
|
236
|
+
viewport=True,
|
|
237
|
+
font_size=16,
|
|
238
|
+
color=ZINC["100"],
|
|
239
|
+
name="panel_title",
|
|
240
|
+
render=False,
|
|
241
|
+
)
|
|
242
|
+
plotter.add_text(
|
|
243
|
+
"Switch the scene between Field and Dielectric.",
|
|
244
|
+
position=(0.08, 0.89),
|
|
245
|
+
viewport=True,
|
|
246
|
+
font_size=10,
|
|
247
|
+
color=ZINC["200"],
|
|
248
|
+
name="panel_subtitle",
|
|
249
|
+
render=False,
|
|
250
|
+
)
|
|
251
|
+
plotter.add_text(
|
|
252
|
+
"Display Mode",
|
|
253
|
+
position=(0.08, 0.82),
|
|
254
|
+
viewport=True,
|
|
255
|
+
font_size=11,
|
|
256
|
+
color=ZINC["100"],
|
|
257
|
+
name="panel_mode_label",
|
|
258
|
+
render=False,
|
|
259
|
+
)
|
|
260
|
+
plotter.add_text(
|
|
261
|
+
"Scene Toggles",
|
|
262
|
+
position=(0.08, 0.66),
|
|
263
|
+
viewport=True,
|
|
264
|
+
font_size=11,
|
|
265
|
+
color=ZINC["100"],
|
|
266
|
+
name="panel_toggle_label",
|
|
267
|
+
render=False,
|
|
268
|
+
)
|
|
269
|
+
plotter.add_text(
|
|
270
|
+
"Parameters",
|
|
271
|
+
position=(0.08, 0.49),
|
|
272
|
+
viewport=True,
|
|
273
|
+
font_size=11,
|
|
274
|
+
color=ZINC["100"],
|
|
275
|
+
name="panel_param_label",
|
|
276
|
+
render=False,
|
|
277
|
+
)
|
|
278
|
+
_update_status_panel(plotter, state)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _rebuild_scene(plotter, grid, state, actors):
|
|
282
|
+
plotter.subplot(0, 0)
|
|
283
|
+
_clear_scene_actors(plotter, actors)
|
|
284
|
+
_update_scene_title(plotter, state)
|
|
285
|
+
|
|
286
|
+
if state["show_outline"]:
|
|
287
|
+
actors["outline"] = _add_outline(plotter, grid)
|
|
288
|
+
|
|
289
|
+
if state["mode"] == "field":
|
|
290
|
+
actors["field_volume"] = _add_field_volume(plotter, grid, state)
|
|
291
|
+
actors["field_contour"] = (
|
|
292
|
+
_add_field_contour(plotter, grid, state) if state["show_contour"] else None
|
|
293
|
+
)
|
|
294
|
+
actors["field_slices"] = (
|
|
295
|
+
_add_field_slices(plotter, grid, state) if state["show_slices"] else None
|
|
296
|
+
)
|
|
297
|
+
return
|
|
298
|
+
|
|
299
|
+
actors["dielectric_mesh"] = _add_dielectric_mesh(plotter, grid, state)
|
|
300
|
+
actors["dielectric_slices"] = (
|
|
301
|
+
_add_permittivity_slices(plotter, grid, state) if state["show_slices"] else None
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _add_labeled_checkbox(plotter, callback, value, label, button_position, text_position):
|
|
306
|
+
plotter.add_checkbox_button_widget(
|
|
307
|
+
callback,
|
|
308
|
+
value=value,
|
|
309
|
+
position=button_position,
|
|
310
|
+
size=26,
|
|
311
|
+
border_size=2,
|
|
312
|
+
color_on="#f97316",
|
|
313
|
+
color_off=ZINC["600"],
|
|
314
|
+
background_color=ZINC["100"],
|
|
315
|
+
)
|
|
316
|
+
plotter.add_text(
|
|
317
|
+
label,
|
|
318
|
+
position=text_position,
|
|
319
|
+
viewport=True,
|
|
320
|
+
font_size=10,
|
|
321
|
+
color=ZINC["100"],
|
|
322
|
+
render=False,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _add_control_widgets(plotter, grid, state, actors, window_size):
|
|
327
|
+
plotter.subplot(0, 1)
|
|
328
|
+
checkbox_x = 24
|
|
329
|
+
y0 = int(window_size[1] * 0.56)
|
|
330
|
+
gap = int(window_size[1] * 0.055)
|
|
331
|
+
|
|
332
|
+
def refresh_and_render():
|
|
333
|
+
_rebuild_scene(plotter, grid, state, actors)
|
|
334
|
+
_update_status_panel(plotter, state)
|
|
335
|
+
plotter.render()
|
|
336
|
+
|
|
337
|
+
def update_mode(value):
|
|
338
|
+
state["mode"] = "field" if str(value).lower().startswith("field") else "dielectric"
|
|
339
|
+
refresh_and_render()
|
|
340
|
+
|
|
341
|
+
def toggle_slices(value):
|
|
342
|
+
state["show_slices"] = bool(value)
|
|
343
|
+
refresh_and_render()
|
|
344
|
+
|
|
345
|
+
def toggle_outline(value):
|
|
346
|
+
state["show_outline"] = bool(value)
|
|
347
|
+
refresh_and_render()
|
|
348
|
+
|
|
349
|
+
def toggle_contour(value):
|
|
350
|
+
state["show_contour"] = bool(value)
|
|
351
|
+
refresh_and_render()
|
|
352
|
+
|
|
353
|
+
def update_volume_opacity(value):
|
|
354
|
+
state["volume_opacity"] = float(value)
|
|
355
|
+
refresh_and_render()
|
|
356
|
+
|
|
357
|
+
def update_iso_frac(value):
|
|
358
|
+
state["iso_frac"] = float(np.clip(value, 0.05, 0.95))
|
|
359
|
+
state["iso_value"] = _iso_value_from_frac(state["field_max"], state["iso_frac"])
|
|
360
|
+
refresh_and_render()
|
|
361
|
+
|
|
362
|
+
def update_dielectric_threshold(value):
|
|
363
|
+
state["dielectric_threshold"] = float(
|
|
364
|
+
np.clip(value, state["eps_min"], state["eps_max"])
|
|
365
|
+
)
|
|
366
|
+
refresh_and_render()
|
|
367
|
+
|
|
368
|
+
plotter.add_text_slider_widget(
|
|
369
|
+
update_mode,
|
|
370
|
+
data=["Field", "Dielectric"],
|
|
371
|
+
value=0,
|
|
372
|
+
pointa=(0.12, 0.77),
|
|
373
|
+
pointb=(0.88, 0.77),
|
|
374
|
+
color=ZINC["100"],
|
|
375
|
+
interaction_event="end",
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
_add_labeled_checkbox(
|
|
379
|
+
plotter,
|
|
380
|
+
toggle_slices,
|
|
381
|
+
state["show_slices"],
|
|
382
|
+
"Show slices",
|
|
383
|
+
(checkbox_x, y0),
|
|
384
|
+
(0.20, 0.57),
|
|
385
|
+
)
|
|
386
|
+
_add_labeled_checkbox(
|
|
387
|
+
plotter,
|
|
388
|
+
toggle_outline,
|
|
389
|
+
state["show_outline"],
|
|
390
|
+
"Show outline",
|
|
391
|
+
(checkbox_x, y0 - gap),
|
|
392
|
+
(0.20, 0.515),
|
|
393
|
+
)
|
|
394
|
+
_add_labeled_checkbox(
|
|
395
|
+
plotter,
|
|
396
|
+
toggle_contour,
|
|
397
|
+
state["show_contour"],
|
|
398
|
+
"Field contour",
|
|
399
|
+
(checkbox_x, y0 - 2 * gap),
|
|
400
|
+
(0.20, 0.46),
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
plotter.add_slider_widget(
|
|
404
|
+
update_volume_opacity,
|
|
405
|
+
rng=(0.03, 0.45),
|
|
406
|
+
value=state["volume_opacity"],
|
|
407
|
+
title="Field volume opacity",
|
|
408
|
+
pointa=(0.10, 0.42),
|
|
409
|
+
pointb=(0.90, 0.42),
|
|
410
|
+
fmt="%.2f",
|
|
411
|
+
interaction_event="end",
|
|
412
|
+
color=ZINC["100"],
|
|
413
|
+
)
|
|
414
|
+
plotter.add_slider_widget(
|
|
415
|
+
update_iso_frac,
|
|
416
|
+
rng=(0.05, 0.95),
|
|
417
|
+
value=state["iso_frac"],
|
|
418
|
+
title="Field isosurface fraction",
|
|
419
|
+
pointa=(0.10, 0.34),
|
|
420
|
+
pointb=(0.90, 0.34),
|
|
421
|
+
fmt="%.2f",
|
|
422
|
+
interaction_event="end",
|
|
423
|
+
color=ZINC["100"],
|
|
424
|
+
)
|
|
425
|
+
plotter.add_slider_widget(
|
|
426
|
+
update_dielectric_threshold,
|
|
427
|
+
rng=(state["eps_min"], max(state["eps_max"], state["eps_min"] + 1e-6)),
|
|
428
|
+
value=state["dielectric_threshold"],
|
|
429
|
+
title="Dielectric cutoff",
|
|
430
|
+
pointa=(0.10, 0.18),
|
|
431
|
+
pointb=(0.90, 0.18),
|
|
432
|
+
fmt="%.3f",
|
|
433
|
+
interaction_event="end",
|
|
434
|
+
color=ZINC["100"],
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def show_pyvista_solution(grid, iso_frac=0.35, headless=False, screenshot=None):
|
|
439
|
+
import pyvista as pv
|
|
440
|
+
|
|
441
|
+
pv.global_theme.allow_empty_mesh = True
|
|
442
|
+
|
|
443
|
+
off_screen = headless or screenshot is not None
|
|
444
|
+
state = _build_view_state(grid, iso_frac)
|
|
445
|
+
window_size = (1920, 980)
|
|
446
|
+
plotter = pv.Plotter(
|
|
447
|
+
shape=(1, 2),
|
|
448
|
+
col_weights=[4, 2],
|
|
449
|
+
border=True,
|
|
450
|
+
border_color=ZINC["600"],
|
|
451
|
+
window_size=window_size,
|
|
452
|
+
off_screen=off_screen,
|
|
453
|
+
)
|
|
454
|
+
if hasattr(plotter, "enable_depth_peeling"):
|
|
455
|
+
plotter.enable_depth_peeling()
|
|
456
|
+
|
|
457
|
+
actors = {
|
|
458
|
+
"outline": None,
|
|
459
|
+
"field_volume": None,
|
|
460
|
+
"field_contour": None,
|
|
461
|
+
"field_slices": None,
|
|
462
|
+
"dielectric_mesh": None,
|
|
463
|
+
"dielectric_slices": None,
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
_setup_scene_view(plotter, state)
|
|
467
|
+
_setup_control_panel(plotter, state)
|
|
468
|
+
_rebuild_scene(plotter, grid, state, actors)
|
|
469
|
+
plotter.subplot(0, 0)
|
|
470
|
+
if hasattr(plotter, "add_camera_orientation_widget"):
|
|
471
|
+
plotter.add_camera_orientation_widget()
|
|
472
|
+
_add_control_widgets(plotter, grid, state, actors, window_size)
|
|
473
|
+
|
|
474
|
+
if screenshot is not None:
|
|
475
|
+
plotter.show(screenshot=str(screenshot), auto_close=True)
|
|
476
|
+
return
|
|
477
|
+
|
|
478
|
+
if headless:
|
|
479
|
+
plotter.show(auto_close=True)
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
print("PyVista controls: left-drag rotate, right-drag zoom, middle-drag pan.")
|
|
483
|
+
print("Use the zinc control panel on the right to switch between Field and Dielectric.")
|
|
484
|
+
plotter.show()
|