emerge 1.0.7__py3-none-any.whl → 1.1.0__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/__init__.py +14 -3
- emerge/_emerge/geo/pcb.py +132 -59
- emerge/_emerge/geo/shapes.py +11 -7
- emerge/_emerge/geometry.py +23 -8
- emerge/_emerge/logsettings.py +26 -2
- emerge/_emerge/mesh3d.py +5 -5
- emerge/_emerge/mesher.py +40 -8
- emerge/_emerge/physics/microwave/adaptive_mesh.py +113 -22
- emerge/_emerge/physics/microwave/microwave_3d.py +131 -81
- emerge/_emerge/physics/microwave/microwave_bc.py +157 -8
- emerge/_emerge/plot/pyvista/display.py +26 -16
- emerge/_emerge/settings.py +124 -6
- emerge/_emerge/simmodel.py +220 -148
- emerge/_emerge/simstate.py +106 -0
- emerge/_emerge/simulation_data.py +11 -23
- emerge/_emerge/solve_interfaces/cudss_interface.py +20 -1
- emerge/_emerge/solver.py +1 -1
- {emerge-1.0.7.dist-info → emerge-1.1.0.dist-info}/METADATA +7 -3
- {emerge-1.0.7.dist-info → emerge-1.1.0.dist-info}/RECORD +22 -21
- {emerge-1.0.7.dist-info → emerge-1.1.0.dist-info}/WHEEL +0 -0
- {emerge-1.0.7.dist-info → emerge-1.1.0.dist-info}/entry_points.txt +0 -0
- {emerge-1.0.7.dist-info → emerge-1.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -29,6 +29,7 @@ from ...bc import BoundaryCondition, BoundaryConditionSet, Periodic
|
|
|
29
29
|
from ...periodic import PeriodicCell, HexCell, RectCell
|
|
30
30
|
from ...material import Material
|
|
31
31
|
from ...const import Z0, C0, EPS0, MU0
|
|
32
|
+
from ...logsettings import DEBUG_COLLECTOR
|
|
32
33
|
|
|
33
34
|
############################################################
|
|
34
35
|
# UTILITY FUNCTIONS #
|
|
@@ -59,6 +60,7 @@ class MWBoundaryConditionSet(BoundaryConditionSet):
|
|
|
59
60
|
self.RectangularWaveguide: type[RectangularWaveguide] = self._construct_bc(RectangularWaveguide)
|
|
60
61
|
self.Periodic: type[Periodic] = self._construct_bc(Periodic)
|
|
61
62
|
self.FloquetPort: type[FloquetPort] = self._construct_bc(FloquetPort)
|
|
63
|
+
self.UserDefinedPort: type[UserDefinedPort] = self._construct_bc(UserDefinedPort)
|
|
62
64
|
|
|
63
65
|
self._cell: PeriodicCell | None = None
|
|
64
66
|
|
|
@@ -341,7 +343,6 @@ class PortMode:
|
|
|
341
343
|
norm_factor: float = 1
|
|
342
344
|
freq: float = 0
|
|
343
345
|
neff: float = 1
|
|
344
|
-
TEM: bool = True
|
|
345
346
|
Z0: float = 50.0
|
|
346
347
|
polarity: float = 1.0
|
|
347
348
|
modetype: Literal['TEM','TE','TM'] = 'TEM'
|
|
@@ -459,7 +460,7 @@ class ModalPort(PortBC):
|
|
|
459
460
|
port_number: int,
|
|
460
461
|
cs: CoordinateSystem | None = None,
|
|
461
462
|
power: float = 1,
|
|
462
|
-
|
|
463
|
+
modetype: Literal['TE','TM','TEM'] | None = None,
|
|
463
464
|
mixed_materials: bool = False):
|
|
464
465
|
"""Generes a ModalPort boundary condition for a port that requires eigenmode solutions for the mode.
|
|
465
466
|
|
|
@@ -476,7 +477,7 @@ class ModalPort(PortBC):
|
|
|
476
477
|
port_number (int): The port number as an integer
|
|
477
478
|
cs (CoordinateSystem, optional): The local coordinate system of the port face. Defaults to None.
|
|
478
479
|
power (float, optional): The radiated power. Defaults to 1.
|
|
479
|
-
|
|
480
|
+
modetype (str[TE, TM, TEM], optional): Wether the mode should be considered as a TEM mode. Defaults to False
|
|
480
481
|
mixed_materials (bool, optional): Wether the port consists of multiple different dielectrics. This requires
|
|
481
482
|
A recalculation of the port mode at every frequency
|
|
482
483
|
"""
|
|
@@ -490,7 +491,7 @@ class ModalPort(PortBC):
|
|
|
490
491
|
self.selected_mode: int = 0
|
|
491
492
|
self.modes: dict[float, list[PortMode]] = defaultdict(list)
|
|
492
493
|
|
|
493
|
-
self.
|
|
494
|
+
self.forced_modetype: Literal['TE','TM','TEM'] | None = modetype
|
|
494
495
|
self.mixed_materials: bool = mixed_materials
|
|
495
496
|
self.initialized: bool = False
|
|
496
497
|
self._first_k0: float | None = None
|
|
@@ -506,7 +507,25 @@ class ModalPort(PortBC):
|
|
|
506
507
|
raise ValueError('No Coordinate System could be derived.')
|
|
507
508
|
self._er: np.ndarray | None = None
|
|
508
509
|
self._ur: np.ndarray | None = None
|
|
510
|
+
|
|
511
|
+
self.vintline: list[Line] = []
|
|
512
|
+
|
|
513
|
+
def set_integration_line(self, c1: tuple[float, float, float], c2: tuple[float, float, float], N: int = 21) -> None:
|
|
514
|
+
"""Define the integration line start and end point
|
|
515
|
+
|
|
516
|
+
Args:
|
|
517
|
+
c1 (tuple[float, float, float]): The start coordinate
|
|
518
|
+
c2 (tuple[float, float, float]): The end coordinate
|
|
519
|
+
N (int, optional): The number of integration points. Defaults to 21.
|
|
520
|
+
"""
|
|
521
|
+
self.vintline.append(Line.from_points(c1, c2, N))
|
|
509
522
|
|
|
523
|
+
def reset(self) -> None:
|
|
524
|
+
self.modes: dict[float, list[PortMode]] = defaultdict(list)
|
|
525
|
+
self.initialized: bool = False
|
|
526
|
+
self.plus_terminal: list[tuple[int, int]] = []
|
|
527
|
+
self.minus_terminal: list[tuple[int, int]] = []
|
|
528
|
+
|
|
510
529
|
def portZ0(self, k0: float) -> complex | float | None:
|
|
511
530
|
return self.get_mode(k0).Z0
|
|
512
531
|
|
|
@@ -524,6 +543,11 @@ class ModalPort(PortBC):
|
|
|
524
543
|
"""
|
|
525
544
|
self.alignment_vectors = [_parse_axis(ax) for ax in axes]
|
|
526
545
|
|
|
546
|
+
def _get_alignment_vector(self, index: int) -> np.ndarray | None:
|
|
547
|
+
if len(self.alignment_vectors) > index:
|
|
548
|
+
return self.alignment_vectors[index].np
|
|
549
|
+
return None
|
|
550
|
+
|
|
527
551
|
def set_terminals(self, positive: Selection | GeoObject | None = None,
|
|
528
552
|
negative: Selection | GeoObject | None = None,
|
|
529
553
|
ground: Selection | GeoObject | None = None) -> None:
|
|
@@ -616,7 +640,7 @@ class ModalPort(PortBC):
|
|
|
616
640
|
beta: float,
|
|
617
641
|
k0: float,
|
|
618
642
|
residual: float,
|
|
619
|
-
|
|
643
|
+
number: int,
|
|
620
644
|
freq: float) -> PortMode | None:
|
|
621
645
|
"""Add a mode function to the ModalPort
|
|
622
646
|
|
|
@@ -627,16 +651,17 @@ class ModalPort(PortBC):
|
|
|
627
651
|
beta (float): The out-of-plane propagation constant
|
|
628
652
|
k0 (float): The free space phase constant
|
|
629
653
|
residual (float): The solution residual
|
|
630
|
-
TEM (bool): Whether its a TEM mode
|
|
631
654
|
freq (float): The frequency of the port mode
|
|
632
655
|
|
|
633
656
|
Returns:
|
|
634
657
|
PortMode: The port mode object.
|
|
635
658
|
"""
|
|
636
|
-
mode = PortMode(field, E_function, H_function, k0, beta, residual,
|
|
659
|
+
mode = PortMode(field, E_function, H_function, k0, beta, residual, freq=freq)
|
|
660
|
+
|
|
637
661
|
if mode.energy < 1e-4:
|
|
638
662
|
logger.debug(f'Ignoring mode due to a low mode energy: {mode.energy}')
|
|
639
663
|
return None
|
|
664
|
+
|
|
640
665
|
self.modes[k0].append(mode)
|
|
641
666
|
self.initialized = True
|
|
642
667
|
|
|
@@ -659,7 +684,7 @@ class ModalPort(PortBC):
|
|
|
659
684
|
|
|
660
685
|
def get_beta(self, k0: float) -> float:
|
|
661
686
|
mode = self.get_mode(k0)
|
|
662
|
-
if
|
|
687
|
+
if self.forced_modetype=='TEM':
|
|
663
688
|
beta = mode.beta/mode.k0 * k0
|
|
664
689
|
else:
|
|
665
690
|
freq = k0*299792458/(2*np.pi)
|
|
@@ -816,6 +841,125 @@ class RectangularWaveguide(PortBC):
|
|
|
816
841
|
Exg, Eyg, Ezg = self.cs.in_global_basis(Ex, Ey, Ez)
|
|
817
842
|
return np.array([Exg, Eyg, Ezg])
|
|
818
843
|
|
|
844
|
+
def _f_zero(k0,x,y,z):
|
|
845
|
+
"Zero field function"
|
|
846
|
+
return np.zeros_like(x, dtype=np.complex128)
|
|
847
|
+
|
|
848
|
+
class UserDefinedPort(PortBC):
|
|
849
|
+
|
|
850
|
+
_include_stiff: bool = True
|
|
851
|
+
_include_mass: bool = False
|
|
852
|
+
_include_force: bool = True
|
|
853
|
+
|
|
854
|
+
def __init__(self,
|
|
855
|
+
face: FaceSelection | GeoSurface,
|
|
856
|
+
port_number: int,
|
|
857
|
+
Ex: Callable | None = None,
|
|
858
|
+
Ey: Callable | None = None,
|
|
859
|
+
Ez: Callable | None = None,
|
|
860
|
+
kz: Callable | None = None,
|
|
861
|
+
power: float = 1.0,
|
|
862
|
+
modetype: Literal['TEM','TE','TM'] = 'TEM',
|
|
863
|
+
cs: CoordinateSystem | None = None):
|
|
864
|
+
"""Creates a user defined port field
|
|
865
|
+
|
|
866
|
+
The UserDefinedPort is defined based on user defined field callables. All undefined callables will default to 0 field or k0.
|
|
867
|
+
|
|
868
|
+
All spatial field functions should be defined using the template:
|
|
869
|
+
>>> def Ec(k0: float, x: np.ndarray, y: np.ndarray, z: np.ndarray) -> np.ndarray
|
|
870
|
+
>>> return #shape like x
|
|
871
|
+
|
|
872
|
+
Args:
|
|
873
|
+
face (FaceSelection, GeoSurface): The port boundary face selection
|
|
874
|
+
port_number (int): The port number
|
|
875
|
+
Ex (Callable): The Ex(k0,x,y,z) field
|
|
876
|
+
Ey (Callable): The Ey(k0,x,y,z) field
|
|
877
|
+
Ez (Callable): The Ez(k0,x,y,z) field
|
|
878
|
+
kz (Callable): The out of plane propagation constant kz(k0)
|
|
879
|
+
power (float): The port output power
|
|
880
|
+
"""
|
|
881
|
+
super().__init__(face)
|
|
882
|
+
if cs is None:
|
|
883
|
+
cs = GCS
|
|
884
|
+
|
|
885
|
+
self.cs = cs
|
|
886
|
+
self.port_number: int= port_number
|
|
887
|
+
self.active: bool = False
|
|
888
|
+
self.power: float = power
|
|
889
|
+
self.type: str = 'TE'
|
|
890
|
+
if Ex is None:
|
|
891
|
+
Ex = _f_zero
|
|
892
|
+
if Ey is None:
|
|
893
|
+
Ey = _f_zero
|
|
894
|
+
if Ez is None:
|
|
895
|
+
Ez = _f_zero
|
|
896
|
+
if kz is None:
|
|
897
|
+
kz = lambda k0: k0
|
|
898
|
+
|
|
899
|
+
self._fex: Callable = Ex
|
|
900
|
+
self._fey: Callable = Ey
|
|
901
|
+
self._fez: Callable = Ez
|
|
902
|
+
self._fkz: Callable = kz
|
|
903
|
+
self.type = modetype
|
|
904
|
+
|
|
905
|
+
def get_basis(self) -> np.ndarray:
|
|
906
|
+
return self.cs._basis
|
|
907
|
+
|
|
908
|
+
def get_inv_basis(self) -> np.ndarray:
|
|
909
|
+
return self.cs._basis_inv
|
|
910
|
+
|
|
911
|
+
def modetype(self, k0):
|
|
912
|
+
return self.type
|
|
913
|
+
|
|
914
|
+
def get_amplitude(self, k0: float) -> float:
|
|
915
|
+
return np.sqrt(self.power)
|
|
916
|
+
|
|
917
|
+
def get_beta(self, k0: float) -> float:
|
|
918
|
+
''' Return the out of plane propagation constant. βz.'''
|
|
919
|
+
return self._fkz(k0)
|
|
920
|
+
|
|
921
|
+
def get_gamma(self, k0: float) -> complex:
|
|
922
|
+
"""Computes the γ-constant for matrix assembly. This constant is required for the Robin boundary condition.
|
|
923
|
+
|
|
924
|
+
Args:
|
|
925
|
+
k0 (float): The free space propagation constant.
|
|
926
|
+
|
|
927
|
+
Returns:
|
|
928
|
+
complex: The γ-constant
|
|
929
|
+
"""
|
|
930
|
+
return 1j*self.get_beta(k0)
|
|
931
|
+
|
|
932
|
+
def get_Uinc(self, x_global: np.ndarray, y_global: np.ndarray, z_global: np.ndarray, k0: float) -> np.ndarray:
|
|
933
|
+
return -2*1j*self.get_beta(k0)*self.port_mode_3d_global(x_global, y_global, z_global, k0)
|
|
934
|
+
|
|
935
|
+
def port_mode_3d(self,
|
|
936
|
+
x_local: np.ndarray,
|
|
937
|
+
y_local: np.ndarray,
|
|
938
|
+
k0: float,
|
|
939
|
+
which: Literal['E','H'] = 'E') -> np.ndarray:
|
|
940
|
+
x_global, y_global, z_global = self.cs.in_global_cs(x_local, y_local, 0*x_local)
|
|
941
|
+
|
|
942
|
+
Egxyz = self.port_mode_3d_global(x_global,y_global,z_global,k0,which=which)
|
|
943
|
+
|
|
944
|
+
Ex, Ey, Ez = self.cs.in_local_basis(Egxyz[0,:], Egxyz[1,:], Egxyz[2,:])
|
|
945
|
+
|
|
946
|
+
Exyz = np.array([Ex, Ey, Ez])
|
|
947
|
+
return Exyz
|
|
948
|
+
|
|
949
|
+
def port_mode_3d_global(self,
|
|
950
|
+
x_global: np.ndarray,
|
|
951
|
+
y_global: np.ndarray,
|
|
952
|
+
z_global: np.ndarray,
|
|
953
|
+
k0: float,
|
|
954
|
+
which: Literal['E','H'] = 'E') -> np.ndarray:
|
|
955
|
+
'''Compute the port mode field for global xyz coordinates.'''
|
|
956
|
+
xl, yl, _ = self.cs.in_local_cs(x_global, y_global, z_global)
|
|
957
|
+
Ex = self._fex(k0, x_global, y_global, z_global)
|
|
958
|
+
Ey = self._fey(k0, x_global, y_global, z_global)
|
|
959
|
+
Ez = self._fez(k0, x_global, y_global, z_global)
|
|
960
|
+
Exg, Eyg, Ezg = self.cs.in_global_basis(Ex, Ey, Ez)
|
|
961
|
+
return np.array([Exg, Eyg, Ezg])
|
|
962
|
+
|
|
819
963
|
class LumpedPort(PortBC):
|
|
820
964
|
|
|
821
965
|
_include_stiff: bool = True
|
|
@@ -874,6 +1018,11 @@ class LumpedPort(PortBC):
|
|
|
874
1018
|
self.vintline: list[Line] = []
|
|
875
1019
|
self.v_integration = True
|
|
876
1020
|
|
|
1021
|
+
# Sanity checks
|
|
1022
|
+
if self.width > 0.5 or self.height > 0.5:
|
|
1023
|
+
DEBUG_COLLECTOR.add_report(f'{self}: A lumped port width/height larger than 0.5m has been detected: width={self.width:.3f}m. Height={self.height:.3f}.m. Perhaps you forgot a unit like mm, um, or mil')
|
|
1024
|
+
|
|
1025
|
+
|
|
877
1026
|
@property
|
|
878
1027
|
def surfZ(self) -> float:
|
|
879
1028
|
"""The surface sheet impedance for the lumped port
|
|
@@ -15,20 +15,21 @@
|
|
|
15
15
|
# along with this program; if not, see
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
17
|
from __future__ import annotations
|
|
18
|
-
import time
|
|
19
18
|
from ...mesh3d import Mesh3D
|
|
19
|
+
from ...simstate import SimState
|
|
20
20
|
from ...geometry import GeoObject
|
|
21
21
|
from ...selection import FaceSelection, DomainSelection, EdgeSelection, Selection, encode_data
|
|
22
22
|
from ...physics.microwave.microwave_bc import PortBC, ModalPort
|
|
23
|
-
import numpy as np
|
|
24
|
-
import pyvista as pv
|
|
25
|
-
from typing import Iterable, Literal, Callable, Any
|
|
26
23
|
from ..display import BaseDisplay
|
|
27
24
|
from .display_settings import PVDisplaySettings
|
|
28
|
-
from matplotlib.colors import ListedColormap
|
|
29
25
|
from .cmap_maker import make_colormap
|
|
30
26
|
|
|
27
|
+
import time
|
|
28
|
+
import numpy as np
|
|
29
|
+
import pyvista as pv
|
|
30
|
+
from typing import Iterable, Literal, Callable, Any
|
|
31
31
|
from itertools import cycle
|
|
32
|
+
from loguru import logger
|
|
32
33
|
### Color scale
|
|
33
34
|
|
|
34
35
|
# Define the colors we want to use
|
|
@@ -232,8 +233,8 @@ class _AnimObject:
|
|
|
232
233
|
|
|
233
234
|
class PVDisplay(BaseDisplay):
|
|
234
235
|
|
|
235
|
-
def __init__(self,
|
|
236
|
-
self.
|
|
236
|
+
def __init__(self, state: SimState):
|
|
237
|
+
self._state: SimState = state
|
|
237
238
|
self.set: PVDisplaySettings = PVDisplaySettings()
|
|
238
239
|
|
|
239
240
|
# Animation options
|
|
@@ -260,6 +261,9 @@ class PVDisplay(BaseDisplay):
|
|
|
260
261
|
self._cbar_lim: tuple[float, float] | None = None
|
|
261
262
|
self.camera_position = (1, -1, 1) # +X, +Z, -Y
|
|
262
263
|
|
|
264
|
+
@property
|
|
265
|
+
def _mesh(self) -> Mesh3D:
|
|
266
|
+
return self._state.mesh
|
|
263
267
|
|
|
264
268
|
def cbar(self, name: str, n_labels: int = 5, interactive: bool = False, clim: tuple[float, float] | None = None ) -> PVDisplay:
|
|
265
269
|
self._cbar_args = dict(title=name, n_labels=n_labels, interactive=interactive)
|
|
@@ -302,8 +306,7 @@ class PVDisplay(BaseDisplay):
|
|
|
302
306
|
self._ruler.min_length = max(1e-3, min(self._mesh.edge_lengths))
|
|
303
307
|
self._update_camera()
|
|
304
308
|
self._add_aux_items()
|
|
305
|
-
|
|
306
|
-
# self._plot.enable_anti_aliasing(self.set.anti_aliassing)
|
|
309
|
+
self._add_background()
|
|
307
310
|
if self._do_animate:
|
|
308
311
|
self._wire_close_events()
|
|
309
312
|
self.add_text('Press Q to close!',color='red', position='upper_left')
|
|
@@ -313,14 +316,17 @@ class PVDisplay(BaseDisplay):
|
|
|
313
316
|
self._plot.show()
|
|
314
317
|
|
|
315
318
|
self._reset()
|
|
316
|
-
|
|
317
|
-
def set_mesh(self, mesh: Mesh3D):
|
|
318
|
-
"""Define the mesh to be used
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
def _add_background(self):
|
|
321
|
+
from pyvista import examples
|
|
322
|
+
from requests.exceptions import ConnectionError
|
|
323
|
+
|
|
324
|
+
try:
|
|
325
|
+
cubemap = examples.download_sky_box_cube_map()
|
|
326
|
+
self._plot.set_environment_texture(cubemap)
|
|
327
|
+
except ConnectionError:
|
|
328
|
+
logger.warning(f'No internet, no background texture will be used.')
|
|
329
|
+
|
|
324
330
|
|
|
325
331
|
def _reset(self):
|
|
326
332
|
""" Resets key display parameters."""
|
|
@@ -456,6 +462,10 @@ class PVDisplay(BaseDisplay):
|
|
|
456
462
|
## OBLIGATORY METHODS
|
|
457
463
|
def add_object(self, obj: GeoObject | Selection, mesh: bool = False, volume_mesh: bool = True, label: bool = False, *args, **kwargs):
|
|
458
464
|
|
|
465
|
+
if isinstance(obj, GeoObject):
|
|
466
|
+
if obj._hidden:
|
|
467
|
+
return
|
|
468
|
+
|
|
459
469
|
show_edges = False
|
|
460
470
|
opacity = obj.opacity
|
|
461
471
|
line_width = 0.5
|
emerge/_emerge/settings.py
CHANGED
|
@@ -1,12 +1,130 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
1
17
|
|
|
2
|
-
from typing import Literal
|
|
3
18
|
|
|
4
19
|
class Settings:
|
|
5
|
-
|
|
6
20
|
def __init__(self):
|
|
7
|
-
self.
|
|
8
|
-
self.
|
|
9
|
-
self.
|
|
10
|
-
self.
|
|
21
|
+
self._mw_2dbc: bool = True
|
|
22
|
+
self._mw_2dbc_lim: float = 10.0
|
|
23
|
+
self._mw_2dbc_peclim: float = 1e8
|
|
24
|
+
self._mw_3d_peclim: float = 1e7
|
|
25
|
+
self._mw_cap_sp_single: bool = True
|
|
26
|
+
self._mw_cap_sp_col: bool = True
|
|
27
|
+
self._mw_recip_sp: bool = False
|
|
28
|
+
self._size_check: bool = True
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
############################################################
|
|
33
|
+
# GETTERS #
|
|
34
|
+
############################################################
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def mw_2dbc(self) -> bool:
|
|
38
|
+
""" This variable determines is 2D boundary conditions will be automatically assigned based on material properties.
|
|
39
|
+
"""
|
|
40
|
+
return self._mw_2dbc
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def mw_2dbc_lim(self) -> float:
|
|
44
|
+
"""This variable is the bulk conductivity limit in S/m beyond which a surface material will automatically be assigned as a SurfaceImpedance boundary condition."""
|
|
45
|
+
return self._mw_2dbc_lim
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def mw_2dbc_peclim(self) -> float:
|
|
49
|
+
"""This variable determines a bulk conductivity limit in S/m beyond which a conductor is assigned PEC instead of a SurfaceImpedance boundary condition."""
|
|
50
|
+
return self._mw_2dbc_peclim
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def mw_3d_peclim(self) -> float:
|
|
54
|
+
"""This variable determines if bulk conductors with a bulk conductivity beyond a limit (.mw_3d_peclim) are considered PEC.
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
return self._mw_3d_peclim
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def size_check(self) -> bool:
|
|
61
|
+
"""If a total volume check should be considered (100,000 tetrahedra) to hard crash the simulation assuming that the problem size will be too high to solver.
|
|
62
|
+
100.000 Tetrahedra would yield approximately 700k Degrees of Freedom
|
|
63
|
+
"""
|
|
64
|
+
return self._size_check
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def mw_cap_sp_single(self) -> bool:
|
|
68
|
+
"""If Single S-parameters should be capped with their magnitude to at most 1.0"""
|
|
69
|
+
return self._mw_cap_sp_single
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def mw_cap_sp_col(self) -> bool:
|
|
73
|
+
"""If Single S-parameters columns should be power normalized to 1.0"""
|
|
74
|
+
return self._mw_cap_sp_col
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def mw_recip_sp(self) -> bool:
|
|
78
|
+
"""If reciprodicty should be explicitly enforced"""
|
|
79
|
+
return self._mw_recip_sp
|
|
80
|
+
############################################################
|
|
81
|
+
# SETTERS #
|
|
82
|
+
############################################################
|
|
83
|
+
|
|
84
|
+
@mw_2dbc.setter
|
|
85
|
+
def mw_2dbc(self, value: bool) -> None:
|
|
86
|
+
""" This variable determines is 2D boundary conditions will be automatically assigned based on material properties.
|
|
87
|
+
"""
|
|
88
|
+
self._mw_2dbc = value
|
|
11
89
|
|
|
90
|
+
@mw_2dbc_lim.setter
|
|
91
|
+
def mw_2dbc_lim(self, value: float):
|
|
92
|
+
"""This variable is the bulk conductivity limit in S/m beyond which a surface material will automatically be assigned as a SurfaceImpedance boundary condition."""
|
|
93
|
+
self._mw_2dbc_lim = value
|
|
94
|
+
|
|
95
|
+
@mw_2dbc_peclim.setter
|
|
96
|
+
def mw_2dbc_peclim(self, value: float):
|
|
97
|
+
"""This variable determines a bulk conductivity limit in S/m beyond which a conductor is assigned PEC instead of a SurfaceImpedance boundary condition."""
|
|
98
|
+
|
|
99
|
+
self._mw_2dbc_peclim = value
|
|
100
|
+
|
|
101
|
+
@mw_3d_peclim.setter
|
|
102
|
+
def mw_3d_peclim(self, value: float):
|
|
103
|
+
"""This variable determines if bulk conductors with a bulk conductivity beyond a limit (.mw_3d_peclim) are considered PEC.
|
|
104
|
+
|
|
105
|
+
"""
|
|
106
|
+
self._mw_3d_peclim = value
|
|
107
|
+
|
|
108
|
+
@size_check.setter
|
|
109
|
+
def size_check(self, value: bool):
|
|
110
|
+
"""If a total volume check should be considered (100,000 tetrahedra) to hard crash the simulation assuming that the problem size will be too high to solver.
|
|
111
|
+
100.000 Tetrahedra would yield approximately 700k Degrees of Freedom
|
|
112
|
+
"""
|
|
113
|
+
self._size_check = value
|
|
114
|
+
|
|
115
|
+
@mw_cap_sp_single.setter
|
|
116
|
+
def mw_cap_sp_single(self, value: bool) -> bool:
|
|
117
|
+
"""If Single S-parameters should be capped with their magnitude to at most 1.0"""
|
|
118
|
+
self._mw_cap_sp_single = value
|
|
119
|
+
|
|
120
|
+
@mw_cap_sp_col.setter
|
|
121
|
+
def mw_cap_sp_col(self, value: bool) -> bool:
|
|
122
|
+
"""If Single S-parameters columns should be power normalized to 1.0"""
|
|
123
|
+
self._mw_cap_sp_col = value
|
|
124
|
+
|
|
125
|
+
@mw_recip_sp.setter
|
|
126
|
+
def mw_recip_sp(self, value: bool) -> bool:
|
|
127
|
+
"""If reciprodicty should be explicitly enforced"""
|
|
128
|
+
self._mw_recip_sp = value
|
|
129
|
+
|
|
12
130
|
DEFAULT_SETTINGS = Settings()
|