emerge 1.0.7__py3-none-any.whl → 1.1.1__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 +15 -3
- emerge/_emerge/const.py +2 -1
- emerge/_emerge/elements/ned2_interp.py +122 -42
- emerge/_emerge/geo/__init__.py +1 -1
- emerge/_emerge/geo/operations.py +20 -0
- emerge/_emerge/geo/pcb.py +162 -71
- emerge/_emerge/geo/shapes.py +12 -7
- emerge/_emerge/geo/step.py +177 -41
- emerge/_emerge/geometry.py +189 -27
- emerge/_emerge/logsettings.py +26 -2
- emerge/_emerge/material.py +2 -0
- emerge/_emerge/mesh3d.py +6 -8
- emerge/_emerge/mesher.py +67 -11
- emerge/_emerge/mth/common_functions.py +1 -1
- emerge/_emerge/mth/optimized.py +2 -2
- emerge/_emerge/physics/microwave/adaptive_mesh.py +549 -116
- emerge/_emerge/physics/microwave/assembly/assembler.py +9 -1
- emerge/_emerge/physics/microwave/microwave_3d.py +133 -83
- emerge/_emerge/physics/microwave/microwave_bc.py +158 -8
- emerge/_emerge/physics/microwave/microwave_data.py +94 -5
- emerge/_emerge/plot/pyvista/display.py +36 -23
- emerge/_emerge/selection.py +17 -2
- emerge/_emerge/settings.py +124 -6
- emerge/_emerge/simmodel.py +273 -150
- emerge/_emerge/simstate.py +106 -0
- emerge/_emerge/simulation_data.py +11 -23
- emerge/_emerge/solve_interfaces/cudss_interface.py +20 -1
- emerge/_emerge/solver.py +4 -4
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/METADATA +7 -3
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/RECORD +33 -32
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/WHEEL +0 -0
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/entry_points.txt +0 -0
- {emerge-1.0.7.dist-info → emerge-1.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -18,75 +18,240 @@
|
|
|
18
18
|
from .microwave_data import MWField
|
|
19
19
|
import numpy as np
|
|
20
20
|
from ...mth.optimized import matmul, outward_normal, cross_c
|
|
21
|
-
from numba import njit, f8, c16, i8, types # type: ignore
|
|
21
|
+
from numba import njit, f8, c16, i8, types, prange # type: ignore, p
|
|
22
|
+
from loguru import logger
|
|
23
|
+
from ...const import C0, MU0, EPS0
|
|
24
|
+
|
|
25
|
+
@njit(cache=True, nogil=True)
|
|
26
|
+
def diam_circum_circle(v1: np.ndarray, v2: np.ndarray, v3: np.ndarray, eps: float = 1e-14) -> float:
|
|
27
|
+
"""
|
|
28
|
+
Diameter of the circumcircle of triangle (v1,v2,v3) in 3D.
|
|
29
|
+
Uses D = (|AB|*|AC|*|BC|) / ||AB x AC||.
|
|
30
|
+
Returns np.inf if points are (near) collinear.
|
|
31
|
+
"""
|
|
32
|
+
AB = v2 - v1
|
|
33
|
+
AC = v3 - v1
|
|
34
|
+
BC = v3 - v2
|
|
35
|
+
|
|
36
|
+
a = np.linalg.norm(BC)
|
|
37
|
+
b = np.linalg.norm(AC)
|
|
38
|
+
c = np.linalg.norm(AB)
|
|
39
|
+
|
|
40
|
+
cross = np.cross(AB, AC)
|
|
41
|
+
denom = np.linalg.norm(cross) # 2 * area of triangle
|
|
42
|
+
|
|
43
|
+
if denom < eps:
|
|
44
|
+
return np.inf # degenerate/collinear
|
|
45
|
+
|
|
46
|
+
return (a * b * c) / denom # diameter = 2R
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@njit(cache=True, nogil=True)
|
|
50
|
+
def circum_sphere_diam(v1: np.ndarray, v2: np.ndarray, v3: np.ndarray, v4: np.ndarray, eps: float = 1e-14) -> float:
|
|
51
|
+
"""
|
|
52
|
+
Diameter of the circumsphere of tetrahedron (v1,v2,v3,v4) in 3D.
|
|
53
|
+
Solves for center c from 2*(pi - p4) · c = |pi|^2 - |p4|^2, i=1..3.
|
|
54
|
+
Returns np.inf if points are (near) coplanar/degenerate.
|
|
55
|
+
"""
|
|
56
|
+
p1, p2, p3, p4 = v1, v2, v3, v4
|
|
57
|
+
|
|
58
|
+
M = np.empty((3, 3), dtype=np.float64)
|
|
59
|
+
M[0, :] = 2.0 * (p1 - p4)
|
|
60
|
+
M[1, :] = 2.0 * (p2 - p4)
|
|
61
|
+
M[2, :] = 2.0 * (p3 - p4)
|
|
62
|
+
|
|
63
|
+
# manual 3x3 determinant (Numba-friendly)
|
|
64
|
+
det = (
|
|
65
|
+
M[0,0] * (M[1,1]*M[2,2] - M[1,2]*M[2,1])
|
|
66
|
+
- M[0,1] * (M[1,0]*M[2,2] - M[1,2]*M[2,0])
|
|
67
|
+
+ M[0,2] * (M[1,0]*M[2,1] - M[1,1]*M[2,0])
|
|
68
|
+
)
|
|
69
|
+
if np.abs(det) < eps:
|
|
70
|
+
return np.inf # coplanar/degenerate
|
|
71
|
+
|
|
72
|
+
rhs = np.empty(3, dtype=np.float64)
|
|
73
|
+
rhs[0] = np.dot(p1, p1) - np.dot(p4, p4)
|
|
74
|
+
rhs[1] = np.dot(p2, p2) - np.dot(p4, p4)
|
|
75
|
+
rhs[2] = np.dot(p3, p3) - np.dot(p4, p4)
|
|
76
|
+
|
|
77
|
+
# Solve for circumcenter
|
|
78
|
+
c = np.linalg.solve(M, rhs)
|
|
79
|
+
|
|
80
|
+
# Radius = distance to any vertex
|
|
81
|
+
R = np.linalg.norm(c - p1)
|
|
82
|
+
return 2.0 * R # diameter
|
|
83
|
+
|
|
84
|
+
def print_sparam_matrix(pre: str, S: np.ndarray):
|
|
85
|
+
"""
|
|
86
|
+
Print an N x N complex S-parameter matrix in dB∠deg format.
|
|
87
|
+
Magnitude in dB rounded to 2 decimals, phase in degrees with 1 decimal.
|
|
88
|
+
"""
|
|
89
|
+
S = np.asarray(S)
|
|
90
|
+
if S.ndim != 2 or S.shape[0] != S.shape[1]:
|
|
91
|
+
raise ValueError("S must be a square (N x N) complex matrix")
|
|
92
|
+
|
|
93
|
+
N = S.shape[0]
|
|
94
|
+
logger.debug(pre+"S-parameter matrix (dB ∠ deg):")
|
|
95
|
+
|
|
96
|
+
for i in range(N):
|
|
97
|
+
row_str = []
|
|
98
|
+
for j in range(N):
|
|
99
|
+
mag_db = 20 * np.log10(np.abs(S[i, j]) + np.finfo(float).eps)
|
|
100
|
+
phase_deg = np.degrees(np.angle(S[i, j]))
|
|
101
|
+
row_str.append(f"{mag_db:6.2f} dB ∠ {phase_deg:6.1f}°")
|
|
102
|
+
logger.debug(" | ".join(row_str))
|
|
22
103
|
|
|
23
104
|
def compute_convergence(Sold: np.ndarray, Snew: np.ndarray) -> float:
|
|
24
105
|
"""
|
|
25
106
|
Return a single scalar: max |Snew - Sold|.
|
|
26
107
|
Works for shapes (N,N) or (..., N, N); reduces over all axes.
|
|
27
108
|
"""
|
|
109
|
+
|
|
28
110
|
Sold = np.asarray(Sold)
|
|
29
111
|
Snew = np.asarray(Snew)
|
|
112
|
+
print_sparam_matrix('Old:',Sold)
|
|
113
|
+
print_sparam_matrix('New',Snew)
|
|
30
114
|
if Sold.shape != Snew.shape:
|
|
31
115
|
raise ValueError("Sold and Snew must have identical shapes")
|
|
32
|
-
|
|
116
|
+
#amp_conv = float(np.abs(np.abs(Snew) - np.abs(Sold)).max())
|
|
117
|
+
mag_conv = float(np.abs(np.abs(Snew)-np.abs(Sold)).max())
|
|
118
|
+
amp_conv = float(np.abs(Snew - Sold).max())
|
|
119
|
+
phase_conv = float(np.abs(np.angle(np.diag(Sold)/np.diag(Snew))).max()) * 180/np.pi
|
|
120
|
+
return amp_conv, mag_conv, phase_conv
|
|
33
121
|
|
|
34
122
|
def select_refinement_indices(errors: np.ndarray, refine: float) -> np.ndarray:
|
|
35
123
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
124
|
+
Dörfler marking:
|
|
125
|
+
Choose the minimal number of elements whose squared error sum
|
|
126
|
+
reaches at least 'refine' (theta in [0,1]) of the global squared error.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
errors: 1D or ND array of local error indicators (nonnegative).
|
|
130
|
+
refine: theta in [0,1]; fraction of total error energy to target.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
np.ndarray of indices (ints) sorted by decreasing error.
|
|
40
134
|
"""
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
135
|
+
# Flatten and sanitize
|
|
136
|
+
errs = np.abs(np.ravel(errors).astype(float))
|
|
137
|
+
n = errs.size
|
|
138
|
+
if n == 0:
|
|
139
|
+
return np.empty(0, dtype=int)
|
|
140
|
+
|
|
141
|
+
errs[~np.isfinite(errs)] = 0.0 # replace NaN/inf by 0
|
|
142
|
+
theta = float(np.clip(refine, 0.0, 1.0))
|
|
143
|
+
if theta <= 0.0:
|
|
144
|
+
return np.empty(0, dtype=int)
|
|
145
|
+
|
|
146
|
+
# Dörfler uses squared indicators
|
|
147
|
+
ind = errs * errs
|
|
148
|
+
total = ind.sum()
|
|
149
|
+
if total <= 0.0:
|
|
150
|
+
return np.empty(0, dtype=int)
|
|
151
|
+
|
|
152
|
+
# Sort by decreasing indicator
|
|
153
|
+
order = np.argsort(ind)[::-1]
|
|
154
|
+
sum_error = 0
|
|
155
|
+
indices = []
|
|
156
|
+
for index in order:
|
|
157
|
+
sum_error += ind[index]
|
|
158
|
+
indices.append(index)
|
|
159
|
+
if sum_error >= refine*total:
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
#cum = np.cumsum(ind[order])
|
|
163
|
+
|
|
164
|
+
# Smallest m with cumulative ≥ theta * total
|
|
165
|
+
#m = int(np.searchsorted(cum, theta * total, side="left")) + 1
|
|
166
|
+
|
|
167
|
+
#chosen = order[:m] # already from largest to smallest
|
|
168
|
+
return np.array(indices)#chosen.astype(int)
|
|
169
|
+
|
|
170
|
+
@njit(f8[:](i8, f8[:,:], f8, f8, f8[:]), cache=True, nogil=True, parallel=False)
|
|
171
|
+
def compute_size(id: int, coords: np.ndarray, q: float, scaler: float, dss: np.ndarray) -> float:
|
|
172
|
+
"""Optimized function to compute the size impressed by size constraint points on each other size constraint point.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
id (int): _description_
|
|
176
|
+
coords (np.ndarray): _description_
|
|
177
|
+
q (float): _description_
|
|
178
|
+
scaler (float): _description_
|
|
179
|
+
dss (np.ndarray): _description_
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
float: _description_
|
|
183
|
+
"""
|
|
184
|
+
N = dss.shape[0]
|
|
185
|
+
sizes = np.zeros((N,), dtype=np.float64)-1.0
|
|
72
186
|
x, y, z = coords[:,id]
|
|
73
|
-
for n in
|
|
187
|
+
for n in prange(N):
|
|
74
188
|
if n == id:
|
|
189
|
+
sizes[n] = dss[n]*scaler
|
|
75
190
|
continue
|
|
76
|
-
nsize = dss[n]/q - (1-q)/q * ((x-coords[0,n])**2 + (y-coords[1,n])**2 + (z-coords[2,n])**2)**0.5
|
|
77
|
-
sizes
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
191
|
+
nsize = scaler*dss[n]/q - (1-q)/q * max(0, (((x-coords[0,n])**2 + (y-coords[1,n])**2 + (z-coords[2,n])**2)**0.5-dss[n]/3))
|
|
192
|
+
sizes[n] = nsize
|
|
193
|
+
return sizes
|
|
194
|
+
|
|
195
|
+
@njit(f8[:](f8[:,:], i8, i8[:]), cache=True, nogil=True, parallel=True)
|
|
196
|
+
def nbmin(matrix, axis, include):
|
|
197
|
+
|
|
198
|
+
if axis==0:
|
|
199
|
+
N = matrix.shape[1]
|
|
200
|
+
out = np.empty((N,), dtype=np.float64)
|
|
201
|
+
for n in prange(N):
|
|
202
|
+
out[n] = np.min(matrix[include==1,n])
|
|
203
|
+
return out
|
|
204
|
+
if axis==1:
|
|
205
|
+
N = matrix.shape[0]
|
|
206
|
+
out = np.empty((N,), dtype=np.float64)
|
|
207
|
+
for n in prange(N):
|
|
208
|
+
out[n] = np.min(matrix[n,include==1])
|
|
209
|
+
return out
|
|
210
|
+
else:
|
|
211
|
+
out = np.empty((N,), dtype=np.float64)
|
|
212
|
+
return out
|
|
213
|
+
|
|
214
|
+
@njit(i8(i8, f8[:,:], f8, i8[:]), cache=True, nogil=True, parallel=False)
|
|
215
|
+
def can_remove(index: int, M: np.ndarray, scaling: float, include: np.ndarray) -> int:
|
|
216
|
+
|
|
217
|
+
ratio = np.min(M[index,:] / nbmin(M, 1, include))
|
|
218
|
+
|
|
219
|
+
if ratio > scaling:
|
|
220
|
+
return 1
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
@njit(i8[:](f8[:,:], f8, f8[:], f8, f8), cache=True, nogil=True, parallel=False)
|
|
224
|
+
def reduce_point_set(coords: np.ndarray, q: float, dss: np.ndarray, scaler: float, keep_percentage: float) -> list[int]:
|
|
225
|
+
N = dss.shape[0]
|
|
226
|
+
impressed_size = np.zeros((N,N), np.float64)
|
|
227
|
+
|
|
228
|
+
include = np.ones((N,), dtype=np.int64)
|
|
229
|
+
|
|
83
230
|
for n in range(N):
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
231
|
+
impressed_size[:,n] = compute_size(n, coords, q, scaler, dss)
|
|
232
|
+
|
|
233
|
+
current_min = nbmin(impressed_size, 1, include)
|
|
234
|
+
|
|
235
|
+
counter = 0
|
|
236
|
+
for i in range(N):
|
|
237
|
+
|
|
238
|
+
if include[i]==0:
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
if (N-counter)/N < keep_percentage:
|
|
242
|
+
break
|
|
243
|
+
|
|
244
|
+
n_imposed = np.sum(impressed_size[:,i] <= (current_min*1.3))
|
|
245
|
+
|
|
246
|
+
if n_imposed == 0:
|
|
247
|
+
include[i] = 0
|
|
248
|
+
counter = counter + 1
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
ids = np.arange(N)
|
|
253
|
+
output = ids[include==1]
|
|
254
|
+
return output
|
|
90
255
|
|
|
91
256
|
|
|
92
257
|
@njit(i8[:, :](i8[:], i8[:, :]), cache=True, nogil=True)
|
|
@@ -166,16 +331,42 @@ def tet_coefficients(xs, ys, zs):
|
|
|
166
331
|
|
|
167
332
|
return aas, bbs, ccs, dds, V
|
|
168
333
|
|
|
169
|
-
|
|
334
|
+
DPTS_2D = np.array([[0.22338159, 0.22338159, 0.22338159, 0.10995174, 0.10995174,
|
|
335
|
+
0.10995174],
|
|
336
|
+
[0.10810302, 0.44594849, 0.44594849, 0.81684757, 0.09157621,
|
|
337
|
+
0.09157621],
|
|
338
|
+
[0.44594849, 0.44594849, 0.10810302, 0.09157621, 0.09157621,
|
|
339
|
+
0.81684757],
|
|
340
|
+
[0.44594849, 0.10810302, 0.44594849, 0.09157621, 0.81684757,
|
|
341
|
+
0.09157621]], dtype=np.float64)
|
|
342
|
+
|
|
343
|
+
DPTS_3D = np.array([[-0.078933 , 0.04573333, 0.04573333, 0.04573333, 0.04573333,
|
|
344
|
+
0.14933333, 0.14933333, 0.14933333, 0.14933333, 0.14933333,
|
|
345
|
+
0.14933333],
|
|
346
|
+
[ 0.25 , 0.78571429, 0.07142857, 0.07142857, 0.07142857,
|
|
347
|
+
0.39940358, 0.39940358, 0.39940358, 0.10059642, 0.10059642,
|
|
348
|
+
0.10059642],
|
|
349
|
+
[ 0.25 , 0.07142857, 0.07142857, 0.07142857, 0.78571429,
|
|
350
|
+
0.10059642, 0.10059642, 0.39940358, 0.39940358, 0.39940358,
|
|
351
|
+
0.10059642],
|
|
352
|
+
[ 0.25 , 0.07142857, 0.07142857, 0.78571429, 0.07142857,
|
|
353
|
+
0.39940358, 0.10059642, 0.10059642, 0.39940358, 0.10059642,
|
|
354
|
+
0.39940358],
|
|
355
|
+
[ 0.25 , 0.07142857, 0.78571429, 0.07142857, 0.07142857,
|
|
356
|
+
0.10059642, 0.39940358, 0.10059642, 0.10059642, 0.39940358,
|
|
357
|
+
0.39940358]], dtype=np.float64)
|
|
358
|
+
|
|
359
|
+
@njit(c16[:,:](f8[:,:], f8[:,:], c16[:], i8[:,:], i8[:,:]), cache=True, nogil=True)
|
|
170
360
|
def compute_field(coords: np.ndarray,
|
|
171
361
|
vertices: np.ndarray,
|
|
172
362
|
Etet: np.ndarray,
|
|
173
363
|
l_edge_ids: np.ndarray,
|
|
174
364
|
l_tri_ids: np.ndarray):
|
|
175
365
|
|
|
176
|
-
x = coords[0]
|
|
177
|
-
y = coords[1]
|
|
178
|
-
z = coords[2]
|
|
366
|
+
x = coords[0,:]
|
|
367
|
+
y = coords[1,:]
|
|
368
|
+
z = coords[2,:]
|
|
369
|
+
N = coords.shape[1]
|
|
179
370
|
|
|
180
371
|
xvs = vertices[0,:]
|
|
181
372
|
yvs = vertices[1,:]
|
|
@@ -188,11 +379,12 @@ def compute_field(coords: np.ndarray,
|
|
|
188
379
|
Em2s = Etet[10:16]
|
|
189
380
|
Ef2s = Etet[16:20]
|
|
190
381
|
|
|
191
|
-
Exl =
|
|
192
|
-
Eyl =
|
|
193
|
-
Ezl =
|
|
382
|
+
Exl = np.zeros_like(x, dtype=np.complex128)
|
|
383
|
+
Eyl = np.zeros_like(x, dtype=np.complex128)
|
|
384
|
+
Ezl = np.zeros_like(x, dtype=np.complex128)
|
|
385
|
+
|
|
386
|
+
V1 = (6*V)**3
|
|
194
387
|
|
|
195
|
-
V1 = (216*V**3)
|
|
196
388
|
for ie in range(6):
|
|
197
389
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
198
390
|
edgeids = l_edge_ids[:, ie]
|
|
@@ -243,10 +435,10 @@ def compute_field(coords: np.ndarray,
|
|
|
243
435
|
Eyl += ey
|
|
244
436
|
Ezl += ez
|
|
245
437
|
|
|
246
|
-
out = np.zeros((3,), dtype=np.complex128)
|
|
247
|
-
out[0] = Exl
|
|
248
|
-
out[1] = Eyl
|
|
249
|
-
out[2] = Ezl
|
|
438
|
+
out = np.zeros((3,N), dtype=np.complex128)
|
|
439
|
+
out[0,:] = Exl
|
|
440
|
+
out[1,:] = Eyl
|
|
441
|
+
out[2,:] = Ezl
|
|
250
442
|
return out
|
|
251
443
|
|
|
252
444
|
@njit(c16[:,:](f8[:,:], f8[:,:], c16[:], i8[:,:], i8[:,:]), cache=True, nogil=True)
|
|
@@ -275,8 +467,8 @@ def compute_curl(coords: np.ndarray,
|
|
|
275
467
|
Eyl = np.zeros((x.shape[0],), dtype=np.complex128)
|
|
276
468
|
Ezl = np.zeros((x.shape[0],), dtype=np.complex128)
|
|
277
469
|
|
|
278
|
-
V1 =
|
|
279
|
-
V2 =
|
|
470
|
+
V1 = 216*V**3
|
|
471
|
+
V2 = 72*V**3
|
|
280
472
|
|
|
281
473
|
for ie in range(6):
|
|
282
474
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
@@ -370,8 +562,103 @@ def compute_curl(coords: np.ndarray,
|
|
|
370
562
|
out[2,:] = Ezl
|
|
371
563
|
return out
|
|
372
564
|
|
|
373
|
-
@njit(c16[:](f8[
|
|
374
|
-
def
|
|
565
|
+
@njit(c16[:](f8[:,:], f8[:,:], c16[:], i8[:,:], i8[:,:], c16[:,:]), cache=True, nogil=True)
|
|
566
|
+
def compute_div(coords: np.ndarray,
|
|
567
|
+
vertices: np.ndarray,
|
|
568
|
+
Etet: np.ndarray,
|
|
569
|
+
l_edge_ids: np.ndarray,
|
|
570
|
+
l_tri_ids: np.ndarray,
|
|
571
|
+
Um: np.ndarray):
|
|
572
|
+
|
|
573
|
+
uxx, uxy, uxz = Um[0,0], Um[0,1], Um[0,2]
|
|
574
|
+
uyx, uyy, uyz = Um[1,0], Um[1,1], Um[1,2]
|
|
575
|
+
uzx, uzy, uzz = Um[2,0], Um[2,1], Um[2,2]
|
|
576
|
+
|
|
577
|
+
xs = coords[0,:]
|
|
578
|
+
ys = coords[1,:]
|
|
579
|
+
zs = coords[2,:]
|
|
580
|
+
|
|
581
|
+
xvs = vertices[0,:]
|
|
582
|
+
yvs = vertices[1,:]
|
|
583
|
+
zvs = vertices[2,:]
|
|
584
|
+
|
|
585
|
+
a_s, b_s, c_s, d_s, V = tet_coefficients(xvs, yvs, zvs)
|
|
586
|
+
|
|
587
|
+
Em1s = Etet[0:6]
|
|
588
|
+
Ef1s = Etet[6:10]
|
|
589
|
+
Em2s = Etet[10:16]
|
|
590
|
+
Ef2s = Etet[16:20]
|
|
591
|
+
|
|
592
|
+
difE = np.zeros((xs.shape[0],), dtype=np.complex128)
|
|
593
|
+
|
|
594
|
+
V1 = (216*V**3)
|
|
595
|
+
V2 = (72*V**3)
|
|
596
|
+
|
|
597
|
+
for ie in range(6):
|
|
598
|
+
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
599
|
+
edgeids = l_edge_ids[:, ie]
|
|
600
|
+
a1, a2 = a_s[edgeids]
|
|
601
|
+
b1, b2 = b_s[edgeids]
|
|
602
|
+
c1, c2 = c_s[edgeids]
|
|
603
|
+
d1, d2 = d_s[edgeids]
|
|
604
|
+
x1, x2 = xvs[edgeids]
|
|
605
|
+
y1, y2 = yvs[edgeids]
|
|
606
|
+
z1, z2 = zvs[edgeids]
|
|
607
|
+
|
|
608
|
+
L = np.sqrt((x1 - x2)**2 + (y1 - y2)**2 + (z1 - z2)**2)
|
|
609
|
+
C1 = (a2 + b2*xs + c2*ys + d2*zs)
|
|
610
|
+
C2 = (a1 + b1*xs + c1*ys + d1*zs)
|
|
611
|
+
C3 = (b1*C1 - b2*C2)
|
|
612
|
+
C4 = (c1*C1 - c2*C2)
|
|
613
|
+
C5 = (d1*C1 - d2*C2)
|
|
614
|
+
C6 = (b1*c2 - b2*c1)
|
|
615
|
+
C7 = (c1*d2 - c2*d1)
|
|
616
|
+
C8 = (b1*d2 - b2*d1)
|
|
617
|
+
difE += (Em1*L*(b1*uxx*C3 + b1*uxy*C4 + b1*uxz*C5 + c1*uyx*C3 + c1*uyy*C4 + c1*uyz*C5 + d1*uzx*C3
|
|
618
|
+
+ d1*uzy*C4 + d1*uzz*C5 - uxy*C6*C2 - uxz*C8*C2 + uyx*C6*C2 - uyz*C7*C2 + uzx*C8*C2 + uzy*C7*C2) +
|
|
619
|
+
Em2*L*(b2*uxx*C3 + b2*uxy*C4 + b2*uxz*C5 + c2*uyx*C3 + c2*uyy*C4 + c2*uyz*C5 + d2*uzx*C3
|
|
620
|
+
+ d2*uzy*C4 + d2*uzz*C5 - uxy*C6*C1 - uxz*C8*C1 + uyx*C6*C1 - uyz*C7*C1 + uzx*C8*C1 + uzy*C7*C1))/V1
|
|
621
|
+
|
|
622
|
+
for ie in range(4):
|
|
623
|
+
Em1, Em2 = Ef1s[ie], Ef2s[ie]
|
|
624
|
+
triids = l_tri_ids[:, ie]
|
|
625
|
+
a1, a2, a3 = a_s[triids]
|
|
626
|
+
b1, b2, b3 = b_s[triids]
|
|
627
|
+
c1, c2, c3 = c_s[triids]
|
|
628
|
+
d1, d2, d3 = d_s[triids]
|
|
629
|
+
|
|
630
|
+
x1, x2, x3 = xvs[l_tri_ids[:, ie]]
|
|
631
|
+
y1, y2, y3 = yvs[l_tri_ids[:, ie]]
|
|
632
|
+
z1, z2, z3 = zvs[l_tri_ids[:, ie]]
|
|
633
|
+
|
|
634
|
+
L1 = np.sqrt((x1-x3)**2 + (y1-y3)**2 + (z1-z3)**2)
|
|
635
|
+
L2 = np.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)
|
|
636
|
+
C1 = (a3 + b3*xs + c3*ys + d3*zs)
|
|
637
|
+
C2 = (a1 + b1*xs + c1*ys + d1*zs)
|
|
638
|
+
C6 = (a2 + b2*xs + c2*ys + d2*zs)
|
|
639
|
+
C3 = (b1*C1 - b3*C2)
|
|
640
|
+
C4 = (c1*C1 - c3*C2)
|
|
641
|
+
C5 = (d1*C1 - d3*C2)
|
|
642
|
+
C7 = (b1*c3 - b3*c1)
|
|
643
|
+
C8 = (b1*d3 - b3*d1)
|
|
644
|
+
C9 = (c1*d3 - c3*d1)
|
|
645
|
+
C10 = (b1*C6 - b2*C2)
|
|
646
|
+
C11 = (c1*C6 - c2*C2)
|
|
647
|
+
C12 = (d1*C6 - d2*C2)
|
|
648
|
+
C13 = (b1*c2 - b2*c1)
|
|
649
|
+
C14 = (b1*d2 - b2*d1)
|
|
650
|
+
C15 = (c1*d2 - c2*d1)
|
|
651
|
+
|
|
652
|
+
difE += (-Em1*L1*(b2*uxx*C3 + b2*uxy*C4 + b2*uxz*C5 + c2*uyx*C3 + c2*uyy*C4 + c2*uyz*C5 + d2*uzx*C3
|
|
653
|
+
+ d2*uzy*C4 + d2*uzz*C5 - uxy*C7*C6 - uxz*C8*C6 + uyx*C7*C6 - uyz*C9*C6 + uzx*C8*C6 + uzy*C9*C6)
|
|
654
|
+
+ Em2*L2*(b3*uxx*C10 + b3*uxy*C11 + b3*uxz*C12 + c3*uyx*C10 + c3*uyy*C11 + c3*uyz*C12
|
|
655
|
+
+ d3*uzx*C10 + d3*uzy*C11 + d3*uzz*C12 - uxy*C13*C1 - uxz*C14*C1 + uyx*C13*C1 - uyz*C15*C1 + uzx*C14*C1 + uzy*C15*C1))/V1
|
|
656
|
+
|
|
657
|
+
return difE
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
@njit(c16[:](f8[:,:], c16[:], i8[:,:], i8[:,:], c16[:,:]), cache=True, nogil=True)
|
|
661
|
+
def compute_curl_curl(
|
|
375
662
|
vertices: np.ndarray,
|
|
376
663
|
Etet: np.ndarray,
|
|
377
664
|
l_edge_ids: np.ndarray,
|
|
@@ -382,10 +669,6 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
382
669
|
uyx, uyy, uyz = Um[1,0], Um[1,1], Um[1,2]
|
|
383
670
|
uzx, uzy, uzz = Um[2,0], Um[2,1], Um[2,2]
|
|
384
671
|
|
|
385
|
-
x = coords[0]
|
|
386
|
-
y = coords[1]
|
|
387
|
-
z = coords[2]
|
|
388
|
-
|
|
389
672
|
xvs = vertices[0,:]
|
|
390
673
|
yvs = vertices[1,:]
|
|
391
674
|
zvs = vertices[2,:]
|
|
@@ -401,7 +684,7 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
401
684
|
Em2s = Etet[10:16]
|
|
402
685
|
Ef2s = Etet[16:20]
|
|
403
686
|
|
|
404
|
-
V1 = (
|
|
687
|
+
V1 = (6*V)**3
|
|
405
688
|
|
|
406
689
|
for ie in range(6):
|
|
407
690
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
@@ -419,9 +702,9 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
419
702
|
ey = 3*L1*(Em1*(b1**2*c2*uzz - b1**2*d2*uzy - b1*b2*c1*uzz + b1*b2*d1*uzy + b1*c1*d2*uzx - b1*c2*d1*uxz - b1*c2*d1*uzx + b1*d1*d2*uxy + b2*c1*d1*uxz - b2*d1**2*uxy - c1*d1*d2*uxx + c2*d1**2*uxx) + Em2*(b1*b2*c2*uzz - b1*b2*d2*uzy - b1*c2*d2*uxz + b1*d2**2*uxy - b2**2*c1*uzz + b2**2*d1*uzy + b2*c1*d2*uxz + b2*c1*d2*uzx - b2*c2*d1*uzx - b2*d1*d2*uxy - c1*d2**2*uxx + c2*d1*d2*uxx))
|
|
420
703
|
ez = -3*L1*(Em1*(b1**2*c2*uyz - b1**2*d2*uyy - b1*b2*c1*uyz + b1*b2*d1*uyy - b1*c1*c2*uxz + b1*c1*d2*uxy + b1*c1*d2*uyx - b1*c2*d1*uyx + b2*c1**2*uxz - b2*c1*d1*uxy - c1**2*d2*uxx + c1*c2*d1*uxx) + Em2*(b1*b2*c2*uyz - b1*b2*d2*uyy - b1*c2**2*uxz + b1*c2*d2*uxy - b2**2*c1*uyz + b2**2*d1*uyy + b2*c1*c2*uxz + b2*c1*d2*uyx - b2*c2*d1*uxy - b2*c2*d1*uyx - c1*c2*d2*uxx + c2**2*d1*uxx))
|
|
421
704
|
|
|
422
|
-
Exl += ex
|
|
423
|
-
Eyl += ey
|
|
424
|
-
Ezl += ez
|
|
705
|
+
Exl += ex/V1
|
|
706
|
+
Eyl += ey/V1
|
|
707
|
+
Ezl += ez/V1
|
|
425
708
|
|
|
426
709
|
for ie in range(4):
|
|
427
710
|
Em1, Em2 = Ef1s[ie], Ef2s[ie]
|
|
@@ -438,13 +721,13 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
438
721
|
L1 = np.sqrt((x1-x3)**2 + (y1-y3)**2 + (z1-z3)**2)
|
|
439
722
|
L2 = np.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)
|
|
440
723
|
|
|
441
|
-
ex =
|
|
442
|
-
ey =
|
|
443
|
-
ez = -
|
|
724
|
+
ex = (Em1*L1*(3*c2*uzx*(c1*d3 - c3*d1) + 3*c2*uzz*(b1*c3 - b3*c1) - 3*d2*uyx*(c1*d3 - c3*d1) + 3*d2*uyy*(b1*d3 - b3*d1) - uyz*(-b2*(c1*d3 - c3*d1) + c2*(b1*d3 - b3*d1) + 2*d2*(b1*c3 - b3*c1)) - uzy*(b2*(c1*d3 - c3*d1) + 2*c2*(b1*d3 - b3*d1) + d2*(b1*c3 - b3*c1))) - Em2*L2*(3*c3*uzx*(c1*d2 - c2*d1) + 3*c3*uzz*(b1*c2 - b2*c1) - 3*d3*uyx*(c1*d2 - c2*d1) + 3*d3*uyy*(b1*d2 - b2*d1) - uyz*(-b3*(c1*d2 - c2*d1) + c3*(b1*d2 - b2*d1) + 2*d3*(b1*c2 - b2*c1)) - uzy*(b3*(c1*d2 - c2*d1) + 2*c3*(b1*d2 - b2*d1) + d3*(b1*c2 - b2*c1))))
|
|
725
|
+
ey = (Em1*L1*(3*b2*uzy*(b1*d3 - b3*d1) - 3*b2*uzz*(b1*c3 - b3*c1) + 3*d2*uxx*(c1*d3 - c3*d1) - 3*d2*uxy*(b1*d3 - b3*d1) + uxz*(-b2*(c1*d3 - c3*d1) + c2*(b1*d3 - b3*d1) + 2*d2*(b1*c3 - b3*c1)) - uzx*(2*b2*(c1*d3 - c3*d1) + c2*(b1*d3 - b3*d1) - d2*(b1*c3 - b3*c1))) - Em2*L2*(3*b3*uzy*(b1*d2 - b2*d1) - 3*b3*uzz*(b1*c2 - b2*c1) + 3*d3*uxx*(c1*d2 - c2*d1) - 3*d3*uxy*(b1*d2 - b2*d1) + uxz*(-b3*(c1*d2 - c2*d1) + c3*(b1*d2 - b2*d1) + 2*d3*(b1*c2 - b2*c1)) - uzx*(2*b3*(c1*d2 - c2*d1) + c3*(b1*d2 - b2*d1) - d3*(b1*c2 - b2*c1))))
|
|
726
|
+
ez = -(Em1*L1*(3*b2*uyy*(b1*d3 - b3*d1) - 3*b2*uyz*(b1*c3 - b3*c1) + 3*c2*uxx*(c1*d3 - c3*d1) + 3*c2*uxz*(b1*c3 - b3*c1) - uxy*(b2*(c1*d3 - c3*d1) + 2*c2*(b1*d3 - b3*d1) + d2*(b1*c3 - b3*c1)) - uyx*(2*b2*(c1*d3 - c3*d1) + c2*(b1*d3 - b3*d1) - d2*(b1*c3 - b3*c1))) - Em2*L2*(3*b3*uyy*(b1*d2 - b2*d1) - 3*b3*uyz*(b1*c2 - b2*c1) + 3*c3*uxx*(c1*d2 - c2*d1) + 3*c3*uxz*(b1*c2 - b2*c1) - uxy*(b3*(c1*d2 - c2*d1) + 2*c3*(b1*d2 - b2*d1) + d3*(b1*c2 - b2*c1)) - uyx*(2*b3*(c1*d2 - c2*d1) + c3*(b1*d2 - b2*d1) - d3*(b1*c2 - b2*c1))))
|
|
444
727
|
|
|
445
|
-
Exl += ex
|
|
446
|
-
Eyl += ey
|
|
447
|
-
Ezl += ez
|
|
728
|
+
Exl += ex/V1
|
|
729
|
+
Eyl += ey/V1
|
|
730
|
+
Ezl += ez/V1
|
|
448
731
|
|
|
449
732
|
out = np.zeros((3,), dtype=np.complex128)
|
|
450
733
|
out[0] = Exl
|
|
@@ -452,9 +735,40 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
452
735
|
out[2] = Ezl
|
|
453
736
|
return out
|
|
454
737
|
|
|
738
|
+
@njit(c16[:,:](c16[:], c16[:,:]), cache=True, fastmath=True, nogil=True)
|
|
739
|
+
def cross_c_arry(a: np.ndarray, b: np.ndarray):
|
|
740
|
+
"""Optimized complex single vector cross product
|
|
741
|
+
|
|
742
|
+
Args:
|
|
743
|
+
a (np.ndarray): (3,) vector a
|
|
744
|
+
b (np.ndarray): (3,) vector b
|
|
745
|
+
|
|
746
|
+
Returns:
|
|
747
|
+
np.ndarray: a ⨉ b
|
|
748
|
+
"""
|
|
749
|
+
crossv = np.empty((3,b.shape[1]), dtype=np.complex128)
|
|
750
|
+
crossv[0,:] = a[1]*b[2,:] - a[2]*b[1,:]
|
|
751
|
+
crossv[1,:] = a[2]*b[0,:] - a[0]*b[2,:]
|
|
752
|
+
crossv[2,:] = a[0]*b[1,:] - a[1]*b[0,:]
|
|
753
|
+
return crossv
|
|
754
|
+
|
|
755
|
+
@njit(c16[:](c16[:], c16[:,:]), cache=True, fastmath=True, nogil=True)
|
|
756
|
+
def dot_c_arry(a: np.ndarray, b: np.ndarray):
|
|
757
|
+
"""Optimized complex single vector cross product
|
|
758
|
+
|
|
759
|
+
Args:
|
|
760
|
+
a (np.ndarray): (3,) vector a
|
|
761
|
+
b (np.ndarray): (3,) vector b
|
|
762
|
+
|
|
763
|
+
Returns:
|
|
764
|
+
np.ndarray: a ⨉ b
|
|
765
|
+
"""
|
|
766
|
+
dotv = a[0]*b[0,:] + a[1]*b[1,:] + a[2]*b[2,:]
|
|
767
|
+
return dotv
|
|
768
|
+
|
|
455
769
|
@njit(types.Tuple((f8[:], f8[:]))(f8[:,:], i8[:,:], i8[:,:], i8[:,:], f8[:,:],
|
|
456
770
|
c16[:], f8[:], f8[:], i8[:,:], i8[:,:],
|
|
457
|
-
f8[:,:], i8[:,:], i8[:,:], c16[:], c16[:], f8), cache=True, nogil=True)
|
|
771
|
+
f8[:,:], i8[:,:], i8[:,:], c16[:], c16[:], i8[:], f8), cache=True, nogil=True)
|
|
458
772
|
def compute_error_single(nodes, tets, tris, edges, centers,
|
|
459
773
|
Efield,
|
|
460
774
|
edge_lengths,
|
|
@@ -466,83 +780,200 @@ def compute_error_single(nodes, tets, tris, edges, centers,
|
|
|
466
780
|
tet_to_field,
|
|
467
781
|
er,
|
|
468
782
|
ur,
|
|
783
|
+
pec_tris,
|
|
469
784
|
k0,) -> np.ndarray:
|
|
470
785
|
|
|
786
|
+
is_pec = np.zeros((tris.shape[1],), dtype=np.bool)
|
|
787
|
+
is_pec[pec_tris] = True
|
|
471
788
|
|
|
472
|
-
#
|
|
789
|
+
# CONSTANTS
|
|
473
790
|
ntet = tets.shape[1]
|
|
474
791
|
nedges = edges.shape[1]
|
|
792
|
+
W0 = k0*C0
|
|
793
|
+
N2D = DPTS_2D.shape[1]
|
|
794
|
+
W_VOL = DPTS_3D[0,:]
|
|
795
|
+
Y0 = np.sqrt(1/MU0)
|
|
475
796
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
error = np.zeros((ntet,), dtype=np.float64)
|
|
797
|
+
# INIT POSTERIORI ERROR ESTIMATE QUANTITIES
|
|
798
|
+
alpha_t = np.zeros((ntet,), dtype=np.complex128)
|
|
479
799
|
max_elem_size = np.zeros((ntet,), dtype=np.float64)
|
|
480
800
|
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
801
|
+
Qf_face1 = np.zeros((4,N2D,ntet), dtype=np.complex128)
|
|
802
|
+
Qf_face2 = np.zeros((4,N2D,ntet), dtype=np.complex128)
|
|
803
|
+
Jf_face1 = np.zeros((4,3,N2D,ntet), dtype=np.complex128)
|
|
804
|
+
Jf_face2 = np.zeros((4,3,N2D,ntet), dtype=np.complex128)
|
|
805
|
+
|
|
806
|
+
areas_fr = np.zeros((4, N2D, ntet), dtype=np.float64)
|
|
807
|
+
Rf_fr = np.zeros((4, ntet), dtype=np.float64)
|
|
808
|
+
adj_tets_mat = -np.ones((4,ntet), dtype=np.int32)
|
|
485
809
|
|
|
486
|
-
|
|
487
810
|
# Compute Error estimate
|
|
488
811
|
for itet in range(ntet):
|
|
489
812
|
uinv = (1/ur[itet])*np.eye(3)
|
|
490
813
|
ermat = er[itet]*np.eye(3)
|
|
814
|
+
erc = er[itet]
|
|
815
|
+
urc = ur[itet]
|
|
491
816
|
|
|
817
|
+
# GEOMETRIC QUANTITIES
|
|
492
818
|
vertices = nodes[:,tets[:, itet]]
|
|
819
|
+
v1 = vertices[:,0]
|
|
820
|
+
v2 = vertices[:,1]
|
|
821
|
+
v3 = vertices[:,2]
|
|
822
|
+
v4 = vertices[:,3]
|
|
493
823
|
|
|
824
|
+
# VOLUME INTEGRATION POINTS
|
|
825
|
+
vxs = DPTS_3D[1,:]*v1[0] + DPTS_3D[2,:]*v2[0] + DPTS_3D[3,:]*v3[0] + DPTS_3D[4,:]*v4[0]
|
|
826
|
+
vys = DPTS_3D[1,:]*v1[1] + DPTS_3D[2,:]*v2[1] + DPTS_3D[3,:]*v3[1] + DPTS_3D[4,:]*v4[1]
|
|
827
|
+
vzs = DPTS_3D[1,:]*v1[2] + DPTS_3D[2,:]*v2[2] + DPTS_3D[3,:]*v3[2] + DPTS_3D[4,:]*v4[2]
|
|
828
|
+
|
|
829
|
+
intpts = np.empty((3,DPTS_3D.shape[1]), dtype=np.float64)
|
|
830
|
+
intpts[0,:] = vxs
|
|
831
|
+
intpts[1,:] = vys
|
|
832
|
+
intpts[2,:] = vzs
|
|
833
|
+
|
|
834
|
+
# TET TRI NODE COUPLINGS
|
|
494
835
|
g_node_ids = tets[:, itet]
|
|
495
836
|
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
496
837
|
g_tri_ids = tris[:, tet_to_field[6:10, itet]-nedges]
|
|
497
|
-
|
|
498
838
|
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
499
839
|
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
500
|
-
|
|
501
|
-
coords = centers[:,itet]
|
|
502
|
-
Ef = Efield[tet_to_field[:,itet]]
|
|
503
|
-
Rv1 = -compute_curl_curl(coords, vertices, Ef, l_edge_ids, l_tri_ids, uinv)
|
|
504
|
-
Rv2 = k0**2*(ermat @ compute_field(coords, vertices, Ef, l_edge_ids, l_tri_ids))
|
|
505
|
-
|
|
506
840
|
triids = tet_to_tri[:,itet]
|
|
507
|
-
|
|
841
|
+
|
|
842
|
+
size_max = circum_sphere_diam(v1,v2,v3,v4)
|
|
843
|
+
#size_max = np.max(edge_lengths[tet_to_edge[:,itet]])
|
|
508
844
|
|
|
509
845
|
Volume = compute_volume(vertices[0,:], vertices[1,:], vertices[2,:])
|
|
510
|
-
|
|
511
|
-
|
|
846
|
+
|
|
847
|
+
Rt = size_max
|
|
848
|
+
# Efield
|
|
849
|
+
Ef = Efield[tet_to_field[:,itet]]
|
|
850
|
+
|
|
851
|
+
# Qt term
|
|
852
|
+
Qt = Volume*EPS0*np.sum(W_VOL*compute_div(intpts, vertices, Ef, l_edge_ids, l_tri_ids, ermat), axis=0)
|
|
853
|
+
|
|
854
|
+
# Jt term
|
|
855
|
+
|
|
856
|
+
Rv1 = compute_curl_curl(vertices, Ef, l_edge_ids, l_tri_ids, uinv)
|
|
857
|
+
Rv2 = -k0**2*(ermat @ compute_field(intpts, vertices, Ef, l_edge_ids, l_tri_ids))
|
|
858
|
+
Rv = 1*Rv2
|
|
859
|
+
Rv[0,:] += Rv1[0] # X-component
|
|
860
|
+
Rv[1,:] += Rv1[1] # Y-component
|
|
861
|
+
Rv[2,:] += Rv1[2] # Z-component
|
|
862
|
+
|
|
863
|
+
Rv[0,:] = Rv[0,:]*W_VOL
|
|
864
|
+
Rv[1,:] = Rv[1,:]*W_VOL
|
|
865
|
+
Rv[2,:] = Rv[2,:]*W_VOL
|
|
866
|
+
|
|
867
|
+
Jt = -Volume*np.sum(1/(1j*W0*MU0) * Rv, axis=1)
|
|
868
|
+
|
|
869
|
+
Gt = (1j*W0*np.exp(-1j*k0*Rt)/(4*np.pi*Rt))
|
|
870
|
+
alpha_t[itet] = - Gt/(erc*EPS0) * Qt*Qt - Gt*urc*MU0 * np.sum(Jt*Jt)
|
|
871
|
+
|
|
872
|
+
# Face Residual computation
|
|
873
|
+
|
|
874
|
+
all_face_coords = np.empty((3,4*N2D), dtype=np.float64)
|
|
875
|
+
for itri in range(4):
|
|
876
|
+
triid = triids[itri]
|
|
877
|
+
tnodes = nodes[:,tris[:,triid]]
|
|
878
|
+
n1 = tnodes[:,0]
|
|
879
|
+
n2 = tnodes[:,1]
|
|
880
|
+
n3 = tnodes[:,2]
|
|
881
|
+
all_face_coords[0,itri*N2D:(itri+1)*N2D] = DPTS_2D[1,:]*n1[0] + DPTS_2D[2,:]*n2[0] + DPTS_2D[3,:]*n3[0]
|
|
882
|
+
all_face_coords[1,itri*N2D:(itri+1)*N2D] = DPTS_2D[1,:]*n1[1] + DPTS_2D[2,:]*n2[1] + DPTS_2D[3,:]*n3[1]
|
|
883
|
+
all_face_coords[2,itri*N2D:(itri+1)*N2D] = DPTS_2D[1,:]*n1[2] + DPTS_2D[2,:]*n2[2] + DPTS_2D[3,:]*n3[2]
|
|
884
|
+
|
|
885
|
+
Qf_all = erc*EPS0*compute_field(all_face_coords, vertices, Ef, l_edge_ids, l_tri_ids)
|
|
886
|
+
Jf_all = -1/(1j*MU0*W0)*matmul(uinv, compute_curl(all_face_coords, vertices, Ef, l_edge_ids, l_tri_ids))
|
|
887
|
+
E_face_all = compute_field(all_face_coords, vertices, Ef, l_edge_ids, l_tri_ids)
|
|
512
888
|
tetc = centers[:,itet].flatten()
|
|
513
889
|
|
|
514
|
-
max_elem_size[itet] =
|
|
890
|
+
max_elem_size[itet] = size_max
|
|
515
891
|
|
|
516
892
|
for iface in range(4):
|
|
517
|
-
|
|
893
|
+
tri_index = triids[iface]
|
|
894
|
+
|
|
895
|
+
pec_face = is_pec[tri_index]
|
|
896
|
+
|
|
897
|
+
i1, i2, i3 = tris[:, tri_index]
|
|
898
|
+
|
|
899
|
+
slc1 = iface*N2D
|
|
900
|
+
slc2 = slc1+N2D
|
|
901
|
+
|
|
518
902
|
normal = outward_normal(nodes[:,i1], nodes[:,i2], nodes[:,i3], tetc).astype(np.complex128)
|
|
519
903
|
|
|
904
|
+
area = areas[triids[iface]]
|
|
905
|
+
|
|
906
|
+
n1 = nodes[:,i1]
|
|
907
|
+
n2 = nodes[:,i2]
|
|
908
|
+
n3 = nodes[:,i3]
|
|
909
|
+
l1 = np.linalg.norm(n2-n1)
|
|
910
|
+
l2 = np.linalg.norm(n3-n1)
|
|
911
|
+
l3 = np.linalg.norm(n3-n2)
|
|
912
|
+
Rf = np.max(np.array([l1, l2, l3]))
|
|
913
|
+
Rf = diam_circum_circle(n1,n2,n3)
|
|
914
|
+
Rf_fr[iface,itet] = Rf
|
|
915
|
+
areas_fr[iface, :, itet] = area
|
|
916
|
+
|
|
520
917
|
adj_tets = [int(tri_to_tet[j,triids[iface]]) for j in range(2)]
|
|
521
918
|
adj_tets = [num for num in adj_tets if num not in (itet, -1234)]
|
|
522
919
|
|
|
523
920
|
if len(adj_tets) == 0:
|
|
524
921
|
continue
|
|
525
|
-
area = areas[triids[iface]]
|
|
526
922
|
|
|
527
|
-
|
|
923
|
+
if pec_face is True:
|
|
924
|
+
Jtan = Y0*np.sqrt(1/urc)*cross_c_arry(normal, -cross_c_arry(normal, E_face_all[:, slc1: slc2]))
|
|
925
|
+
|
|
926
|
+
itet_adj = adj_tets[0]
|
|
927
|
+
iface_adj = np.argwhere(tet_to_tri[:,itet_adj]==triids[iface])[0][0]
|
|
928
|
+
|
|
929
|
+
Jf_face1[iface, :, :, itet] = Jtan
|
|
930
|
+
|
|
931
|
+
adj_tets_mat[iface,itet] = itet_adj
|
|
932
|
+
continue
|
|
528
933
|
|
|
529
934
|
itet_adj = adj_tets[0]
|
|
530
935
|
iface_adj = np.argwhere(tet_to_tri[:,itet_adj]==triids[iface])[0][0]
|
|
531
936
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
937
|
+
Jf_face1[iface, :, :, itet] = cross_c_arry(normal, Jf_all[:, slc1:slc2])
|
|
938
|
+
Jf_face2[iface_adj, :, :, itet_adj] = -cross_c_arry(normal, Jf_all[:, slc1:slc2])
|
|
939
|
+
|
|
940
|
+
Qf_face1[iface, :, itet] = dot_c_arry(normal, Qf_all[:, slc1:slc2])
|
|
941
|
+
Qf_face2[iface_adj, :, itet_adj] = -dot_c_arry(normal, Qf_all[:, slc1:slc2])
|
|
535
942
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
fdiff = np.abs(face_error1 - face_error2)
|
|
539
|
-
fnorm = fdiff[:,0,:]**2 + fdiff[:,1,:]**2 + fdiff[:,2,:]**2
|
|
540
|
-
ferror = np.sum(fnorm*hfs, axis=0)
|
|
541
|
-
error = hks**2*error + 0.5*ferror
|
|
943
|
+
adj_tets_mat[iface,itet] = itet_adj
|
|
542
944
|
|
|
945
|
+
# Compute 2D Gauss quadrature weight matrix
|
|
946
|
+
fWs = np.empty_like(areas_fr, dtype=np.float64)
|
|
947
|
+
for i in range(N2D):
|
|
948
|
+
fWs[:,i,:] = DPTS_2D[0,i]
|
|
949
|
+
|
|
950
|
+
# Compute the εE field difference (4, NDPTS, NTET)
|
|
951
|
+
Qf_delta = Qf_face1 - Qf_face2
|
|
952
|
+
Jf_delta = Jf_face1 - Jf_face2
|
|
953
|
+
|
|
954
|
+
# Perform Gauss-Quadrature integration (4, NTET)
|
|
955
|
+
Qf_int = np.sum(Qf_delta*areas_fr*fWs, axis=1)
|
|
956
|
+
Jf_int_x = np.sum(Jf_delta[:,0,:,:]*areas_fr*fWs, axis=1)
|
|
957
|
+
Jf_int_y = np.sum(Jf_delta[:,1,:,:]*areas_fr*fWs, axis=1)
|
|
958
|
+
Jf_int_z = np.sum(Jf_delta[:,2,:,:]*areas_fr*fWs, axis=1)
|
|
959
|
+
|
|
960
|
+
Gf = (1j*W0*np.exp(-1j*k0*Rf_fr)/(4*np.pi*Rf_fr))
|
|
961
|
+
alpha_Df = - Gf/(er*EPS0)*(Qf_int*Qf_int) - Gf*(ur*MU0) * (Jf_int_x*Jf_int_x + Jf_int_y*Jf_int_y + Jf_int_z*Jf_int_z)
|
|
962
|
+
|
|
963
|
+
alpha_Nf = np.zeros((4, ntet), dtype=np.complex128)
|
|
964
|
+
for it in range(ntet):
|
|
965
|
+
for iface in range(4):
|
|
966
|
+
it2 = adj_tets_mat[iface, it]
|
|
967
|
+
if it2==-1:
|
|
968
|
+
continue
|
|
969
|
+
alpha_Nf[iface,it] = alpha_t[it2]
|
|
970
|
+
|
|
971
|
+
alpha_f = np.sum((alpha_t/(alpha_t + alpha_Nf + 1e-13))*alpha_Df, axis=0)
|
|
972
|
+
error = (np.abs(alpha_t + alpha_f))**0.5
|
|
973
|
+
|
|
543
974
|
return error, max_elem_size
|
|
544
975
|
|
|
545
|
-
def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
976
|
+
def compute_error_estimate(field: MWField, pec_tris: list[int]) -> np.ndarray:
|
|
546
977
|
mesh = field.mesh
|
|
547
978
|
|
|
548
979
|
nodes = mesh.nodes
|
|
@@ -559,8 +990,10 @@ def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
|
559
990
|
tet_to_field = field.basis.tet_to_field
|
|
560
991
|
er = field._der
|
|
561
992
|
ur = field._dur
|
|
993
|
+
|
|
562
994
|
Ls = mesh.edge_lengths
|
|
563
995
|
|
|
996
|
+
pec_tris = np.sort(np.unique(np.array(pec_tris)))
|
|
564
997
|
errors = []
|
|
565
998
|
for key in field._fields.keys():
|
|
566
999
|
excitation = field._fields[key]
|
|
@@ -568,7 +1001,7 @@ def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
|
568
1001
|
error, sizes = compute_error_single(nodes, tets, tris, edges,
|
|
569
1002
|
centers, excitation, Ls, As,
|
|
570
1003
|
tet_to_edge, tet_to_tri, tri_centers,
|
|
571
|
-
tri_to_tet, tet_to_field, er, ur, field.k0)
|
|
1004
|
+
tri_to_tet, tet_to_field, er, ur, pec_tris, field.k0)
|
|
572
1005
|
|
|
573
1006
|
errors.append(error)
|
|
574
1007
|
|