emerge 0.5.1__py3-none-any.whl → 0.5.2__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/_emerge/bc.py +11 -8
- emerge/_emerge/cs.py +2 -2
- emerge/_emerge/elements/femdata.py +14 -14
- emerge/_emerge/elements/index_interp.py +1 -1
- emerge/_emerge/elements/ned2_interp.py +1 -1
- emerge/_emerge/elements/nedelec2.py +4 -4
- emerge/_emerge/elements/nedleg2.py +9 -9
- emerge/_emerge/geo/horn.py +1 -1
- emerge/_emerge/geo/modeler.py +18 -19
- emerge/_emerge/geo/operations.py +13 -10
- emerge/_emerge/geo/pcb.py +70 -69
- emerge/_emerge/geo/pcb_tools/macro.py +14 -13
- emerge/_emerge/geo/pmlbox.py +1 -1
- emerge/_emerge/geometry.py +46 -32
- emerge/_emerge/logsettings.py +3 -3
- emerge/_emerge/material.py +11 -11
- emerge/_emerge/mesh3d.py +81 -59
- emerge/_emerge/mesher.py +26 -21
- emerge/_emerge/mth/pairing.py +2 -2
- emerge/_emerge/periodic.py +34 -31
- emerge/_emerge/physics/microwave/adaptive_freq.py +14 -11
- emerge/_emerge/physics/microwave/assembly/assembler.py +61 -57
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +43 -8
- emerge/_emerge/physics/microwave/assembly/robinbc.py +5 -5
- emerge/_emerge/physics/microwave/microwave_3d.py +40 -20
- emerge/_emerge/physics/microwave/microwave_bc.py +114 -95
- emerge/_emerge/physics/microwave/microwave_data.py +33 -33
- emerge/_emerge/physics/microwave/simjob.py +12 -12
- emerge/_emerge/physics/microwave/sparam.py +12 -12
- emerge/_emerge/physics/microwave/touchstone.py +1 -1
- emerge/_emerge/plot/display.py +12 -6
- emerge/_emerge/plot/pyvista/display.py +44 -39
- emerge/_emerge/plot/pyvista/display_settings.py +1 -1
- emerge/_emerge/plot/simple_plots.py +15 -15
- emerge/_emerge/selection.py +35 -39
- emerge/_emerge/simmodel.py +29 -39
- emerge/_emerge/simulation_data.py +19 -14
- emerge/_emerge/solve_interfaces/pardiso_interface.py +24 -18
- emerge/_emerge/solver.py +52 -52
- emerge/lib.py +243 -243
- {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/METADATA +1 -1
- emerge-0.5.2.dist-info/RECORD +81 -0
- emerge/_emerge/plot/grapher.py +0 -93
- emerge-0.5.1.dist-info/RECORD +0 -82
- {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/WHEEL +0 -0
- {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/entry_points.txt +0 -0
- {emerge-0.5.1.dist-info → emerge-0.5.2.dist-info}/licenses/LICENSE +0 -0
emerge/_emerge/periodic.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from .cs import Axis, _parse_axis, GCS
|
|
2
|
-
from .selection import FaceSelection, SELECTOR_OBJ
|
|
1
|
+
from .cs import Axis, _parse_axis, GCS, _parse_vector
|
|
2
|
+
from .selection import FaceSelection, SELECTOR_OBJ, Selection
|
|
3
3
|
from .geo import GeoPrism, XYPolygon, Alignment, XYPlate
|
|
4
4
|
from .bc import BoundaryCondition
|
|
5
5
|
from typing import Generator
|
|
@@ -21,7 +21,7 @@ def _rotnorm(v: np.ndarray) -> np.ndarray:
|
|
|
21
21
|
ax = ax/np.linalg.norm(ax)
|
|
22
22
|
return ax
|
|
23
23
|
|
|
24
|
-
def _pair_selection(f1:
|
|
24
|
+
def _pair_selection(f1: Selection, f2: Selection, translation: tuple[float, float, float]):
|
|
25
25
|
if len(f1.tags) == 1:
|
|
26
26
|
return [f1,], [f2,]
|
|
27
27
|
c1s = [np.array(c) for c in f1.centers]
|
|
@@ -32,8 +32,8 @@ def _pair_selection(f1: FaceSelection, f2: FaceSelection, translation: tuple[flo
|
|
|
32
32
|
for t1, c1 in zip(f1.tags, c1s):
|
|
33
33
|
for t2, c2 in zip(f2.tags, c2s):
|
|
34
34
|
if np.linalg.norm((c1 + ds)-c2) < 1e-6:
|
|
35
|
-
f1s.append(
|
|
36
|
-
f2s.append(
|
|
35
|
+
f1s.append(Selection([t1,]))
|
|
36
|
+
f2s.append(Selection([t2,]))
|
|
37
37
|
return f1s, f2s
|
|
38
38
|
|
|
39
39
|
|
|
@@ -42,15 +42,16 @@ def _pair_selection(f1: FaceSelection, f2: FaceSelection, translation: tuple[flo
|
|
|
42
42
|
# BASE PERIODIC CELL CLASS #
|
|
43
43
|
############################################################
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
# TODO: This must be moved to mw physics if possible
|
|
46
46
|
class PeriodicCell:
|
|
47
47
|
|
|
48
48
|
def __init__(self,
|
|
49
|
-
origins: list[tuple[float, float, float]],
|
|
50
|
-
vectors: list[tuple[float, float, float] | Axis]):
|
|
51
|
-
|
|
49
|
+
origins: list[tuple[float, float, float] | list[float] | np.ndarray | Axis],
|
|
50
|
+
vectors: list[tuple[float, float, float] | list[float] | np.ndarray | Axis]):
|
|
51
|
+
|
|
52
|
+
self.origins: list[tuple[float, float, float]] = [_parse_vector(origin) for origin in origins] # type: ignore
|
|
52
53
|
self.vectors: list[Axis] = [_parse_axis(vec) for vec in vectors]
|
|
53
|
-
self.excluded_faces:
|
|
54
|
+
self.excluded_faces: Selection | None = None
|
|
54
55
|
self._bcs: list[Periodic] = []
|
|
55
56
|
self._ports: list[BoundaryCondition] = []
|
|
56
57
|
|
|
@@ -71,7 +72,7 @@ class PeriodicCell:
|
|
|
71
72
|
"""
|
|
72
73
|
raise NotImplementedError('This method is not implemented for this subclass.')
|
|
73
74
|
|
|
74
|
-
def cell_data(self) -> Generator[tuple[
|
|
75
|
+
def cell_data(self) -> Generator[tuple[Selection, Selection, np.ndarray], None, None]:
|
|
75
76
|
"""An iterator that yields the two faces of the hex cell plus a cell periodicity vector
|
|
76
77
|
|
|
77
78
|
Yields:
|
|
@@ -84,15 +85,17 @@ class PeriodicCell:
|
|
|
84
85
|
"""
|
|
85
86
|
bcs = []
|
|
86
87
|
for f1, f2, a in self.cell_data():
|
|
88
|
+
f1_new = f1
|
|
89
|
+
f2_new = f2
|
|
87
90
|
if self.excluded_faces is not None:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
bcs.append(Periodic(
|
|
91
|
+
f1_new = f1 - self.excluded_faces # type: ignore
|
|
92
|
+
f2_new = f2 - self.excluded_faces # type: ignore
|
|
93
|
+
bcs.append(Periodic(f1_new, f2_new, tuple(a)))
|
|
91
94
|
self._bcs = bcs
|
|
92
95
|
return bcs
|
|
93
96
|
|
|
94
97
|
@property
|
|
95
|
-
def bcs(self) -> list[
|
|
98
|
+
def bcs(self) -> list[BoundaryCondition]:
|
|
96
99
|
"""Returns a list of Periodic boundary conditions for the given PeriodicCell
|
|
97
100
|
|
|
98
101
|
Args:
|
|
@@ -103,7 +106,7 @@ class PeriodicCell:
|
|
|
103
106
|
"""
|
|
104
107
|
if not self._bcs:
|
|
105
108
|
raise ValueError('Periodic Boundary conditions not generated')
|
|
106
|
-
return self._bcs + self._ports
|
|
109
|
+
return self._bcs + self._ports # type: ignore
|
|
107
110
|
|
|
108
111
|
def set_scanangle(self, theta: float, phi: float, degree: bool = True) -> None:
|
|
109
112
|
"""Sets the scanangle for the periodic condition. (0,0) is defined along the Z-axis
|
|
@@ -126,8 +129,8 @@ class PeriodicCell:
|
|
|
126
129
|
bc.uy = uy
|
|
127
130
|
bc.uz = uz
|
|
128
131
|
for port in self._ports:
|
|
129
|
-
port.scan_theta = theta
|
|
130
|
-
port.scan_phi = phi
|
|
132
|
+
port.scan_theta = theta # type: ignore
|
|
133
|
+
port.scan_phi = phi # type: ignore
|
|
131
134
|
|
|
132
135
|
def port_face(self, z: float):
|
|
133
136
|
raise NotImplementedError('')
|
|
@@ -200,9 +203,9 @@ class RectCell(PeriodicCell):
|
|
|
200
203
|
class HexCell(PeriodicCell):
|
|
201
204
|
|
|
202
205
|
def __init__(self,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
point1: tuple[float, float, float],
|
|
207
|
+
point2: tuple[float, float, float],
|
|
208
|
+
point3: tuple[float, float, float]):
|
|
206
209
|
"""Generates a Hexagonal periodic tiling by providing 4 coordinates. The layout of the tiling is as following
|
|
207
210
|
Assuming a hexagon with a single vertext at the top and bottom and two vertices on the left and right faces ⬢
|
|
208
211
|
|
|
@@ -211,7 +214,7 @@ class HexCell(PeriodicCell):
|
|
|
211
214
|
p2 (tuple[float, float, float]): left face bottom vertex
|
|
212
215
|
p3 (tuple[float, float, float]): bottom vertex
|
|
213
216
|
"""
|
|
214
|
-
p1, p2, p3 =
|
|
217
|
+
p1, p2, p3 = np.array(point1), np.array(point2), np.array(point3)
|
|
215
218
|
p4 = -p1
|
|
216
219
|
self.p1: np.ndarray = p1
|
|
217
220
|
self.p2: np.ndarray = p2
|
|
@@ -240,7 +243,7 @@ class HexCell(PeriodicCell):
|
|
|
240
243
|
poly = XYPolygon(xs, ys).geo(GCS.displace(0,0,zs[0]))
|
|
241
244
|
return poly
|
|
242
245
|
|
|
243
|
-
def cell_data(self) -> Generator[
|
|
246
|
+
def cell_data(self) -> Generator[tuple[Selection, Selection, np.ndarray], None, None]:
|
|
244
247
|
nrm = np.linalg.norm
|
|
245
248
|
|
|
246
249
|
o = self.o1[:-1]
|
|
@@ -250,7 +253,7 @@ class HexCell(PeriodicCell):
|
|
|
250
253
|
f2s = SELECTOR_OBJ.inplane(*self.f12[0], *self.f12[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
251
254
|
vec = - (self.p1 + self.p2)
|
|
252
255
|
|
|
253
|
-
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
|
|
256
|
+
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
|
|
254
257
|
yield f1, f2, vec
|
|
255
258
|
|
|
256
259
|
o = self.o2[:-1]
|
|
@@ -259,7 +262,7 @@ class HexCell(PeriodicCell):
|
|
|
259
262
|
f1s = SELECTOR_OBJ.inplane(*self.f21[0], *self.f21[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
|
|
260
263
|
f2s = SELECTOR_OBJ.inplane(*self.f22[0], *self.f22[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
261
264
|
vec = - (self.p2 + self.p3)
|
|
262
|
-
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
|
|
265
|
+
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
|
|
263
266
|
yield f1, f2, vec
|
|
264
267
|
|
|
265
268
|
o = self.o3[:-1]
|
|
@@ -268,18 +271,18 @@ class HexCell(PeriodicCell):
|
|
|
268
271
|
f1s = SELECTOR_OBJ.inplane(*self.f31[0], *self.f31[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])-o)>w) or (abs((np.array([x,y])-o) @ n ) > 1e-6))
|
|
269
272
|
f2s = SELECTOR_OBJ.inplane(*self.f32[0], *self.f32[1]).exclude(lambda x, y, z: (nrm(np.array([x,y])+o)>w) or (abs((np.array([x,y])+o) @ n ) > 1e-6))
|
|
270
273
|
vec = - (self.p3 - self.p1)
|
|
271
|
-
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)):
|
|
274
|
+
for f1, f2 in zip(*_pair_selection(f1s, f2s, vec)): # type: ignore
|
|
272
275
|
yield f1, f2, vec
|
|
273
276
|
|
|
274
277
|
def volume(self,
|
|
275
278
|
z1: float,
|
|
276
279
|
z2: float) -> GeoPrism:
|
|
277
280
|
xs, ys, zs = zip(self.p1, self.p2, self.p3)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
poly = XYPolygon(
|
|
281
|
+
xs2 = np.array(xs) # type: ignore
|
|
282
|
+
ys2 = np.array(ys) # type: ignore
|
|
283
|
+
xs3 = np.concatenate([xs2, -xs2]) # type: ignore
|
|
284
|
+
ys3 = np.concatenate([ys2, -ys2]) # type: ignore
|
|
285
|
+
poly = XYPolygon(xs3, ys3)
|
|
283
286
|
length = z2-z1
|
|
284
287
|
return poly.extrude(length, cs=GCS.displace(0,0,z1))
|
|
285
288
|
|
|
@@ -49,14 +49,17 @@ Modification history
|
|
|
49
49
|
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
+
# ty: ignore
|
|
53
|
+
|
|
52
54
|
import numpy as np
|
|
53
55
|
from typing import Literal
|
|
54
56
|
from loguru import logger
|
|
57
|
+
|
|
55
58
|
def cc(z):
|
|
56
59
|
return z.conjugate()
|
|
57
60
|
|
|
58
61
|
def model(s, poles, residues, d, h):
|
|
59
|
-
return
|
|
62
|
+
return sum([r/(s-p) for p, r in zip(poles, residues)]) + d + s*h
|
|
60
63
|
|
|
61
64
|
def vectfit_step(f: np.ndarray, s: np.ndarray, poles: np.ndarray) -> np.ndarray:
|
|
62
65
|
"""
|
|
@@ -166,12 +169,12 @@ def calculate_residues(f: np.ndarray, s: np.ndarray, poles: np.ndarray, rcond=-1
|
|
|
166
169
|
b = np.concatenate((b.real, b.imag))
|
|
167
170
|
cA = np.linalg.cond(A)
|
|
168
171
|
if cA > 1e13:
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
logger.warning('Warning!: Ill Conditioned Matrix. Consider scaling the problem down')
|
|
173
|
+
logger.warning('Cond(A)', cA)
|
|
171
174
|
x, residuals, rnk, s = np.linalg.lstsq(A, b, rcond=rcond)
|
|
172
175
|
|
|
173
176
|
# Recover complex values
|
|
174
|
-
x = np.complex128
|
|
177
|
+
x = x.astype(np.complex128)
|
|
175
178
|
for i, ci in enumerate(cindex):
|
|
176
179
|
if ci == 1:
|
|
177
180
|
r1, r2 = x[i:i+2]
|
|
@@ -190,7 +193,7 @@ def vectfit_auto(f: np.ndarray,
|
|
|
190
193
|
inc_real: bool = False,
|
|
191
194
|
loss_ratio: float = 1e-2,
|
|
192
195
|
rcond: int = -1,
|
|
193
|
-
track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float]:
|
|
196
|
+
track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float, np.ndarray]:
|
|
194
197
|
w = s.imag
|
|
195
198
|
pole_locs = np.linspace(w[0], w[-1], n_poles+2)[1:-1]
|
|
196
199
|
lr = loss_ratio
|
|
@@ -208,7 +211,7 @@ def vectfit_auto(f: np.ndarray,
|
|
|
208
211
|
|
|
209
212
|
if track_poles:
|
|
210
213
|
return poles, residues, d, h, np.array(poles_list)
|
|
211
|
-
return poles, residues, d, h
|
|
214
|
+
return poles, residues, d, h, np.array([])
|
|
212
215
|
|
|
213
216
|
def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
|
|
214
217
|
n_poles: int = 10,
|
|
@@ -216,10 +219,10 @@ def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
|
|
|
216
219
|
inc_real: bool = False,
|
|
217
220
|
loss_ratio: float = 1e-2,
|
|
218
221
|
rcond: int = -1,
|
|
219
|
-
track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float]:
|
|
222
|
+
track_poles: bool = False) -> tuple[np.ndarray, np.ndarray, float, float, np.ndarray]:
|
|
220
223
|
s_scale = abs(s[-1])
|
|
221
224
|
f_scale = abs(f[-1])
|
|
222
|
-
poles_s, residues_s, d_s, h_s = vectfit_auto(f / f_scale,
|
|
225
|
+
poles_s, residues_s, d_s, h_s, tracked_poles = vectfit_auto(f / f_scale,
|
|
223
226
|
s / s_scale,
|
|
224
227
|
n_poles=n_poles,
|
|
225
228
|
n_iter = n_iter,
|
|
@@ -231,7 +234,7 @@ def vectfit_auto_rescale(f: np.ndarray, s: np.ndarray,
|
|
|
231
234
|
residues = residues_s * f_scale * s_scale
|
|
232
235
|
d = d_s * f_scale
|
|
233
236
|
h = h_s * f_scale / s_scale
|
|
234
|
-
return poles, residues, d, h
|
|
237
|
+
return poles, residues, d, h, tracked_poles
|
|
235
238
|
|
|
236
239
|
|
|
237
240
|
class SparamModel:
|
|
@@ -251,7 +254,7 @@ class SparamModel:
|
|
|
251
254
|
fdense = np.linspace(min(self.f), max(self.f), max(201, 10*self.f.shape[0]))
|
|
252
255
|
success = False
|
|
253
256
|
for nps in range(1,maxpoles):
|
|
254
|
-
poles, residues, d, h = vectfit_auto_rescale(Sparam, s, n_poles=nps, inc_real=inc_real)
|
|
257
|
+
poles, residues, d, h, _ = vectfit_auto_rescale(Sparam, s, n_poles=nps, inc_real=inc_real)
|
|
255
258
|
self.poles: np.ndarray = poles
|
|
256
259
|
self.residues: np.ndarray = residues
|
|
257
260
|
self.d = d
|
|
@@ -268,7 +271,7 @@ class SparamModel:
|
|
|
268
271
|
logger.warning('Could not model S-parameters. Try a denser grid')
|
|
269
272
|
|
|
270
273
|
else:
|
|
271
|
-
poles, residues, d, h = vectfit_auto_rescale(Sparam, s, n_poles=n_poles, inc_real=inc_real)
|
|
274
|
+
poles, residues, d, h, _ = vectfit_auto_rescale(Sparam, s, n_poles=n_poles, inc_real=inc_real)
|
|
272
275
|
self.poles: np.ndarray = poles
|
|
273
276
|
self.residues: np.ndarray = residues
|
|
274
277
|
self.d = d
|
|
@@ -124,7 +124,7 @@ class Assembler:
|
|
|
124
124
|
sig: np.ndarray,
|
|
125
125
|
k0: float,
|
|
126
126
|
port: PortBC,
|
|
127
|
-
bcs: list[BoundaryCondition]) -> tuple[
|
|
127
|
+
bcs: list[BoundaryCondition]) -> tuple[csr_matrix, csr_matrix, np.ndarray, NedelecLegrange2]:
|
|
128
128
|
"""Computes the boundary mode analysis matrices
|
|
129
129
|
|
|
130
130
|
Args:
|
|
@@ -194,12 +194,9 @@ class Assembler:
|
|
|
194
194
|
tids = nedlegfield.tri_to_field[:, i2]
|
|
195
195
|
pec_ids.extend(list(tids))
|
|
196
196
|
|
|
197
|
-
port._field = nedlegfield
|
|
198
|
-
port._pece = pec_edges
|
|
199
|
-
port._pecv = pec_vertices
|
|
200
197
|
# Process all port boundary Conditions
|
|
201
|
-
|
|
202
|
-
solve_ids = [i for i in range(nedlegfield.n_field) if i not in
|
|
198
|
+
pec_ids_set: set[int] = set(pec_ids)
|
|
199
|
+
solve_ids = [i for i in range(nedlegfield.n_field) if i not in pec_ids_set]
|
|
203
200
|
|
|
204
201
|
return E, B, np.array(solve_ids), nedlegfield
|
|
205
202
|
|
|
@@ -234,7 +231,7 @@ class Assembler:
|
|
|
234
231
|
|
|
235
232
|
mesh = field.mesh
|
|
236
233
|
er = er - 1j*sig/(W0*EPS0)*np.repeat(np.eye(3)[:, :, np.newaxis], er.shape[2], axis=2)
|
|
237
|
-
is_frequency_dependent: bool = np.any((sig > 0) & (sig < self.conductivity_limit))
|
|
234
|
+
is_frequency_dependent: bool = np.any((sig > 0) & (sig < self.conductivity_limit)) # type: ignore
|
|
238
235
|
|
|
239
236
|
|
|
240
237
|
if cache_matrices and not is_frequency_dependent and self.cached_matrices is not None:
|
|
@@ -254,7 +251,7 @@ class Assembler:
|
|
|
254
251
|
|
|
255
252
|
# ISOLATE BOUNDARY CONDITIONS TO ASSEMBLE
|
|
256
253
|
pec_bcs: list[PEC] = [bc for bc in bcs if isinstance(bc,PEC)]
|
|
257
|
-
robin_bcs: list[
|
|
254
|
+
robin_bcs: list[RobinBC] = [bc for bc in bcs if isinstance(bc,RobinBC)]
|
|
258
255
|
port_bcs: list[PortBC] = [bc for bc in bcs if isinstance(bc, PortBC)]
|
|
259
256
|
periodic_bcs: list[Periodic] = [bc for bc in bcs if isinstance(bc, Periodic)]
|
|
260
257
|
|
|
@@ -264,7 +261,7 @@ class Assembler:
|
|
|
264
261
|
|
|
265
262
|
# Process all PEC Boundary Conditions
|
|
266
263
|
logger.debug(' Implementing PEC Boundary Conditions.')
|
|
267
|
-
pec_ids = []
|
|
264
|
+
pec_ids: list[int] = []
|
|
268
265
|
# Conductivity above al imit, consider it all PEC
|
|
269
266
|
for itet in range(field.n_tets):
|
|
270
267
|
if sig[itet] > self.conductivity_limit:
|
|
@@ -310,13 +307,13 @@ class Assembler:
|
|
|
310
307
|
basis = plane_basis_from_points(mesh.nodes[:,nodes]) + 1e-16
|
|
311
308
|
ibasis = np.linalg.pinv(basis)
|
|
312
309
|
if bc._include_force:
|
|
313
|
-
|
|
314
|
-
Bempty, b_p = assemble_robin_bc_excited(field, Bempty, tri_ids, Ufunc, gamma, ibasis, bc.cs.origin, gauss_points)
|
|
315
310
|
|
|
316
|
-
|
|
311
|
+
Bempty, b_p = assemble_robin_bc_excited(field, Bempty, tri_ids, Ufunc, gamma, ibasis, bc.cs.origin, gauss_points) # type: ignore
|
|
312
|
+
|
|
313
|
+
port_vectors[bc.port_number] += b_p # type: ignore
|
|
317
314
|
|
|
318
315
|
else:
|
|
319
|
-
Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma)
|
|
316
|
+
Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma) # type: ignore
|
|
320
317
|
B_p = field.generate_csr(Bempty)
|
|
321
318
|
K = K + B_p
|
|
322
319
|
|
|
@@ -326,20 +323,20 @@ class Assembler:
|
|
|
326
323
|
|
|
327
324
|
# Periodic BCs
|
|
328
325
|
Pmats = []
|
|
329
|
-
remove = set()
|
|
326
|
+
remove: set[int] = set()
|
|
330
327
|
has_periodic = False
|
|
331
328
|
|
|
332
|
-
for
|
|
329
|
+
for pbc in periodic_bcs:
|
|
333
330
|
has_periodic = True
|
|
334
|
-
tri_ids_1 = mesh.get_triangles(
|
|
335
|
-
edge_ids_1 = mesh.get_edges(
|
|
336
|
-
tri_ids_2 = mesh.get_triangles(
|
|
337
|
-
edge_ids_2 = mesh.get_edges(
|
|
338
|
-
dv = np.array(
|
|
331
|
+
tri_ids_1 = mesh.get_triangles(pbc.face1.tags)
|
|
332
|
+
edge_ids_1 = mesh.get_edges(pbc.face1.tags)
|
|
333
|
+
tri_ids_2 = mesh.get_triangles(pbc.face2.tags)
|
|
334
|
+
edge_ids_2 = mesh.get_edges(pbc.face2.tags)
|
|
335
|
+
dv = np.array(pbc.dv)
|
|
339
336
|
linked_tris = pair_coordinates(mesh.tri_centers, tri_ids_1, tri_ids_2, dv, 1e-9)
|
|
340
337
|
linked_edges = pair_coordinates(mesh.edge_centers, edge_ids_1, edge_ids_2, dv, 1e-9)
|
|
341
|
-
dv = np.array(
|
|
342
|
-
phi =
|
|
338
|
+
dv = np.array(pbc.dv)
|
|
339
|
+
phi = pbc.phi(K0)
|
|
343
340
|
|
|
344
341
|
Pmat, rows = gen_periodic_matrix(tri_ids_1,
|
|
345
342
|
edge_ids_1,
|
|
@@ -356,15 +353,15 @@ class Assembler:
|
|
|
356
353
|
Pmat = Pmats[0]
|
|
357
354
|
for P2 in Pmats[1:]:
|
|
358
355
|
Pmat = Pmat @ P2
|
|
359
|
-
|
|
356
|
+
remove_array = np.sort(np.unique(list(remove)))
|
|
360
357
|
all_indices = np.arange(NF)
|
|
361
|
-
keep_indices = np.setdiff1d(all_indices,
|
|
358
|
+
keep_indices = np.setdiff1d(all_indices, remove_array)
|
|
362
359
|
Pmat = Pmat[:,keep_indices]
|
|
363
360
|
else:
|
|
364
361
|
Pmat = None
|
|
365
362
|
|
|
366
|
-
|
|
367
|
-
solve_ids = np.array([i for i in range(E.shape[0]) if i not in
|
|
363
|
+
pec_ids_set = set(pec_ids)
|
|
364
|
+
solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids_set])
|
|
368
365
|
|
|
369
366
|
if has_periodic:
|
|
370
367
|
mask = np.zeros((NF,))
|
|
@@ -426,11 +423,11 @@ class Assembler:
|
|
|
426
423
|
NF = E.shape[0]
|
|
427
424
|
|
|
428
425
|
pecs: list[PEC] = [bc for bc in bcs if isinstance(bc,PEC)]
|
|
429
|
-
robin_bcs: list[RectangularWaveguide] = [bc for bc in bcs if isinstance(bc,RobinBC)]
|
|
426
|
+
robin_bcs: list[RectangularWaveguide] = [bc for bc in bcs if isinstance(bc,RobinBC)] # type: ignore
|
|
430
427
|
periodic: list[Periodic] = [bc for bc in bcs if isinstance(bc, Periodic)]
|
|
431
428
|
|
|
432
429
|
# Process all PEC Boundary Conditions
|
|
433
|
-
pec_ids = []
|
|
430
|
+
pec_ids: list = []
|
|
434
431
|
|
|
435
432
|
logger.debug(' Implementing PEC Boundary Conditions.')
|
|
436
433
|
|
|
@@ -459,23 +456,30 @@ class Assembler:
|
|
|
459
456
|
if len(robin_bcs) > 0:
|
|
460
457
|
logger.debug(' Implementing Robin Boundary Conditions.')
|
|
461
458
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
459
|
+
if len(robin_bcs) > 0:
|
|
460
|
+
logger.debug(' Implementing Robin Boundary Conditions.')
|
|
461
|
+
|
|
462
|
+
gauss_points = gaus_quad_tri(4)
|
|
463
|
+
Bempty = field.empty_tri_matrix()
|
|
464
|
+
for bc in robin_bcs:
|
|
465
|
+
|
|
466
|
+
for tag in bc.tags:
|
|
467
|
+
face_tags = [tag,]
|
|
468
|
+
|
|
469
|
+
tri_ids = mesh.get_triangles(face_tags)
|
|
470
|
+
nodes = mesh.get_nodes(face_tags)
|
|
471
|
+
edge_ids = list(mesh.tri_to_edge[:,tri_ids].flatten())
|
|
472
|
+
|
|
473
|
+
gamma = bc.get_gamma(k0)
|
|
474
|
+
|
|
475
|
+
ibasis = bc.get_inv_basis()
|
|
476
|
+
if ibasis is None:
|
|
477
|
+
basis = plane_basis_from_points(mesh.nodes[:,nodes]) + 1e-16
|
|
478
|
+
ibasis = np.linalg.pinv(basis)
|
|
479
|
+
|
|
480
|
+
Bempty = assemble_robin_bc(field, Bempty, tri_ids, gamma) # type: ignore
|
|
481
|
+
B_p = field.generate_csr(Bempty)
|
|
482
|
+
B = B + B_p
|
|
479
483
|
|
|
480
484
|
if len(periodic) > 0:
|
|
481
485
|
logger.debug(' Implementing Periodic Boundary Conditions.')
|
|
@@ -485,17 +489,17 @@ class Assembler:
|
|
|
485
489
|
remove = set()
|
|
486
490
|
has_periodic = False
|
|
487
491
|
|
|
488
|
-
for
|
|
492
|
+
for bcp in periodic:
|
|
489
493
|
has_periodic = True
|
|
490
|
-
tri_ids_1 = mesh.get_triangles(
|
|
491
|
-
edge_ids_1 = mesh.get_edges(
|
|
492
|
-
tri_ids_2 = mesh.get_triangles(
|
|
493
|
-
edge_ids_2 = mesh.get_edges(
|
|
494
|
-
dv = np.array(
|
|
494
|
+
tri_ids_1 = mesh.get_triangles(bcp.face1.tags)
|
|
495
|
+
edge_ids_1 = mesh.get_edges(bcp.face1.tags)
|
|
496
|
+
tri_ids_2 = mesh.get_triangles(bcp.face2.tags)
|
|
497
|
+
edge_ids_2 = mesh.get_edges(bcp.face2.tags)
|
|
498
|
+
dv = np.array(bcp.dv)
|
|
495
499
|
linked_tris = pair_coordinates(mesh.tri_centers, tri_ids_1, tri_ids_2, dv, 1e-9)
|
|
496
500
|
linked_edges = pair_coordinates(mesh.edge_centers, edge_ids_1, edge_ids_2, dv, 1e-9)
|
|
497
|
-
dv = np.array(
|
|
498
|
-
phi =
|
|
501
|
+
dv = np.array(bcp.dv)
|
|
502
|
+
phi = bcp.phi(k0)
|
|
499
503
|
|
|
500
504
|
Pmat, rows = gen_periodic_matrix(tri_ids_1,
|
|
501
505
|
edge_ids_1,
|
|
@@ -513,15 +517,15 @@ class Assembler:
|
|
|
513
517
|
for P2 in Pmats[1:]:
|
|
514
518
|
Pmat = Pmat @ P2
|
|
515
519
|
Pmat = Pmat.tocsr()
|
|
516
|
-
|
|
520
|
+
remove_array = np.sort(np.array(list(remove)))
|
|
517
521
|
all_indices = np.arange(NF)
|
|
518
|
-
keep_indices = np.setdiff1d(all_indices,
|
|
522
|
+
keep_indices = np.setdiff1d(all_indices, remove_array)
|
|
519
523
|
Pmat = Pmat[:,keep_indices]
|
|
520
524
|
else:
|
|
521
525
|
Pmat = None
|
|
522
526
|
|
|
523
|
-
|
|
524
|
-
solve_ids = np.array([i for i in range(E.shape[0]) if i not in
|
|
527
|
+
pec_ids_set = set(pec_ids)
|
|
528
|
+
solve_ids = np.array([i for i in range(E.shape[0]) if i not in pec_ids_set])
|
|
525
529
|
|
|
526
530
|
if has_periodic:
|
|
527
531
|
mask = np.zeros((NF,))
|
|
@@ -17,21 +17,33 @@
|
|
|
17
17
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
from ....elements.nedleg2 import NedelecLegrange2
|
|
20
|
-
from scipy.sparse import
|
|
20
|
+
from scipy.sparse import csr_matrix
|
|
21
21
|
from numba_progress import ProgressBar, ProgressBarType
|
|
22
22
|
from ....mth.optimized import local_mapping, matinv, compute_distances
|
|
23
23
|
from numba import c16, types, f8, i8, njit, prange
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
############################################################
|
|
28
|
+
# FIELD MAPPING #
|
|
29
|
+
############################################################
|
|
30
|
+
|
|
25
31
|
@njit(i8[:,:](i8, i8[:,:], i8[:,:], i8[:,:]), cache=True, nogil=True)
|
|
26
32
|
def local_tri_to_edgeid(itri: int, tris, edges, tri_to_edge) -> np.ndarray:
|
|
27
33
|
global_edge_map = edges[:, tri_to_edge[:,itri]]
|
|
28
34
|
return local_mapping(tris[:, itri], global_edge_map)
|
|
29
35
|
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
############################################################
|
|
39
|
+
# PYTHON INTERFACE #
|
|
40
|
+
############################################################
|
|
41
|
+
|
|
30
42
|
def generelized_eigenvalue_matrix(field: NedelecLegrange2,
|
|
31
43
|
er: np.ndarray,
|
|
32
44
|
ur: np.ndarray,
|
|
33
45
|
basis: np.ndarray,
|
|
34
|
-
k0: float,) -> tuple[
|
|
46
|
+
k0: float,) -> tuple[csr_matrix, csr_matrix]:
|
|
35
47
|
|
|
36
48
|
tris = field.mesh.tris
|
|
37
49
|
edges = field.mesh.edges
|
|
@@ -47,11 +59,16 @@ def generelized_eigenvalue_matrix(field: NedelecLegrange2,
|
|
|
47
59
|
|
|
48
60
|
nfield = field.n_field
|
|
49
61
|
|
|
50
|
-
E =
|
|
51
|
-
B =
|
|
62
|
+
E = csr_matrix((dataE, (rows, cols)), shape=(nfield, nfield))
|
|
63
|
+
B = csr_matrix((dataB, (rows, cols)), shape=(nfield, nfield))
|
|
52
64
|
|
|
53
65
|
return E, B
|
|
54
66
|
|
|
67
|
+
|
|
68
|
+
############################################################
|
|
69
|
+
# MATRIX MULTIPLICATION #
|
|
70
|
+
############################################################
|
|
71
|
+
|
|
55
72
|
@njit(c16[:,:](c16[:,:], c16[:,:]), cache=True, nogil=True)
|
|
56
73
|
def matmul(a, b):
|
|
57
74
|
out = np.empty((2,b.shape[1]), dtype=np.complex128)
|
|
@@ -59,7 +76,10 @@ def matmul(a, b):
|
|
|
59
76
|
out[1,:] = a[1,0]*b[0,:] + a[1,1]*b[1,:]
|
|
60
77
|
return out
|
|
61
78
|
|
|
62
|
-
|
|
79
|
+
|
|
80
|
+
############################################################
|
|
81
|
+
# GAUSS QUADRATURE IMPLEMENTATION #
|
|
82
|
+
############################################################
|
|
63
83
|
|
|
64
84
|
@njit(c16(c16[:], c16[:], types.Array(types.float64, 1, 'A', readonly=True)), cache=True, nogil=True)
|
|
65
85
|
def _gqi(v1, v2, W):
|
|
@@ -185,7 +205,12 @@ def _nf2_curl(coeff, coords):
|
|
|
185
205
|
ys = coords[1,:]
|
|
186
206
|
return b3*(c1*(a2 + b2*xs + c2*ys) - c2*(a1 + b1*xs + c1*ys)) - c3*(b1*(a2 + b2*xs + c2*ys) - b2*(a1 + b1*xs + c1*ys)) - 2*(b1*c2 - b2*c1)*(a3 + b3*xs + c3*ys) + 0*1j
|
|
187
207
|
|
|
188
|
-
|
|
208
|
+
|
|
209
|
+
############################################################
|
|
210
|
+
# TRIANGLE BARYCENTRIC COORDINATE LIN. COEFFICIENTS #
|
|
211
|
+
############################################################
|
|
212
|
+
|
|
213
|
+
|
|
189
214
|
@njit(types.Tuple((f8[:], f8[:], f8[:], f8))(f8[:], f8[:]), cache = True, nogil=True)
|
|
190
215
|
def tri_coefficients(vxs, vys):
|
|
191
216
|
|
|
@@ -211,13 +236,23 @@ def tri_coefficients(vxs, vys):
|
|
|
211
236
|
Cs = np.array([c1, c2, c3])*sign
|
|
212
237
|
return As, Bs, Cs, A
|
|
213
238
|
|
|
214
|
-
|
|
239
|
+
|
|
240
|
+
############################################################
|
|
241
|
+
# CONSTANT DEFINITION #
|
|
242
|
+
############################################################
|
|
243
|
+
|
|
215
244
|
|
|
216
245
|
DPTS = np.array([[0.22338159, 0.22338159, 0.22338159, 0.10995174, 0.10995174, 0.10995174],
|
|
217
246
|
[0.10810302, 0.44594849, 0.44594849, 0.81684757, 0.09157621, 0.09157621],
|
|
218
247
|
[0.44594849, 0.44594849, 0.10810302, 0.09157621, 0.09157621, 0.81684757],
|
|
219
248
|
[0.44594849, 0.10810302, 0.44594849, 0.09157621, 0.81684757, 0.09157621]], dtype=np.float64)
|
|
220
249
|
|
|
250
|
+
|
|
251
|
+
############################################################
|
|
252
|
+
# NUMBA OPTIMIZED ASSEMBLER #
|
|
253
|
+
############################################################
|
|
254
|
+
|
|
255
|
+
|
|
221
256
|
@njit(types.Tuple((c16[:,:], c16[:,:]))(f8[:,:], i8[:,:], c16[:,:], c16[:,:], f8), cache=True, nogil=True)
|
|
222
257
|
def generalized_matrix_GQ(tri_vertices, local_edge_map, Ms, Mm, k0):
|
|
223
258
|
'''Nedelec-2 Triangle stiffness and mass submatrix'''
|
|
@@ -402,7 +437,7 @@ def _matrix_builder(nodes, tris, edges, tri_to_field, ur, er, k0, pgb: ProgressB
|
|
|
402
437
|
|
|
403
438
|
tri_to_edge = tri_to_field[:3,:]
|
|
404
439
|
|
|
405
|
-
for itri in prange(ntritot):
|
|
440
|
+
for itri in prange(ntritot): # type: ignore
|
|
406
441
|
p = itri*196
|
|
407
442
|
if np.mod(itri,10)==0:
|
|
408
443
|
pgb.update(10)
|
|
@@ -252,7 +252,7 @@ def ned2_tri_stiff_force(lcs_vertices, gamma, lcs_Uinc, DPTs):
|
|
|
252
252
|
def compute_bc_entries_excited(vertices_local, tris, Bmat, Bvec, surf_triangle_indices, gamma, Ulocal_all, DPTs, tri_to_field):
|
|
253
253
|
N = 64
|
|
254
254
|
Niter = surf_triangle_indices.shape[0]
|
|
255
|
-
for i in prange(Niter):
|
|
255
|
+
for i in prange(Niter): # type: ignore
|
|
256
256
|
itri = surf_triangle_indices[i]
|
|
257
257
|
|
|
258
258
|
vertex_ids = tris[:, itri]
|
|
@@ -388,7 +388,7 @@ def ned2_tri_stiff(vertices, edge_lengths, gamma):
|
|
|
388
388
|
def compute_bc_entries(vertices, tris, Bmat, all_edge_lengths, surf_triangle_indices, gamma):
|
|
389
389
|
N = 64
|
|
390
390
|
Niter = surf_triangle_indices.shape[0]
|
|
391
|
-
for i in prange(Niter):
|
|
391
|
+
for i in prange(Niter): # type: ignore
|
|
392
392
|
itri = surf_triangle_indices[i]
|
|
393
393
|
|
|
394
394
|
vertex_ids = tris[:, itri]
|
|
@@ -404,7 +404,7 @@ def assemble_robin_bc_excited(field: Nedelec2,
|
|
|
404
404
|
Bmat: np.ndarray,
|
|
405
405
|
surf_triangle_indices: np.ndarray,
|
|
406
406
|
Ufunc: Callable,
|
|
407
|
-
gamma:
|
|
407
|
+
gamma: complex,
|
|
408
408
|
local_basis: np.ndarray,
|
|
409
409
|
origin: np.ndarray,
|
|
410
410
|
DPTs: np.ndarray):
|
|
@@ -424,8 +424,8 @@ def assemble_robin_bc_excited(field: Nedelec2,
|
|
|
424
424
|
|
|
425
425
|
def assemble_robin_bc(field: Nedelec2,
|
|
426
426
|
Bmat: np.ndarray,
|
|
427
|
-
|
|
428
|
-
|
|
427
|
+
surf_triangle_indices: np.ndarray,
|
|
428
|
+
gamma: np.ndarray):
|
|
429
429
|
vertices = field.mesh.nodes
|
|
430
430
|
all_edge_lengths = field.mesh.edge_lengths[field.mesh.tri_to_edge]
|
|
431
431
|
Bmat = compute_bc_entries(vertices, field.mesh.tris, Bmat, all_edge_lengths, surf_triangle_indices, gamma)
|