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

163 lines
4.9 KiB
Python

#!/usr/bin/env python3
"""Align BoundaryFlag with sbc/ef/mag/scd/pbc index blocks in OpticsFEM JSON files."""
from __future__ import annotations
import json
import sys
from pathlib import Path
PMC, PEC, SBC, ELE, PBC, MAG, SCD = 0, 1, 2, 3, 4, 8, 9
ROOT = Path(__file__).resolve().parents[2]
OPTICS_JSON_GLOBS = [
ROOT / "3D opticsfem-master" / "*.json",
ROOT / "3D opticsfem-master" / "scat3D" / "Release" / "*.json",
ROOT / "3D opticsfem-master" / "scat3D_SBC" / "Release" / "*.json",
ROOT / "三维matlab代码" / "matlab 3D一阶散射问题" / "*.json",
ROOT / "三维matlab代码" / "matlab 3D一阶基+散射边界条件" / "*.json",
ROOT / "三维matlab代码" / "matlab 3D一阶基 + bele" / "*.json",
ROOT / "三维matlab代码" / "matlab 3D一阶基+散射边界条件+单周期边界" / "PBCmesh.json",
]
def collect_pbc_domains(doc: dict) -> set[int]:
domains: set[int] = set()
for key in ("pbc", "pbc2"):
if key not in doc:
continue
block = doc[key]
for field in ("srcIndex", "dstIndex"):
if field in block:
domains.update(int(x) for x in block[field])
return domains
def build_boundary_flag(doc: dict) -> list[int] | None:
if "NbrBoundary" not in doc or "BoundaryFlag" not in doc:
return None
n = int(doc["NbrBoundary"])
flags = [PMC] * n
for d in collect_pbc_domains(doc):
if 1 <= d <= n:
flags[d - 1] = PBC
if "sbc" in doc:
for d in doc["sbc"]["Index"]:
d = int(d)
if 1 <= d <= n:
flags[d - 1] = SBC
if "ef" in doc:
for d in doc["ef"]["index"]:
d = int(d)
if 1 <= d <= n:
flags[d - 1] = ELE
if "mag" in doc:
for d in doc["mag"]["index"]:
d = int(d)
if 1 <= d <= n:
flags[d - 1] = MAG
if "scd" in doc:
for d in doc["scd"]["index"]:
d = int(d)
if 1 <= d <= n:
flags[d - 1] = SCD
# Legacy 14-face cube: domain 6 is material interface, not an outer SBC face.
if n == 14 and "sbc" in doc:
sbc_set = {int(x) for x in doc["sbc"]["Index"]}
if 6 not in sbc_set and flags[5] == SBC:
flags[5] = PMC
return flags
def validate(doc: dict, path: Path) -> list[str]:
issues: list[str] = []
if "BoundaryFlag" not in doc:
return issues
flags = doc["BoundaryFlag"]
n = len(flags)
if "sbc" in doc:
for d in doc["sbc"]["Index"]:
d = int(d)
if not (1 <= d <= n):
issues.append(f"sbc.Index {d} out of range")
elif flags[d - 1] != SBC:
issues.append(f"sbc.Index {d} but BoundaryFlag[{d}]={flags[d - 1]} (expected {SBC})")
if "ef" in doc:
for d in doc["ef"]["index"]:
d = int(d)
if flags[d - 1] != ELE:
issues.append(f"ef.index {d} but BoundaryFlag[{d}]={flags[d - 1]} (expected {ELE})")
if "mag" in doc:
for d in doc["mag"]["index"]:
d = int(d)
if flags[d - 1] != MAG:
issues.append(f"mag.index {d} flag={flags[d - 1]} (expected {MAG})")
for d in collect_pbc_domains(doc):
if 1 <= d <= n and flags[d - 1] != PBC:
issues.append(f"pbc domain {d} but BoundaryFlag[{d}]={flags[d - 1]} (expected {PBC})")
return issues
def main() -> int:
write = "--write" in sys.argv
paths: list[Path] = []
for pattern in OPTICS_JSON_GLOBS:
if pattern.parent.exists():
paths.extend(sorted(pattern.parent.glob(pattern.name)))
changed = 0
for path in paths:
if path.name in ("CMakeSettings.json",):
continue
try:
doc = json.loads(path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
continue
if "FemType" not in doc:
continue
new_flags = build_boundary_flag(doc)
if new_flags is None:
continue
old_flags = doc.get("BoundaryFlag")
issues_before = validate(doc, path) if old_flags else ["missing BoundaryFlag"]
if old_flags == new_flags and not issues_before:
print(f"OK {path.relative_to(ROOT)}")
continue
print(f"FIX {path.relative_to(ROOT)}")
if old_flags != new_flags:
print(f" old: {old_flags}")
print(f" new: {new_flags}")
for msg in issues_before:
print(f" was: {msg}")
if write:
doc["BoundaryFlag"] = new_flags
path.write_text(json.dumps(doc, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
changed += 1
if write:
print(f"\nUpdated {changed} file(s).")
else:
print("\nDry run. Re-run with --write to apply.")
return 0
if __name__ == "__main__":
raise SystemExit(main())