emerge 0.6.9__tar.gz → 0.6.11__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.9 → emerge-0.6.11}/.bumpversion.toml +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/PKG-INFO +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/emerge/__init__.py +12 -3
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/bc.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/cs.py +36 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/polybased.py +58 -26
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/shapes.py +6 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geometry.py +17 -8
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/logsettings.py +13 -2
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/material.py +10 -1
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mesh3d.py +15 -4
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/microwave_3d.py +14 -10
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/microwave_bc.py +18 -20
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/matplotlib/mpldisplay.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/pyvista/display.py +15 -14
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/simple_plots.py +15 -3
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/simmodel.py +6 -5
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/solve_interfaces/cudss_interface.py +10 -4
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/solver.py +14 -4
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo10_sgh.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo11_lumped_element_filter.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo12_mode_alignment.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo13_helix_antenna.py +2 -2
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo14_boundary_selection.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo1_stepped_imp_filter.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo2_combline_filter.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo3_coupled_line_filter.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo4_patch_antenna.py +1 -1
- emerge-0.6.11/examples/demo5_revolve.py +15 -0
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo6_striplines_with_vias.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo7_periodic_cells.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo8_waveguide_bpf_synthesis.py +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/examples/demo9_dielectric_resonator.py +2 -2
- {emerge-0.6.9 → emerge-0.6.11}/pyproject.toml +1 -1
- {emerge-0.6.9 → emerge-0.6.11}/uv.lock +1 -1
- emerge-0.6.9/examples/demo5_revolve.py +0 -25
- {emerge-0.6.9 → emerge-0.6.11}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/.gitignore +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/.opt +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/.python-version +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/LICENSE +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/README.md +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/THIRD_PARTY_LICENSES.md +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/UMFPACK_Install_windows.md +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/UMFPACK_installer_windows.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/__main__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/_cache_check.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/const.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/coord.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/dataset.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/femdata.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/index_interp.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/ned2_interp.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/nedelec2.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/elements/nedleg2.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/horn.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/modeler.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/operations.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/pcb.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/pmlbox.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo/step.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/geo2d.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/howto.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mesher.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mth/common_functions.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mth/integrals.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mth/optimized.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/mth/pairing.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/periodic.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/assembler.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/generalized_eigen_hb.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/periodicbc.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/microwave_data.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/periodic.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/sc.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/simjob.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/sparam.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/display.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/plot.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/projects/__init__.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/projects/_gen_base.txt +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/projects/_load_base.txt +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/projects/generate_project.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/selection.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/simulation_data.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/solve_interfaces/pardiso_interface.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/_emerge/system.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/cli.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/ext.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/lib.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/plot.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/emerge/pyvista.py +0 -0
- {emerge-0.6.9 → emerge-0.6.11}/src/__init__.py +0 -0
|
@@ -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.11"
|
|
22
22
|
|
|
23
23
|
############################################################
|
|
24
24
|
# HANDLE ENVIRONMENT VARIABLES #
|
|
@@ -43,12 +43,13 @@ from loguru import logger
|
|
|
43
43
|
|
|
44
44
|
LOG_CONTROLLER.set_default()
|
|
45
45
|
logger.debug('Importing modules')
|
|
46
|
+
LOG_CONTROLLER._set_log_buffer()
|
|
46
47
|
|
|
47
48
|
from ._emerge.simmodel import Simulation
|
|
48
49
|
from ._emerge.material import Material, FreqCoordDependent, FreqDependent, CoordDependent
|
|
49
50
|
from ._emerge import bc
|
|
50
51
|
from ._emerge.solver import SolverBicgstab, SolverGMRES, SolveRoutine, ReverseCuthillMckee, Sorter, SolverPardiso, SolverUMFPACK, SolverSuperLU, EMSolver
|
|
51
|
-
from ._emerge.cs import CoordinateSystem, CS, GCS, Plane, Axis, XAX, YAX, ZAX, XYPLANE, XZPLANE, YZPLANE, YXPLANE, ZXPLANE, ZYPLANE
|
|
52
|
+
from ._emerge.cs import CoordinateSystem, CS, GCS, Plane, Axis, XAX, YAX, ZAX, XYPLANE, XZPLANE, YZPLANE, YXPLANE, ZXPLANE, ZYPLANE, cs
|
|
52
53
|
from ._emerge.coord import Line
|
|
53
54
|
from ._emerge import geo
|
|
54
55
|
from ._emerge.selection import Selection, FaceSelection, DomainSelection, EdgeSelection
|
|
@@ -62,4 +63,12 @@ from ._emerge.howto import _HowtoClass
|
|
|
62
63
|
|
|
63
64
|
howto = _HowtoClass()
|
|
64
65
|
|
|
65
|
-
logger.debug('Importing complete!')
|
|
66
|
+
logger.debug('Importing complete!')
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
############################################################
|
|
70
|
+
# CONSTANTS #
|
|
71
|
+
############################################################
|
|
72
|
+
|
|
73
|
+
CENTER = geo.Alignment.CENTER
|
|
74
|
+
CORNER = geo.Alignment.CORNER
|
|
@@ -530,3 +530,39 @@ CS = CoordinateSystem
|
|
|
530
530
|
# The global coordinate system
|
|
531
531
|
GCS = CoordinateSystem(XAX, YAX, ZAX, np.zeros(3), _is_global=True)
|
|
532
532
|
|
|
533
|
+
def cs(axes: str, origin: tuple[float, float, float] = (0.,0.,0.,)) -> CoordinateSystem:
|
|
534
|
+
"""Generate a coordinate system based on a simple string
|
|
535
|
+
The string must contain the letters x, X, y, Y, z and/or Z.
|
|
536
|
+
Small letters refer to positive axes and capitals to negative axes.
|
|
537
|
+
|
|
538
|
+
The string must be 3 characters long.
|
|
539
|
+
|
|
540
|
+
The first position indices which global axis gets assigned to the new local X-axis
|
|
541
|
+
The second position indicates the Y-axis
|
|
542
|
+
The third position indicates the Z-axis
|
|
543
|
+
|
|
544
|
+
Thus, rotating the global XYZ coordinate system 90 degrees around the Z axis would yield: yXz
|
|
545
|
+
|
|
546
|
+
Args:
|
|
547
|
+
axes (str): The axis description
|
|
548
|
+
origin (tuple[float, float, float], optional): The origin of the coordinate system. Defaults to (0.,0.,0.,).
|
|
549
|
+
|
|
550
|
+
Returns:
|
|
551
|
+
CoordinateSystem: The resultant coordinate system
|
|
552
|
+
"""
|
|
553
|
+
if len(axes) != 3:
|
|
554
|
+
raise ValueError('Axis object must be of length 3')
|
|
555
|
+
|
|
556
|
+
axlib = {
|
|
557
|
+
'x': Axis(np.array([1.0,0.,0.])),
|
|
558
|
+
'X': Axis(np.array([-1.0,0.,0.])),
|
|
559
|
+
'y': Axis(np.array([0.,1.0,0.])),
|
|
560
|
+
'Y': Axis(np.array([0.,-1.0,0.])),
|
|
561
|
+
'z': Axis(np.array([0.,0.,1.0])),
|
|
562
|
+
'Z': Axis(np.array([0.,0.,-1.0])),
|
|
563
|
+
}
|
|
564
|
+
ax_obj = [axlib[ax] for ax in axes]
|
|
565
|
+
|
|
566
|
+
return (ax_obj[0]*ax_obj[1]*ax_obj[2]).displace(*origin)
|
|
567
|
+
|
|
568
|
+
|
|
@@ -206,6 +206,7 @@ def orthonormalize(axis: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray
|
|
|
206
206
|
Zaxis = np.abs(Zaxis/np.linalg.norm(Zaxis))
|
|
207
207
|
return Xaxis, Yaxis, Zaxis
|
|
208
208
|
|
|
209
|
+
|
|
209
210
|
class GeoPrism(GeoVolume):
|
|
210
211
|
"""The GepPrism class generalizes the GeoVolume for extruded convex polygons.
|
|
211
212
|
Besides having a volumetric definitions, the class offers a .front_face
|
|
@@ -216,35 +217,65 @@ class GeoPrism(GeoVolume):
|
|
|
216
217
|
"""
|
|
217
218
|
def __init__(self,
|
|
218
219
|
volume_tag: int,
|
|
219
|
-
front_tag: int,
|
|
220
|
-
side_tags: list[int],
|
|
220
|
+
front_tag: int | None = None,
|
|
221
|
+
side_tags: list[int] | None = None,
|
|
222
|
+
_axis: Axis | None = None):
|
|
221
223
|
super().__init__(volume_tag)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
if front_tag is not None and side_tags is not None:
|
|
228
|
+
self.front_tag: int = front_tag
|
|
229
|
+
self.back_tag: int = None
|
|
224
230
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
n1 = gmsh.model.get_normal(self.front_tag, (0,0))
|
|
228
|
-
self._add_face_pointer('back', o1, n1)
|
|
231
|
+
gmsh.model.occ.synchronize()
|
|
232
|
+
self._add_face_pointer('back', tag=self.front_tag)
|
|
229
233
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
self.back_tag = tag
|
|
239
|
-
break
|
|
234
|
+
tags = gmsh.model.get_boundary(self.dimtags, oriented=False)
|
|
235
|
+
|
|
236
|
+
for dim, tag in tags:
|
|
237
|
+
if (dim,tag) in side_tags:
|
|
238
|
+
continue
|
|
239
|
+
self._add_face_pointer('front',tag=tag)
|
|
240
|
+
self.back_tag = tag
|
|
241
|
+
break
|
|
240
242
|
|
|
241
|
-
|
|
243
|
+
self.side_tags: list[int] = [dt[1] for dt in tags if dt[1]!=self.front_tag and dt[1]!=self.back_tag]
|
|
242
244
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
245
|
+
for tag in self.side_tags:
|
|
246
|
+
|
|
247
|
+
self._add_face_pointer(f'side{tag}', tag=tag)
|
|
248
|
+
self.back_tag = tag
|
|
249
|
+
|
|
250
|
+
elif _axis is not None:
|
|
251
|
+
_axis = _parse_axis(_axis)
|
|
252
|
+
gmsh.model.occ.synchronize()
|
|
253
|
+
tags = gmsh.model.get_boundary(self.dimtags, oriented=False)
|
|
254
|
+
faces = []
|
|
255
|
+
for dim, tag in tags:
|
|
256
|
+
o1 = np.array(gmsh.model.occ.get_center_of_mass(2, tag))
|
|
257
|
+
n1 = np.array(gmsh.model.get_normal(tag, (0,0)))
|
|
258
|
+
if abs(np.sum(n1*_axis.np)) > 0.99:
|
|
259
|
+
dax = sum(o1 * _axis.np)
|
|
260
|
+
faces.append((o1, n1, dax, tag))
|
|
261
|
+
|
|
262
|
+
faces = sorted(faces, key=lambda x: x[2])
|
|
263
|
+
ftags = []
|
|
264
|
+
if len(faces) >= 2:
|
|
265
|
+
ftags.append(faces[0][3])
|
|
266
|
+
ftags.append(faces[-1][3])
|
|
267
|
+
self._add_face_pointer('front',faces[0][0], faces[0][1])
|
|
268
|
+
self._add_face_pointer('back', faces[-1][0], faces[-1][1])
|
|
269
|
+
elif len(faces)==1:
|
|
270
|
+
ftags.append(faces[0][3])
|
|
271
|
+
self._add_face_pointer('cap',faces[0][0], faces[0][1])
|
|
272
|
+
|
|
273
|
+
ictr = 1
|
|
274
|
+
for dim, tag in tags:
|
|
275
|
+
if tag in ftags:
|
|
276
|
+
continue
|
|
277
|
+
self._add_face_pointer(f'side{ictr}', tag=tag)
|
|
278
|
+
ictr += 1
|
|
248
279
|
|
|
249
280
|
def outside(self, *exclude: Literal['front','back']) -> FaceSelection:
|
|
250
281
|
"""Select all outside faces except for the once specified by outside
|
|
@@ -462,9 +493,10 @@ class XYPolygon:
|
|
|
462
493
|
ax, ay, az = axis
|
|
463
494
|
|
|
464
495
|
volume = gmsh.model.occ.revolve(poly_fin.dimtags, x,y,z, ax, ay, az, angle*np.pi/180)
|
|
496
|
+
|
|
465
497
|
tags = [t for d,t in volume if d==3]
|
|
466
|
-
|
|
467
|
-
return GeoPrism(tags,
|
|
498
|
+
poly_fin.remove()
|
|
499
|
+
return GeoPrism(tags, _axis=axis)
|
|
468
500
|
|
|
469
501
|
@staticmethod
|
|
470
502
|
def circle(radius: float,
|
|
@@ -27,6 +27,12 @@ from typing import Literal
|
|
|
27
27
|
from functools import reduce
|
|
28
28
|
|
|
29
29
|
class Alignment(Enum):
|
|
30
|
+
"""The alignment Enum describes if a box, cube or rectangle location
|
|
31
|
+
is specified for the center or the bottom - front - left corner (min X Y and Z)
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
Enum (_type_): _description_
|
|
35
|
+
"""
|
|
30
36
|
CENTER = 1
|
|
31
37
|
CORNER = 2
|
|
32
38
|
|
|
@@ -250,7 +250,7 @@ class GeoObject:
|
|
|
250
250
|
return self.material._metal
|
|
251
251
|
|
|
252
252
|
@property
|
|
253
|
-
def
|
|
253
|
+
def selection(self) -> Selection:
|
|
254
254
|
'''Returns a corresponding Face/Domain or Edge Selection object'''
|
|
255
255
|
if self.dim==1:
|
|
256
256
|
return EdgeSelection(self.tags)
|
|
@@ -285,9 +285,18 @@ class GeoObject:
|
|
|
285
285
|
|
|
286
286
|
def _add_face_pointer(self,
|
|
287
287
|
name: str,
|
|
288
|
-
origin: np.ndarray,
|
|
289
|
-
normal: np.ndarray
|
|
290
|
-
|
|
288
|
+
origin: np.ndarray | None = None,
|
|
289
|
+
normal: np.ndarray | None = None,
|
|
290
|
+
tag: int | None = None):
|
|
291
|
+
if tag is not None:
|
|
292
|
+
o = gmsh.model.occ.get_center_of_mass(2, tag)
|
|
293
|
+
n = gmsh.model.get_normal(tag, (0,0))
|
|
294
|
+
self._face_pointers[name] = _FacePointer(o, n)
|
|
295
|
+
return
|
|
296
|
+
if origin is not None and normal is not None:
|
|
297
|
+
self._face_pointers[name] = _FacePointer(origin, normal)
|
|
298
|
+
return
|
|
299
|
+
raise ValueError('Eitehr a tag or an origin + normal must be provided!')
|
|
291
300
|
|
|
292
301
|
def make_copy(self) -> GeoObject:
|
|
293
302
|
new_dimtags = gmsh.model.occ.copy(self.dimtags)
|
|
@@ -508,14 +517,14 @@ class GeoVolume(GeoObject):
|
|
|
508
517
|
self.tags = [tag,]
|
|
509
518
|
|
|
510
519
|
@property
|
|
511
|
-
def
|
|
520
|
+
def selection(self) -> DomainSelection:
|
|
512
521
|
return DomainSelection(self.tags)
|
|
513
522
|
|
|
514
523
|
class GeoPoint(GeoObject):
|
|
515
524
|
dim = 0
|
|
516
525
|
|
|
517
526
|
@property
|
|
518
|
-
def
|
|
527
|
+
def selection(self) -> PointSelection:
|
|
519
528
|
return PointSelection(self.tags)
|
|
520
529
|
|
|
521
530
|
def __init__(self, tag: int | list[int]):
|
|
@@ -531,7 +540,7 @@ class GeoEdge(GeoObject):
|
|
|
531
540
|
dim = 1
|
|
532
541
|
|
|
533
542
|
@property
|
|
534
|
-
def
|
|
543
|
+
def selection(self) -> EdgeSelection:
|
|
535
544
|
return EdgeSelection(self.tags)
|
|
536
545
|
|
|
537
546
|
def __init__(self, tag: int | list[int]):
|
|
@@ -549,7 +558,7 @@ class GeoSurface(GeoObject):
|
|
|
549
558
|
dim = 2
|
|
550
559
|
|
|
551
560
|
@property
|
|
552
|
-
def
|
|
561
|
+
def selection(self) -> FaceSelection:
|
|
553
562
|
return FaceSelection(self.tags)
|
|
554
563
|
|
|
555
564
|
def __init__(self, tag: int | list[int]):
|
|
@@ -18,11 +18,14 @@
|
|
|
18
18
|
from loguru import logger
|
|
19
19
|
import sys
|
|
20
20
|
from typing import Literal
|
|
21
|
-
from enum import Enum
|
|
22
21
|
from pathlib import Path
|
|
23
22
|
import os
|
|
24
|
-
import
|
|
23
|
+
from collections import deque
|
|
25
24
|
|
|
25
|
+
_LOG_BUFFER = deque()
|
|
26
|
+
|
|
27
|
+
def _log_sink(message):
|
|
28
|
+
_LOG_BUFFER.append(message)
|
|
26
29
|
############################################################
|
|
27
30
|
# FORMATS #
|
|
28
31
|
############################################################
|
|
@@ -82,6 +85,14 @@ class LogController:
|
|
|
82
85
|
format=FORMAT_DICT.get(loglevel, INFO_FORMAT))
|
|
83
86
|
self.std_handlers.append(handle_id)
|
|
84
87
|
|
|
88
|
+
def _set_log_buffer(self):
|
|
89
|
+
logger.add(_log_sink)
|
|
90
|
+
|
|
91
|
+
def _flush_log_buffer(self):
|
|
92
|
+
for msg in list(_LOG_BUFFER):
|
|
93
|
+
logger.opt(depth=6).log(msg.record["level"].name, msg.record["message"])
|
|
94
|
+
_LOG_BUFFER.clear()
|
|
95
|
+
|
|
85
96
|
def set_std_loglevel(self, loglevel: str):
|
|
86
97
|
handler = {"sink": sys.stdout,
|
|
87
98
|
"level": loglevel,
|
|
@@ -310,6 +310,8 @@ class Material:
|
|
|
310
310
|
|
|
311
311
|
self.color: str = color
|
|
312
312
|
self.opacity: float = opacity
|
|
313
|
+
self._hash_key: int = -1
|
|
314
|
+
|
|
313
315
|
if _neff is None:
|
|
314
316
|
self._neff: Callable = lambda f: np.sqrt(self.ur._fmax(f)*self.er._fmax(f))
|
|
315
317
|
else:
|
|
@@ -318,8 +320,14 @@ class Material:
|
|
|
318
320
|
self._color_rgb = tuple(int(hex_str[i:i+2], 16)/255.0 for i in (0, 2, 4))
|
|
319
321
|
self._metal: bool = _metal
|
|
320
322
|
|
|
323
|
+
def __hash__(self):
|
|
324
|
+
return self._hash_key
|
|
325
|
+
|
|
321
326
|
def __str__(self) -> str:
|
|
322
|
-
return f'Material({self.name})'
|
|
327
|
+
return f'Material({self.name}, {self._hash_key})'
|
|
328
|
+
|
|
329
|
+
def __repr__(self):
|
|
330
|
+
return f'Material({self.name}, {self._hash_key})'
|
|
323
331
|
|
|
324
332
|
def initialize(self, xs: np.ndarray, ys: np.ndarray, zs: np.ndarray, ids: np.ndarray):
|
|
325
333
|
"""Initializes the Material properties to be evaluated at xyz-coordinates for
|
|
@@ -343,6 +351,7 @@ class Material:
|
|
|
343
351
|
self.ur.reset()
|
|
344
352
|
self.tand.reset()
|
|
345
353
|
self.cond.reset()
|
|
354
|
+
self._hash_key = -1
|
|
346
355
|
|
|
347
356
|
@property
|
|
348
357
|
def frequency_dependent(self) -> bool:
|
|
@@ -544,22 +544,33 @@ class Mesh3D(Mesh):
|
|
|
544
544
|
#arry = np.zeros((3,3,self.n_tets,), dtype=np.complex128)
|
|
545
545
|
for vol in volumes:
|
|
546
546
|
vol.material.reset()
|
|
547
|
-
|
|
547
|
+
|
|
548
548
|
materials = []
|
|
549
|
+
i = 0
|
|
550
|
+
for vol in volumes:
|
|
551
|
+
if vol.material not in materials:
|
|
552
|
+
materials.append(vol.material)
|
|
553
|
+
vol.material._hash_key = i
|
|
554
|
+
i += 1
|
|
549
555
|
|
|
550
556
|
xs = self.centers[0,:]
|
|
551
557
|
ys = self.centers[1,:]
|
|
552
558
|
zs = self.centers[2,:]
|
|
553
559
|
|
|
560
|
+
matassign = -1*np.ones((self.n_tets,), dtype=np.int64)
|
|
561
|
+
|
|
554
562
|
for volume in sorted(volumes, key=lambda x: x._priority):
|
|
555
563
|
|
|
556
564
|
for dimtag in volume.dimtags:
|
|
557
565
|
etype, etag_list, ntags = gmsh.model.mesh.get_elements(*dimtag)
|
|
558
566
|
for etags in etag_list:
|
|
559
567
|
tet_ids = np.array([self.tet_t2i[t] for t in etags])
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
568
|
+
matassign[tet_ids] = volume.material._hash_key
|
|
569
|
+
|
|
570
|
+
for mat in materials:
|
|
571
|
+
ids = np.argwhere(matassign==mat._hash_key).flatten()
|
|
572
|
+
mat.initialize(xs[ids], ys[ids], zs[ids], ids)
|
|
573
|
+
|
|
563
574
|
|
|
564
575
|
return materials
|
|
565
576
|
|
|
@@ -53,7 +53,7 @@ def run_job_multi(job: SimJob) -> SimJob:
|
|
|
53
53
|
Returns:
|
|
54
54
|
SimJob: The solved SimJob
|
|
55
55
|
"""
|
|
56
|
-
routine = DEFAULT_ROUTINE.
|
|
56
|
+
routine = DEFAULT_ROUTINE._configure_routine('MP')
|
|
57
57
|
for A, b, ids, reuse, aux in job.iter_Ab():
|
|
58
58
|
solution, report = routine.solve(A, b, ids, reuse, id=job.id)
|
|
59
59
|
report.add(**aux)
|
|
@@ -311,22 +311,25 @@ class Microwave3D:
|
|
|
311
311
|
|
|
312
312
|
if points.size==0:
|
|
313
313
|
raise SimulationError(f'The lumped port {port} has no nodes associated with it')
|
|
314
|
+
|
|
314
315
|
xs = self.mesh.nodes[0,points]
|
|
315
316
|
ys = self.mesh.nodes[1,points]
|
|
316
317
|
zs = self.mesh.nodes[2,points]
|
|
317
318
|
|
|
318
319
|
dotprod = xs*field_axis[0] + ys*field_axis[1] + zs*field_axis[2]
|
|
319
320
|
|
|
320
|
-
start_id =
|
|
321
|
+
start_id = np.argwhere(dotprod == np.min(dotprod)).flatten()
|
|
322
|
+
|
|
323
|
+
xs = xs[start_id]
|
|
324
|
+
ys = ys[start_id]
|
|
325
|
+
zs = zs[start_id]
|
|
321
326
|
|
|
322
|
-
start = _pick_central(self.mesh.nodes[:,start_id.flatten()])
|
|
323
|
-
logger.info(f'Starting node = {_dimstring(start)}')
|
|
324
|
-
end = start + port.Vdirection.np*port.height
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
port.vintline = Line.from_points(start, end, 21)
|
|
328
327
|
|
|
329
|
-
|
|
328
|
+
for x,y,z in zip(xs, ys, zs):
|
|
329
|
+
start = np.array([x,y,z])
|
|
330
|
+
end = start + port.Vdirection.np*port.height
|
|
331
|
+
port.vintline.append(Line.from_points(start, end, 21))
|
|
332
|
+
logger.trace(f'Port[{port.port_number}] integration line {start} -> {end}.')
|
|
330
333
|
|
|
331
334
|
port.v_integration = True
|
|
332
335
|
|
|
@@ -1003,7 +1006,8 @@ class Microwave3D:
|
|
|
1003
1006
|
if bc.Z0 is None:
|
|
1004
1007
|
raise SimulationError('Trying to compute the impedance of a boundary condition with no characteristic impedance.')
|
|
1005
1008
|
|
|
1006
|
-
|
|
1009
|
+
Voltages = [line.line_integral(fieldfunction) for line in bc.vintline]
|
|
1010
|
+
V = sum(Voltages)/len(Voltages)
|
|
1007
1011
|
|
|
1008
1012
|
if bc.active:
|
|
1009
1013
|
if bc.voltage is None:
|
|
@@ -306,11 +306,17 @@ class AbsorbingBoundary(RobinBC):
|
|
|
306
306
|
Returns:
|
|
307
307
|
complex: The γ-constant
|
|
308
308
|
"""
|
|
309
|
-
|
|
309
|
+
if self.order == 2:
|
|
310
|
+
|
|
311
|
+
p0 = 1.06103
|
|
312
|
+
p2 = -0.84883
|
|
313
|
+
ky = k0*0.5
|
|
314
|
+
return 1j*k0*p0 - 1j*p2*ky**2/k0
|
|
315
|
+
else:
|
|
316
|
+
Factor = 1
|
|
317
|
+
return 1j*self.get_beta(k0)*Factor
|
|
310
318
|
|
|
311
|
-
|
|
312
|
-
return np.zeros((3, len(x_local)), dtype=np.complex128)
|
|
313
|
-
|
|
319
|
+
|
|
314
320
|
@dataclass
|
|
315
321
|
class PortMode:
|
|
316
322
|
modefield: np.ndarray
|
|
@@ -825,10 +831,10 @@ class LumpedPort(PortBC):
|
|
|
825
831
|
self.Vdirection: Axis = direction # type: ignore
|
|
826
832
|
self.type = 'TEM'
|
|
827
833
|
|
|
828
|
-
logger.info('Constructing coordinate system from normal port')
|
|
829
|
-
self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
|
|
830
|
-
|
|
831
|
-
self.vintline: Line
|
|
834
|
+
# logger.info('Constructing coordinate system from normal port')
|
|
835
|
+
# self.cs = Axis(self.selection.normal).construct_cs() # type: ignore
|
|
836
|
+
self.cs = GCS
|
|
837
|
+
self.vintline: list[Line] = []
|
|
832
838
|
self.v_integration = True
|
|
833
839
|
|
|
834
840
|
@property
|
|
@@ -881,14 +887,7 @@ class LumpedPort(PortBC):
|
|
|
881
887
|
k0: float,
|
|
882
888
|
which: Literal['E','H'] = 'E') -> np.ndarray:
|
|
883
889
|
''' Compute the port mode E-field in local coordinates (XY) + Z out of plane.'''
|
|
884
|
-
|
|
885
|
-
px, py, pz = self.cs.in_local_basis(*self.Vdirection.np)
|
|
886
|
-
|
|
887
|
-
Ex = px*np.ones_like(x_local)
|
|
888
|
-
Ey = py*np.ones_like(x_local)
|
|
889
|
-
Ez = pz*np.ones_like(x_local)
|
|
890
|
-
Exyz = np.array([Ex, Ey, Ez])
|
|
891
|
-
return Exyz
|
|
890
|
+
raise RuntimeError('This function should never be called in this context.')
|
|
892
891
|
|
|
893
892
|
def port_mode_3d_global(self,
|
|
894
893
|
x_global: np.ndarray,
|
|
@@ -911,10 +910,9 @@ class LumpedPort(PortBC):
|
|
|
911
910
|
Returns:
|
|
912
911
|
np.ndarray: The E-field in (3,N) indexing.
|
|
913
912
|
"""
|
|
914
|
-
|
|
915
|
-
Ex, Ey, Ez = self.
|
|
916
|
-
|
|
917
|
-
return np.array([Exg, Eyg, Ezg])
|
|
913
|
+
ON = np.ones_like(x_global)
|
|
914
|
+
Ex, Ey, Ez = self.Vdirection.np
|
|
915
|
+
return np.array([Ex*ON, Ey*ON, Ez*ON])
|
|
918
916
|
|
|
919
917
|
|
|
920
918
|
class LumpedElement(RobinBC):
|
|
@@ -186,7 +186,7 @@ def _norm(x, y, z):
|
|
|
186
186
|
|
|
187
187
|
def _select(obj: GeoObject | Selection) -> Selection:
|
|
188
188
|
if isinstance(obj, GeoObject):
|
|
189
|
-
return obj.
|
|
189
|
+
return obj.selection
|
|
190
190
|
return obj
|
|
191
191
|
|
|
192
192
|
def _merge(lst: Iterable[GeoObject | Selection]) -> Selection:
|
|
@@ -523,18 +523,19 @@ class PVDisplay(BaseDisplay):
|
|
|
523
523
|
d = _min_distance(xf, yf, zf)
|
|
524
524
|
|
|
525
525
|
if port.vintline is not None:
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
526
|
+
for line in port.vintline:
|
|
527
|
+
xs, ys, zs = line.cpoint
|
|
528
|
+
p_line = pv.Line(
|
|
529
|
+
pointa=(xs[0], ys[0], zs[0]),
|
|
530
|
+
pointb=(xs[-1], ys[-1], zs[-1]),
|
|
531
|
+
)
|
|
532
|
+
self._plot.add_mesh(
|
|
533
|
+
p_line,
|
|
534
|
+
color='red',
|
|
535
|
+
pickable=False,
|
|
536
|
+
line_width=3.0,
|
|
537
|
+
)
|
|
538
|
+
|
|
538
539
|
if k0 is None:
|
|
539
540
|
if isinstance(port, ModalPort):
|
|
540
541
|
k0 = port.get_mode(0).k0
|
|
@@ -596,7 +597,7 @@ class PVDisplay(BaseDisplay):
|
|
|
596
597
|
|
|
597
598
|
|
|
598
599
|
if scale=='log':
|
|
599
|
-
T = lambda x: np.log10(np.abs(x))
|
|
600
|
+
T = lambda x: np.log10(np.abs(x+1e-12))
|
|
600
601
|
elif scale=='symlog':
|
|
601
602
|
T = lambda x: np.sign(x) * np.log10(1 + np.abs(x*np.log(10)))
|
|
602
603
|
else:
|
|
@@ -422,9 +422,14 @@ def plot_sp(f: np.ndarray | list[np.ndarray], S: list[np.ndarray] | np.ndarray,
|
|
|
422
422
|
show_plot: bool = True,
|
|
423
423
|
figdata: tuple | None = None) -> tuple[plt.Figure, plt.Axes, plt.Axes]:
|
|
424
424
|
"""Plot S-parameters in dB and phase
|
|
425
|
+
|
|
426
|
+
One may provide:
|
|
427
|
+
- A single frequency with a single S-parameter
|
|
428
|
+
- A single frequency with a list of S-parameters
|
|
429
|
+
- A list of frequencies with a list of S-parameters
|
|
425
430
|
|
|
426
431
|
Args:
|
|
427
|
-
f (np.ndarray): Frequency vector
|
|
432
|
+
f (np.ndarray | list[np.ndarray]): Frequency vector or list of frequencies
|
|
428
433
|
S (list[np.ndarray] | np.ndarray): S-parameters to plot (list or single array)
|
|
429
434
|
dblim (list, optional): Decibel y-axis limit. Defaults to [-80, 5].
|
|
430
435
|
xunit (str, optional): Frequency unit. Defaults to "GHz".
|
|
@@ -535,7 +540,7 @@ def plot_sp(f: np.ndarray | list[np.ndarray], S: list[np.ndarray] | np.ndarray,
|
|
|
535
540
|
|
|
536
541
|
|
|
537
542
|
def plot_ff(
|
|
538
|
-
theta: np.ndarray,
|
|
543
|
+
theta: np.ndarray | list[np.ndarray],
|
|
539
544
|
E: Union[np.ndarray, Sequence[np.ndarray]],
|
|
540
545
|
grid: bool = True,
|
|
541
546
|
dB: bool = False,
|
|
@@ -554,7 +559,7 @@ def plot_ff(
|
|
|
554
559
|
|
|
555
560
|
Parameters
|
|
556
561
|
----------
|
|
557
|
-
theta : np.ndarray
|
|
562
|
+
theta : np.ndarray | list[np.ndarray]
|
|
558
563
|
Angle array (radians).
|
|
559
564
|
E : np.ndarray or sequence of np.ndarray
|
|
560
565
|
Complex E-field samples; magnitude will be plotted.
|
|
@@ -575,6 +580,12 @@ def plot_ff(
|
|
|
575
580
|
E_list = [E]
|
|
576
581
|
else:
|
|
577
582
|
E_list = list(E)
|
|
583
|
+
|
|
584
|
+
if not isinstance(theta, list):
|
|
585
|
+
thetas = [theta for _ in E_list]
|
|
586
|
+
else:
|
|
587
|
+
thetas = theta
|
|
588
|
+
|
|
578
589
|
n_series = len(E_list)
|
|
579
590
|
|
|
580
591
|
# Style broadcasting
|
|
@@ -591,6 +602,7 @@ def plot_ff(
|
|
|
591
602
|
|
|
592
603
|
fig, ax = plt.subplots()
|
|
593
604
|
for i, Ei in enumerate(E_list):
|
|
605
|
+
theta = thetas[i]
|
|
594
606
|
mag = np.abs(Ei)
|
|
595
607
|
if dB:
|
|
596
608
|
mag = 20*np.log10(mag)
|