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

73 lines
2.3 KiB
Python

#!/usr/bin/env python3
"""Compare two OpticsFEM COO exports (Ai/Aj/Av, Bv_real/Bv_imag)."""
from __future__ import annotations
import argparse
from pathlib import Path
import numpy as np
from scipy import sparse
def load_coo(prefix: Path) -> tuple[sparse.csr_matrix, np.ndarray]:
ai = np.loadtxt(prefix / "Ai.txt", dtype=np.int64)
aj = np.loadtxt(prefix / "Aj.txt", dtype=np.int64)
av: list[complex] = []
for line in (prefix / "Av.txt").read_text(encoding="utf-8", errors="ignore").splitlines():
line = line.strip()
if not line:
continue
if line.startswith("(") and line.endswith(")"):
re_s, im_s = line[1:-1].split(",")
av.append(complex(float(re_s), float(im_s)))
else:
av.append(complex(float(line)))
a = sparse.coo_matrix((np.asarray(av), (ai, aj))).tocsr()
br = np.loadtxt(prefix / "Bv_real.txt")
bi = np.loadtxt(prefix / "Bv_imag.txt")
b = br + 1j * bi
return a, b
def main() -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("ref", type=Path, help="reference directory (e.g. MATLAB OutFile_fem4_ab)")
ap.add_argument("other", type=Path, help="other directory (e.g. C++ OutFile)")
args = ap.parse_args()
a_ref, b_ref = load_coo(args.ref)
a_other, b_other = load_coo(args.other)
print(f"ref: n={a_ref.shape[0]} nnz={a_ref.nnz} |b|={np.linalg.norm(b_ref):.6g}")
print(f"other: n={a_other.shape[0]} nnz={a_other.nnz} |b|={np.linalg.norm(b_other):.6g}")
if a_ref.shape != a_other.shape:
print("[FAIL] matrix shape mismatch")
return 1
if b_ref.size != b_other.size:
print("[FAIL] b length mismatch")
return 1
d = a_ref - a_other
max_abs = abs(d).max()
nnz_diff = d.nnz
rel = max_abs / max(abs(a_ref).max(), 1e-30)
print(f"A max|diff|={max_abs:.6g} rel={rel:.6g} nnz(diff)={nnz_diff}")
b_diff = np.max(np.abs(b_ref - b_other))
print(f"b max|diff|={b_diff:.6g}")
if max_abs < 1e-9 and b_diff < 1e-9:
print("[OK] matrices match within 1e-9")
return 0
if max_abs < 1e-6 and b_diff < 1e-6:
print("[WARN] close but not identical")
return 0
print("[FAIL] significant difference")
return 2
if __name__ == "__main__":
raise SystemExit(main())