emerge 0.4.7__py3-none-any.whl → 0.4.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of emerge might be problematic. Click here for more details.
- emerge/__init__.py +14 -14
- emerge/_emerge/__init__.py +42 -0
- emerge/_emerge/bc.py +197 -0
- emerge/_emerge/coord.py +119 -0
- emerge/_emerge/cs.py +523 -0
- emerge/_emerge/dataset.py +36 -0
- emerge/_emerge/elements/__init__.py +19 -0
- emerge/_emerge/elements/femdata.py +212 -0
- emerge/_emerge/elements/index_interp.py +64 -0
- emerge/_emerge/elements/legrange2.py +172 -0
- emerge/_emerge/elements/ned2_interp.py +645 -0
- emerge/_emerge/elements/nedelec2.py +140 -0
- emerge/_emerge/elements/nedleg2.py +217 -0
- emerge/_emerge/geo/__init__.py +24 -0
- emerge/_emerge/geo/horn.py +107 -0
- emerge/_emerge/geo/modeler.py +449 -0
- emerge/_emerge/geo/operations.py +254 -0
- emerge/_emerge/geo/pcb.py +1244 -0
- emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
- emerge/_emerge/geo/pcb_tools/macro.py +79 -0
- emerge/_emerge/geo/pmlbox.py +204 -0
- emerge/_emerge/geo/polybased.py +529 -0
- emerge/_emerge/geo/shapes.py +427 -0
- emerge/_emerge/geo/step.py +77 -0
- emerge/_emerge/geo2d.py +86 -0
- emerge/_emerge/geometry.py +510 -0
- emerge/_emerge/howto.py +214 -0
- emerge/_emerge/logsettings.py +5 -0
- emerge/_emerge/material.py +118 -0
- emerge/_emerge/mesh3d.py +730 -0
- emerge/_emerge/mesher.py +339 -0
- emerge/_emerge/mth/common_functions.py +33 -0
- emerge/_emerge/mth/integrals.py +71 -0
- emerge/_emerge/mth/optimized.py +357 -0
- emerge/_emerge/periodic.py +263 -0
- emerge/_emerge/physics/__init__.py +0 -0
- emerge/_emerge/physics/microwave/__init__.py +1 -0
- emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
- emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
- emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
- emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
- emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
- emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
- emerge/_emerge/physics/microwave/periodic.py +82 -0
- emerge/_emerge/physics/microwave/port_functions.py +53 -0
- emerge/_emerge/physics/microwave/sc.py +175 -0
- emerge/_emerge/physics/microwave/simjob.py +147 -0
- emerge/_emerge/physics/microwave/sparam.py +138 -0
- emerge/_emerge/physics/microwave/touchstone.py +140 -0
- emerge/_emerge/plot/__init__.py +0 -0
- emerge/_emerge/plot/display.py +394 -0
- emerge/_emerge/plot/grapher.py +93 -0
- emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
- emerge/_emerge/plot/pyvista/__init__.py +1 -0
- emerge/_emerge/plot/pyvista/display.py +931 -0
- emerge/_emerge/plot/pyvista/display_settings.py +24 -0
- emerge/_emerge/plot/simple_plots.py +551 -0
- emerge/_emerge/plot.py +225 -0
- emerge/_emerge/projects/__init__.py +0 -0
- emerge/_emerge/projects/_gen_base.txt +32 -0
- emerge/_emerge/projects/_load_base.txt +24 -0
- emerge/_emerge/projects/generate_project.py +40 -0
- emerge/_emerge/selection.py +596 -0
- emerge/_emerge/simmodel.py +444 -0
- emerge/_emerge/simulation_data.py +411 -0
- emerge/_emerge/solver.py +993 -0
- emerge/_emerge/system.py +54 -0
- emerge/cli.py +19 -0
- emerge/lib.py +1 -1
- emerge/plot.py +1 -1
- {emerge-0.4.7.dist-info → emerge-0.4.8.dist-info}/METADATA +1 -1
- emerge-0.4.8.dist-info/RECORD +78 -0
- emerge-0.4.8.dist-info/entry_points.txt +2 -0
- emerge-0.4.7.dist-info/RECORD +0 -9
- emerge-0.4.7.dist-info/entry_points.txt +0 -2
- {emerge-0.4.7.dist-info → emerge-0.4.8.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
import numpy as np
|
|
20
|
+
from ..mesh3d import Mesh3D
|
|
21
|
+
from .femdata import FEMBasis
|
|
22
|
+
from .ned2_interp import ned2_tet_interp, ned2_tet_interp_curl
|
|
23
|
+
from ..mth.optimized import local_mapping
|
|
24
|
+
from .index_interp import index_interp
|
|
25
|
+
|
|
26
|
+
############### Nedelec2 Class
|
|
27
|
+
|
|
28
|
+
class Nedelec2(FEMBasis):
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def __init__(self, mesh: Mesh3D):
|
|
32
|
+
super().__init__(mesh)
|
|
33
|
+
|
|
34
|
+
self.nedges: int = self.mesh.n_edges
|
|
35
|
+
self.ntris: int = self.mesh.n_tris
|
|
36
|
+
self.ntets: int = self.mesh.n_tets
|
|
37
|
+
|
|
38
|
+
self.nfield: int = 2*self.nedges + 2*self.ntris
|
|
39
|
+
|
|
40
|
+
######## MESH Derived
|
|
41
|
+
|
|
42
|
+
nedges = self.mesh.n_edges
|
|
43
|
+
ntris = self.mesh.n_tris
|
|
44
|
+
|
|
45
|
+
self.tet_to_field: np.ndarray = np.zeros((20, self.mesh.tets.shape[1]), dtype=int)
|
|
46
|
+
self.tet_to_field[:6,:] = self.mesh.tet_to_edge
|
|
47
|
+
self.tet_to_field[6:10,:] = self.mesh.tet_to_tri + nedges
|
|
48
|
+
self.tet_to_field[10:16,:] = self.mesh.tet_to_edge + (ntris+nedges)
|
|
49
|
+
self.tet_to_field[16:20,:] = self.mesh.tet_to_tri + (ntris+2*nedges)
|
|
50
|
+
|
|
51
|
+
self.edge_to_field: np.ndarray = np.zeros((2,nedges), dtype=int)
|
|
52
|
+
|
|
53
|
+
self.edge_to_field[0,:] = np.arange(nedges)
|
|
54
|
+
self.edge_to_field[1,:] = np.arange(nedges) + ntris + nedges
|
|
55
|
+
|
|
56
|
+
self.tri_to_field: np.ndarray = np.zeros((8,ntris), dtype=int)
|
|
57
|
+
|
|
58
|
+
self.tri_to_field[:3,:] = self.mesh.tri_to_edge
|
|
59
|
+
self.tri_to_field[3,:] = np.arange(ntris) + nedges
|
|
60
|
+
self.tri_to_field[4:7,:] = self.mesh.tri_to_edge + nedges + ntris
|
|
61
|
+
self.tri_to_field[7,:] = np.arange(ntris) + 2*nedges + ntris
|
|
62
|
+
|
|
63
|
+
##
|
|
64
|
+
self._field: np.ndarray = None
|
|
65
|
+
self.n_tet_dofs = 20
|
|
66
|
+
self.n_tri_dofs = 8
|
|
67
|
+
self._all_tet_ids = np.arange(self.ntets)
|
|
68
|
+
|
|
69
|
+
self.empty_tri_rowcol()
|
|
70
|
+
|
|
71
|
+
def interpolate(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, tetids: np.ndarray = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
72
|
+
'''
|
|
73
|
+
Interpolate the provided field data array at the given xs, ys and zs coordinates
|
|
74
|
+
'''
|
|
75
|
+
if tetids is None:
|
|
76
|
+
tetids = self._all_tet_ids
|
|
77
|
+
return 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
|
+
|
|
79
|
+
def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, c: np.ndarray, tetids: np.ndarray = None) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
80
|
+
"""
|
|
81
|
+
Interpolates the curl of the field at the given points.
|
|
82
|
+
"""
|
|
83
|
+
if tetids is None:
|
|
84
|
+
tetids = self._all_tet_ids
|
|
85
|
+
return 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)
|
|
86
|
+
|
|
87
|
+
def interpolate_index(self, xs: np.ndarray,
|
|
88
|
+
ys: np.ndarray,
|
|
89
|
+
zs: np.ndarray,
|
|
90
|
+
tetids: np.ndarray = None) -> np.ndarray:
|
|
91
|
+
if tetids is None:
|
|
92
|
+
tetids = self._all_tet_ids
|
|
93
|
+
|
|
94
|
+
return index_interp(np.array([xs, ys, zs]), self.mesh.tets, self.mesh.nodes, tetids)
|
|
95
|
+
###### INDEX MAPPINGS
|
|
96
|
+
|
|
97
|
+
def local_tet_to_triid(self, itet: int) -> np.ndarray:
|
|
98
|
+
tri_ids = self.tet_to_field[6:10, itet] - self.n_edges
|
|
99
|
+
global_tri_map = self.mesh.tris[:, tri_ids]
|
|
100
|
+
return local_mapping(self.mesh.tets[:, itet], global_tri_map)
|
|
101
|
+
|
|
102
|
+
def local_tet_to_edgeid(self, itet: int) -> np.ndarray:
|
|
103
|
+
global_edge_map = self.mesh.edges[:, self.tet_to_field[:6,itet]]
|
|
104
|
+
return local_mapping(self.mesh.tets[:, itet], global_edge_map)
|
|
105
|
+
|
|
106
|
+
def local_tri_to_edgeid(self, itri: int) -> np.ndarray:
|
|
107
|
+
global_edge_map = self.mesh.edges[:, self.tri_to_field[:3,itri]]
|
|
108
|
+
return local_mapping(self.mesh.tris[:, itri], global_edge_map)
|
|
109
|
+
|
|
110
|
+
def map_edge_to_field(self, edge_ids: np.ndarray) -> np.ndarray:
|
|
111
|
+
"""
|
|
112
|
+
Returns the field ids for the edges.
|
|
113
|
+
"""
|
|
114
|
+
# Concatinate the edges with the edges + ntris + nedges
|
|
115
|
+
edge_ids = np.array(edge_ids)
|
|
116
|
+
return np.concatenate((edge_ids, edge_ids + self.ntris + self.nedges))
|
|
117
|
+
|
|
118
|
+
########
|
|
119
|
+
# @staticmethod
|
|
120
|
+
# def tet_stiff_mass_submatrix(tet_vertices: np.ndarray,
|
|
121
|
+
# edge_lengths: np.ndarray,
|
|
122
|
+
# local_edge_map: np.ndarray,
|
|
123
|
+
# local_tri_map: np.ndarray,
|
|
124
|
+
# C_stiffness: float,
|
|
125
|
+
# C_mass: float) -> tuple[np.ndarray, np.ndarray]:
|
|
126
|
+
# return ned2_tet_stiff_mass(tet_vertices, edge_lengths, local_edge_map, local_tri_map, C_stiffness, C_mass)
|
|
127
|
+
|
|
128
|
+
# @staticmethod
|
|
129
|
+
# def tri_stiff_vec_matrix(lcs_vertices: np.ndarray,
|
|
130
|
+
# gamma: complex,
|
|
131
|
+
# lcs_Uinc: np.ndarray,
|
|
132
|
+
# DPTs: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
|
133
|
+
# return ned2_tri_stiff_force(lcs_vertices, gamma, lcs_Uinc, DPTs)
|
|
134
|
+
|
|
135
|
+
# @staticmethod
|
|
136
|
+
# def tri_surf_integral(lcs_vertices: np.ndarray,
|
|
137
|
+
# edge_lengths: np.ndarray,
|
|
138
|
+
# lcs_Uinc: np.ndarray,
|
|
139
|
+
# DPTs: np.ndarray) -> complex:
|
|
140
|
+
# return ned2_tri_surface_integral(lcs_vertices, edge_lengths, lcs_Uinc, DPTs)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
import numpy as np
|
|
20
|
+
from ..mesh3d import SurfaceMesh
|
|
21
|
+
from .femdata import FEMBasis
|
|
22
|
+
from .ned2_interp import ned2_tri_interp_full, ned2_tri_interp_curl
|
|
23
|
+
from ..mth.optimized import matinv
|
|
24
|
+
from ..cs import CoordinateSystem
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## TODO: TEMPORARY SOLUTION FIX THIS
|
|
28
|
+
|
|
29
|
+
class FieldFunctionClass:
|
|
30
|
+
""" This Class serves as a picklable class so that ModalPort boundary conditions
|
|
31
|
+
can actually be stored with the Simulation3D class. Functions aren't picklable in
|
|
32
|
+
Python.
|
|
33
|
+
|
|
34
|
+
I am not happy with the existence of this class, it feels too ad-hoc but for now it
|
|
35
|
+
is the simplest way. It stores all actually required information needed to do a
|
|
36
|
+
surface field interpolation without needing to store the Mesh3D and SurfaceMesh class
|
|
37
|
+
objects plus the NedelecLegrange2 classes with the Simulation3D.
|
|
38
|
+
|
|
39
|
+
As it stands currently, only the GMSH mesh is stored plus the geometry objects. The
|
|
40
|
+
mesh is reconstructed as it is deterministic.
|
|
41
|
+
"""
|
|
42
|
+
def __init__(self,
|
|
43
|
+
field: np.ndarray,
|
|
44
|
+
cs: CoordinateSystem,
|
|
45
|
+
nodes: np.ndarray,
|
|
46
|
+
tris: np.ndarray,
|
|
47
|
+
tri_to_field: np.ndarray,
|
|
48
|
+
EH: str = 'E',
|
|
49
|
+
diadic: np.ndarray = None,
|
|
50
|
+
beta: float = None,
|
|
51
|
+
constant: float = 1.0):
|
|
52
|
+
self.field: np.ndarray = field
|
|
53
|
+
self.cs: CoordinateSystem = cs
|
|
54
|
+
self.nodes: np.ndarray = nodes
|
|
55
|
+
self.tris: np.ndarray = tris
|
|
56
|
+
self.tri_to_field: np.ndarray = tri_to_field
|
|
57
|
+
self.eh: str = EH
|
|
58
|
+
self.diadic: np.ndarray = diadic
|
|
59
|
+
self.beta: float = beta
|
|
60
|
+
self.constant: float = constant
|
|
61
|
+
if EH == 'H':
|
|
62
|
+
if diadic is None:
|
|
63
|
+
self.diadic = np.eye(3)[:,:,np.newaxis()] * np.ones((self.tris.shape[1]))
|
|
64
|
+
|
|
65
|
+
def __call__(self, xs: np.ndarray,
|
|
66
|
+
ys: np.ndarray,
|
|
67
|
+
zs: np.ndarray):
|
|
68
|
+
xl, yl, zl = self.cs.in_local_cs(xs, ys, zs)
|
|
69
|
+
if self.eh == 'E':
|
|
70
|
+
Fxl, Fyl, Fzl = self.calcE(xl, yl)
|
|
71
|
+
else:
|
|
72
|
+
Fxl, Fyl, Fzl = self.calcH(xl, yl)
|
|
73
|
+
Fx, Fy, Fz = self.cs.in_global_basis(Fxl, Fyl, Fzl)
|
|
74
|
+
return np.array([Fx, Fy, Fz])*self.constant
|
|
75
|
+
|
|
76
|
+
def calcE(self, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
77
|
+
coordinates = np.array([xs, ys])
|
|
78
|
+
return ned2_tri_interp_full(coordinates,
|
|
79
|
+
self.field,
|
|
80
|
+
self.tris,
|
|
81
|
+
self.nodes,
|
|
82
|
+
self.tri_to_field)
|
|
83
|
+
|
|
84
|
+
def calcH(self, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
85
|
+
coordinates = np.array([xs, ys])
|
|
86
|
+
|
|
87
|
+
return ned2_tri_interp_curl(coordinates,
|
|
88
|
+
self.field,
|
|
89
|
+
self.tris,
|
|
90
|
+
self.nodes,
|
|
91
|
+
self.tri_to_field,
|
|
92
|
+
self.diadic,
|
|
93
|
+
self.beta)
|
|
94
|
+
|
|
95
|
+
############### Nedelec2 Class
|
|
96
|
+
|
|
97
|
+
class NedelecLegrange2(FEMBasis):
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def __init__(self, mesh: SurfaceMesh, cs: CoordinateSystem):
|
|
101
|
+
|
|
102
|
+
self.mesh: SurfaceMesh = mesh
|
|
103
|
+
|
|
104
|
+
self.cs: CoordinateSystem = cs
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
nodes = self.mesh.nodes
|
|
108
|
+
self.local_nodes: np.ndarray = np.array(self.cs.in_local_cs(nodes[0,:], nodes[1,:], nodes[2,:]))
|
|
109
|
+
|
|
110
|
+
## Counters
|
|
111
|
+
self.n_nodes: int = self.mesh.n_nodes
|
|
112
|
+
self.n_edges: int = self.mesh.n_edges
|
|
113
|
+
self.n_tris: int = self.mesh.n_tris
|
|
114
|
+
self.n_tri_dofs: int = None
|
|
115
|
+
|
|
116
|
+
self.n_field: int = 2*self.n_edges + 2*self.n_tris + self.n_nodes + self.n_edges
|
|
117
|
+
self.n_xy: int = 2*self.n_edges + 2*self.n_tris
|
|
118
|
+
|
|
119
|
+
######## MESH Derived
|
|
120
|
+
Nn = self.mesh.n_nodes
|
|
121
|
+
Ne = self.mesh.n_edges
|
|
122
|
+
Nt = self.mesh.n_tris
|
|
123
|
+
|
|
124
|
+
self.tri_to_field: np.ndarray = np.zeros((8 + 6, self.n_tris), dtype=int)
|
|
125
|
+
|
|
126
|
+
self.tri_to_field[:3,:] = self.mesh.tri_to_edge
|
|
127
|
+
self.tri_to_field[3,:] = np.arange(Nt) + Ne
|
|
128
|
+
self.tri_to_field[4:7,:] = self.mesh.tri_to_edge + Ne + Nt
|
|
129
|
+
self.tri_to_field[7,:] = np.arange(Nt) + 2*Ne + Nt
|
|
130
|
+
self.tri_to_field[8:11,:] = self.mesh.tris + (2*Ne + 2*Nt) # + E + T + E + T
|
|
131
|
+
self.tri_to_field[11:14,:] = self.mesh.tri_to_edge + (2*Ne + 2*Nt + Nn)
|
|
132
|
+
|
|
133
|
+
self.edge_to_field: np.ndarray = np.zeros((5,Ne), dtype=int) #edge mode 1, edge mode 2, edge legrande mode, edge vertex mode 1, edge vertex mode 2
|
|
134
|
+
|
|
135
|
+
self.edge_to_field[0,:] = np.arange(Ne)
|
|
136
|
+
self.edge_to_field[1,:] = np.arange(Ne) + Nt + Ne
|
|
137
|
+
self.edge_to_field[2,:] = np.arange(Ne) + Ne*2 + Nt*2 + Nn
|
|
138
|
+
self.edge_to_field[3:,:] = self.mesh.edges + Ne*2 + Nt*2
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
self._field: np.ndarray = None
|
|
142
|
+
self._rows: np.ndarray = None
|
|
143
|
+
self._cols: np.ndarray = None
|
|
144
|
+
|
|
145
|
+
def __call__(self, **kwargs) -> NedelecLegrange2:
|
|
146
|
+
self._field = self.fielddata(**kwargs)
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def interpolate_Ef(self, field: np.ndarray) -> FieldFunctionClass:
|
|
150
|
+
'''Generates the Interpolation function as a function object for a given coordiante basis and origin.'''
|
|
151
|
+
|
|
152
|
+
# def func(xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> np.ndarray:
|
|
153
|
+
# xl, yl, zl = self.cs.in_local_cs(xs, ys, zs)
|
|
154
|
+
# Exl, Eyl, Ezl = self.tri_interpolate(field, xl, yl)
|
|
155
|
+
# Ex, Ey, Ez = self.cs.in_global_basis(Exl, Eyl, Ezl)
|
|
156
|
+
# return np.array([Ex, Ey, Ez])
|
|
157
|
+
return FieldFunctionClass(field, self.cs, self.local_nodes, self.mesh.tris, self.tri_to_field, 'E')
|
|
158
|
+
|
|
159
|
+
def interpolate_Hf(self, field: np.ndarray, k0: float, ur: np.ndarray, beta: float) -> FieldFunctionClass:
|
|
160
|
+
'''Generates the Interpolation function as a function object for a given coordiante basis and origin.'''
|
|
161
|
+
constant = 1j/ ((k0*299792458)*(4*np.pi*1e-7))
|
|
162
|
+
urinv = np.zeros_like(ur)
|
|
163
|
+
|
|
164
|
+
for i in range(ur.shape[2]):
|
|
165
|
+
urinv[:,:,i] = matinv(ur[:,:,i])
|
|
166
|
+
|
|
167
|
+
# def func(xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> np.ndarray:
|
|
168
|
+
# xl, yl, _ = self.cs.in_local_cs(xs, ys, zs)
|
|
169
|
+
# Exl, Eyl, Ezl = self.tri_interpolate_curl(field, xl, yl, urinv, beta)
|
|
170
|
+
# Ex, Ey, Ez = self.cs.in_global_basis(Exl, Eyl, Ezl)
|
|
171
|
+
# return np.array([Ex, Ey, Ez])*constant
|
|
172
|
+
return FieldFunctionClass(field, self.cs, self.local_nodes, self.mesh.tris, self.tri_to_field, 'H', urinv, beta, constant)
|
|
173
|
+
|
|
174
|
+
def tri_interpolate(self, field, xs: np.ndarray, ys: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
175
|
+
coordinates = np.array([xs, ys])
|
|
176
|
+
return ned2_tri_interp_full(coordinates,
|
|
177
|
+
field,
|
|
178
|
+
self.mesh.tris,
|
|
179
|
+
self.local_nodes,
|
|
180
|
+
self.tri_to_field)
|
|
181
|
+
|
|
182
|
+
def tri_interpolate_curl(self, field, xs: np.ndarray, ys: np.ndarray, diadic: np.ndarray = None, beta: float = 0) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
183
|
+
coordinates = np.array([xs, ys])
|
|
184
|
+
if diadic is None:
|
|
185
|
+
diadic = np.eye(3)[:,:,np.newaxis()] * np.ones((self.mesh.n_tris))
|
|
186
|
+
return ned2_tri_interp_curl(coordinates,
|
|
187
|
+
field,
|
|
188
|
+
self.mesh.tris,
|
|
189
|
+
self.local_nodes,
|
|
190
|
+
self.tri_to_field,
|
|
191
|
+
diadic,
|
|
192
|
+
beta)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# def interpolate_curl(self, field: np.ndarray, xs: np.ndarray, ys: np.ndarray, zs:np.ndarray, c: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
196
|
+
# """
|
|
197
|
+
# Interpolates the curl of the field at the given points.
|
|
198
|
+
# """
|
|
199
|
+
# return 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)
|
|
200
|
+
|
|
201
|
+
# def fieldf(self, field: np.ndarray, basis: np.ndarray = None, origin: np.ndarray = None) -> Callable:
|
|
202
|
+
# if basis is None:
|
|
203
|
+
# basis = np.eye(3)
|
|
204
|
+
|
|
205
|
+
# if origin is None:
|
|
206
|
+
# origin = np.zeros(3)
|
|
207
|
+
|
|
208
|
+
# ibasis = np.linalg.pinv(basis)
|
|
209
|
+
# def func(xs: np.ndarray, ys: np.ndarray, zs: np.ndarray) -> np.ndarray:
|
|
210
|
+
# xyz = np.array([xs, ys, zs]) + origin[:, np.newaxis]
|
|
211
|
+
# xyzg = basis @ xyz
|
|
212
|
+
# return ibasis @ np.array(self.interpolate(field, xyzg[0,:], xyzg[1,:], xyzg[2,:]))
|
|
213
|
+
# return func
|
|
214
|
+
|
|
215
|
+
###### INDEX MAPPINGS
|
|
216
|
+
|
|
217
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
from .pcb import PCBLayouter
|
|
19
|
+
from .pmlbox import pmlbox
|
|
20
|
+
from .horn import Horn
|
|
21
|
+
from .shapes import Cyllinder, CoaxCyllinder, Box, XYPlate, HalfSphere, Sphere, Plate, OldBox, Alignment
|
|
22
|
+
from .operations import subtract, add, embed, remove, rotate, mirror, change_coordinate_system, translate, intersect
|
|
23
|
+
from .polybased import XYPolygon, GeoPrism
|
|
24
|
+
from .step import STEPItems
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# EMerge is an open source Python based FEM EM simulation module.
|
|
2
|
+
# Copyright (C) 2025 Robert Fennis.
|
|
3
|
+
|
|
4
|
+
# This program is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU General Public License
|
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
|
7
|
+
# of the License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program; if not, see
|
|
16
|
+
# <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
from ..geometry import GeoVolume
|
|
19
|
+
from ..cs import CoordinateSystem
|
|
20
|
+
from ..selection import FaceSelection
|
|
21
|
+
|
|
22
|
+
import gmsh
|
|
23
|
+
|
|
24
|
+
class Horn(GeoVolume):
|
|
25
|
+
|
|
26
|
+
def __init__(self,
|
|
27
|
+
start_aperture: tuple[float, float],
|
|
28
|
+
end_aperture: tuple[float, float],
|
|
29
|
+
height: float,
|
|
30
|
+
cs: CoordinateSystem):
|
|
31
|
+
"""Generate a Horn geometry. The horn is defined by a start and ending rectangle.
|
|
32
|
+
|
|
33
|
+
Start aperture is always defined at z=0 and the end at z=height.
|
|
34
|
+
The horn can be reoriented by choosing a different coordinate system.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
start_aperture (tuple[float, float]): The width/height of the start aperture
|
|
38
|
+
end_aperture (tuple[float, float]): The width/height of the end aperture
|
|
39
|
+
height (float): The height of the horn
|
|
40
|
+
cs (CoordinateSystem): The coordinate systme to place the horn in.
|
|
41
|
+
"""
|
|
42
|
+
super().__init__([])
|
|
43
|
+
p0 = cs.origin
|
|
44
|
+
p1 = p0 + cs.zax.np * height
|
|
45
|
+
|
|
46
|
+
wax = cs.xax.np
|
|
47
|
+
hax = cs.yax.np
|
|
48
|
+
dax = cs.zax.np
|
|
49
|
+
|
|
50
|
+
w1, h1 = start_aperture
|
|
51
|
+
w2, h2 = end_aperture
|
|
52
|
+
|
|
53
|
+
p11 = p0 + wax *w1/2 + hax * h1/2
|
|
54
|
+
p12 = p0 + wax *w1/2 - hax * h1/2
|
|
55
|
+
p13 = p0 - wax *w1/2 - hax * h1/2
|
|
56
|
+
p14 = p0 - wax *w1/2 + hax * h1/2
|
|
57
|
+
|
|
58
|
+
p21 = p1 + wax *w2/2 + hax * h2/2
|
|
59
|
+
p22 = p1 + wax *w2/2 - hax * h2/2
|
|
60
|
+
p23 = p1 - wax *w2/2 - hax * h2/2
|
|
61
|
+
p24 = p1 - wax *w2/2 + hax * h2/2
|
|
62
|
+
|
|
63
|
+
pt11 = gmsh.model.occ.addPoint(*p11)
|
|
64
|
+
pt12 = gmsh.model.occ.addPoint(*p12)
|
|
65
|
+
pt13 = gmsh.model.occ.addPoint(*p13)
|
|
66
|
+
pt14 = gmsh.model.occ.addPoint(*p14)
|
|
67
|
+
pt21 = gmsh.model.occ.addPoint(*p21)
|
|
68
|
+
pt22 = gmsh.model.occ.addPoint(*p22)
|
|
69
|
+
pt23 = gmsh.model.occ.addPoint(*p23)
|
|
70
|
+
pt24 = gmsh.model.occ.addPoint(*p24)
|
|
71
|
+
|
|
72
|
+
l1r = gmsh.model.occ.addLine(pt11, pt12)
|
|
73
|
+
l1b = gmsh.model.occ.addLine(pt12, pt13)
|
|
74
|
+
l1l = gmsh.model.occ.addLine(pt13, pt14)
|
|
75
|
+
l1t = gmsh.model.occ.addLine(pt14, pt11)
|
|
76
|
+
|
|
77
|
+
l2r = gmsh.model.occ.addLine(pt21, pt22)
|
|
78
|
+
l2b = gmsh.model.occ.addLine(pt22, pt23)
|
|
79
|
+
l2l = gmsh.model.occ.addLine(pt23, pt24)
|
|
80
|
+
l2t = gmsh.model.occ.addLine(pt24, pt21)
|
|
81
|
+
|
|
82
|
+
dtr = gmsh.model.occ.addLine(pt11, pt21)
|
|
83
|
+
dbr = gmsh.model.occ.addLine(pt12, pt22)
|
|
84
|
+
dbl = gmsh.model.occ.addLine(pt13, pt23)
|
|
85
|
+
dtl = gmsh.model.occ.addLine(pt14, pt24)
|
|
86
|
+
|
|
87
|
+
wbot = gmsh.model.occ.addWire([l1r, l1b, l1l, l1t])
|
|
88
|
+
wtop = gmsh.model.occ.addWire([l2r, l2b, l2l, l2t])
|
|
89
|
+
wright = gmsh.model.occ.addWire([l1r, dbr, l2r, dtr])
|
|
90
|
+
wleft = gmsh.model.occ.addWire([l1l, dbl, l2l, dtl])
|
|
91
|
+
wfront = gmsh.model.occ.addWire([l1b, dbl, l2b, dbr])
|
|
92
|
+
wback = gmsh.model.occ.addWire([l1t, dtr, l2t, dtl])
|
|
93
|
+
|
|
94
|
+
s1 = gmsh.model.occ.addSurfaceFilling(wbot)
|
|
95
|
+
s2 = gmsh.model.occ.addSurfaceFilling(wtop)
|
|
96
|
+
s3 = gmsh.model.occ.addSurfaceFilling(wright)
|
|
97
|
+
s4 = gmsh.model.occ.addSurfaceFilling(wleft)
|
|
98
|
+
s5 = gmsh.model.occ.addSurfaceFilling(wfront)
|
|
99
|
+
s6 = gmsh.model.occ.addSurfaceFilling(wback)
|
|
100
|
+
|
|
101
|
+
sv = gmsh.model.occ.addSurfaceLoop([s1, s2, s3, s4, s5, s6])
|
|
102
|
+
|
|
103
|
+
self.tags: list[int] = [gmsh.model.occ.addVolume([sv,]),]
|
|
104
|
+
|
|
105
|
+
pc = p0 + dax * height/2
|
|
106
|
+
self._add_face_pointer('front', pc - height/2*dax, -dax)
|
|
107
|
+
self._add_face_pointer('back', pc + height/2*dax, dax)
|