| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 |
- """Simulation result model."""
- import json
- from datetime import datetime
- from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Float
- from ..database import Base
- class SimulationResult(Base):
- """A single simulation result row (from scan_results.csv).
- P3-M1: Extended with fidelity level, confidence grade,
- constraint margins, surrogate prediction, and cross-validation
- per third-party review.
- """
- __tablename__ = "simulation_results"
- id = Column(Integer, primary_key=True, index=True)
- plan_id = Column(Integer, ForeignKey("simulation_plans.id"), nullable=False, index=True)
- run_index = Column(Integer, default=0)
- status = Column(String(20), default="OK") # OK / FAILED / UNCERTAIN
- solve_time_s = Column(Float, default=0.0)
- params_json = Column(Text, default="{}") # JSON: parameter values
- metrics_json = Column(Text, default="{}") # JSON: metric values
- # Lossless Motor-CAD export archive: JSON list of
- # {section, name, value, unit} covering every exported EM+thermal field.
- raw_json = Column(Text, default="[]")
- error_message = Column(Text, default="")
- created_at = Column(DateTime, default=datetime.utcnow)
- # P3: Multi-fidelity and confidence fields
- fidelity_level = Column(String(30), default="L1_motorcad_emag", index=True) # L0-L4
- confidence_grade = Column(String(2), default="C", index=True) # A/B/C/D
- model_template_version = Column(String(100), default="") # Motor-CAD template / PCB model version
- solver_settings_hash = Column(String(64), default="") # Hash of solver config
- constraint_margins_json = Column(Text, default="{}") # JSON: hard constraint margins
- surrogate_prediction_json = Column(Text, default="{}") # JSON: surrogate prediction + uncertainty
- cross_validation_json = Column(Text, default="{}") # JSON: Motor-CAD vs Maxwell/JMAG deviation
- convergence_status_json = Column(Text, default="{}") # JSON: six-type convergence status
- def get_params(self) -> dict:
- try:
- return json.loads(self.params_json) if self.params_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_params(self, data: dict) -> None:
- self.params_json = json.dumps(data, ensure_ascii=False)
- def get_metrics(self) -> dict:
- try:
- return json.loads(self.metrics_json) if self.metrics_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_metrics(self, data: dict) -> None:
- self.metrics_json = json.dumps(data, ensure_ascii=False)
- def get_raw(self) -> list:
- """Full-fidelity export rows: [{section, name, value, unit}, ...]."""
- try:
- data = json.loads(self.raw_json) if self.raw_json else []
- return data if isinstance(data, list) else []
- except (json.JSONDecodeError, TypeError):
- return []
- def set_raw(self, data: list) -> None:
- self.raw_json = json.dumps(data or [], ensure_ascii=False)
- # P3: Helper methods for extended fields
- def get_constraint_margins(self) -> dict:
- try:
- return json.loads(self.constraint_margins_json) if self.constraint_margins_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_constraint_margins(self, data: dict) -> None:
- self.constraint_margins_json = json.dumps(data, ensure_ascii=False)
- def get_surrogate_prediction(self) -> dict:
- try:
- return json.loads(self.surrogate_prediction_json) if self.surrogate_prediction_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_surrogate_prediction(self, data: dict) -> None:
- self.surrogate_prediction_json = json.dumps(data, ensure_ascii=False)
- def get_cross_validation(self) -> dict:
- try:
- return json.loads(self.cross_validation_json) if self.cross_validation_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_cross_validation(self, data: dict) -> None:
- self.cross_validation_json = json.dumps(data, ensure_ascii=False)
- def get_convergence_status(self) -> dict:
- try:
- return json.loads(self.convergence_status_json) if self.convergence_status_json else {}
- except (json.JSONDecodeError, TypeError):
- return {}
- def set_convergence_status(self, data: dict) -> None:
- self.convergence_status_json = json.dumps(data, ensure_ascii=False)
|