run_single.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. """Single-point electromagnetic simulation verification script.
  2. Loads the MARS SSSR baseline model, runs one magnetic calculation
  3. with the model's default operating point, and extracts Phase 1 core
  4. metrics: average torque, torque ripple (%), system efficiency,
  5. total losses.
  6. Usage:
  7. python scripts/run_single.py
  8. python scripts/run_single.py --model models/MARS-12S10P_SSSR_D76-C150_V5.0-0819.mot
  9. python scripts/run_single.py --quit (close Motor-CAD after run)
  10. All source is ASCII.
  11. """
  12. from __future__ import annotations
  13. import argparse
  14. import json
  15. import os
  16. import sys
  17. import time
  18. from datetime import datetime
  19. from pathlib import Path
  20. # Add project root to sys.path so we can import src.solver_core.
  21. PROJECT_ROOT = Path(__file__).resolve().parent.parent
  22. sys.path.insert(0, str(PROJECT_ROOT))
  23. from src.solver_core import ( # noqa: E402
  24. MotorCADSolver,
  25. METRIC_LABELS,
  26. REQUIRED_METRICS,
  27. )
  28. DEFAULT_MODEL = PROJECT_ROOT / "models" / "MARS-12S10P_SSSR_D76-C150_V5.0-0819.mot"
  29. DEFAULT_OUTPUT = PROJECT_ROOT / "output"
  30. def _log(text: str) -> None:
  31. stamp = datetime.now().strftime("%H:%M:%S")
  32. print(f"[{stamp}] {text}", flush=True)
  33. def main() -> int:
  34. parser = argparse.ArgumentParser(description="Single-point Motor-CAD simulation")
  35. parser.add_argument("--model", type=str, default=str(DEFAULT_MODEL),
  36. help="Path to .mot model file")
  37. parser.add_argument("--output-dir", type=str, default=str(DEFAULT_OUTPUT),
  38. help="Directory for raw export and result JSON")
  39. parser.add_argument("--quit", action="store_true",
  40. help="Close Motor-CAD after simulation (default: keep open)")
  41. args = parser.parse_args()
  42. model_path = Path(args.model)
  43. if not model_path.exists():
  44. print(f"ERROR: Model file not found: {model_path}")
  45. return 1
  46. output_dir = Path(args.output_dir)
  47. output_dir.mkdir(parents=True, exist_ok=True)
  48. _log("=" * 60)
  49. _log("PCB Axial Flux Motor - Single Point Simulation")
  50. _log("=" * 60)
  51. _log(f"Model: {model_path.name}")
  52. _log(f"Output: {output_dir}")
  53. _log("")
  54. solver = MotorCADSolver(log_cb=_log)
  55. result = {}
  56. try:
  57. solver.connect()
  58. _log("")
  59. _log("Running single-point simulation (model default operating point)...")
  60. _log("")
  61. result = solver.run_single(
  62. model_path=model_path,
  63. params=None, # use model default operating point
  64. output_dir=output_dir,
  65. tag="single",
  66. )
  67. _log("")
  68. _log("=" * 60)
  69. _log("RESULTS")
  70. _log("=" * 60)
  71. _log(f"Status: {result['status']}")
  72. _log(f"Solve time: {result['solve_time_s']}s")
  73. if result.get("error"):
  74. _log(f"Error: {result['error']}")
  75. _log("")
  76. metrics = result.get("metrics", {})
  77. # Print Phase 1 required metrics first.
  78. _log("--- Phase 1 Core Metrics ---")
  79. for key in REQUIRED_METRICS:
  80. label = METRIC_LABELS.get(key, key)
  81. value = metrics.get(key, "N/A")
  82. if isinstance(value, float):
  83. _log(f" {label}: {value:.4f}")
  84. else:
  85. _log(f" {label}: {value}")
  86. _log("")
  87. # Print all other extracted metrics.
  88. other = {k: v for k, v in metrics.items() if k not in REQUIRED_METRICS}
  89. if other:
  90. _log("--- Additional Metrics ---")
  91. for key, value in other.items():
  92. label = METRIC_LABELS.get(key, key)
  93. if isinstance(value, float):
  94. _log(f" {label}: {value:.4f}")
  95. else:
  96. _log(f" {label}: {value}")
  97. _log("")
  98. # Save result JSON.
  99. ts = datetime.now().strftime("%Y%m%d_%H%M%S")
  100. result_path = output_dir / f"single_result_{ts}.json"
  101. with open(result_path, "w", encoding="utf-8") as f:
  102. json.dump(result, f, indent=2, ensure_ascii=False)
  103. _log(f"Result saved: {result_path}")
  104. if result["status"] == "OK":
  105. _log("")
  106. _log("SUCCESS: All Phase 1 required metrics extracted.")
  107. elif result["status"] == "UNCERTAIN":
  108. _log("")
  109. _log("WARNING: Some required metrics missing - check raw export.")
  110. else:
  111. _log("")
  112. _log("FAILED: Simulation or extraction failed.")
  113. except KeyboardInterrupt:
  114. _log("Interrupted by user.")
  115. except Exception as exc:
  116. _log(f"FATAL: {type(exc).__name__}: {exc}")
  117. import traceback
  118. _log(traceback.format_exc())
  119. return 1
  120. finally:
  121. if args.quit:
  122. solver.disconnect()
  123. else:
  124. _log("")
  125. _log("Motor-CAD kept open for manual inspection.")
  126. _log("Use --quit flag to auto-close.")
  127. _log("")
  128. _log("Done.")
  129. return 0 if result.get("status") in ("OK", "UNCERTAIN") else 1
  130. if __name__ == "__main__":
  131. sys.exit(main())