Xmod Co-simulation — [best]
@abstractmethod def step(self, t: float, dt: float, inputs: Dict[str, np.ndarray]) -> XModStep: """Advance model by dt from t with given inputs.""" pass
def run_with_iteration(self, t_start: float, t_end: float, max_iter: int = 10): """Jacobi-style fixed-point iteration at each time step.""" t = t_start outputs = {name: {} for name in self.models} while t < t_end - 1e-12: converged = False for _iter in range(max_iter): inputs_for = {name: {} for name in self.models} for fm, fp, tm, tp in self.connections: if fm in outputs and fp in outputs[fm]: inputs_for[tm][tp] = outputs[fm][fp] new_outputs = {} for name, model in self.models.items(): step_result = model.step(t, self.dt, inputs_for[name]) new_outputs[name] = step_result.outputs # Check convergence (norm of output change) diff = 0.0 for name in self.models: for port in new_outputs[name]: old_val = outputs[name].get(port, 0) diff += np.linalg.norm(new_outputs[name][port] - old_val) outputs = new_outputs if diff < 1e-8: converged = True break print(f"t={t:.3f}, iter={_iter+1}, converged={converged}") t += self.dt Demo ---------------------------------------------------------------------- if name == " main ": # Build system: controller -> spring-mass-damper plant = SpringMassDamper("mass_spring", m=1.0, k=10.0, c=0.5) ctrl = PController("controller", Kp=20.0, x_ref=1.0) xmod co-simulation
def step(self, t: float, dt: float, inputs: Dict[str, np.ndarray]) -> XModStep: F_ext = inputs.get("F_ext", np.array([0.0]))[0] # Simple semi-implicit Euler a = (F_ext - self.k * self.x - self.c * self.v) / self.m self.v += a * dt self.x += self.v * dt return XModStep( outputs={"x": np.array([self.x]), "v": np.array([self.v])}, new_time=t + dt ) @abstractmethod def step(self, t: float, dt: float, inputs:
def get_state(self): return {} # stateless @abstractmethod def step(self
