emerge 0.4.10__tar.gz → 0.5.0__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.4.10 → emerge-0.5.0}/.bumpversion.toml +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/PKG-INFO +2 -3
- {emerge-0.4.10 → emerge-0.5.0}/emerge/__init__.py +15 -8
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/bc.py +41 -2
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/__init__.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/pcb.py +49 -11
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/howto.py +2 -2
- emerge-0.5.0/emerge/_emerge/logsettings.py +83 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/mesh3d.py +30 -12
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/mth/common_functions.py +28 -1
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/mth/integrals.py +25 -3
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/mth/optimized.py +126 -33
- emerge-0.5.0/emerge/_emerge/mth/pairing.py +97 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/periodic.py +22 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/assembly/assembler.py +129 -155
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/assembly/curlcurl.py +35 -3
- emerge-0.5.0/emerge/_emerge/physics/microwave/assembly/periodicbc.py +130 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/microwave_3d.py +3 -3
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/microwave_bc.py +5 -4
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/microwave_data.py +2 -2
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/projects/_gen_base.txt +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/simmodel.py +137 -126
- emerge-0.5.0/emerge/_emerge/solve_interfaces/pardiso_interface.py +468 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/solver.py +104 -55
- emerge-0.5.0/emerge/lib.py +292 -0
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo10_sgh.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo11_lumped_element_filter.py +3 -3
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo1_stepped_imp_filter.py +3 -3
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo2_combline_filter.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo3_coupled_line_filter.py +2 -2
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo3_patch_antenna.py +2 -2
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo4_boundary_selection.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo6_striplines_with_vias.py +2 -2
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo7_periodic_cells.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo8_waveguide_bpf_synthesis.py +2 -2
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo9_dielectric_resonator.py +1 -1
- {emerge-0.4.10 → emerge-0.5.0}/pyproject.toml +2 -4
- {emerge-0.4.10 → emerge-0.5.0}/uv.lock +22 -38
- emerge-0.4.10/emerge/_emerge/logsettings.py +0 -5
- emerge-0.4.10/emerge/lib.py +0 -57
- {emerge-0.4.10 → emerge-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/.gitignore +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/.python-version +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/LICENSE +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/README.md +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/UMFPACK_Install_windows.md +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/__main__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/coord.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/cs.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/dataset.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/femdata.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/index_interp.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/legrange2.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/ned2_interp.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/nedelec2.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/elements/nedleg2.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/horn.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/modeler.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/operations.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/pcb_tools/calculator.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/pcb_tools/macro.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/pmlbox.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/polybased.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/shapes.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo/step.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geo2d.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/geometry.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/material.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/mesher.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/adaptive_freq.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/assembly/robinbc.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/periodic.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/port_functions.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/sc.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/simjob.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/sparam.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/physics/microwave/touchstone.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/display.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/grapher.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/matplotlib/mpldisplay.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/pyvista/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/pyvista/display.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/pyvista/display_settings.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot/simple_plots.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/plot.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/projects/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/projects/_load_base.txt +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/projects/generate_project.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/selection.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/simulation_data.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/_emerge/system.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/cli.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/plot.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/emerge/pyvista.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/examples/demo5_revolve.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/src/__init__.py +0 -0
- {emerge-0.4.10 → emerge-0.5.0}/src/_img/logo.jpeg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emerge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: An open source EM FEM simulator in Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/FennisRobert/EMerge
|
|
6
6
|
Project-URL: Issues, https://github.com/FennisRobert/EMerge/issues
|
|
@@ -10,13 +10,12 @@ Requires-Dist: gmsh>=4.13.1
|
|
|
10
10
|
Requires-Dist: joblib>=1.5.1
|
|
11
11
|
Requires-Dist: loguru>=0.7.3
|
|
12
12
|
Requires-Dist: matplotlib>=3.8.0
|
|
13
|
+
Requires-Dist: mkl!=2024.0; platform_machine == 'x86_64' or platform_machine == 'AMD64'
|
|
13
14
|
Requires-Dist: numba-progress>=1.1.3
|
|
14
15
|
Requires-Dist: numba>=0.57.0
|
|
15
16
|
Requires-Dist: numpy<2.3,>=1.24
|
|
16
17
|
Requires-Dist: pyvista>=0.45.2
|
|
17
18
|
Requires-Dist: scipy>=1.14.0
|
|
18
|
-
Provides-Extra: pypardiso
|
|
19
|
-
Requires-Dist: pypardiso; (platform_machine == 'x86_64' or platform_machine == 'AMD64') and extra == 'pypardiso'
|
|
20
19
|
Provides-Extra: umfpack
|
|
21
20
|
Requires-Dist: scikit-umfpack; (sys_platform != 'win32') and extra == 'umfpack'
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
@@ -18,22 +18,29 @@ along with this program; if not, see
|
|
|
18
18
|
"""
|
|
19
19
|
import os
|
|
20
20
|
|
|
21
|
-
NTHREADS = "1"
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
############################################################
|
|
23
|
+
# HANDLE ENVIRONMENT VARIABLES #
|
|
24
|
+
############################################################
|
|
25
|
+
|
|
26
|
+
NTHREADS = "1"
|
|
27
|
+
os.environ["EMERGE_STD_LOGLEVEL"] = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
|
|
28
|
+
os.environ["EMERGE_FILE_LOGLEVEL"] = os.getenv("EMERGE_FILE_LOGLEVEL", default="DEBUG")
|
|
29
|
+
os.environ["OMP_NUM_THREADS"] = os.getenv("OMP_NUM_THREADS", default="4")
|
|
30
|
+
os.environ["MKL_NUM_THREADS"] = os.getenv("MKL_NUM_THREADS", default="4")
|
|
25
31
|
os.environ["OPENBLAS_NUM_THREADS"] = NTHREADS
|
|
26
32
|
os.environ["VECLIB_MAXIMUM_THREADS"] = NTHREADS
|
|
27
33
|
os.environ["NUMEXPR_NUM_THREADS"] = NTHREADS
|
|
28
34
|
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
############################################################
|
|
37
|
+
# IMPORT MODULES #
|
|
38
|
+
############################################################
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
logger
|
|
40
|
+
from ._emerge.logsettings import LOG_CONTROLLER
|
|
41
|
+
from loguru import logger
|
|
36
42
|
|
|
43
|
+
LOG_CONTROLLER.set_default()
|
|
37
44
|
logger.debug('Importing modules')
|
|
38
45
|
|
|
39
46
|
from ._emerge.simmodel import Simulation3D
|
|
@@ -31,8 +31,11 @@ class BCDimension(Enum):
|
|
|
31
31
|
|
|
32
32
|
def _unique(input: list[int]) -> list:
|
|
33
33
|
""" Returns a sorted list of all unique integers/floats in a list."""
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
return sorted(list(set(input)))
|
|
35
|
+
|
|
36
|
+
############################################################
|
|
37
|
+
# BASE BOUNDARY CONDITION CLASS #
|
|
38
|
+
############################################################
|
|
36
39
|
|
|
37
40
|
class BoundaryCondition:
|
|
38
41
|
"""A generalized class for all boundary condition objects.
|
|
@@ -141,13 +144,47 @@ class BoundaryConditionSet:
|
|
|
141
144
|
return obj
|
|
142
145
|
return constr
|
|
143
146
|
|
|
147
|
+
def assigned(self, dim: int = 2) -> list[int]:
|
|
148
|
+
"""Returns all boundary tags that have a boundary condition assigned to them
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
dim (int, optional): The dimension. Defaults to 2.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
list[int]: The list of tags
|
|
155
|
+
"""
|
|
156
|
+
tags = []
|
|
157
|
+
for bc in self.boundary_conditions:
|
|
158
|
+
if bc.dim != dim:
|
|
159
|
+
continue
|
|
160
|
+
tags.extend(bc.tags)
|
|
161
|
+
return tags
|
|
162
|
+
|
|
144
163
|
def count(self, bctype: type) -> int:
|
|
164
|
+
"""Returns the number of a certain boundary condition type
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
bctype (type): The boundary condtion type
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
int: The number of occurances
|
|
171
|
+
"""
|
|
145
172
|
return len(self.oftype(bctype))
|
|
146
173
|
|
|
147
174
|
def oftype(self, bctype: type) -> list[BoundaryCondition]:
|
|
175
|
+
"""Returns a list of all boundary conditions of a certain type.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
bctype (type): The boundar condition type
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
list[BoundaryCondition]: The list of boundary conditions
|
|
182
|
+
"""
|
|
148
183
|
return [item for item in self. boundary_conditions if isinstance(item, bctype)]
|
|
149
184
|
|
|
150
185
|
def reset(self) -> None:
|
|
186
|
+
"""Resets the boundary conditions that are defined
|
|
187
|
+
"""
|
|
151
188
|
self.boundary_conditions = []
|
|
152
189
|
|
|
153
190
|
def assign(self,
|
|
@@ -158,6 +195,8 @@ class BoundaryConditionSet:
|
|
|
158
195
|
Args:
|
|
159
196
|
bcs *(BoundaryCondition): A list of boundary condition objects.
|
|
160
197
|
"""
|
|
198
|
+
if bc in self.boundary_conditions:
|
|
199
|
+
return
|
|
161
200
|
self._initialized = True
|
|
162
201
|
wordmap = {
|
|
163
202
|
0: 'point',
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
# along with this program; if not, see
|
|
16
16
|
# <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
from .pcb import
|
|
18
|
+
from .pcb import PCB
|
|
19
19
|
from .pmlbox import pmlbox
|
|
20
20
|
from .horn import Horn
|
|
21
21
|
from .shapes import Cyllinder, CoaxCyllinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment
|
|
@@ -35,9 +35,26 @@ from dataclasses import dataclass
|
|
|
35
35
|
import math
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
############################################################
|
|
39
|
+
# EXCEPTIONS #
|
|
40
|
+
############################################################
|
|
41
|
+
|
|
42
|
+
class RouteException(Exception):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
############################################################
|
|
46
|
+
# CONSTANTS #
|
|
47
|
+
############################################################
|
|
48
|
+
|
|
49
|
+
SIZE_NAMES = Literal['0402','0603','1005','1608','2012','3216','3225','4532','5025','6332']
|
|
39
50
|
_SMD_SIZE_DICT = {x: (float(x[:2])*0.05, float(x[2:])*0.1) for x in ['0402','0603','1005','1608','2012','3216','3225','4532','5025','6332']}
|
|
40
51
|
|
|
52
|
+
|
|
53
|
+
############################################################
|
|
54
|
+
# FUNCTIONS #
|
|
55
|
+
############################################################
|
|
56
|
+
|
|
57
|
+
|
|
41
58
|
def approx(a,b):
|
|
42
59
|
return abs(a-b) < 1e-8
|
|
43
60
|
|
|
@@ -51,8 +68,10 @@ def _rot_mat(angle):
|
|
|
51
68
|
ang = -angle * np.pi/180
|
|
52
69
|
return np.array([[np.cos(ang), -np.sin(ang)], [np.sin(ang), np.cos(ang)]])
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
############################################################
|
|
72
|
+
# CLASSES #
|
|
73
|
+
############################################################
|
|
74
|
+
|
|
56
75
|
|
|
57
76
|
class PCBPoly:
|
|
58
77
|
|
|
@@ -240,10 +259,14 @@ class StripTurn(RouteElement):
|
|
|
240
259
|
|
|
241
260
|
return [(xend, yend), (x2, y2), (x1, y1)]
|
|
242
261
|
|
|
262
|
+
############################################################
|
|
263
|
+
# THE STRIP PATH CLASS #
|
|
264
|
+
############################################################
|
|
265
|
+
|
|
243
266
|
class StripPath:
|
|
244
267
|
|
|
245
|
-
def __init__(self, pcb:
|
|
246
|
-
self.pcb:
|
|
268
|
+
def __init__(self, pcb: PCB):
|
|
269
|
+
self.pcb: PCB = pcb
|
|
247
270
|
self.path: list[RouteElement] = []
|
|
248
271
|
self.z: float = 0
|
|
249
272
|
|
|
@@ -423,7 +446,7 @@ class StripPath:
|
|
|
423
446
|
self.pcb._checkpoint = self
|
|
424
447
|
return paths
|
|
425
448
|
|
|
426
|
-
def lumped_element(self, impedance_function: Callable, size:
|
|
449
|
+
def lumped_element(self, impedance_function: Callable, size: SIZE_NAMES | tuple) -> StripPath:
|
|
427
450
|
"""Adds a lumped element to the PCB.
|
|
428
451
|
|
|
429
452
|
The first argument should be the impedance function as function of frequency. For a capacitor this would be:
|
|
@@ -730,7 +753,7 @@ class StripPath:
|
|
|
730
753
|
return self
|
|
731
754
|
|
|
732
755
|
def macro(self, path: str, width: float = None, start_dir: tuple[float, float] = None) -> StripPath:
|
|
733
|
-
"""Parse an EMerge macro command string
|
|
756
|
+
r"""Parse an EMerge macro command string
|
|
734
757
|
|
|
735
758
|
The start direction by default is the abslute current heading. If a specified heading is provided
|
|
736
759
|
the macro language will assume that as the current heading and generate commands accordingly.
|
|
@@ -743,7 +766,7 @@ class StripPath:
|
|
|
743
766
|
- v X: Turn to down and move X forward
|
|
744
767
|
- ^ X: Turn to up and move X forward
|
|
745
768
|
- T X,Y: Taper X forward to width Y
|
|
746
|
-
-
|
|
769
|
+
- \\ X: Turn relative right 90 degrees and X forward
|
|
747
770
|
- / X: Turn relative left 90 degrees and X forward
|
|
748
771
|
|
|
749
772
|
(*) All commands X can also be provided as X,Y to change the width
|
|
@@ -771,8 +794,12 @@ class StripPath:
|
|
|
771
794
|
if element_nr >= len(self.path):
|
|
772
795
|
self.path.append(RouteElement())
|
|
773
796
|
return self.path[element_nr]
|
|
774
|
-
|
|
775
|
-
|
|
797
|
+
|
|
798
|
+
############################################################
|
|
799
|
+
# PCB DESIGN CLASS #
|
|
800
|
+
############################################################
|
|
801
|
+
|
|
802
|
+
class PCB:
|
|
776
803
|
def __init__(self,
|
|
777
804
|
thickness: float,
|
|
778
805
|
unit: float = 0.001,
|
|
@@ -1241,4 +1268,15 @@ class PCBLayouter:
|
|
|
1241
1268
|
polys = GeoSurface(tags)
|
|
1242
1269
|
polys.material = COPPER
|
|
1243
1270
|
return polys
|
|
1244
|
-
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
############################################################
|
|
1274
|
+
# DEPRICATED #
|
|
1275
|
+
############################################################
|
|
1276
|
+
|
|
1277
|
+
class PCBLayouter(PCB):
|
|
1278
|
+
|
|
1279
|
+
def __init__(self, *args, **kwargs):
|
|
1280
|
+
logger.warning('PCBLayouter will be depricated. Use PCB instead.')
|
|
1281
|
+
super().__init__(*args, **kwargs)
|
|
1282
|
+
|
|
@@ -49,14 +49,14 @@ class _HowtoClass:
|
|
|
49
49
|
|
|
50
50
|
After making geometries, you should pass all of them to
|
|
51
51
|
the simulation object
|
|
52
|
-
>>> model.
|
|
52
|
+
>>> model.commit_geometry(box, sphere,...)
|
|
53
53
|
|
|
54
54
|
You can also directly store geometries into the model class by treating
|
|
55
55
|
it as a list (using __getitem__ and __setitem__)
|
|
56
56
|
>>> model['box'] = emerge.geo.Box(...)
|
|
57
57
|
|
|
58
58
|
In this case, all geometries will be automatically included. You shoul still call:
|
|
59
|
-
>>> model.
|
|
59
|
+
>>> model.commit_geometry()
|
|
60
60
|
|
|
61
61
|
"""
|
|
62
62
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from loguru import logger
|
|
2
|
+
import sys
|
|
3
|
+
from typing import Literal
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
############################################################
|
|
10
|
+
# FORMATS #
|
|
11
|
+
############################################################
|
|
12
|
+
|
|
13
|
+
TRACE_FORMAT = (
|
|
14
|
+
"{time: YY/MM/DD - (ddd) - HH:mm:ss.SSSS} | <green>{elapsed}</green> [ <level>{level}</level> ] "
|
|
15
|
+
" <level>{message}</level>"
|
|
16
|
+
)
|
|
17
|
+
DEBUG_FORMAT = (
|
|
18
|
+
"<green>{elapsed}</green> [<level>{level}</level>] "
|
|
19
|
+
" <level>{message}</level>"
|
|
20
|
+
)
|
|
21
|
+
INFO_FORMAT = (
|
|
22
|
+
"<green>{elapsed}</green> [<level>{level}</level>] "
|
|
23
|
+
" <level>{message}</level>"
|
|
24
|
+
)
|
|
25
|
+
WARNING_FORMAT = (
|
|
26
|
+
"<green>{elapsed}</green> [<level>{level}</level>] "
|
|
27
|
+
" <level>{message}</level>"
|
|
28
|
+
)
|
|
29
|
+
ERROR_FORMAT = (
|
|
30
|
+
"<green>{elapsed}</green> [<level>{level}</level>] "
|
|
31
|
+
" <level>{message}</level>"
|
|
32
|
+
)
|
|
33
|
+
FORMAT_DICT = {
|
|
34
|
+
'TRACE': TRACE_FORMAT,
|
|
35
|
+
'DEBUG': DEBUG_FORMAT,
|
|
36
|
+
'INFO': INFO_FORMAT,
|
|
37
|
+
'WARNING': WARNING_FORMAT,
|
|
38
|
+
'ERROR': ERROR_FORMAT,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
LLTYPE = Literal['TRACE','DEBUG','INFO','WARNING','ERROR']
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
############################################################
|
|
45
|
+
# LOG CONTROLLER #
|
|
46
|
+
############################################################
|
|
47
|
+
|
|
48
|
+
class LogController:
|
|
49
|
+
|
|
50
|
+
def __init__(self):
|
|
51
|
+
logger.remove()
|
|
52
|
+
self.std_handlers: list[int] = []
|
|
53
|
+
self.file_handlers: list[int] = []
|
|
54
|
+
self.level: LLTYPE = 'INFO'
|
|
55
|
+
self.file_level: LLTYPE = 'INFO'
|
|
56
|
+
|
|
57
|
+
def set_default(self):
|
|
58
|
+
value = os.getenv("EMERGE_STD_LOGLEVEL", default="INFO")
|
|
59
|
+
self.set_std_loglevel(value)
|
|
60
|
+
|
|
61
|
+
def add_std_logger(self, loglevel: LLTYPE) -> None:
|
|
62
|
+
handle_id = logger.add(sys.stderr,
|
|
63
|
+
level=loglevel,
|
|
64
|
+
format=FORMAT_DICT.get(loglevel, INFO_FORMAT))
|
|
65
|
+
self.std_handlers.append(handle_id)
|
|
66
|
+
|
|
67
|
+
def set_std_loglevel(self, loglevel: str):
|
|
68
|
+
handler = {"sink": sys.stdout,
|
|
69
|
+
"level": loglevel,
|
|
70
|
+
"format": FORMAT_DICT.get(loglevel, INFO_FORMAT)}
|
|
71
|
+
logger.configure(handlers=[handler])
|
|
72
|
+
self.level = loglevel
|
|
73
|
+
os.environ["EMERGE_STD_LOGLEVEL"] = loglevel
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def set_write_file(self, path: Path, loglevel: str = 'TRACE'):
|
|
77
|
+
|
|
78
|
+
handler_id = logger.add(str(path / 'logging.log'), mode='w', level=loglevel, format=FORMAT_DICT.get(loglevel, INFO_FORMAT), colorize=False, backtrace=True, diagnose=True)
|
|
79
|
+
self.file_handlers.append(handler_id)
|
|
80
|
+
self.file_level = loglevel
|
|
81
|
+
os.environ["EMERGE_FILE_LOGLEVEL"] = loglevel
|
|
82
|
+
|
|
83
|
+
LOG_CONTROLLER = LogController()
|
|
@@ -27,6 +27,7 @@ from .mth.optimized import outward_normal
|
|
|
27
27
|
from loguru import logger
|
|
28
28
|
from functools import cache
|
|
29
29
|
from .bc import Periodic
|
|
30
|
+
from .mth.pairing import pair_coordinates
|
|
30
31
|
|
|
31
32
|
@njit(f8(f8[:], f8[:], f8[:]), cache=True, nogil=True)
|
|
32
33
|
def area(x1: np.ndarray, x2: np.ndarray, x3: np.ndarray):
|
|
@@ -87,8 +88,6 @@ class Mesh3D:
|
|
|
87
88
|
|
|
88
89
|
self.geometry: Mesher = mesher
|
|
89
90
|
|
|
90
|
-
self.exterior_face_tags: list[int] = None
|
|
91
|
-
|
|
92
91
|
# All spatial objects
|
|
93
92
|
self.nodes: np.ndarray = None
|
|
94
93
|
self.n_i2t: dict = None
|
|
@@ -143,6 +142,7 @@ class Mesh3D:
|
|
|
143
142
|
## Memory
|
|
144
143
|
self.ftag_to_tri: dict[int, list[int]] = dict()
|
|
145
144
|
self.ftag_to_node: dict[int, list[int]] = dict()
|
|
145
|
+
self.ftag_to_edge: dict[int, list[int]] = dict()
|
|
146
146
|
self.vtag_to_tet: dict[int, list[int]] = dict()
|
|
147
147
|
|
|
148
148
|
@property
|
|
@@ -245,6 +245,17 @@ class Mesh3D:
|
|
|
245
245
|
nodes.extend(self.ftag_to_node[facetag])
|
|
246
246
|
|
|
247
247
|
return np.array(sorted(list(set(nodes))))
|
|
248
|
+
|
|
249
|
+
def get_edges(self, face_tags: Union[int, list[int]]) -> np.ndarray:
|
|
250
|
+
'''Returns a numpyarray of all the edges that belong to the given face tags'''
|
|
251
|
+
if isinstance(face_tags, int):
|
|
252
|
+
face_tags = [face_tags,]
|
|
253
|
+
|
|
254
|
+
edges = []
|
|
255
|
+
for facetag in face_tags:
|
|
256
|
+
edges.extend(self.ftag_to_edge[facetag])
|
|
257
|
+
|
|
258
|
+
return np.array(sorted(list(set(edges))))
|
|
248
259
|
|
|
249
260
|
|
|
250
261
|
def update(self, periodic_bcs: list[Periodic] = None):
|
|
@@ -405,7 +416,8 @@ class Mesh3D:
|
|
|
405
416
|
self.ftag_to_node[t] = node_tags
|
|
406
417
|
node_tags = np.squeeze(np.array(node_tags)).reshape(-1,3).T
|
|
407
418
|
self.ftag_to_tri[t] = [self.get_tri(node_tags[0,i], node_tags[1,i], node_tags[2,i]) for i in range(node_tags.shape[1])]
|
|
408
|
-
|
|
419
|
+
self.ftag_to_edge[t] = sorted(list(np.unique(self.tri_to_edge[:,self.ftag_to_tri[t]].flatten())))
|
|
420
|
+
|
|
409
421
|
vol_dimtags = gmsh.model.get_entities(3)
|
|
410
422
|
for d,t in vol_dimtags:
|
|
411
423
|
domain_tag, v_tags, node_tags = gmsh.model.mesh.get_elements(3, t)
|
|
@@ -457,16 +469,8 @@ class Mesh3D:
|
|
|
457
469
|
node_ids_1 = sorted(list(set(node_ids_1)))
|
|
458
470
|
node_ids_2 = sorted(list(set(node_ids_2)))
|
|
459
471
|
dv = np.array(bc.dv)
|
|
460
|
-
nodemapdict = defaultdict(lambda: [None, None])
|
|
461
|
-
|
|
462
|
-
mult = int(10**(-np.round(np.log10(dsmin))+2))
|
|
463
472
|
|
|
464
|
-
|
|
465
|
-
nodemapdict[gen_key(self.nodes[:,i1], mult)][0] = i1
|
|
466
|
-
nodemapdict[gen_key(self.nodes[:,i2]-dv, mult)][1] = i2
|
|
467
|
-
|
|
468
|
-
nodemap = {i1: i2 for i1, i2 in nodemapdict.values()}
|
|
469
|
-
|
|
473
|
+
nodemap = pair_coordinates(self.nodes, node_ids_1, node_ids_2, dv, dsmin/2)
|
|
470
474
|
node_ids_2_unsorted = [nodemap[i] for i in sorted(node_ids_1)]
|
|
471
475
|
node_ids_2_sorted = sorted(node_ids_2_unsorted)
|
|
472
476
|
conv_map = {i1: i2 for i1, i2 in zip(node_ids_2_unsorted, node_ids_2_sorted)}
|
|
@@ -565,6 +569,20 @@ class Mesh3D:
|
|
|
565
569
|
def boundary_surface(self,
|
|
566
570
|
face_tags: Union[int, list[int]],
|
|
567
571
|
origin: tuple[float, float, float] = None) -> SurfaceMesh:
|
|
572
|
+
"""Returns a SurfaceMesh class that is a 2D mesh isolated from the 3D mesh
|
|
573
|
+
|
|
574
|
+
The mesh will be based on the given set of face tags.
|
|
575
|
+
|
|
576
|
+
In order to properly allign the normal vectors, an alignment origin can be provided.
|
|
577
|
+
If not provided, the center point of all boundaries will be used.
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
face_tags (Union[int, list[int]]): The list of face tags to use
|
|
581
|
+
origin (tuple[float, float, float], optional): The normal vecor alignment origin.. Defaults to None.
|
|
582
|
+
|
|
583
|
+
Returns:
|
|
584
|
+
SurfaceMesh: The resultant surface mesh
|
|
585
|
+
"""
|
|
568
586
|
tri_ids = self.get_triangles(face_tags)
|
|
569
587
|
if origin is None:
|
|
570
588
|
nodes = self.nodes[:,self.get_nodes(face_tags)]
|
|
@@ -18,16 +18,43 @@
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
|
|
20
20
|
def norm(Field: np.ndarray) -> np.ndarray:
|
|
21
|
-
|
|
21
|
+
""" Computes the complex norm of a field (3,N)
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
Field (np.ndarray): The input field, shape (3,N)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
np.ndarray: The complex norm in shape (N,)
|
|
28
|
+
"""
|
|
22
29
|
return np.sqrt(np.abs(Field[0,:])**2 + np.abs(Field[1,:])**2 + np.abs(Field[2,:])**2)
|
|
23
30
|
|
|
24
31
|
def coax_rout(rin: float,
|
|
25
32
|
eps_r: float = 1,
|
|
26
33
|
Z0: float = 50) -> float:
|
|
34
|
+
"""Computes the outer radius given a dielectric constant, inner radius and characteristic impedance
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
rin (float): The inner radius
|
|
38
|
+
eps_r (float, optional): The dielectric permittivity. Defaults to 1.
|
|
39
|
+
Z0 (float, optional): The impedance. Defaults to 50.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
float: The outer radius
|
|
43
|
+
"""
|
|
27
44
|
return rin*10**(Z0*np.sqrt(eps_r)/138)
|
|
28
45
|
|
|
29
46
|
def coax_rin(rout: float,
|
|
30
47
|
eps_r: float = 1,
|
|
31
48
|
Z0: float = 50) -> float:
|
|
49
|
+
"""Computes the inner radius given a dielectric constant, outer radius and characteristic impedance
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
rin (float): The outer radius
|
|
53
|
+
eps_r (float, optional): The dielectric permittivity. Defaults to 1.
|
|
54
|
+
Z0 (float, optional): The impedance. Defaults to 50.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
float: The inner radius
|
|
58
|
+
"""
|
|
32
59
|
return rout/10**(Z0*np.sqrt(eps_r)/138)
|
|
33
60
|
|
|
@@ -17,10 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
from typing import Callable
|
|
20
|
-
from numba import njit, f8, i8,
|
|
20
|
+
from numba import njit, f8, i8, c16
|
|
21
21
|
from .optimized import gaus_quad_tri, generate_int_points_tri, calc_area
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
############################################################
|
|
25
|
+
# NUMBA OPTIMIZED INTEGRALS #
|
|
26
|
+
############################################################
|
|
27
|
+
|
|
24
28
|
@njit(c16(f8[:,:], i8[:,:], c16[:], f8[:,:], c16[:,:]), cache=True, nogil=True)
|
|
25
29
|
def _fast_integral_c(nodes, triangles, constants, DPTs, field_values):
|
|
26
30
|
tot = np.complex128(0.0)
|
|
@@ -48,16 +52,34 @@ def _fast_integral_f(nodes, triangles, constants, DPTs, field_values):
|
|
|
48
52
|
tot = tot + constants[it] * field * A
|
|
49
53
|
return tot
|
|
50
54
|
|
|
55
|
+
|
|
56
|
+
############################################################
|
|
57
|
+
# PYTHON WRAPPER #
|
|
58
|
+
############################################################
|
|
59
|
+
|
|
51
60
|
def surface_integral(nodes: np.ndarray,
|
|
52
61
|
triangles: np.ndarray,
|
|
53
62
|
function: Callable,
|
|
54
63
|
constants: np.ndarray = None,
|
|
55
|
-
|
|
64
|
+
gq_order: int = 4) -> complex:
|
|
65
|
+
"""Computes the surface integral of a scalar-field
|
|
66
|
+
|
|
67
|
+
Computes I = Σ ∫∫ C ᐧ f(x,y,z) dA
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
nodes (np.ndarray): The integration nodes
|
|
71
|
+
triangles (np.ndarray): The integration triangles
|
|
72
|
+
function (Callable): The scalar field f(x,y,z)
|
|
73
|
+
constants (np.ndarray, optional): The constant C. Defaults to None.
|
|
74
|
+
ndpts (int, optional): Order of gauss quadrature points. Defaults to 4.
|
|
56
75
|
|
|
76
|
+
Returns:
|
|
77
|
+
complex: The integral I.
|
|
78
|
+
"""
|
|
57
79
|
if constants is None:
|
|
58
80
|
constants = np.ones(triangles.shape[1])
|
|
59
81
|
|
|
60
|
-
DPTs = gaus_quad_tri(
|
|
82
|
+
DPTs = gaus_quad_tri(gq_order)
|
|
61
83
|
xall_flat, yall_flat, zall_flat, shape = generate_int_points_tri(nodes, triangles, DPTs)
|
|
62
84
|
|
|
63
85
|
fvals = function(xall_flat, yall_flat, zall_flat)
|