优化仿真求解性能并修复流量闭合问题(初版)
This commit is contained in:
1 parent
57b459bc72
commit
5332a788f3
55 files changed
+8973
-549
No files matched your search
@@ -0,0 +1,161 @@
|
||||
"""Process-local warm-up for the numerical simulation runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
import logging
|
||||
import os
|
||||
from threading import Lock
|
||||
from time import perf_counter
|
||||
from typing import Literal
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
WarmupStatus = Literal["completed", "failed", "disabled"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SimulationWarmupReport:
|
||||
status: WarmupStatus
|
||||
duration_ms: float
|
||||
error: str | None = None
|
||||
|
||||
def as_dict(self) -> dict[str, object]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
_WARMUP_LOCK = Lock()
|
||||
_WARMUP_REPORT: SimulationWarmupReport | None = None
|
||||
|
||||
|
||||
def simulation_warmup_enabled() -> bool:
|
||||
raw_value = os.getenv("SIMULATIONAPP_WARMUP", "on").strip().lower()
|
||||
if raw_value in {"", "1", "true", "yes", "on"}:
|
||||
return True
|
||||
if raw_value in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
raise ValueError(
|
||||
"SIMULATIONAPP_WARMUP must be one of: on, off, true, false, 1, 0."
|
||||
)
|
||||
|
||||
|
||||
def _run_numerical_warmup() -> None:
|
||||
"""Exercise only in-memory SciPy paths used by real simulations."""
|
||||
|
||||
import numpy as np
|
||||
from scipy.integrate import BDF, DOP853, LSODA, RK23, RK45, Radau, solve_ivp
|
||||
from scipy.optimize import brentq, least_squares
|
||||
from scipy.optimize._numdiff import group_columns
|
||||
from scipy.sparse import csc_matrix, csr_matrix
|
||||
|
||||
# Importing these classes is intentional even though the micro solve below
|
||||
# uses BDF: the stepwise solver selects them dynamically at runtime.
|
||||
solver_types = (BDF, DOP853, LSODA, RK23, RK45, Radau)
|
||||
if len(solver_types) != 6:
|
||||
raise RuntimeError("SciPy solver warm-up did not load every supported method.")
|
||||
|
||||
sparsity = csc_matrix(np.array([[1.0]], dtype=float))
|
||||
groups = group_columns(sparsity)
|
||||
if groups.shape != (1,):
|
||||
raise RuntimeError("SciPy Jacobian grouping warm-up returned an invalid shape.")
|
||||
|
||||
integration = solve_ivp(
|
||||
lambda _time, state: -state,
|
||||
(0.0, 1.0e-4),
|
||||
np.array([1.0], dtype=float),
|
||||
method="BDF",
|
||||
t_eval=np.array([0.0, 1.0e-4], dtype=float),
|
||||
jac_sparsity=sparsity,
|
||||
rtol=1.0e-6,
|
||||
atol=1.0e-9,
|
||||
)
|
||||
if not integration.success or not np.isfinite(integration.y).all():
|
||||
raise RuntimeError("SciPy integration warm-up did not complete successfully.")
|
||||
|
||||
algebraic_sparsity = csr_matrix(np.eye(2, dtype=bool))
|
||||
algebraic = least_squares(
|
||||
lambda state: np.array(
|
||||
[state[0] - 1.0, state[1] - 2.0],
|
||||
dtype=float,
|
||||
),
|
||||
np.array([0.5, 0.5], dtype=float),
|
||||
bounds=(
|
||||
np.array([0.0, 0.0], dtype=float),
|
||||
np.array([3.0, 3.0], dtype=float),
|
||||
),
|
||||
jac_sparsity=algebraic_sparsity,
|
||||
tr_solver="lsmr",
|
||||
)
|
||||
if (
|
||||
not algebraic.success
|
||||
or not np.isfinite(algebraic.x).all()
|
||||
or not np.allclose(algebraic.x, np.array([1.0, 2.0]), atol=1.0e-8)
|
||||
):
|
||||
raise RuntimeError("SciPy algebraic warm-up did not complete successfully.")
|
||||
|
||||
root = brentq(lambda value: value - 0.5, 0.0, 1.0)
|
||||
if abs(root - 0.5) > 1.0e-12:
|
||||
raise RuntimeError("SciPy scalar root warm-up returned an invalid result.")
|
||||
|
||||
# Compile the cached v3 XSD through the same public validation path. The
|
||||
# intentionally incomplete document is never accepted or persisted.
|
||||
from app.system_xml import validate_system_xml_document
|
||||
|
||||
validate_system_xml_document(b"<System/>")
|
||||
|
||||
|
||||
def warm_up_simulation_runtime() -> SimulationWarmupReport:
|
||||
"""Warm one worker exactly once, returning a startup diagnostic report.
|
||||
|
||||
Ordinary warm-up failures are reported but do not prevent the editor and
|
||||
non-simulation APIs from starting. ``MemoryError`` remains fatal because
|
||||
continuing a worker under memory exhaustion is unsafe.
|
||||
"""
|
||||
|
||||
global _WARMUP_REPORT
|
||||
|
||||
with _WARMUP_LOCK:
|
||||
if _WARMUP_REPORT is not None:
|
||||
return _WARMUP_REPORT
|
||||
if not simulation_warmup_enabled():
|
||||
_WARMUP_REPORT = SimulationWarmupReport(
|
||||
status="disabled",
|
||||
duration_ms=0.0,
|
||||
)
|
||||
return _WARMUP_REPORT
|
||||
|
||||
started = perf_counter()
|
||||
try:
|
||||
_run_numerical_warmup()
|
||||
except MemoryError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
_WARMUP_REPORT = SimulationWarmupReport(
|
||||
status="failed",
|
||||
duration_ms=(perf_counter() - started) * 1000.0,
|
||||
error=f"{type(exc).__name__}: {exc}",
|
||||
)
|
||||
LOGGER.exception("Simulation runtime warm-up failed; startup will continue.")
|
||||
else:
|
||||
_WARMUP_REPORT = SimulationWarmupReport(
|
||||
status="completed",
|
||||
duration_ms=(perf_counter() - started) * 1000.0,
|
||||
)
|
||||
LOGGER.info(
|
||||
"Simulation runtime warm-up completed in %.1f ms.",
|
||||
_WARMUP_REPORT.duration_ms,
|
||||
)
|
||||
return _WARMUP_REPORT
|
||||
|
||||
|
||||
def _reset_simulation_warmup_for_tests() -> None:
|
||||
global _WARMUP_REPORT
|
||||
with _WARMUP_LOCK:
|
||||
_WARMUP_REPORT = None
|
||||
|
||||
|
||||
__all__ = [
|
||||
"SimulationWarmupReport",
|
||||
"simulation_warmup_enabled",
|
||||
"warm_up_simulation_runtime",
|
||||
]
|
||||
Reference in new issue
Block a user