"""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()