"""Export SBCmesh.mat (MATLAB getMesh.m output) to OpticsFEM SBCmesh.dat format.""" from __future__ import annotations import argparse from pathlib import Path import scipy.io as sio def _as_rows(arr, ncol: int) -> list[list[int]]: if arr.ndim == 1: return [list(arr)] if arr.shape[0] == ncol and arr.shape[1] != ncol: arr = arr.T return [list(row) for row in arr] def export_sbcmesh_mat_to_dat(mat_path: Path, dat_path: Path) -> None: data = sio.loadmat(str(mat_path), squeeze_me=True, struct_as_record=False) mesh = data["mesh"] lines: list[str] = [] lines.append("NbrVertex") lines.append(str(int(mesh.NbrVertex))) lines.append("Vertex") for row in _as_rows(mesh.Vertex, 3): lines.append(f"{row[0]:.16g} {row[1]:.16g} {row[2]:.16g}") lines.append("NbrTet") lines.append(str(int(mesh.NbrTet))) lines.append("Tet") for row in _as_rows(mesh.Tet, 4): lines.append(f"{int(row[0])} {int(row[1])} {int(row[2])} {int(row[3])}") lines.append("DomainOfTet") dom_tet = mesh.DomainOfTet.flatten() for v in dom_tet: lines.append(str(int(v))) lines.append("NbrEdge") lines.append(str(int(mesh.NbrEdge))) lines.append("Edge") for row in _as_rows(mesh.Edge, 2): lines.append(f"{int(row[0])} {int(row[1])}") lines.append("EdgeOfTet") eot_rows = _as_rows(mesh.EdgeOfTet, 6) zero_based = any(int(v) == 0 for row in eot_rows for v in row) for row in eot_rows: vals = [int(x) + (1 if zero_based else 0) for x in row] lines.append(" ".join(str(x) for x in vals)) lines.append("NbrTri") lines.append(str(int(mesh.NbrTri))) lines.append("Tri") for row in _as_rows(mesh.Tri, 3): lines.append(f"{int(row[0])} {int(row[1])} {int(row[2])}") lines.append("DomainOfTri") dom_tri = mesh.DomainOfTri.flatten() for v in dom_tri: lines.append(str(int(v))) lines.append("ConnOfTri") for row in _as_rows(mesh.ConnOfTri, 2): lines.append(f"{int(row[0])} {int(row[1])}") norm_rows = [1, 2, 3, 4, 5, 14] norm_face = mesh.NormOfFace lines.append("NormOfFace") lines.append(str(len(norm_rows))) for d in norm_rows: n = norm_face[d - 1] if norm_face.shape[0] >= d else norm_face[d] if hasattr(n, "__len__") and len(n) >= 3: nx, ny, nz = float(n[0]), float(n[1]), float(n[2]) else: defaults = { 1: (-1, 0, 0), 2: (0, -1, 0), 3: (0, 0, -1), 4: (0, 0, 1), 5: (0, 1, 0), 14: (1, 0, 0), } nx, ny, nz = defaults[d] lines.append(f"{d} {nx:.16g} {ny:.16g} {nz:.16g}") dat_path.parent.mkdir(parents=True, exist_ok=True) dat_path.write_text("\n".join(lines) + "\n", encoding="ascii") print(f"Wrote {dat_path} ({len(lines)} lines)") print( f" NbrVertex={mesh.NbrVertex}, NbrTet={mesh.NbrTet}, " f"NbrEdge={mesh.NbrEdge}, NbrTri={mesh.NbrTri}" ) def main() -> None: parser = argparse.ArgumentParser(description="Export SBCmesh.mat to SBCmesh.dat") parser.add_argument("mat_file", type=Path, help="Input SBCmesh.mat path") parser.add_argument( "-o", "--output", type=Path, default=None, help="Output SBCmesh.dat path (default: same dir as mat)", ) args = parser.parse_args() out = args.output or args.mat_file.with_name("SBCmesh.dat") export_sbcmesh_mat_to_dat(args.mat_file, out) if __name__ == "__main__": main()