XIAN-FEM-2026June/3D opticsfem-master/tools/diagnose_assembly.py

213 lines
9.4 KiB
Python

"""MATLAB assembly_equ/out/inc 忠实复现,用于与 C++ 对比定位问题。"""
import numpy as np
from pathlib import Path
from scipy import sparse
from scipy.sparse.linalg import spsolve
PI = np.pi
def get_bf(typ, num, u, v, w):
if typ == 1:
table = {
1: np.array([-v, u, 0.0]),
2: np.array([-w, 0.0, u]),
3: np.array([-1 + v + w, -u, -u]),
4: np.array([0.0, -w, v]),
5: np.array([-v, -1 + u + w, -v]),
6: np.array([-w, -w, -1 + u + v]),
}
return table[num]
table = {
1: np.array([0.0, 0.0, 2.0]),
2: np.array([0.0, -2.0, 0.0]),
3: np.array([0.0, 2.0, -2.0]),
4: np.array([2.0, 0.0, 0.0]),
5: np.array([-2.0, 0.0, 2.0]),
6: np.array([2.0, -2.0, 0.0]),
}
return table[num]
def gp_tet():
u = np.array([0.25, 0.166666666667, 0.166666666667, 0.166666666667, 0.5])
v = np.array([0.25, 0.166666666667, 0.166666666667, 0.5, 0.166666666667])
w = np.array([0.25, 0.166666666667, 0.5, 0.166666666667, 0.166666666667])
wg = np.array([-0.133333333333, 0.075, 0.075, 0.075, 0.075])
return u, v, w, wg
def gp_tri():
u = np.array([0.333333333333333, 0.6, 0.2, 0.2])
v = np.array([0.3333333333333333, 0.2, 0.6, 0.2])
wg = np.array([-0.28125, 0.260416666666, 0.260416666666, 0.260416666666])
return u, v, wg
def cross_n(n, a):
return np.cross(n, np.cross(a, n))
def load_mesh(path):
lines = Path(path).read_text().splitlines()
i = 0
def need(t):
nonlocal i
assert lines[i] == t, (lines[i], t)
i += 1
need("NbrVertex"); nv = int(lines[i]); i += 1
need("Vertex")
V = np.array([list(map(float, lines[i + k].split())) for k in range(nv)]); i += nv
need("NbrTet"); nt = int(lines[i]); i += 1
need("Tet")
T = np.array([list(map(int, lines[i + k].split())) for k in range(nt)], int); i += nt
need("DomainOfTet")
domT = np.array([int(lines[i + k]) for k in range(nt)]); i += nt
need("NbrEdge"); ne = int(lines[i]); i += 1
need("Edge"); i += ne
need("EdgeOfTet")
EOT = np.array([list(map(int, lines[i + k].split())) for k in range(nt)], int); i += nt
need("NbrTri"); ntri = int(lines[i]); i += 1
need("Tri"); i += ntri
need("DomainOfTri")
domTri = np.array([int(lines[i + k]) for k in range(ntri)]); i += ntri
need("ConnOfTri")
conn = np.array([list(map(int, lines[i + k].split())) for k in range(ntri)], int); i += ntri
need("NormOfFace"); nn = int(lines[i]); i += 1
norm = {}
for _ in range(nn):
parts = list(map(float, lines[i].split())); i += 1
d = int(parts[0]); norm[d] = np.array(parts[1:4])
norm.setdefault(4, np.array([0.0, 0.0, 1.0]))
return dict(V=V, T=T, domT=domT, EOT=EOT, domTri=domTri, conn=conn, norm=norm, ne=ne, nt=nt, ntri=ntri)
def find_tri(domains, domTri):
out = []
for d in domains:
out.extend(np.where(domTri == d)[0].tolist())
return np.array(sorted(set(out)))
def assemble_equ(mesh, lda0, eps, use_inv_jac=True):
k0 = 2 * PI / lda0
u, v, w, wg = gp_tet()
rows, cols, vals = [], [], []
V, T, domT, EOT = mesh["V"], mesh["T"], mesh["domT"], mesh["EOT"]
for n in range(mesh["nt"]):
x = V[T[n] - 1, 0]; y = V[T[n] - 1, 1]; z = V[T[n] - 1, 2]
Jac = np.column_stack([x[:3] - x[3], y[:3] - y[3], z[:3] - z[3]])
detJ = np.linalg.det(Jac)
DetJac = abs(detJ)
InvJac = np.linalg.inv(Jac)
TJac = Jac.T / detJ # curl map unchanged
ep = eps[domT[n] - 1]
Ae = np.zeros((6, 6), complex)
for k in range(len(u)):
E = np.zeros((3, 6)); curlE = np.zeros((3, 6))
for j in range(6):
ref = get_bf(1, j + 1, u[k], v[k], w[k])
E[:, j] = (InvJac if use_inv_jac else Jac) @ ref
curlE[:, j] = TJac @ get_bf(2, j + 1, u[k], v[k], w[k])
for i in range(6):
for j in range(6):
Ae[i, j] += wg[k] * DetJac * np.dot(curlE[:, i], curlE[:, j]) - wg[k] * DetJac * k0 * k0 * ep * np.dot(E[:, i], E[:, j])
for i in range(6):
for j in range(6):
rows.append(EOT[n, i] - 1); cols.append(EOT[n, j] - 1); vals.append(Ae[i, j])
return rows, cols, vals
def face_map(num_face):
if num_face == 1: return np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1]), [1,2,4], [0,1,3]
if num_face == 2: return np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,0]), [1,3,5], [0,2,4]
if num_face == 3: return np.array([1,0,0]), np.array([0,0,0]), np.array([0,1,0]), [2,3,6], [1,2,5]
return np.array([0,0,0]), np.array([1,0,0]), np.array([0,1,0]), [4,5,6], [3,4,5]
def assemble_sbc(mesh, tri_idx, eps, lda0, Einc, is_inc, use_inv_jac=True):
k0 = 2 * PI / lda0
u, v, wg = gp_tri()
rows, cols, vals, b = [], [], [], {}
V, T, EOT, conn, norm = mesh["V"], mesh["T"], mesh["EOT"], mesh["conn"], mesh["norm"]
for tri in tri_idx:
domain = mesh["domTri"][tri]
num_tet = conn[tri, 0] - 1
num_face = conn[tri, 1]
x = V[T[num_tet] - 1, 0]; y = V[T[num_tet] - 1, 1]; z = V[T[num_tet] - 1, 2]
x2, y2, z2, bf_idx, edge_slot = face_map(num_face)
if num_face == 1: x3, y3, z3 = x[:3], y[:3], z[:3]
elif num_face == 2: x3, y3, z3 = x[[0,1,3]], y[[0,1,3]], z[[0,1,3]]
elif num_face == 3: x3, y3, z3 = x[[0,2,3]], y[[0,2,3]], z[[0,2,3]]
else: x3, y3, z3 = x[1:], y[1:], z[1:]
Jac = np.column_stack([x[:3] - x[3], y[:3] - y[3], z[:3] - z[3]])
InvJac = np.linalg.inv(Jac)
a = np.linalg.norm(np.array([x3[0]-x3[1], y3[0]-y3[1], z3[0]-z3[1]]))
b1 = np.linalg.norm(np.array([x3[0]-x3[2], y3[0]-y3[2], z3[0]-z3[2]]))
c = np.linalg.norm(np.array([x3[1]-x3[2], y3[1]-y3[2], z3[1]-z3[2]]))
integ = 0.25 * np.sqrt((a+b1+c)*(a+b1-c)*(a-b1+c)*(b1+c-a))
normal = norm[domain]
ep = eps[mesh["domT"][num_tet] - 1]
nn = np.sqrt(ep)
map_idx = [EOT[num_tet, s] - 1 for s in edge_slot]
Ae = np.zeros((3, 3), complex)
Be = np.zeros(3, complex)
for k in range(len(u)):
wgp = 1 - u[k] - v[k]
u2 = x2 @ [u[k], v[k], wgp]; v2 = y2 @ [u[k], v[k], wgp]; w2 = z2 @ [u[k], v[k], wgp]
Egp = np.zeros((3, 3))
for j in range(3):
ref = get_bf(1, bf_idx[j], u2, v2, w2)
Egp[:, j] = (InvJac if use_inv_jac else Jac) @ ref
for i in range(3):
for j in range(3):
tj = cross_n(normal, Egp[:, j])
Ae[i, j] += 1j * k0 * nn * integ * wg[k] * np.dot(Egp[:, i], tj) * 2
if is_inc:
t_inc = cross_n(normal, Einc)
for i in range(3):
Be[i] -= 1j * k0 * nn * 2 * integ * wg[k] * np.dot(Egp[:, i], t_inc) * 2
for i in range(3):
for j in range(3):
rows.append(map_idx[i]); cols.append(map_idx[j]); vals.append(Ae[i, j])
if is_inc:
b[map_idx[i]] = b.get(map_idx[i], 0) + Be[i]
return rows, cols, vals, (b if is_inc else {})
def solve_case(mesh, lda0, eps, out_dom, inc_dom, Einc, use_inv_jac, label):
r, c, v = assemble_equ(mesh, lda0, eps, use_inv_jac)
ro, co, vo, _ = assemble_sbc(mesh, find_tri(out_dom, mesh["domTri"]), eps, lda0, Einc, False, use_inv_jac)
ri, ci, vi, bi = assemble_sbc(mesh, find_tri(inc_dom, mesh["domTri"]), eps, lda0, Einc, True, use_inv_jac)
rows = r + ro + ri; cols = c + co + ci; vals = v + vo + vi
n = mesh["ne"]
A = sparse.coo_matrix((vals, (rows, cols)), shape=(n, n)).tocsr()
b = np.zeros(n, complex)
for k, val in bi.items(): b[k] = val
x = spsolve(A, b)
print(f"=== {label} ===")
print(f"nnz={A.nnz} |b|={np.linalg.norm(b):.6f} |x|={np.linalg.norm(x):.6f}")
return A, b, x
def compare_cpp(cpp_dir, A_ref, b_ref):
coo = Path(cpp_dir) / "coo.txt"
if not coo.exists():
print("cpp dump not found:", coo)
return
hdr = coo.read_text().splitlines()[0].split()
nnz, n = int(hdr[0]), int(hdr[1])
rows, cols, re, im = [], [], [], []
for line in coo.read_text().splitlines()[1:]:
p = line.split(); rows.append(int(p[0])); cols.append(int(p[1])); re.append(float(p[2])); im.append(float(p[3]))
A_cpp = sparse.coo_matrix((np.array(re)+1j*np.array(im), (rows, cols)), shape=(n, n)).tocsr()
b_cpp = np.zeros(n, complex)
for i, line in enumerate((Path(cpp_dir)/"b.txt").read_text().splitlines()):
p = line.split(); b_cpp[i] = float(p[0])+1j*float(p[1])
dA = A_cpp - A_ref
print("=== C++ vs ref (InvJac) ===")
print(f"max|dA|={np.max(np.abs(dA.data)) if dA.nnz else 0:.6e} rel|db|={np.linalg.norm(b_cpp-b_ref)/(np.linalg.norm(b_ref)+1e-30):.6e}")
print(f"cpp |b|={np.linalg.norm(b_cpp):.6f} solve|x|={np.linalg.norm(spsolve(A_cpp,b_cpp)):.6f}")
def main():
mesh_path = Path(r"C:\Users\Administrator\Desktop\西电-合作\三维matlab代码\matlab 3D一阶散射问题\SBCmesh.dat")
mesh = load_mesh(mesh_path)
lda0, eps = 0.8, np.array([1.0, 1.5])
out_dom = [1, 2, 3, 5, 14]; inc_dom = [4]
Einc = np.array([1.0, 0.0, 0.0])
A1, b1, x1 = solve_case(mesh, lda0, eps, out_dom, inc_dom, Einc, True, "InvJac (MATLAB Jac\\)")
A2, b2, x2 = solve_case(mesh, lda0, eps, out_dom, inc_dom, Einc, False, "Jac@ (Piola forward)")
compare_cpp(Path(r"C:\Users\Administrator\Desktop\西电-合作\3D opticsfem-master\build\Release\cpp_dump"), A1, b1)
if __name__ == "__main__":
main()