emerge 0.5.1__py3-none-any.whl → 0.5.3__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.
Potentially problematic release.
This version of emerge might be problematic. Click here for more details.
- emerge/_emerge/bc.py +14 -20
- emerge/_emerge/const.py +5 -0
- emerge/_emerge/cs.py +2 -2
- emerge/_emerge/elements/femdata.py +14 -14
- emerge/_emerge/elements/index_interp.py +1 -1
- emerge/_emerge/elements/ned2_interp.py +1 -1
- emerge/_emerge/elements/nedelec2.py +4 -4
- emerge/_emerge/elements/nedleg2.py +10 -10
- emerge/_emerge/geo/horn.py +1 -1
- emerge/_emerge/geo/modeler.py +18 -19
- emerge/_emerge/geo/operations.py +13 -10
- emerge/_emerge/geo/pcb.py +180 -82
- emerge/_emerge/geo/pcb_tools/calculator.py +2 -2
- emerge/_emerge/geo/pcb_tools/macro.py +14 -13
- emerge/_emerge/geo/pmlbox.py +1 -1
- emerge/_emerge/geometry.py +47 -33
- emerge/_emerge/logsettings.py +15 -16
- emerge/_emerge/material.py +15 -11
- emerge/_emerge/mesh3d.py +81 -59
- emerge/_emerge/mesher.py +26 -21
- emerge/_emerge/mth/integrals.py +1 -1
- emerge/_emerge/mth/pairing.py +2 -2
- emerge/_emerge/periodic.py +34 -31
- emerge/_emerge/physics/microwave/adaptive_freq.py +15 -16
- emerge/_emerge/physics/microwave/assembly/assembler.py +120 -93
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +1 -8
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
- emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
- emerge/_emerge/physics/microwave/microwave_3d.py +71 -44
- emerge/_emerge/physics/microwave/microwave_bc.py +206 -117
- emerge/_emerge/physics/microwave/microwave_data.py +36 -38
- emerge/_emerge/physics/microwave/sc.py +26 -26
- emerge/_emerge/physics/microwave/simjob.py +20 -15
- emerge/_emerge/physics/microwave/sparam.py +12 -12
- emerge/_emerge/physics/microwave/touchstone.py +1 -1
- emerge/_emerge/plot/display.py +12 -6
- emerge/_emerge/plot/pyvista/display.py +44 -39
- emerge/_emerge/plot/pyvista/display_settings.py +1 -1
- emerge/_emerge/plot/simple_plots.py +15 -15
- emerge/_emerge/selection.py +35 -39
- emerge/_emerge/simmodel.py +41 -47
- emerge/_emerge/simulation_data.py +24 -15
- emerge/_emerge/solve_interfaces/cudss_interface.py +238 -0
- emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
- emerge/_emerge/solver.py +314 -136
- emerge/cli.py +1 -1
- emerge/lib.py +245 -248
- {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/METADATA +5 -1
- emerge-0.5.3.dist-info/RECORD +83 -0
- emerge/_emerge/plot/grapher.py +0 -93
- emerge-0.5.1.dist-info/RECORD +0 -82
- {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/WHEEL +0 -0
- {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/entry_points.txt +0 -0
- {emerge-0.5.1.dist-info → emerge-0.5.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -22,7 +22,7 @@ from ...selection import FaceSelection, DomainSelection, EdgeSelection, Selectio
|
|
|
22
22
|
from ...physics.microwave.microwave_bc import PortBC, ModalPort
|
|
23
23
|
import numpy as np
|
|
24
24
|
import pyvista as pv
|
|
25
|
-
from typing import Iterable, Literal, Callable
|
|
25
|
+
from typing import Iterable, Literal, Callable, Any
|
|
26
26
|
from ..display import BaseDisplay
|
|
27
27
|
from .display_settings import PVDisplaySettings
|
|
28
28
|
from matplotlib.colors import ListedColormap
|
|
@@ -155,7 +155,7 @@ def _select(obj: GeoObject | Selection) -> Selection:
|
|
|
155
155
|
return obj.select
|
|
156
156
|
return obj
|
|
157
157
|
|
|
158
|
-
def _merge(lst:
|
|
158
|
+
def _merge(lst: Iterable[GeoObject | Selection]) -> Selection:
|
|
159
159
|
selections = [_select(item) for item in lst]
|
|
160
160
|
dim = selections[0].dim
|
|
161
161
|
all_tags = []
|
|
@@ -168,6 +168,8 @@ def _merge(lst: list[GeoObject | Selection]) -> Selection:
|
|
|
168
168
|
return FaceSelection(all_tags)
|
|
169
169
|
elif dim==3:
|
|
170
170
|
return DomainSelection(all_tags)
|
|
171
|
+
else:
|
|
172
|
+
return Selection(all_tags)
|
|
171
173
|
|
|
172
174
|
class _AnimObject:
|
|
173
175
|
""" A private class containing the required information for plot items in a view
|
|
@@ -199,7 +201,7 @@ class PVDisplay(BaseDisplay):
|
|
|
199
201
|
self._stop: bool = False
|
|
200
202
|
self._objs: list[_AnimObject] = []
|
|
201
203
|
self._do_animate: bool = False
|
|
202
|
-
self._Nsteps: int
|
|
204
|
+
self._Nsteps: int = 0
|
|
203
205
|
self._fps: int = 25
|
|
204
206
|
self._ruler: ScreenRuler = ScreenRuler(self, 0.001)
|
|
205
207
|
self._selector: ScreenSelector = ScreenSelector(self)
|
|
@@ -208,10 +210,11 @@ class PVDisplay(BaseDisplay):
|
|
|
208
210
|
|
|
209
211
|
self._plot = pv.Plotter()
|
|
210
212
|
|
|
211
|
-
self._plot.add_key_event("m", self.activate_ruler)
|
|
212
|
-
self._plot.add_key_event("f", self.activate_object)
|
|
213
|
+
self._plot.add_key_event("m", self.activate_ruler) # type: ignore
|
|
214
|
+
self._plot.add_key_event("f", self.activate_object) # type: ignore
|
|
213
215
|
|
|
214
216
|
self._ctr: int = 0
|
|
217
|
+
|
|
215
218
|
def activate_ruler(self):
|
|
216
219
|
self._plot.disable_picking()
|
|
217
220
|
self._selector.turn_off()
|
|
@@ -310,7 +313,7 @@ class PVDisplay(BaseDisplay):
|
|
|
310
313
|
points = self._mesh.nodes.T
|
|
311
314
|
return pv.UnstructuredGrid(cells, celltypes, points)
|
|
312
315
|
|
|
313
|
-
def mesh(self, obj: GeoObject | Selection | Iterable) -> pv.UnstructuredGrid:
|
|
316
|
+
def mesh(self, obj: GeoObject | Selection | Iterable) -> pv.UnstructuredGrid | None:
|
|
314
317
|
if isinstance(obj, Iterable):
|
|
315
318
|
obj = _merge(obj)
|
|
316
319
|
else:
|
|
@@ -320,11 +323,13 @@ class PVDisplay(BaseDisplay):
|
|
|
320
323
|
return self.mesh_volume(obj)
|
|
321
324
|
elif isinstance(obj, FaceSelection):
|
|
322
325
|
return self.mesh_surface(obj)
|
|
326
|
+
else:
|
|
327
|
+
return None
|
|
323
328
|
|
|
324
329
|
## OBLIGATORY METHODS
|
|
325
|
-
def add_object(self, obj: GeoObject | Selection
|
|
326
|
-
kwargs = setdefault(kwargs, color=obj.color_rgb, opacity=obj.opacity, silhouette=True)
|
|
327
|
-
self._plot.add_mesh(self.mesh(obj),
|
|
330
|
+
def add_object(self, obj: GeoObject | Selection, *args, **kwargs):
|
|
331
|
+
kwargs = setdefault(kwargs, color=obj.color_rgb, opacity=obj.opacity, silhouette=True, pickable=True)
|
|
332
|
+
self._plot.add_mesh(self.mesh(obj), *args, **kwargs)
|
|
328
333
|
|
|
329
334
|
def add_scatter(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray):
|
|
330
335
|
"""Adds a scatter point cloud
|
|
@@ -342,8 +347,8 @@ class PVDisplay(BaseDisplay):
|
|
|
342
347
|
dv=(0,0,0),
|
|
343
348
|
XYZ=None,
|
|
344
349
|
field: Literal['E','H'] = 'E',
|
|
345
|
-
k0: float = None,
|
|
346
|
-
mode_number: int = None) ->
|
|
350
|
+
k0: float | None = None,
|
|
351
|
+
mode_number: int | None = None) -> None:
|
|
347
352
|
|
|
348
353
|
if XYZ:
|
|
349
354
|
X,Y,Z = XYZ
|
|
@@ -408,10 +413,10 @@ class PVDisplay(BaseDisplay):
|
|
|
408
413
|
field: np.ndarray,
|
|
409
414
|
scale: Literal['lin','log','symlog'] = 'lin',
|
|
410
415
|
cmap: cmap_names = 'coolwarm',
|
|
411
|
-
clim: tuple[float, float] = None,
|
|
416
|
+
clim: tuple[float, float] | None = None,
|
|
412
417
|
opacity: float = 1.0,
|
|
413
418
|
symmetrize: bool = True,
|
|
414
|
-
_fieldname: str = None,
|
|
419
|
+
_fieldname: str | None = None,
|
|
415
420
|
**kwargs,):
|
|
416
421
|
"""Add a surface plot to the display
|
|
417
422
|
The X,Y,Z coordinates must be a 2D grid of data points. The field must be a real field with the same size.
|
|
@@ -460,11 +465,11 @@ class PVDisplay(BaseDisplay):
|
|
|
460
465
|
kwargs = setdefault(kwargs, cmap=cmap, clim=clim, opacity=opacity, pickable=False, multi_colors=True)
|
|
461
466
|
actor = self._plot.add_mesh(grid, scalars=name, **kwargs)
|
|
462
467
|
|
|
463
|
-
if self.
|
|
468
|
+
if self._do_animate:
|
|
464
469
|
def on_update(obj: _AnimObject, phi: complex):
|
|
465
470
|
field = obj.T(np.real(obj.field*phi))
|
|
466
471
|
obj.grid['anim'] = field
|
|
467
|
-
self._objs.append(_AnimObject(field_flat, T, grid, actor, on_update))
|
|
472
|
+
self._objs.append(_AnimObject(field_flat, T, grid, actor, on_update)) # type: ignore
|
|
468
473
|
|
|
469
474
|
|
|
470
475
|
def add_title(self, title: str) -> None:
|
|
@@ -481,14 +486,14 @@ class PVDisplay(BaseDisplay):
|
|
|
481
486
|
def add_text(self, text: str,
|
|
482
487
|
color: str = 'black',
|
|
483
488
|
position: Literal['lower_left', 'lower_right', 'upper_left', 'upper_right', 'lower_edge', 'upper_edge', 'right_edge', 'left_edge']='upper_right',
|
|
484
|
-
abs_position: tuple[float, float, float] = None):
|
|
489
|
+
abs_position: tuple[float, float, float] | None = None):
|
|
485
490
|
viewport = False
|
|
486
491
|
if abs_position is not None:
|
|
487
|
-
|
|
492
|
+
final_position = abs_position
|
|
488
493
|
viewport = True
|
|
489
494
|
self._plot.add_text(
|
|
490
495
|
text,
|
|
491
|
-
position=
|
|
496
|
+
position=final_position,
|
|
492
497
|
color=color,
|
|
493
498
|
font_size=18,
|
|
494
499
|
viewport=viewport)
|
|
@@ -496,7 +501,7 @@ class PVDisplay(BaseDisplay):
|
|
|
496
501
|
def add_quiver(self, x: np.ndarray, y: np.ndarray, z: np.ndarray,
|
|
497
502
|
dx: np.ndarray, dy: np.ndarray, dz: np.ndarray,
|
|
498
503
|
scale: float = 1,
|
|
499
|
-
color: tuple[float, float, float] = None,
|
|
504
|
+
color: tuple[float, float, float] | None = None,
|
|
500
505
|
scalemode: Literal['lin','log'] = 'lin'):
|
|
501
506
|
"""Add a quiver plot to the display
|
|
502
507
|
|
|
@@ -562,18 +567,18 @@ class PVDisplay(BaseDisplay):
|
|
|
562
567
|
grid = pv.StructuredGrid(X,Y,Z)
|
|
563
568
|
field = V.flatten(order='F')
|
|
564
569
|
grid['anim'] = np.real(field)
|
|
565
|
-
levels = np.linspace(vmin, vmax, Nlevels)
|
|
570
|
+
levels = list(np.linspace(vmin, vmax, Nlevels))
|
|
566
571
|
contour = grid.contour(isosurfaces=levels)
|
|
567
572
|
actor = self._plot.add_mesh(contour, opacity=0.25, cmap=cmap, pickable=False)
|
|
568
573
|
|
|
569
|
-
if self.
|
|
574
|
+
if self._do_animate:
|
|
570
575
|
def on_update(obj: _AnimObject, phi: complex):
|
|
571
576
|
new_vals = np.real(obj.field * phi)
|
|
572
577
|
obj.grid['anim'] = new_vals
|
|
573
578
|
new_contour = obj.grid.contour(isosurfaces=levels)
|
|
574
|
-
obj.actor.GetMapper().SetInputData(new_contour)
|
|
579
|
+
obj.actor.GetMapper().SetInputData(new_contour) # type: ignore
|
|
575
580
|
|
|
576
|
-
self._objs.append(_AnimObject(field, lambda x: x, grid, actor, on_update))
|
|
581
|
+
self._objs.append(_AnimObject(field, lambda x: x, grid, actor, on_update)) # type: ignore
|
|
577
582
|
|
|
578
583
|
def _add_aux_items(self) -> None:
|
|
579
584
|
saved_camera = {
|
|
@@ -591,8 +596,8 @@ class PVDisplay(BaseDisplay):
|
|
|
591
596
|
plane = pv.Plane(
|
|
592
597
|
center=(0, 0, 0),
|
|
593
598
|
direction=(1, 0, 0), # normal vector pointing along +X
|
|
594
|
-
i_size=length,
|
|
595
|
-
j_size=length,
|
|
599
|
+
i_size=length, # type: ignore
|
|
600
|
+
j_size=length, # type: ignore
|
|
596
601
|
i_resolution=1,
|
|
597
602
|
j_resolution=1
|
|
598
603
|
)
|
|
@@ -617,8 +622,8 @@ class PVDisplay(BaseDisplay):
|
|
|
617
622
|
plane = pv.Plane(
|
|
618
623
|
center=(0, 0, 0),
|
|
619
624
|
direction=(0, 1, 0), # normal vector pointing along +X
|
|
620
|
-
i_size=length,
|
|
621
|
-
j_size=length,
|
|
625
|
+
i_size=length, # type: ignore
|
|
626
|
+
j_size=length, # type: ignore
|
|
622
627
|
i_resolution=1,
|
|
623
628
|
j_resolution=1
|
|
624
629
|
)
|
|
@@ -642,8 +647,8 @@ class PVDisplay(BaseDisplay):
|
|
|
642
647
|
plane = pv.Plane(
|
|
643
648
|
center=(0, 0, 0),
|
|
644
649
|
direction=(0, 0, 1), # normal vector pointing along +X
|
|
645
|
-
i_size=length,
|
|
646
|
-
j_size=length,
|
|
650
|
+
i_size=length, # type: ignore
|
|
651
|
+
j_size=length, # type: ignore
|
|
647
652
|
i_resolution=1,
|
|
648
653
|
j_resolution=1
|
|
649
654
|
)
|
|
@@ -776,11 +781,11 @@ class PVDisplay(BaseDisplay):
|
|
|
776
781
|
|
|
777
782
|
if self.set.add_light:
|
|
778
783
|
light = pv.Light()
|
|
779
|
-
light.set_direction_angle(*self.set.light_angle)
|
|
784
|
+
light.set_direction_angle(*self.set.light_angle) # type: ignore
|
|
780
785
|
self._plot.add_light(light)
|
|
781
786
|
|
|
782
|
-
self._plot.set_background(self.set.background_bottom, top=self.set.background_top)
|
|
783
|
-
self._plot.add_axes()
|
|
787
|
+
self._plot.set_background(self.set.background_bottom, top=self.set.background_top) # type: ignore
|
|
788
|
+
self._plot.add_axes() # type: ignore
|
|
784
789
|
|
|
785
790
|
self._plot.camera.position = saved_camera["position"]
|
|
786
791
|
self._plot.camera.focal_point = saved_camera["focal_point"]
|
|
@@ -837,7 +842,7 @@ class ScreenSelector:
|
|
|
837
842
|
celltypes = np.full(ntris, fill_value=pv.CellType.TRIANGLE, dtype=np.uint8)
|
|
838
843
|
points = self.disp._mesh.nodes.T
|
|
839
844
|
grid = pv.UnstructuredGrid(cells, celltypes, points)
|
|
840
|
-
grid._tag = key
|
|
845
|
+
grid._tag = key # type: ignore
|
|
841
846
|
self.grids.append(grid)
|
|
842
847
|
self.surfs[key] = points[nodes,:].T
|
|
843
848
|
|
|
@@ -856,14 +861,14 @@ class ScreenSelector:
|
|
|
856
861
|
meany = np.mean(ys)
|
|
857
862
|
meanz = np.mean(zs)
|
|
858
863
|
data = (meanx, meany, meanz, min(xs), min(ys), min(zs), max(xs), max(ys), max(zs))
|
|
859
|
-
encoded = encode_data(data)
|
|
864
|
+
encoded = encode_data(data) #type: ignore
|
|
860
865
|
print(f'Face code key={key}: ', encoded)
|
|
861
866
|
|
|
862
867
|
self.disp._plot.enable_mesh_picking(callback, style='surface', left_clicking=True, use_actor=True)
|
|
863
868
|
|
|
864
869
|
def turn_off(self) -> None:
|
|
865
870
|
for actor in self.select_actors:
|
|
866
|
-
self.disp._plot.remove_actor(actor)
|
|
871
|
+
self.disp._plot.remove_actor(actor) # type: ignore
|
|
867
872
|
self.select_actors = []
|
|
868
873
|
for actor in self.original_actors:
|
|
869
874
|
if isinstance(actor, pv.Text):
|
|
@@ -876,9 +881,9 @@ class ScreenRuler:
|
|
|
876
881
|
def __init__(self, display: PVDisplay, min_length: float):
|
|
877
882
|
self.disp: PVDisplay = display
|
|
878
883
|
self.points: list[tuple] = [(0,0,0),(0,0,0)]
|
|
879
|
-
self.text: pv.Text = None
|
|
880
|
-
self.ruler = None
|
|
881
|
-
self.state = False
|
|
884
|
+
self.text: pv.Text | None = None
|
|
885
|
+
self.ruler: Any = None
|
|
886
|
+
self.state: bool = False
|
|
882
887
|
self.min_length: float = min_length
|
|
883
888
|
|
|
884
889
|
@freeze
|
|
@@ -916,7 +921,7 @@ class ScreenRuler:
|
|
|
916
921
|
|
|
917
922
|
def set_ruler(self) -> None:
|
|
918
923
|
if self.ruler is None:
|
|
919
|
-
self.ruler = self.disp._plot.add_ruler(self.points[0], self.points[1], title=f'{1000*self.dist:.2f}mm')
|
|
924
|
+
self.ruler = self.disp._plot.add_ruler(self.points[0], self.points[1], title=f'{1000*self.dist:.2f}mm') # type: ignore
|
|
920
925
|
else:
|
|
921
926
|
p1 = self.ruler.GetPositionCoordinate()
|
|
922
927
|
p2 = self.ruler.GetPosition2Coordinate()
|
|
@@ -17,7 +17,7 @@ class PVDisplaySettings:
|
|
|
17
17
|
self.show_zgrid: bool = True
|
|
18
18
|
self.grid_line_width: float = 1.0
|
|
19
19
|
self.add_light: bool = False
|
|
20
|
-
self.light_angle: float = (20
|
|
20
|
+
self.light_angle: tuple[float, float] = (20., -20.)
|
|
21
21
|
self.cast_shadows: bool = True
|
|
22
22
|
self.background_bottom: str = "#c0d2e8"
|
|
23
23
|
self.background_top: str = "#ffffff"
|
|
@@ -36,7 +36,7 @@ ggplot_styles = {
|
|
|
36
36
|
|
|
37
37
|
plt.rcParams.update(ggplot_styles)
|
|
38
38
|
|
|
39
|
-
def _gen_grid(xs: tuple, ys: tuple, N = 201) -> list[np.ndarray]:
|
|
39
|
+
def _gen_grid(xs: tuple, ys: tuple, N = 201) -> list[tuple[np.ndarray, np.ndarray]]:
|
|
40
40
|
"""Generate a grid of lines for the Smith Chart
|
|
41
41
|
|
|
42
42
|
Args:
|
|
@@ -279,18 +279,18 @@ def smith(f: np.ndarray, S: np.ndarray) -> None:
|
|
|
279
279
|
def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
|
|
280
280
|
dblim=[-40, 5],
|
|
281
281
|
xunit="GHz",
|
|
282
|
-
levelindicator: int | float =None,
|
|
282
|
+
levelindicator: int | float | None = None,
|
|
283
283
|
noise_floor=-150,
|
|
284
|
-
fill_areas: list[tuple]= None,
|
|
285
|
-
spec_area: list[tuple[float]] = None,
|
|
284
|
+
fill_areas: list[tuple] | None = None,
|
|
285
|
+
spec_area: list[tuple[float,...]] | None = None,
|
|
286
286
|
unwrap_phase=False,
|
|
287
287
|
logx: bool = False,
|
|
288
|
-
labels: list[str] = None,
|
|
289
|
-
linestyles: list[str] = None,
|
|
290
|
-
colorcycle: list[int] = None,
|
|
291
|
-
filename: str = None,
|
|
288
|
+
labels: list[str] | None = None,
|
|
289
|
+
linestyles: list[str] | None = None,
|
|
290
|
+
colorcycle: list[int] | None = None,
|
|
291
|
+
filename: str | None = None,
|
|
292
292
|
show_plot: bool = True,
|
|
293
|
-
figdata: tuple = None) ->
|
|
293
|
+
figdata: tuple | None = None) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
|
|
294
294
|
"""Plot S-parameters in dB and phase
|
|
295
295
|
|
|
296
296
|
Args:
|
|
@@ -321,7 +321,7 @@ def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
|
|
|
321
321
|
if colorcycle is None:
|
|
322
322
|
colorcycle = [i for i, S in enumerate(S)]
|
|
323
323
|
|
|
324
|
-
unitdivider = {"MHz": 1e6, "GHz": 1e9, "kHz": 1e3}
|
|
324
|
+
unitdivider: dict[str, float] = {"MHz": 1e6, "GHz": 1e9, "kHz": 1e3}
|
|
325
325
|
fnew = f / unitdivider[xunit]
|
|
326
326
|
|
|
327
327
|
if figdata is None:
|
|
@@ -372,14 +372,14 @@ def plot_sp(f: np.ndarray, S: list[np.ndarray] | np.ndarray,
|
|
|
372
372
|
# Configure magnitude plot (ax_mag)
|
|
373
373
|
ax_mag.set_ylabel("Magnitude (dB)")
|
|
374
374
|
ax_mag.set_xlabel(f"Frequency ({xunit})")
|
|
375
|
-
ax_mag.axis([min(fnew), max(fnew), dblim[0], max(maxy*1.1,dblim[1])])
|
|
375
|
+
ax_mag.axis([min(fnew), max(fnew), dblim[0], max(maxy*1.1,dblim[1])]) # type: ignore
|
|
376
376
|
ax_mag.axhline(y=0, color="k", linewidth=1)
|
|
377
377
|
ax_mag.xaxis.set_minor_locator(tck.AutoMinorLocator(2))
|
|
378
378
|
ax_mag.yaxis.set_minor_locator(tck.AutoMinorLocator(2))
|
|
379
379
|
# Configure phase plot (ax_phase)
|
|
380
380
|
ax_phase.set_ylabel("Phase (degrees)")
|
|
381
381
|
ax_phase.set_xlabel(f"Frequency ({xunit})")
|
|
382
|
-
ax_phase.axis([min(fnew), max(fnew), minphase, maxphase])
|
|
382
|
+
ax_phase.axis([min(fnew), max(fnew), minphase, maxphase]) # type: ignore
|
|
383
383
|
ax_phase.xaxis.set_minor_locator(tck.AutoMinorLocator(2))
|
|
384
384
|
ax_phase.yaxis.set_minor_locator(tck.AutoMinorLocator(2))
|
|
385
385
|
if logx:
|
|
@@ -532,9 +532,9 @@ def plot_ff_polar(
|
|
|
532
532
|
markers = _broadcast(markers, None) if markers is not None else [None] * n_series
|
|
533
533
|
|
|
534
534
|
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
|
|
535
|
-
ax.set_theta_zero_location(zero_location)
|
|
536
|
-
ax.set_theta_direction(-1 if clockwise else 1)
|
|
537
|
-
ax.set_rlabel_position(rlabel_angle)
|
|
535
|
+
ax.set_theta_zero_location(zero_location) # type: ignore
|
|
536
|
+
ax.set_theta_direction(-1 if clockwise else 1) # type: ignore
|
|
537
|
+
ax.set_rlabel_position(rlabel_angle) # type: ignore
|
|
538
538
|
|
|
539
539
|
for i, Ei in enumerate(E_list):
|
|
540
540
|
mag = np.abs(Ei)
|
emerge/_emerge/selection.py
CHANGED
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
|
-
import gmsh
|
|
19
|
+
import gmsh # type: ignore
|
|
20
20
|
import numpy as np
|
|
21
|
-
from scipy.spatial import ConvexHull
|
|
21
|
+
from scipy.spatial import ConvexHull # type: ignore
|
|
22
22
|
from .cs import Axis, CoordinateSystem, _parse_vector, Plane
|
|
23
|
-
from typing import Callable, TypeVar
|
|
23
|
+
from typing import Callable, TypeVar, Iterable, Any
|
|
24
24
|
|
|
25
|
-
def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str,
|
|
25
|
+
def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, Any]:
|
|
26
26
|
"""Tries to find a rectangle as convex-hull of a set of points with a given normal vector.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
@@ -66,9 +66,9 @@ def align_rectangle_frame(pts3d: np.ndarray, normal: np.ndarray) -> dict[str, np
|
|
|
66
66
|
xmax, ymax = rot.max(axis=0)
|
|
67
67
|
area = (xmax-xmin)*(ymax-ymin)
|
|
68
68
|
if area < best[1]:
|
|
69
|
-
best = (theta, area, (xmin,xmax,ymin,ymax), R)
|
|
69
|
+
best = (theta, area, (xmin,xmax,ymin,ymax), R) # type: ignore
|
|
70
70
|
|
|
71
|
-
theta, _, (xmin,xmax,ymin,ymax), R = best
|
|
71
|
+
theta, _, (xmin,xmax,ymin,ymax), R = best # type: ignore
|
|
72
72
|
|
|
73
73
|
# 6. rectangle axes in 3D
|
|
74
74
|
u = np.cos(-theta)*e_x + np.sin(-theta)*e_y
|
|
@@ -172,9 +172,9 @@ class Selection:
|
|
|
172
172
|
|
|
173
173
|
"""
|
|
174
174
|
dim: int = -1
|
|
175
|
-
def __init__(self, tags: list[int] = None):
|
|
175
|
+
def __init__(self, tags: list[int] | set[int] | tuple[int] | None = None):
|
|
176
176
|
|
|
177
|
-
self._tags: set[int] =
|
|
177
|
+
self._tags: set[int] = set()
|
|
178
178
|
|
|
179
179
|
if tags is not None:
|
|
180
180
|
if not isinstance(tags, (list,set,tuple)):
|
|
@@ -182,7 +182,7 @@ class Selection:
|
|
|
182
182
|
self._tags = set(tags)
|
|
183
183
|
|
|
184
184
|
@staticmethod
|
|
185
|
-
def from_dim_tags(dim: int, tags: list[int]
|
|
185
|
+
def from_dim_tags(dim: int, tags: list[int] | set[int]) -> Selection:
|
|
186
186
|
if dim==0:
|
|
187
187
|
return PointSelection(tags)
|
|
188
188
|
elif dim==1:
|
|
@@ -198,7 +198,7 @@ class Selection:
|
|
|
198
198
|
return list(self._tags)
|
|
199
199
|
|
|
200
200
|
@property
|
|
201
|
-
def color_rgb(self) -> tuple[
|
|
201
|
+
def color_rgb(self) -> tuple[float, float, float]:
|
|
202
202
|
return (0.5,0.5,1.0)
|
|
203
203
|
|
|
204
204
|
@property
|
|
@@ -225,14 +225,14 @@ class Selection:
|
|
|
225
225
|
return [gmsh.model.occ.getCenterOfMass(self.dim, tag) for tag in self.tags]
|
|
226
226
|
|
|
227
227
|
@property
|
|
228
|
-
def points(self) ->
|
|
228
|
+
def points(self) -> list[np.ndarray]:
|
|
229
229
|
'''A list of 3D coordinates of all nodes comprising the selection.'''
|
|
230
230
|
points = gmsh.model.get_boundary(self.dimtags, recursive=True)
|
|
231
231
|
coordinates = [gmsh.model.getValue(*p, []) for p in points]
|
|
232
232
|
return coordinates
|
|
233
233
|
|
|
234
234
|
@property
|
|
235
|
-
def bounding_box(self) -> tuple[
|
|
235
|
+
def bounding_box(self) -> tuple[Iterable, Iterable]:
|
|
236
236
|
if len(self.tags)==1:
|
|
237
237
|
return gmsh.model.occ.getBoundingBox(self.dim, self.tags[0])
|
|
238
238
|
else:
|
|
@@ -248,7 +248,7 @@ class Selection:
|
|
|
248
248
|
maxz = max(maxz, z1)
|
|
249
249
|
return (minx, miny, minz), (maxx, maxy, maxz)
|
|
250
250
|
|
|
251
|
-
def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
|
|
251
|
+
def exclude(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
|
|
252
252
|
"""Exclude points by evaluating a function(x,y,z)-> bool
|
|
253
253
|
|
|
254
254
|
This modifies the selection such that the selection does not contain elements
|
|
@@ -266,10 +266,10 @@ class Selection:
|
|
|
266
266
|
norm = axis.np
|
|
267
267
|
include2 = [abs(gmsh.model.getNormal(tag, np.array([0,0]))@norm)<0.9 for tag in self.tags]
|
|
268
268
|
include = [i1 for i1, i2 in zip(include, include2) if i1 and i2]
|
|
269
|
-
self._tags = [t for incl, t in zip(include, self._tags) if incl]
|
|
269
|
+
self._tags = set([t for incl, t in zip(include, self._tags) if incl])
|
|
270
270
|
return self
|
|
271
271
|
|
|
272
|
-
def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane = None, axis: Axis = None) -> Selection:
|
|
272
|
+
def isolate(self, xyz_excl_function: Callable = lambda x,y,z: True, plane: Plane | None = None, axis: Axis | None = None) -> Selection:
|
|
273
273
|
"""Include points by evaluating a function(x,y,z)-> bool
|
|
274
274
|
|
|
275
275
|
This modifies the selection such that the selection does not contain elements
|
|
@@ -287,7 +287,7 @@ class Selection:
|
|
|
287
287
|
norm = axis.np
|
|
288
288
|
include2 = [(gmsh.model.getNormal(tag, np.array([0,0]))@norm)>0.99 for tag in self.tags]
|
|
289
289
|
include1 = [i1 for i1, i2 in zip(include1, include2) if i1 and i2]
|
|
290
|
-
self._tags = [t for incl, t in zip(include1, self._tags) if incl]
|
|
290
|
+
self._tags = set([t for incl, t in zip(include1, self._tags) if incl])
|
|
291
291
|
return self
|
|
292
292
|
|
|
293
293
|
def __operable__(self, other: Selection) -> None:
|
|
@@ -295,30 +295,28 @@ class Selection:
|
|
|
295
295
|
raise ValueError(f'Selection dimensions must be equal. Trying to operate on dim {self.dim} and {other.dim}')
|
|
296
296
|
pass
|
|
297
297
|
|
|
298
|
-
def __add__(self, other:
|
|
298
|
+
def __add__(self, other: Selection) -> Selection:
|
|
299
299
|
self.__operable__(other)
|
|
300
|
-
return Selection.from_dim_tags(self.dim, self._tags
|
|
300
|
+
return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
|
|
301
301
|
|
|
302
|
-
def __and__(self, other:
|
|
302
|
+
def __and__(self, other: Selection) -> Selection:
|
|
303
303
|
self.__operable__(other)
|
|
304
304
|
return Selection.from_dim_tags(self.dim, self._tags.intersection(other._tags))
|
|
305
305
|
|
|
306
|
-
def __or__(self, other:
|
|
306
|
+
def __or__(self, other: Selection) -> Selection:
|
|
307
307
|
self.__operable__(other)
|
|
308
308
|
return Selection.from_dim_tags(self.dim, self._tags.union(other._tags))
|
|
309
309
|
|
|
310
|
-
def __sub__(self, other:
|
|
310
|
+
def __sub__(self, other: Selection) -> Selection:
|
|
311
311
|
self.__operable__(other)
|
|
312
312
|
return Selection.from_dim_tags(self.dim, self._tags.difference(other.tags))
|
|
313
|
-
|
|
314
|
-
|
|
315
313
|
|
|
316
314
|
class PointSelection(Selection):
|
|
317
315
|
"""A Class representing a selection of points.
|
|
318
316
|
|
|
319
317
|
"""
|
|
320
318
|
dim: int = 0
|
|
321
|
-
def __init__(self, tags: list[int] = None):
|
|
319
|
+
def __init__(self, tags: list[int] | set [int] | None = None):
|
|
322
320
|
super().__init__(tags)
|
|
323
321
|
|
|
324
322
|
class EdgeSelection(Selection):
|
|
@@ -326,7 +324,7 @@ class EdgeSelection(Selection):
|
|
|
326
324
|
|
|
327
325
|
"""
|
|
328
326
|
dim: int = 1
|
|
329
|
-
def __init__(self, tags: list[int] = None):
|
|
327
|
+
def __init__(self, tags: list[int] | set [int] | None = None):
|
|
330
328
|
super().__init__(tags)
|
|
331
329
|
|
|
332
330
|
class FaceSelection(Selection):
|
|
@@ -334,7 +332,7 @@ class FaceSelection(Selection):
|
|
|
334
332
|
|
|
335
333
|
"""
|
|
336
334
|
dim: int = 2
|
|
337
|
-
def __init__(self, tags: list[int] = None):
|
|
335
|
+
def __init__(self, tags: list[int] | set [int] | None = None):
|
|
338
336
|
super().__init__(tags)
|
|
339
337
|
|
|
340
338
|
@property
|
|
@@ -355,14 +353,14 @@ class FaceSelection(Selection):
|
|
|
355
353
|
|
|
356
354
|
pts3d = self.points
|
|
357
355
|
normal = self.normal
|
|
358
|
-
data = align_rectangle_frame(pts3d, normal)
|
|
356
|
+
data = align_rectangle_frame(np.array(pts3d), normal)
|
|
359
357
|
plane = data['axes'][:2]
|
|
360
358
|
origin = data['origin']
|
|
361
359
|
|
|
362
360
|
cs = CoordinateSystem(Axis(plane[0]), Axis(plane[1]), Axis(data['axes'][2]), origin)
|
|
363
361
|
|
|
364
|
-
size1 = np.linalg.norm(data['corners'][1] - data['corners'][0])
|
|
365
|
-
size2 = np.linalg.norm(data['corners'][2] - data['corners'][0])
|
|
362
|
+
size1 = float(np.linalg.norm(data['corners'][1] - data['corners'][0]))
|
|
363
|
+
size2 = float(np.linalg.norm(data['corners'][2] - data['corners'][0]))
|
|
366
364
|
|
|
367
365
|
if size1>size2:
|
|
368
366
|
cs.swapxy()
|
|
@@ -382,7 +380,7 @@ class FaceSelection(Selection):
|
|
|
382
380
|
a NxN numpy array of Y coordinates.
|
|
383
381
|
Z: np.ndarray
|
|
384
382
|
a NxN numpy array of Z coordinates'''
|
|
385
|
-
coordset = []
|
|
383
|
+
coordset: list[tuple[np.ndarray, np.ndarray, np.ndarray]] = []
|
|
386
384
|
for tag in self.tags:
|
|
387
385
|
tags, coord, param = gmsh.model.mesh.getNodes(2, tag, includeBoundary=True)
|
|
388
386
|
|
|
@@ -419,17 +417,15 @@ class FaceSelection(Selection):
|
|
|
419
417
|
Y = [c[1] for c in coordset]
|
|
420
418
|
Z = [c[2] for c in coordset]
|
|
421
419
|
if len(X) == 1:
|
|
422
|
-
X
|
|
423
|
-
|
|
424
|
-
Z = Z[0]
|
|
425
|
-
return X, Y, Z
|
|
420
|
+
return X[0], Y[0], Z[0]
|
|
421
|
+
return np.array(X), np.array(Y), np.array(Z)
|
|
426
422
|
|
|
427
423
|
class DomainSelection(Selection):
|
|
428
424
|
"""A Class representing a selection of domains.
|
|
429
425
|
|
|
430
426
|
"""
|
|
431
427
|
dim: int = 3
|
|
432
|
-
def __init__(self, tags: list[int] = None):
|
|
428
|
+
def __init__(self, tags: list[int] | set[int] | None = None):
|
|
433
429
|
super().__init__(tags)
|
|
434
430
|
|
|
435
431
|
SELECT_CLASS: dict[int, type[Selection]] = {
|
|
@@ -501,7 +497,7 @@ class Selector:
|
|
|
501
497
|
x: float,
|
|
502
498
|
y: float,
|
|
503
499
|
z: float,
|
|
504
|
-
vector: tuple[float, float, float] | np.ndarray | Axis) ->
|
|
500
|
+
vector: tuple[float, float, float] | np.ndarray | Axis) -> Selection:
|
|
505
501
|
'''Returns a list of selections that are in the layer defined by the plane normal vector and the point (x,y,z)
|
|
506
502
|
|
|
507
503
|
The layer is specified by two infinite planes normal to the provided vector. The first plane is originated
|
|
@@ -529,7 +525,7 @@ class Selector:
|
|
|
529
525
|
output = []
|
|
530
526
|
for i, c in enumerate(coords):
|
|
531
527
|
c_local = c - np.array([x,y,z])
|
|
532
|
-
if 0 < np.dot(vector, c_local) < L:
|
|
528
|
+
if 0 < np.dot(vector, c_local) < L: # type: ignore
|
|
533
529
|
output.append(dimtags[i][1])
|
|
534
530
|
return SELECT_CLASS[self._current_dim](output)
|
|
535
531
|
|
|
@@ -565,8 +561,8 @@ class Selector:
|
|
|
565
561
|
normals = [gmsh.model.get_normal(t, (0,0)) for d,t, in dimtags]
|
|
566
562
|
tags = []
|
|
567
563
|
for (d,t), o, n in zip(dimtags, coords, normals):
|
|
568
|
-
normdist = np.abs((o-orig)@norm)
|
|
569
|
-
dotnorm = np.abs(n@norm)
|
|
564
|
+
normdist = np.abs((o-orig) @ norm)
|
|
565
|
+
dotnorm = np.abs(n @ norm)
|
|
570
566
|
if normdist < tolerance and dotnorm > 1-tolerance:
|
|
571
567
|
tags.append(t)
|
|
572
568
|
return FaceSelection(tags)
|