emerge 1.1.0__py3-none-any.whl → 1.2.0__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 +2 -1
- 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 +34 -16
- emerge/_emerge/geo/shapes.py +1 -0
- emerge/_emerge/geo/step.py +177 -41
- emerge/_emerge/geometry.py +166 -19
- emerge/_emerge/material.py +2 -0
- emerge/_emerge/mesh3d.py +1 -3
- emerge/_emerge/mesher.py +33 -9
- emerge/_emerge/mth/common_functions.py +1 -1
- emerge/_emerge/mth/optimized.py +2 -2
- emerge/_emerge/physics/microwave/adaptive_mesh.py +451 -100
- emerge/_emerge/physics/microwave/assembly/assembler.py +9 -1
- emerge/_emerge/physics/microwave/microwave_3d.py +2 -2
- emerge/_emerge/physics/microwave/microwave_bc.py +1 -0
- emerge/_emerge/physics/microwave/microwave_data.py +95 -5
- emerge/_emerge/plot/pyvista/display.py +42 -15
- emerge/_emerge/plot/simple_plots.py +116 -4
- emerge/_emerge/selection.py +17 -2
- emerge/_emerge/simmodel.py +111 -49
- emerge/_emerge/solve_interfaces/cudss_interface.py +1 -1
- emerge/_emerge/solver.py +3 -3
- emerge/plot.py +1 -1
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/METADATA +1 -1
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/RECORD +31 -31
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/WHEEL +0 -0
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/entry_points.txt +0 -0
- {emerge-1.1.0.dist-info → emerge-1.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -17,9 +17,69 @@
|
|
|
17
17
|
|
|
18
18
|
from .microwave_data import MWField
|
|
19
19
|
import numpy as np
|
|
20
|
-
from ...mth.optimized import matmul, outward_normal
|
|
20
|
+
from ...mth.optimized import matmul, outward_normal
|
|
21
21
|
from numba import njit, f8, c16, i8, types, prange # type: ignore, p
|
|
22
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
|
|
23
83
|
|
|
24
84
|
def print_sparam_matrix(pre: str, S: np.ndarray):
|
|
25
85
|
"""
|
|
@@ -40,7 +100,7 @@ def print_sparam_matrix(pre: str, S: np.ndarray):
|
|
|
40
100
|
phase_deg = np.degrees(np.angle(S[i, j]))
|
|
41
101
|
row_str.append(f"{mag_db:6.2f} dB ∠ {phase_deg:6.1f}°")
|
|
42
102
|
logger.debug(" | ".join(row_str))
|
|
43
|
-
|
|
103
|
+
|
|
44
104
|
def compute_convergence(Sold: np.ndarray, Snew: np.ndarray) -> float:
|
|
45
105
|
"""
|
|
46
106
|
Return a single scalar: max |Snew - Sold|.
|
|
@@ -61,37 +121,51 @@ def compute_convergence(Sold: np.ndarray, Snew: np.ndarray) -> float:
|
|
|
61
121
|
|
|
62
122
|
def select_refinement_indices(errors: np.ndarray, refine: float) -> np.ndarray:
|
|
63
123
|
"""
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Returns indices sorted from largest to smallest |error|.
|
|
68
|
-
"""
|
|
69
|
-
errs = np.abs(np.ravel(errors))
|
|
70
|
-
N = errs.size
|
|
71
|
-
if N == 0:
|
|
72
|
-
return np.array([], dtype=int)
|
|
73
|
-
refine = float(np.clip(refine, 0.0, 1.0))
|
|
74
|
-
if refine == 0.0:
|
|
75
|
-
return np.array([], dtype=int)
|
|
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.
|
|
76
127
|
|
|
77
|
-
|
|
78
|
-
|
|
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.
|
|
79
131
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
132
|
+
Returns:
|
|
133
|
+
np.ndarray of indices (ints) sorted by decreasing error.
|
|
134
|
+
"""
|
|
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])
|
|
83
163
|
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
B = sorted_desc[:k]
|
|
164
|
+
# Smallest m with cumulative ≥ theta * total
|
|
165
|
+
#m = int(np.searchsorted(cum, theta * total, side="left")) + 1
|
|
87
166
|
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# Return chosen indices sorted from largest to smallest amplitude
|
|
92
|
-
mask = np.zeros(N, dtype=bool)
|
|
93
|
-
mask[chosen] = True
|
|
94
|
-
return sorted_desc[mask[sorted_desc]]
|
|
167
|
+
#chosen = order[:m] # already from largest to smallest
|
|
168
|
+
return np.array(indices)#chosen.astype(int)
|
|
95
169
|
|
|
96
170
|
@njit(f8[:](i8, f8[:,:], f8, f8, f8[:]), cache=True, nogil=True, parallel=False)
|
|
97
171
|
def compute_size(id: int, coords: np.ndarray, q: float, scaler: float, dss: np.ndarray) -> float:
|
|
@@ -114,7 +188,7 @@ def compute_size(id: int, coords: np.ndarray, q: float, scaler: float, dss: np.n
|
|
|
114
188
|
if n == id:
|
|
115
189
|
sizes[n] = dss[n]*scaler
|
|
116
190
|
continue
|
|
117
|
-
nsize = scaler*dss[n]/q - (1-q)/q * ((x-coords[0,n])**2 + (y-coords[1,n])**2 + (z-coords[2,n])**2)**0.5
|
|
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))
|
|
118
192
|
sizes[n] = nsize
|
|
119
193
|
return sizes
|
|
120
194
|
|
|
@@ -167,7 +241,7 @@ def reduce_point_set(coords: np.ndarray, q: float, dss: np.ndarray, scaler: floa
|
|
|
167
241
|
if (N-counter)/N < keep_percentage:
|
|
168
242
|
break
|
|
169
243
|
|
|
170
|
-
n_imposed = np.sum(impressed_size[:,i] <= (current_min*1.
|
|
244
|
+
n_imposed = np.sum(impressed_size[:,i] <= (current_min*1.3))
|
|
171
245
|
|
|
172
246
|
if n_imposed == 0:
|
|
173
247
|
include[i] = 0
|
|
@@ -257,16 +331,42 @@ def tet_coefficients(xs, ys, zs):
|
|
|
257
331
|
|
|
258
332
|
return aas, bbs, ccs, dds, V
|
|
259
333
|
|
|
260
|
-
|
|
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)
|
|
261
360
|
def compute_field(coords: np.ndarray,
|
|
262
361
|
vertices: np.ndarray,
|
|
263
362
|
Etet: np.ndarray,
|
|
264
363
|
l_edge_ids: np.ndarray,
|
|
265
364
|
l_tri_ids: np.ndarray):
|
|
266
365
|
|
|
267
|
-
x = coords[0]
|
|
268
|
-
y = coords[1]
|
|
269
|
-
z = coords[2]
|
|
366
|
+
x = coords[0,:]
|
|
367
|
+
y = coords[1,:]
|
|
368
|
+
z = coords[2,:]
|
|
369
|
+
N = coords.shape[1]
|
|
270
370
|
|
|
271
371
|
xvs = vertices[0,:]
|
|
272
372
|
yvs = vertices[1,:]
|
|
@@ -279,11 +379,12 @@ def compute_field(coords: np.ndarray,
|
|
|
279
379
|
Em2s = Etet[10:16]
|
|
280
380
|
Ef2s = Etet[16:20]
|
|
281
381
|
|
|
282
|
-
Exl =
|
|
283
|
-
Eyl =
|
|
284
|
-
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
|
|
285
387
|
|
|
286
|
-
V1 = (216*V**3)
|
|
287
388
|
for ie in range(6):
|
|
288
389
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
289
390
|
edgeids = l_edge_ids[:, ie]
|
|
@@ -334,10 +435,10 @@ def compute_field(coords: np.ndarray,
|
|
|
334
435
|
Eyl += ey
|
|
335
436
|
Ezl += ez
|
|
336
437
|
|
|
337
|
-
out = np.zeros((3,), dtype=np.complex128)
|
|
338
|
-
out[0] = Exl
|
|
339
|
-
out[1] = Eyl
|
|
340
|
-
out[2] = Ezl
|
|
438
|
+
out = np.zeros((3,N), dtype=np.complex128)
|
|
439
|
+
out[0,:] = Exl
|
|
440
|
+
out[1,:] = Eyl
|
|
441
|
+
out[2,:] = Ezl
|
|
341
442
|
return out
|
|
342
443
|
|
|
343
444
|
@njit(c16[:,:](f8[:,:], f8[:,:], c16[:], i8[:,:], i8[:,:]), cache=True, nogil=True)
|
|
@@ -366,8 +467,8 @@ def compute_curl(coords: np.ndarray,
|
|
|
366
467
|
Eyl = np.zeros((x.shape[0],), dtype=np.complex128)
|
|
367
468
|
Ezl = np.zeros((x.shape[0],), dtype=np.complex128)
|
|
368
469
|
|
|
369
|
-
V1 =
|
|
370
|
-
V2 =
|
|
470
|
+
V1 = 216*V**3
|
|
471
|
+
V2 = 72*V**3
|
|
371
472
|
|
|
372
473
|
for ie in range(6):
|
|
373
474
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
@@ -461,8 +562,103 @@ def compute_curl(coords: np.ndarray,
|
|
|
461
562
|
out[2,:] = Ezl
|
|
462
563
|
return out
|
|
463
564
|
|
|
464
|
-
@njit(c16[:](f8[
|
|
465
|
-
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(
|
|
466
662
|
vertices: np.ndarray,
|
|
467
663
|
Etet: np.ndarray,
|
|
468
664
|
l_edge_ids: np.ndarray,
|
|
@@ -473,10 +669,6 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
473
669
|
uyx, uyy, uyz = Um[1,0], Um[1,1], Um[1,2]
|
|
474
670
|
uzx, uzy, uzz = Um[2,0], Um[2,1], Um[2,2]
|
|
475
671
|
|
|
476
|
-
x = coords[0]
|
|
477
|
-
y = coords[1]
|
|
478
|
-
z = coords[2]
|
|
479
|
-
|
|
480
672
|
xvs = vertices[0,:]
|
|
481
673
|
yvs = vertices[1,:]
|
|
482
674
|
zvs = vertices[2,:]
|
|
@@ -492,7 +684,7 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
492
684
|
Em2s = Etet[10:16]
|
|
493
685
|
Ef2s = Etet[16:20]
|
|
494
686
|
|
|
495
|
-
V1 = (
|
|
687
|
+
V1 = (6*V)**3
|
|
496
688
|
|
|
497
689
|
for ie in range(6):
|
|
498
690
|
Em1, Em2 = Em1s[ie], Em2s[ie]
|
|
@@ -510,9 +702,9 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
510
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))
|
|
511
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))
|
|
512
704
|
|
|
513
|
-
Exl += ex
|
|
514
|
-
Eyl += ey
|
|
515
|
-
Ezl += ez
|
|
705
|
+
Exl += ex/V1
|
|
706
|
+
Eyl += ey/V1
|
|
707
|
+
Ezl += ez/V1
|
|
516
708
|
|
|
517
709
|
for ie in range(4):
|
|
518
710
|
Em1, Em2 = Ef1s[ie], Ef2s[ie]
|
|
@@ -529,13 +721,13 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
529
721
|
L1 = np.sqrt((x1-x3)**2 + (y1-y3)**2 + (z1-z3)**2)
|
|
530
722
|
L2 = np.sqrt((x1-x2)**2 + (y1-y2)**2 + (z1-z2)**2)
|
|
531
723
|
|
|
532
|
-
ex =
|
|
533
|
-
ey =
|
|
534
|
-
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))))
|
|
535
727
|
|
|
536
|
-
Exl += ex
|
|
537
|
-
Eyl += ey
|
|
538
|
-
Ezl += ez
|
|
728
|
+
Exl += ex/V1
|
|
729
|
+
Eyl += ey/V1
|
|
730
|
+
Ezl += ez/V1
|
|
539
731
|
|
|
540
732
|
out = np.zeros((3,), dtype=np.complex128)
|
|
541
733
|
out[0] = Exl
|
|
@@ -543,9 +735,40 @@ def compute_curl_curl(coords: np.ndarray,
|
|
|
543
735
|
out[2] = Ezl
|
|
544
736
|
return out
|
|
545
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
|
+
|
|
546
769
|
@njit(types.Tuple((f8[:], f8[:]))(f8[:,:], i8[:,:], i8[:,:], i8[:,:], f8[:,:],
|
|
547
770
|
c16[:], f8[:], f8[:], i8[:,:], i8[:,:],
|
|
548
|
-
f8[:,:], i8[:,:], i8[:,:], c16[:], c16[:], f8), cache=True, nogil=True)
|
|
771
|
+
f8[:,:], i8[:,:], i8[:,:], c16[:], c16[:], i8[:], f8), cache=True, nogil=True)
|
|
549
772
|
def compute_error_single(nodes, tets, tris, edges, centers,
|
|
550
773
|
Efield,
|
|
551
774
|
edge_lengths,
|
|
@@ -557,83 +780,209 @@ def compute_error_single(nodes, tets, tris, edges, centers,
|
|
|
557
780
|
tet_to_field,
|
|
558
781
|
er,
|
|
559
782
|
ur,
|
|
783
|
+
pec_tris,
|
|
560
784
|
k0,) -> np.ndarray:
|
|
561
785
|
|
|
786
|
+
tet_is_pec = np.zeros((tris.shape[1],), dtype=np.bool)
|
|
787
|
+
tet_is_pec[pec_tris] = True
|
|
562
788
|
|
|
563
|
-
#
|
|
564
|
-
|
|
565
|
-
|
|
789
|
+
# CONSTANTS
|
|
790
|
+
N_TETS = tets.shape[1]
|
|
791
|
+
N_EDGES = edges.shape[1]
|
|
792
|
+
N2D = DPTS_2D.shape[1]
|
|
793
|
+
WEIGHTS_VOL = DPTS_3D[0,:]
|
|
566
794
|
|
|
795
|
+
W0 = k0*C0
|
|
796
|
+
Y0 = np.sqrt(1/MU0)
|
|
567
797
|
|
|
568
|
-
# INIT POSTERIORI ERROR ESTIMATE
|
|
569
|
-
|
|
570
|
-
max_elem_size = np.zeros((
|
|
798
|
+
# INIT POSTERIORI ERROR ESTIMATE QUANTITIES
|
|
799
|
+
alpha_t = np.zeros((N_TETS,), dtype=np.complex128)
|
|
800
|
+
max_elem_size = np.zeros((N_TETS,), dtype=np.float64)
|
|
571
801
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
802
|
+
Qf_face1 = np.zeros((4,N2D,N_TETS), dtype=np.complex128)
|
|
803
|
+
Qf_face2 = np.zeros((4,N2D,N_TETS), dtype=np.complex128)
|
|
804
|
+
Jf_face1 = np.zeros((4,3,N2D,N_TETS), dtype=np.complex128)
|
|
805
|
+
Jf_face2 = np.zeros((4,3,N2D,N_TETS), dtype=np.complex128)
|
|
806
|
+
|
|
807
|
+
areas_face_residual = np.zeros((4, N2D, N_TETS), dtype=np.float64)
|
|
808
|
+
Rf_face_residual = np.zeros((4, N_TETS), dtype=np.float64)
|
|
809
|
+
adj_tets_mat = -np.ones((4,N_TETS), dtype=np.int32)
|
|
576
810
|
|
|
577
|
-
|
|
578
811
|
# Compute Error estimate
|
|
579
|
-
for itet in range(
|
|
812
|
+
for itet in range(N_TETS):
|
|
580
813
|
uinv = (1/ur[itet])*np.eye(3)
|
|
581
814
|
ermat = er[itet]*np.eye(3)
|
|
815
|
+
erc = er[itet]
|
|
816
|
+
urc = ur[itet]
|
|
582
817
|
|
|
818
|
+
# GEOMETRIC QUANTITIES
|
|
583
819
|
vertices = nodes[:,tets[:, itet]]
|
|
820
|
+
v1 = vertices[:,0]
|
|
821
|
+
v2 = vertices[:,1]
|
|
822
|
+
v3 = vertices[:,2]
|
|
823
|
+
v4 = vertices[:,3]
|
|
824
|
+
|
|
825
|
+
# VOLUME INTEGRATION POINTS
|
|
826
|
+
vxs = DPTS_3D[1,:]*v1[0] + DPTS_3D[2,:]*v2[0] + DPTS_3D[3,:]*v3[0] + DPTS_3D[4,:]*v4[0]
|
|
827
|
+
vys = DPTS_3D[1,:]*v1[1] + DPTS_3D[2,:]*v2[1] + DPTS_3D[3,:]*v3[1] + DPTS_3D[4,:]*v4[1]
|
|
828
|
+
vzs = DPTS_3D[1,:]*v1[2] + DPTS_3D[2,:]*v2[2] + DPTS_3D[3,:]*v3[2] + DPTS_3D[4,:]*v4[2]
|
|
829
|
+
|
|
830
|
+
intpts = np.empty((3,DPTS_3D.shape[1]), dtype=np.float64)
|
|
831
|
+
intpts[0,:] = vxs
|
|
832
|
+
intpts[1,:] = vys
|
|
833
|
+
intpts[2,:] = vzs
|
|
584
834
|
|
|
835
|
+
# TET TRI NODE COUPLINGS
|
|
585
836
|
g_node_ids = tets[:, itet]
|
|
586
837
|
g_edge_ids = edges[:, tet_to_field[:6, itet]]
|
|
587
|
-
g_tri_ids = tris[:, tet_to_field[6:10, itet]-
|
|
588
|
-
|
|
838
|
+
g_tri_ids = tris[:, tet_to_field[6:10, itet]-N_EDGES]
|
|
589
839
|
l_edge_ids = local_mapping(g_node_ids, g_edge_ids)
|
|
590
840
|
l_tri_ids = local_mapping(g_node_ids, g_tri_ids)
|
|
591
|
-
|
|
592
|
-
|
|
841
|
+
triids = tet_to_tri[:,itet]
|
|
842
|
+
|
|
843
|
+
size_max = circum_sphere_diam(v1,v2,v3,v4)
|
|
844
|
+
#size_max = np.max(edge_lengths[tet_to_edge[:,itet]])
|
|
845
|
+
|
|
846
|
+
TET_VOLUME = compute_volume(vertices[0,:], vertices[1,:], vertices[2,:])
|
|
847
|
+
Rt = size_max
|
|
848
|
+
|
|
849
|
+
# Efield
|
|
593
850
|
Ef = Efield[tet_to_field[:,itet]]
|
|
594
|
-
Rv1 = -compute_curl_curl(coords, vertices, Ef, l_edge_ids, l_tri_ids, uinv)
|
|
595
|
-
Rv2 = k0**2*(ermat @ compute_field(coords, vertices, Ef, l_edge_ids, l_tri_ids))
|
|
596
851
|
|
|
597
|
-
|
|
598
|
-
|
|
852
|
+
# Qt term
|
|
853
|
+
Qt = TET_VOLUME*EPS0*np.sum(WEIGHTS_VOL*compute_div(intpts, vertices, Ef, l_edge_ids, l_tri_ids, ermat), axis=0)
|
|
854
|
+
|
|
855
|
+
# Jt term
|
|
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,:]*WEIGHTS_VOL
|
|
864
|
+
Rv[1,:] = Rv[1,:]*WEIGHTS_VOL
|
|
865
|
+
Rv[2,:] = Rv[2,:]*WEIGHTS_VOL
|
|
866
|
+
|
|
867
|
+
Jt = -TET_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
|
|
599
873
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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)
|
|
603
888
|
tetc = centers[:,itet].flatten()
|
|
604
889
|
|
|
605
|
-
max_elem_size[itet] =
|
|
890
|
+
max_elem_size[itet] = size_max
|
|
606
891
|
|
|
607
892
|
for iface in range(4):
|
|
608
|
-
|
|
893
|
+
tri_index = triids[iface]
|
|
894
|
+
|
|
895
|
+
pec_face = tet_is_pec[tri_index]
|
|
896
|
+
|
|
897
|
+
i1, i2, i3 = tris[:, tri_index]
|
|
898
|
+
|
|
899
|
+
slc1 = iface*N2D
|
|
900
|
+
slc2 = slc1+N2D
|
|
901
|
+
|
|
609
902
|
normal = outward_normal(nodes[:,i1], nodes[:,i2], nodes[:,i3], tetc).astype(np.complex128)
|
|
610
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_face_residual[iface,itet] = Rf
|
|
915
|
+
areas_face_residual[iface, :, itet] = area
|
|
916
|
+
|
|
611
917
|
adj_tets = [int(tri_to_tet[j,triids[iface]]) for j in range(2)]
|
|
612
918
|
adj_tets = [num for num in adj_tets if num not in (itet, -1234)]
|
|
613
919
|
|
|
614
920
|
if len(adj_tets) == 0:
|
|
615
921
|
continue
|
|
616
|
-
area = areas[triids[iface]]
|
|
617
922
|
|
|
618
|
-
|
|
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
|
|
619
933
|
|
|
620
934
|
itet_adj = adj_tets[0]
|
|
621
935
|
iface_adj = np.argwhere(tet_to_tri[:,itet_adj]==triids[iface])[0][0]
|
|
622
936
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
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])
|
|
626
942
|
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
fdiff = np.abs(face_error1 - face_error2)
|
|
630
|
-
fnorm = fdiff[:,0,:]**2 + fdiff[:,1,:]**2 + fdiff[:,2,:]**2
|
|
631
|
-
ferror = np.sum(fnorm*hfs, axis=0)
|
|
632
|
-
error = hks**2*error + 0.5*ferror
|
|
943
|
+
adj_tets_mat[iface,itet] = itet_adj
|
|
633
944
|
|
|
945
|
+
# Compute 2D Gauss quadrature weight matrix
|
|
946
|
+
fWs = np.empty_like(areas_face_residual, 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_face_residual*fWs, axis=1)
|
|
956
|
+
Jf_int_x = np.sum(Jf_delta[:,0,:,:]*areas_face_residual*fWs, axis=1)
|
|
957
|
+
Jf_int_y = np.sum(Jf_delta[:,1,:,:]*areas_face_residual*fWs, axis=1)
|
|
958
|
+
Jf_int_z = np.sum(Jf_delta[:,2,:,:]*areas_face_residual*fWs, axis=1)
|
|
959
|
+
|
|
960
|
+
Gf = (1j*W0*np.exp(-1j*k0*Rf_face_residual)/(4*np.pi*Rf_face_residual))
|
|
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, N_TETS), dtype=np.complex128)
|
|
964
|
+
for it in range(N_TETS):
|
|
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
|
+
|
|
634
974
|
return error, max_elem_size
|
|
635
975
|
|
|
636
|
-
def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
976
|
+
def compute_error_estimate(field: MWField, pec_tris: list[int]) -> tuple[np.ndarray, np.ndarray]:
|
|
977
|
+
"""Top level function to compute the EM error of a field solution.
|
|
978
|
+
|
|
979
|
+
Args:
|
|
980
|
+
field (MWField): The MWField object to analyse
|
|
981
|
+
pec_tris (list[int]): A list of triangles that ought to be considered PEC (non-neighbouring.)
|
|
982
|
+
|
|
983
|
+
Returns:
|
|
984
|
+
np.ndarray, np.ndarray: The error estimate in an (Ntet,) float array and the tetrahedral size value.
|
|
985
|
+
"""
|
|
637
986
|
mesh = field.mesh
|
|
638
987
|
|
|
639
988
|
nodes = mesh.nodes
|
|
@@ -650,8 +999,10 @@ def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
|
650
999
|
tet_to_field = field.basis.tet_to_field
|
|
651
1000
|
er = field._der
|
|
652
1001
|
ur = field._dur
|
|
1002
|
+
|
|
653
1003
|
Ls = mesh.edge_lengths
|
|
654
1004
|
|
|
1005
|
+
pec_tris = np.sort(np.unique(np.array(pec_tris)))
|
|
655
1006
|
errors = []
|
|
656
1007
|
for key in field._fields.keys():
|
|
657
1008
|
excitation = field._fields[key]
|
|
@@ -659,7 +1010,7 @@ def compute_error_estimate(field: MWField) -> np.ndarray:
|
|
|
659
1010
|
error, sizes = compute_error_single(nodes, tets, tris, edges,
|
|
660
1011
|
centers, excitation, Ls, As,
|
|
661
1012
|
tet_to_edge, tet_to_tri, tri_centers,
|
|
662
|
-
tri_to_tet, tet_to_field, er, ur, field.k0)
|
|
1013
|
+
tri_to_tet, tet_to_field, er, ur, pec_tris, field.k0)
|
|
663
1014
|
|
|
664
1015
|
errors.append(error)
|
|
665
1016
|
|