emerge 0.6.6__tar.gz → 0.6.8__tar.gz
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-0.6.6 → emerge-0.6.8}/.bumpversion.toml +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/.gitignore +2 -1
- {emerge-0.6.6 → emerge-0.6.8}/LICENSE +2 -2
- {emerge-0.6.6 → emerge-0.6.8}/PKG-INFO +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/__init__.py +2 -2
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/_cache_check.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/femdata.py +3 -2
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/index_interp.py +1 -2
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/ned2_interp.py +16 -16
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/nedelec2.py +17 -6
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/nedleg2.py +21 -9
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/__init__.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/horn.py +0 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/modeler.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/operations.py +44 -4
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/pcb.py +44 -9
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/pcb_tools/calculator.py +2 -3
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/pmlbox.py +35 -11
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/shapes.py +61 -26
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geometry.py +4 -4
- emerge-0.6.8/emerge/_emerge/material.py +368 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mesh3d.py +14 -8
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/assembler.py +43 -20
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/microwave_3d.py +57 -44
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/microwave_bc.py +12 -8
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/microwave_data.py +74 -5
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/pyvista/display.py +132 -24
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/simple_plots.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/simmodel.py +22 -13
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/solver.py +49 -22
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo10_sgh.py +12 -10
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo11_lumped_element_filter.py +3 -1
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo12_mode_alignment.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo13_helix_antenna.py +4 -12
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo14_boundary_selection.py +7 -12
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo1_stepped_imp_filter.py +4 -4
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo2_combline_filter.py +6 -6
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo3_coupled_line_filter.py +3 -3
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo4_patch_antenna.py +38 -33
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo5_revolve.py +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo6_striplines_with_vias.py +13 -13
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo7_periodic_cells.py +22 -19
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo8_waveguide_bpf_synthesis.py +4 -4
- {emerge-0.6.6 → emerge-0.6.8}/examples/demo9_dielectric_resonator.py +2 -1
- {emerge-0.6.6 → emerge-0.6.8}/pyproject.toml +1 -1
- {emerge-0.6.6 → emerge-0.6.8}/uv.lock +1 -1
- emerge-0.6.6/emerge/_emerge/material.py +0 -123
- {emerge-0.6.6 → emerge-0.6.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/.opt +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/.python-version +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/README.md +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/THIRD_PARTY_LICENSES.md +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/UMFPACK_Install_windows.md +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/UMFPACK_installer_windows.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/__main__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/bc.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/const.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/coord.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/cs.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/dataset.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/elements/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/polybased.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo/step.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/geo2d.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/howto.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/logsettings.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mesher.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mth/common_functions.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mth/integrals.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mth/optimized.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/mth/pairing.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/periodic.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/periodic.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/sc.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/simjob.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/sparam.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/display.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/plot.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/projects/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/projects/_gen_base.txt +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/projects/_load_base.txt +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/projects/generate_project.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/selection.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/simulation_data.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/solve_interfaces/cudss_interface.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/_emerge/system.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/cli.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/ext.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/lib.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/plot.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/emerge/pyvista.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/src/__init__.py +0 -0
- {emerge-0.6.6 → emerge-0.6.8}/src/_img/logo.jpeg +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
──────────────────────────────────────────────────────────────────────────────
|
|
2
|
-
PREAMBLE
|
|
2
|
+
PREAMBLE - Licence map and disclaimers (not part of the GNU GPL text)
|
|
3
3
|
──────────────────────────────────────────────────────────────────────────────
|
|
4
4
|
This repository is distributed under two distinct licences:
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@ This repository is distributed under two distinct licences:
|
|
|
11
11
|
When you distribute binaries containing these components, the complete
|
|
12
12
|
corresponding source code must be offered under GPL-2.0+.
|
|
13
13
|
|
|
14
|
-
• **`lib.py`
|
|
14
|
+
• **`lib.py` - materials-property database**
|
|
15
15
|
This single file is dedicated to the public domain under **CC0 1.0**.
|
|
16
16
|
You may copy, modify, merge, publish, distribute, sublicense and/or sell
|
|
17
17
|
copies of `lib.py` without restriction. The data it contains are
|
|
@@ -18,7 +18,7 @@ along with this program; if not, see
|
|
|
18
18
|
"""
|
|
19
19
|
import os
|
|
20
20
|
|
|
21
|
-
__version__ = "0.6.
|
|
21
|
+
__version__ = "0.6.8"
|
|
22
22
|
|
|
23
23
|
############################################################
|
|
24
24
|
# HANDLE ENVIRONMENT VARIABLES #
|
|
@@ -45,7 +45,7 @@ LOG_CONTROLLER.set_default()
|
|
|
45
45
|
logger.debug('Importing modules')
|
|
46
46
|
|
|
47
47
|
from ._emerge.simmodel import Simulation
|
|
48
|
-
from ._emerge.material import Material
|
|
48
|
+
from ._emerge.material import Material, FreqCoordDependent, FreqDependent, CoordDependent
|
|
49
49
|
from ._emerge import bc
|
|
50
50
|
from ._emerge.solver import SolverBicgstab, SolverGMRES, SolveRoutine, ReverseCuthillMckee, Sorter, SolverPardiso, SolverUMFPACK, SolverSuperLU, EMSolver
|
|
51
51
|
from ._emerge.cs import CoordinateSystem, CS, GCS, Plane, Axis, XAX, YAX, ZAX, XYPLANE, XZPLANE, YZPLANE, YXPLANE, ZXPLANE, ZYPLANE
|
|
@@ -22,7 +22,7 @@ _COMPILE_MESSAGE = """
|
|
|
22
22
|
[ EMERGE ]
|
|
23
23
|
⚠ Numba is compiling optimized code; this may take a few minutes.
|
|
24
24
|
• Additional functions may be compiled on-the-fly.
|
|
25
|
-
• Compilation happens only once
|
|
25
|
+
• Compilation happens only once-subsequent runs load from cache.
|
|
26
26
|
Please wait…"""
|
|
27
27
|
|
|
28
28
|
@njit(cache=True)
|
|
@@ -64,7 +64,7 @@ class FEMBasis:
|
|
|
64
64
|
def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, tetids: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
65
65
|
raise NotImplementedError()
|
|
66
66
|
|
|
67
|
-
def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, constants: np.ndarray, tetids: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
67
|
+
def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, constants: np.ndarray, tetids: np.ndarray | None = None, usenan: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
68
68
|
"""
|
|
69
69
|
Interpolates the curl of the field at the given points.
|
|
70
70
|
"""
|
|
@@ -151,7 +151,8 @@ class FEMBasis:
|
|
|
151
151
|
def interpolate_index(self, xs: np.ndarray,
|
|
152
152
|
ys: np.ndarray,
|
|
153
153
|
zs: np.ndarray,
|
|
154
|
-
tetids: np.ndarray | None = None
|
|
154
|
+
tetids: np.ndarray | None = None,
|
|
155
|
+
usenan: bool = True) -> np.ndarray:
|
|
155
156
|
raise NotImplementedError()
|
|
156
157
|
|
|
157
158
|
def map_edge_to_field(self, edge_ids: np.ndarray) -> np.ndarray:
|
|
@@ -27,9 +27,8 @@ def index_interp(coords: np.ndarray,
|
|
|
27
27
|
# Solution has shape (nEdges, nsols)
|
|
28
28
|
nNodes = coords.shape[1]
|
|
29
29
|
|
|
30
|
-
prop = np.
|
|
30
|
+
prop = np.full((nNodes, ), -1, dtype=np.int64)
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
for i_iter in range(tetids.shape[0]):
|
|
34
33
|
itet = tetids[i_iter]
|
|
35
34
|
|
|
@@ -130,10 +130,10 @@ def ned2_tet_interp(coords: np.ndarray,
|
|
|
130
130
|
ys = coords[1,:]
|
|
131
131
|
zs = coords[2,:]
|
|
132
132
|
|
|
133
|
-
Ex = np.
|
|
134
|
-
Ey = np.
|
|
135
|
-
Ez = np.
|
|
136
|
-
|
|
133
|
+
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
134
|
+
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
135
|
+
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
136
|
+
|
|
137
137
|
for i_iter in range(tetids.shape[0]):
|
|
138
138
|
itet = tetids[i_iter]
|
|
139
139
|
|
|
@@ -259,9 +259,9 @@ def ned2_tet_interp_curl(coords: np.ndarray,
|
|
|
259
259
|
ys = coords[1,:]
|
|
260
260
|
zs = coords[2,:]
|
|
261
261
|
|
|
262
|
-
Ex = np.
|
|
263
|
-
Ey = np.
|
|
264
|
-
Ez = np.
|
|
262
|
+
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
263
|
+
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
264
|
+
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
265
265
|
|
|
266
266
|
for i_iter in range(tetids.shape[0]):
|
|
267
267
|
itet = tetids[i_iter]
|
|
@@ -382,9 +382,9 @@ def ned2_tri_interp(coords: np.ndarray,
|
|
|
382
382
|
xs = coords[0,:]
|
|
383
383
|
ys = coords[1,:]
|
|
384
384
|
|
|
385
|
-
Ex = np.
|
|
386
|
-
Ey = np.
|
|
387
|
-
Ez = np.
|
|
385
|
+
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
386
|
+
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
387
|
+
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
388
388
|
|
|
389
389
|
nodes = nodes[:2,:]
|
|
390
390
|
|
|
@@ -491,9 +491,9 @@ def ned2_tri_interp_full(coords: np.ndarray,
|
|
|
491
491
|
xs = coords[0,:]
|
|
492
492
|
ys = coords[1,:]
|
|
493
493
|
|
|
494
|
-
Ex = np.
|
|
495
|
-
Ey = np.
|
|
496
|
-
Ez = np.
|
|
494
|
+
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
495
|
+
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
496
|
+
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
497
497
|
|
|
498
498
|
nodes = nodes[:2,:]
|
|
499
499
|
|
|
@@ -572,9 +572,9 @@ def ned2_tri_interp_curl(coords: np.ndarray,
|
|
|
572
572
|
xs = coords[0,:]
|
|
573
573
|
ys = coords[1,:]
|
|
574
574
|
jB = 1j*beta
|
|
575
|
-
Ex = np.
|
|
576
|
-
Ey = np.
|
|
577
|
-
Ez = np.
|
|
575
|
+
Ex = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
576
|
+
Ey = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
577
|
+
Ez = np.full((nNodes, ), np.nan, dtype=np.complex128)
|
|
578
578
|
|
|
579
579
|
nodes = nodes[:2,:]
|
|
580
580
|
|
|
@@ -68,30 +68,41 @@ class Nedelec2(FEMBasis):
|
|
|
68
68
|
|
|
69
69
|
self.empty_tri_rowcol()
|
|
70
70
|
|
|
71
|
-
def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, tetids: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
71
|
+
def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, tetids: np.ndarray | None = None, usenan: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
72
72
|
'''
|
|
73
73
|
Interpolate the provided field data array at the given xs, ys and zs coordinates
|
|
74
74
|
'''
|
|
75
75
|
if tetids is None:
|
|
76
76
|
tetids = self._all_tet_ids
|
|
77
|
-
|
|
77
|
+
vals = ned2_tet_interp(np.array([xs, ys, zs]), field, self.mesh.tets, self.mesh.tris, self.mesh.edges, self.mesh.nodes, self.tet_to_field, tetids)
|
|
78
|
+
if not usenan:
|
|
79
|
+
vals = np.nan_to_num(vals)
|
|
80
|
+
return vals
|
|
78
81
|
|
|
79
|
-
def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, c: np.ndarray, tetids: np.ndarray | None = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
82
|
+
def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, c: np.ndarray, tetids: np.ndarray | None = None, usenan: bool = True) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
80
83
|
"""
|
|
81
84
|
Interpolates the curl of the field at the given points.
|
|
82
85
|
"""
|
|
83
86
|
if tetids is None:
|
|
84
87
|
tetids = self._all_tet_ids
|
|
85
|
-
|
|
88
|
+
vals = ned2_tet_interp_curl(np.array([xs, ys, zs]), field, self.mesh.tets, self.mesh.tris, self.mesh.edges, self.mesh.nodes, self.tet_to_field, c, tetids)
|
|
89
|
+
if not usenan:
|
|
90
|
+
vals = np.nan_to_num(vals)
|
|
91
|
+
return vals
|
|
86
92
|
|
|
87
93
|
def interpolate_index(self, xs: np.ndarray,
|
|
88
94
|
ys: np.ndarray,
|
|
89
95
|
zs: np.ndarray,
|
|
90
|
-
tetids: np.ndarray | None = None
|
|
96
|
+
tetids: np.ndarray | None = None,
|
|
97
|
+
usenan: bool = True) -> np.ndarray:
|
|
91
98
|
if tetids is None:
|
|
92
99
|
tetids = self._all_tet_ids
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
vals = index_interp(np.array([xs, ys, zs]), self.mesh.tets, self.mesh.nodes, tetids)
|
|
102
|
+
if not usenan:
|
|
103
|
+
vals[vals==-1]==0
|
|
104
|
+
return vals
|
|
105
|
+
|
|
95
106
|
###### INDEX MAPPINGS
|
|
96
107
|
|
|
97
108
|
def local_tet_to_triid(self, itet: int) -> np.ndarray:
|
|
@@ -73,24 +73,30 @@ class FieldFunctionClass:
|
|
|
73
73
|
Fx, Fy, Fz = self.cs.in_global_basis(Fxl, Fyl, Fzl)
|
|
74
74
|
return np.array([Fx, Fy, Fz])*self.constant
|
|
75
75
|
|
|
76
|
-
def calcE(self, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
76
|
+
def calcE(self, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
77
77
|
coordinates = np.array([xs, ys])
|
|
78
|
-
|
|
78
|
+
vals = ned2_tri_interp_full(coordinates,
|
|
79
79
|
self.field,
|
|
80
80
|
self.tris,
|
|
81
81
|
self.nodes,
|
|
82
82
|
self.tri_to_field)
|
|
83
|
+
if not usenan:
|
|
84
|
+
vals = np.nan_to_num(vals)
|
|
85
|
+
return vals
|
|
83
86
|
|
|
84
|
-
def calcH(self, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
87
|
+
def calcH(self, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
85
88
|
coordinates = np.array([xs, ys])
|
|
86
89
|
|
|
87
|
-
|
|
90
|
+
vals = ned2_tri_interp_curl(coordinates,
|
|
88
91
|
self.field,
|
|
89
92
|
self.tris,
|
|
90
93
|
self.nodes,
|
|
91
94
|
self.tri_to_field,
|
|
92
95
|
self.diadic,
|
|
93
96
|
self.beta)
|
|
97
|
+
if not usenan:
|
|
98
|
+
vals = np.nan_to_num(vals)
|
|
99
|
+
return vals
|
|
94
100
|
|
|
95
101
|
############### Nedelec2 Class
|
|
96
102
|
|
|
@@ -178,22 +184,28 @@ class NedelecLegrange2(FEMBasis):
|
|
|
178
184
|
|
|
179
185
|
return FieldFunctionClass(field, self.cs, self.local_nodes, self.mesh.tris, self.tri_to_field, 'H', urinv, beta, constant)
|
|
180
186
|
|
|
181
|
-
def tri_interpolate(self, field, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
187
|
+
def tri_interpolate(self, field, xs: np.ndarray, ys: np.ndarray, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
182
188
|
coordinates = np.array([xs, ys])
|
|
183
|
-
|
|
189
|
+
vals = ned2_tri_interp_full(coordinates,
|
|
184
190
|
field,
|
|
185
191
|
self.mesh.tris,
|
|
186
192
|
self.local_nodes,
|
|
187
193
|
self.tri_to_field)
|
|
194
|
+
if not usenan:
|
|
195
|
+
vals = np.nan_to_num(vals)
|
|
196
|
+
return vals
|
|
188
197
|
|
|
189
|
-
def tri_interpolate_curl(self, field, xs: np.ndarray, ys: np.ndarray, diadic: np.ndarray | None = None, beta: float = 0.0) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
198
|
+
def tri_interpolate_curl(self, field, xs: np.ndarray, ys: np.ndarray, diadic: np.ndarray | None = None, beta: float = 0.0, usenan: bool = False) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
190
199
|
coordinates = np.array([xs, ys])
|
|
191
200
|
if diadic is None:
|
|
192
201
|
diadic = np.eye(3)[:,:,np.newaxis()] * np.ones((self.mesh.n_tris)) # type: ignore
|
|
193
|
-
|
|
202
|
+
vals = ned2_tri_interp_curl(coordinates,
|
|
194
203
|
field,
|
|
195
204
|
self.mesh.tris,
|
|
196
205
|
self.local_nodes,
|
|
197
206
|
self.tri_to_field,
|
|
198
207
|
diadic,
|
|
199
|
-
beta)
|
|
208
|
+
beta)
|
|
209
|
+
if not usenan:
|
|
210
|
+
vals = np.nan_to_num(vals)
|
|
211
|
+
return vals
|
|
@@ -19,6 +19,6 @@ from .pcb import PCB
|
|
|
19
19
|
from .pmlbox import pmlbox
|
|
20
20
|
from .horn import Horn
|
|
21
21
|
from .shapes import Cylinder, CoaxCylinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment, Cone
|
|
22
|
-
from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite
|
|
22
|
+
from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect, unite, expand_surface, stretch
|
|
23
23
|
from .polybased import XYPolygon, GeoPrism, Disc, Curve
|
|
24
24
|
from .step import STEPItems
|
|
@@ -60,7 +60,7 @@ def add(main: T, tool: T,
|
|
|
60
60
|
main._exists = False
|
|
61
61
|
if remove_tool:
|
|
62
62
|
tool._exists = False
|
|
63
|
-
return output # type: ignore
|
|
63
|
+
return output.set_material(main.material) # type: ignore
|
|
64
64
|
|
|
65
65
|
def remove(main: T, tool: T,
|
|
66
66
|
remove_object: bool = True,
|
|
@@ -90,7 +90,7 @@ def remove(main: T, tool: T,
|
|
|
90
90
|
main._exists = False
|
|
91
91
|
if remove_tool:
|
|
92
92
|
tool._exists = False
|
|
93
|
-
return output # type: ignore
|
|
93
|
+
return output.set_material(main.material) # type: ignore
|
|
94
94
|
|
|
95
95
|
subtract = remove
|
|
96
96
|
|
|
@@ -122,7 +122,7 @@ def intersect(main: T, tool: T,
|
|
|
122
122
|
main._exists = False
|
|
123
123
|
if remove_tool:
|
|
124
124
|
tool._exists = False
|
|
125
|
-
return output #type:ignore
|
|
125
|
+
return output.set_material(main.material) #type:ignore
|
|
126
126
|
|
|
127
127
|
def embed(main: GeoVolume, other: GeoSurface) -> None:
|
|
128
128
|
''' Embeds a surface into a volume in the GMSH model.
|
|
@@ -268,6 +268,19 @@ def change_coordinate_system(main: GeoObject,
|
|
|
268
268
|
fp.affine_transform(M2)
|
|
269
269
|
return main
|
|
270
270
|
|
|
271
|
+
def stretch(main: GeoObject, fx: float = 1, fy: float = 1, fz: float = 1, origin: tuple[float, float, float] = (0.0, 0.0, 0.0)) -> GeoObject:
|
|
272
|
+
"""Stretches a geometry with a factor fx, fy and fz along the x, y and Z axes respectively
|
|
273
|
+
|
|
274
|
+
The stretch origin is centered at the provided origin.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
_type_: _description_
|
|
278
|
+
"""
|
|
279
|
+
gmsh.model.occ.dilate(main.dimtags, *origin, fx, fy, fz)
|
|
280
|
+
|
|
281
|
+
return main
|
|
282
|
+
|
|
283
|
+
|
|
271
284
|
@overload
|
|
272
285
|
def unite(*objects: GeoVolume) -> GeoVolume: ...
|
|
273
286
|
|
|
@@ -301,4 +314,31 @@ def unite(*objects: GeoObject) -> GeoObject:
|
|
|
301
314
|
new_obj.set_material(main.material)
|
|
302
315
|
new_obj.prio_set(main._priority)
|
|
303
316
|
|
|
304
|
-
return new_obj
|
|
317
|
+
return new_obj
|
|
318
|
+
|
|
319
|
+
def expand_surface(surface: GeoSurface, distance: float) -> GeoSurface:
|
|
320
|
+
"""EXPERIMENTAL: Expands an input surface. The surface must exist on a 2D plane.
|
|
321
|
+
|
|
322
|
+
The output surface does not inherit material properties.
|
|
323
|
+
|
|
324
|
+
If any problems occur, reach out through email.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
surface (GeoSurface): The input surface to expand
|
|
328
|
+
distance (float): The exapansion distance
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
GeoSurface: The output surface
|
|
332
|
+
"""
|
|
333
|
+
surfs = []
|
|
334
|
+
for tag in surface.tags:
|
|
335
|
+
looptags, _ = gmsh.model.occ.get_curve_loops(tag)
|
|
336
|
+
new_curves = []
|
|
337
|
+
for looptag in looptags:
|
|
338
|
+
curve_tags = gmsh.model.occ.offset_curve(looptag, distance)
|
|
339
|
+
loop_tag = gmsh.model.occ.addCurveLoop([t for d,t in curve_tags])
|
|
340
|
+
new_curves.append(loop_tag)
|
|
341
|
+
surftag = gmsh.model.occ.addPlaneSurface(new_curves)
|
|
342
|
+
surfs.append(surftag)
|
|
343
|
+
surf = GeoSurface(surfs)
|
|
344
|
+
return surf
|
|
@@ -963,8 +963,18 @@ class PCB:
|
|
|
963
963
|
Returns:
|
|
964
964
|
float: the z-height
|
|
965
965
|
"""
|
|
966
|
+
if layer <= 0:
|
|
967
|
+
return self._zs[layer]
|
|
966
968
|
return self._zs[layer-1]
|
|
967
969
|
|
|
970
|
+
@property
|
|
971
|
+
def top(self) -> float:
|
|
972
|
+
return self._zs[-1]
|
|
973
|
+
|
|
974
|
+
@property
|
|
975
|
+
def bottom(self) -> float:
|
|
976
|
+
return self._zs[0]
|
|
977
|
+
|
|
968
978
|
def _get_z(self, element: RouteElement) -> float :
|
|
969
979
|
"""Return the z-height of a given Route Element
|
|
970
980
|
|
|
@@ -979,6 +989,31 @@ class PCB:
|
|
|
979
989
|
return path.z
|
|
980
990
|
raise RouteException('Requesting z-height of route element that is not contained in a path.')
|
|
981
991
|
|
|
992
|
+
def add_vias(self, *coordinates: tuple[float, float], radius: float,
|
|
993
|
+
z1: float | None = None,
|
|
994
|
+
z2: float | None = None,
|
|
995
|
+
segments: int = 6) -> None:
|
|
996
|
+
"""Add a series of vias provided by a list of coordinates.
|
|
997
|
+
|
|
998
|
+
Make sure to define the radius explicitly, otherwise the radius gets interpreted as a coordinate:
|
|
999
|
+
|
|
1000
|
+
>>> pcb.add_vias((x1,y1), (x1,y2), radius=1)
|
|
1001
|
+
|
|
1002
|
+
Args:
|
|
1003
|
+
*coordinates (tuple(float, float)): A series of coordinates
|
|
1004
|
+
radius (float): The radius
|
|
1005
|
+
z1 (float | None, optional): The bottom z-coordinate. Defaults to None.
|
|
1006
|
+
z2 (float | None, optional): The top z-coordinate. Defaults to None.
|
|
1007
|
+
segments (int, optional): The number of segmets for the via. Defaults to 6.
|
|
1008
|
+
"""
|
|
1009
|
+
if z1 is None:
|
|
1010
|
+
z1 = self.z(0)
|
|
1011
|
+
if z2 is None:
|
|
1012
|
+
z2 = self.z(-1)
|
|
1013
|
+
|
|
1014
|
+
for x,y in coordinates:
|
|
1015
|
+
self.vias.append(Via(x,y,z1,z2,radius,segments))
|
|
1016
|
+
|
|
982
1017
|
def load(self, name: str) -> StripLine:
|
|
983
1018
|
"""Acquire the x,y, coordinate associated with the label name.
|
|
984
1019
|
|
|
@@ -1060,7 +1095,7 @@ class PCB:
|
|
|
1060
1095
|
GeoSurface: _description_
|
|
1061
1096
|
"""
|
|
1062
1097
|
if width is None or height is None or origin is None:
|
|
1063
|
-
if self.width is None or self.length is None or self.origin:
|
|
1098
|
+
if self.width is None or self.length is None or self.origin is None:
|
|
1064
1099
|
raise RouteException('Cannot define a plane with no possible definition of its size.')
|
|
1065
1100
|
width = self.width
|
|
1066
1101
|
height = self.length
|
|
@@ -1075,6 +1110,7 @@ class PCB:
|
|
|
1075
1110
|
|
|
1076
1111
|
plane = Plate(origin, (width*self.unit, 0, 0), (0, height*self.unit, 0)) # type: ignore
|
|
1077
1112
|
plane = change_coordinate_system(plane, self.cs) # type: ignore
|
|
1113
|
+
plane.set_material(COPPER)
|
|
1078
1114
|
return plane # type: ignore
|
|
1079
1115
|
|
|
1080
1116
|
def generate_pcb(self,
|
|
@@ -1121,7 +1157,7 @@ class PCB:
|
|
|
1121
1157
|
"""Generate the Air Block object
|
|
1122
1158
|
|
|
1123
1159
|
This requires that the width, depth and origin are deterimed. This
|
|
1124
|
-
can either be done manually or via the .
|
|
1160
|
+
can either be done manually or via the .determine_bounds() method.
|
|
1125
1161
|
|
|
1126
1162
|
Returns:
|
|
1127
1163
|
GeoVolume: The PCB Block
|
|
@@ -1357,20 +1393,19 @@ class PCB:
|
|
|
1357
1393
|
poly = self._gen_poly(pcbpoly.xys, pcbpoly.z)
|
|
1358
1394
|
poly.material = pcbpoly.material
|
|
1359
1395
|
polys.append(poly)
|
|
1396
|
+
xs, ys = zip(*pcbpoly.xys)
|
|
1397
|
+
allx.extend(xs)
|
|
1398
|
+
ally.extend(ys)
|
|
1399
|
+
|
|
1360
1400
|
|
|
1361
1401
|
self.xs = allx
|
|
1362
1402
|
self.ys = ally
|
|
1363
1403
|
|
|
1364
1404
|
self.traces = polys
|
|
1405
|
+
|
|
1365
1406
|
if merge:
|
|
1366
1407
|
polys = unite(*polys)
|
|
1367
|
-
|
|
1368
|
-
# for p in polys:
|
|
1369
|
-
# tags.extend(p.tags)
|
|
1370
|
-
# if p.material != COPPER:
|
|
1371
|
-
# logger.warning(f'Merging a polygon with material {p.material} into a single polygon that will be COPPER.')
|
|
1372
|
-
# polys = GeoSurface(tags)
|
|
1373
|
-
# polys.material = COPPER
|
|
1408
|
+
|
|
1374
1409
|
return polys
|
|
1375
1410
|
|
|
1376
1411
|
############################################################
|
|
@@ -20,9 +20,8 @@ class PCBCalculator:
|
|
|
20
20
|
self.mat = material
|
|
21
21
|
self.unit = unit
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
def z0(self, Z0: float, layer: int = -1, ground_layer: int = 0):
|
|
23
|
+
def z0(self, Z0: float, layer: int = -1, ground_layer: int = 0, f0: float = 1e9):
|
|
25
24
|
th = abs(self.layers[layer] - self.layers[ground_layer])*self.unit
|
|
26
25
|
ws = np.geomspace(1e-6,1e-1,101)
|
|
27
|
-
Z0ms = microstrip_z0(ws, th, self.mat.er)
|
|
26
|
+
Z0ms = microstrip_z0(ws, th, self.mat.er.scalar(f0))
|
|
28
27
|
return np.interp(Z0, Z0ms[::-1], ws[::-1])/self.unit
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
from ..geometry import GeoVolume
|
|
19
19
|
from .shapes import Box, Alignment, Plate
|
|
20
|
-
from ..material import Material, AIR
|
|
20
|
+
from ..material import Material, AIR, CoordDependent
|
|
21
21
|
import numpy as np
|
|
22
22
|
from functools import partial
|
|
23
23
|
|
|
@@ -31,6 +31,15 @@ def _add_pml_layer(center: tuple[float, float, float],
|
|
|
31
31
|
exponent: float,
|
|
32
32
|
deltamax: float,
|
|
33
33
|
material: Material) -> GeoVolume:
|
|
34
|
+
|
|
35
|
+
if material.frequency_dependent:
|
|
36
|
+
raise ValueError('EMerge cannot handle frequency dependent material properties for PML layers at this point.')
|
|
37
|
+
if material.coordinate_dependent:
|
|
38
|
+
raise ValueError('Its not possible to define PML regions for materials that are coordinate dependent.')
|
|
39
|
+
|
|
40
|
+
mater = material.er.scalar(1e9)
|
|
41
|
+
matur = material.ur.scalar(1e9)
|
|
42
|
+
|
|
34
43
|
px, py, pz = center
|
|
35
44
|
W,D,H = dims
|
|
36
45
|
dx, dy, dz = direction
|
|
@@ -69,15 +78,16 @@ def _add_pml_layer(center: tuple[float, float, float],
|
|
|
69
78
|
|
|
70
79
|
def ermat(x, y, z):
|
|
71
80
|
ers = np.zeros((3,3,x.shape[0]), dtype=np.complex128)
|
|
72
|
-
ers[0,0,:] =
|
|
73
|
-
ers[1,1,:] =
|
|
74
|
-
ers[2,2,:] =
|
|
81
|
+
ers[0,0,:] = mater * syf(x,y,z)*szf(x,y,z)/sxf(x,y,z)
|
|
82
|
+
ers[1,1,:] = mater * szf(x,y,z)*sxf(x,y,z)/syf(x,y,z)
|
|
83
|
+
ers[2,2,:] = mater * sxf(x,y,z)*syf(x,y,z)/szf(x,y,z)
|
|
75
84
|
return ers
|
|
85
|
+
|
|
76
86
|
def urmat(x, y, z):
|
|
77
87
|
urs = np.zeros((3,3,x.shape[0]), dtype=np.complex128)
|
|
78
|
-
urs[0,0,:] =
|
|
79
|
-
urs[1,1,:] =
|
|
80
|
-
urs[2,2,:] =
|
|
88
|
+
urs[0,0,:] = matur * syf(x,y,z)*szf(x,y,z)/sxf(x,y,z)
|
|
89
|
+
urs[1,1,:] = matur * szf(x,y,z)*sxf(x,y,z)/syf(x,y,z)
|
|
90
|
+
urs[2,2,:] = matur * sxf(x,y,z)*syf(x,y,z)/szf(x,y,z)
|
|
81
91
|
return urs
|
|
82
92
|
|
|
83
93
|
pml_box = Box(*pml_block_size, new_center, alignment=Alignment.CENTER)
|
|
@@ -106,7 +116,9 @@ def _add_pml_layer(center: tuple[float, float, float],
|
|
|
106
116
|
plate = Plate(np.array([p0x-tW/2, p0y-tD/2, p0z-dz*thickness/2 + dz*(n+1)*thl]), ax1, ax2)
|
|
107
117
|
planes.append(plate)
|
|
108
118
|
|
|
109
|
-
|
|
119
|
+
erfunc = CoordDependent(max_value=mater, matrix=ermat)
|
|
120
|
+
urfunc = CoordDependent(max_value=matur, matrix=urmat)
|
|
121
|
+
pml_box.material = Material(er=erfunc, ur=urfunc,_neff=np.sqrt(mater*matur), color='#bbbbff', opacity=0.1)
|
|
110
122
|
pml_box.max_meshsize = thickness/N_mesh_layers
|
|
111
123
|
pml_box._embeddings = planes
|
|
112
124
|
|
|
@@ -121,9 +133,10 @@ def pmlbox(width: float,
|
|
|
121
133
|
material: Material = AIR,
|
|
122
134
|
thickness: float = 0.1,
|
|
123
135
|
Nlayers: int = 1,
|
|
124
|
-
N_mesh_layers: int =
|
|
136
|
+
N_mesh_layers: int = 5,
|
|
125
137
|
exponent: float = 1.5,
|
|
126
138
|
deltamax: float = 8.0,
|
|
139
|
+
sides: str = '',
|
|
127
140
|
top: bool = False,
|
|
128
141
|
bottom: bool = False,
|
|
129
142
|
left: bool = False,
|
|
@@ -145,10 +158,11 @@ def pmlbox(width: float,
|
|
|
145
158
|
alignment (Alignment, optional): Which point of the box is placed at the given coordinate. Defaults to Alignment.CORNER.
|
|
146
159
|
material (Material, optional): The material of the box. Defaults to AIR.
|
|
147
160
|
thickness (float, optional): The thickness of the PML Layer. Defaults to 0.1.
|
|
148
|
-
Nlayers (int, optional): The number of PML layers
|
|
149
|
-
N_mesh_layers (int, optional): The number of mesh layers. Sets the discretization size accordingly. Defaults to
|
|
161
|
+
Nlayers (int, optional): The number of geometrical PML layers. Defaults to 1.
|
|
162
|
+
N_mesh_layers (int, optional): The number of mesh layers. Sets the discretization size accordingly. Defaults to 5
|
|
150
163
|
exponent (float, optional): The PML gradient growth function. Defaults to 1.5.
|
|
151
164
|
deltamax (float, optional): A PML matching coefficient. Defaults to 8.0.
|
|
165
|
+
sides (str, optional): A string of pml sides as characters ([T]op, [B]ottom, [L]eft, [R]ight, [F]ront, b[A]ck)
|
|
152
166
|
top (bool, optional): Add a top PML layer. Defaults to True.
|
|
153
167
|
bottom (bool, optional): Add a bottom PML layer. Defaults to False.
|
|
154
168
|
left (bool, optional): Add a left PML layer. Defaults to False.
|
|
@@ -159,6 +173,16 @@ def pmlbox(width: float,
|
|
|
159
173
|
Returns:
|
|
160
174
|
list[GeoVolume]: A list of objects [main box, *pml boxes]
|
|
161
175
|
"""
|
|
176
|
+
|
|
177
|
+
sides = sides.lower()
|
|
178
|
+
|
|
179
|
+
top = "t" in sides or top
|
|
180
|
+
bottom = "b" in sides or bottom
|
|
181
|
+
left = "l" in sides or left
|
|
182
|
+
right = "r" in sides or right
|
|
183
|
+
front = "f" in sides or front
|
|
184
|
+
back = "a" in sides or back
|
|
185
|
+
|
|
162
186
|
px, py, pz = position
|
|
163
187
|
if alignment == Alignment.CORNER:
|
|
164
188
|
px = px + width / 2
|