213 lines
9.4 KiB
Python
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()
|