完成求解器雅可比矩阵首轮优化,增加更新目录,整理了文档文件夹,增加了服务启动脚本

This commit is contained in:
lujingze committed 2026-08-17 07:33:31 +00:00
1 parent 6bb0591d32
commit 16a7eb2d6c
48 files changed
+8172 -217

No files matched your search

+148 -14
View File
@@ -12,6 +12,7 @@ CancellationCheck = Callable[[], bool]
AcceptedStepCallback = Callable[[float], None]
IntegrationStatus = Literal["completed", "cancelled", "failed"]
DenseState = Callable[[float], list[float]]
JacobianCallable = Callable[[float, object], object]
@dataclass(frozen=True)
@@ -30,7 +31,9 @@ StateTransitionHandler = Callable[
_MAX_STATE_TRANSITIONS_AT_SAME_TIME = 64
class _IntegrationCancelled(Exception):
class IntegrationCancelled(Exception):
"""Internal control-flow signal shared by RHS and Jacobian evaluation."""
pass
@@ -59,9 +62,19 @@ class SolverSegmentDiagnostics:
solver_start_count: int = 0
state_transition_count: int = 0
recoverable_retry_count: int = 0
jacobian_evaluation_count: int = 0
jacobian_full_build_count: int = 0
jacobian_secant_reuse_count: int = 0
jacobian_audit_failure_count: int = 0
finite_difference_rhs_evaluation_count: int = 0
jacobian_base_rhs_evaluation_count: int = 0
jacobian_jv_audit_rhs_evaluation_count: int = 0
exact_column_build_count: int = 0
exact_column_fallback_count: int = 0
jacobian_assembly_seconds: float = 0.0
def as_dict(self) -> dict[str, float | int]:
return {
result: dict[str, float | int] = {
"startTime": self.start_time,
"requestedStopTime": self.requested_stop_time,
"simulatedUntil": self.simulated_until,
@@ -73,6 +86,61 @@ class SolverSegmentDiagnostics:
"stateTransitionCount": self.state_transition_count,
"recoverableRetryCount": self.recoverable_retry_count,
}
if (
self.jacobian_evaluation_count
or self.finite_difference_rhs_evaluation_count
or self.jacobian_assembly_seconds
):
result.update(
{
"jacobianEvaluationCount": self.jacobian_evaluation_count,
"jacobianFullBuildCount": self.jacobian_full_build_count,
"jacobianSecantReuseCount": self.jacobian_secant_reuse_count,
"jacobianAuditFailureCount": self.jacobian_audit_failure_count,
"finiteDifferenceRhsEvaluationCount": (
self.finite_difference_rhs_evaluation_count
),
"jacobianBaseRhsEvaluationCount": (
self.jacobian_base_rhs_evaluation_count
),
"jacobianJvAuditRhsEvaluationCount": (
self.jacobian_jv_audit_rhs_evaluation_count
),
"exactColumnBuildCount": self.exact_column_build_count,
"exactColumnFallbackCount": (
self.exact_column_fallback_count
),
"jacobianAssemblySeconds": self.jacobian_assembly_seconds,
}
)
return result
_JACOBIAN_DIAGNOSTIC_KEYS = (
"jacobianEvaluationCount",
"fullBuildCount",
"secantReuseCount",
"auditFailureCount",
"finiteDifferenceRhsEvaluationCount",
"baseRhsEvaluationCount",
"jvAuditEvaluationCount",
"exactColumnBuildCount",
"exactColumnFallbackCount",
"assemblySeconds",
)
def _jacobian_diagnostic_snapshot(
jac: JacobianCallable | None,
) -> dict[str, float]:
diagnostics = getattr(jac, "diagnostics", None)
if diagnostics is None:
return {key: 0.0 for key in _JACOBIAN_DIAGNOSTIC_KEYS}
values = diagnostics()
return {
key: float(values.get(key, 0.0))
for key in _JACOBIAN_DIAGNOSTIC_KEYS
}
@dataclass(frozen=True)
@@ -309,7 +377,7 @@ def _runge_kutta_4(
for target_time in t_eval[1:]:
while current_time < target_time:
if cancel_check is not None and cancel_check():
raise _IntegrationCancelled
raise IntegrationCancelled
dt = min(config.max_step, target_time - current_time)
k1 = rhs(current_time, state)
k2 = rhs(current_time + 0.5 * dt, _vector_add(state, k1, 0.5 * dt))
@@ -374,7 +442,7 @@ def _runge_kutta_4(
report_step(current_time)
_append_solution_sample(times, states, target_time, state)
except _IntegrationCancelled:
except IntegrationCancelled:
status = "cancelled"
message = "Simulation was stopped before reaching the requested end time."
_append_solution_sample(times, states, current_time, state)
@@ -451,7 +519,7 @@ def _runge_kutta_4_segmented(
nonlocal current_time, last_transition, same_time_transition_count, state
while current_time < target_time:
if cancel_check is not None and cancel_check():
raise _IntegrationCancelled
raise IntegrationCancelled
dt = min(config.max_step, target_time - current_time)
k1 = rhs(current_time, state)
k2 = rhs(
@@ -565,7 +633,7 @@ def _runge_kutta_4_segmented(
sample_time = float(sample_times[sample_index])
_append_solution_sample(times, states, sample_time, state)
sample_index += 1
except _IntegrationCancelled:
except IntegrationCancelled:
status = "cancelled"
message = "Simulation was stopped before reaching the requested end time."
_append_solution_sample(times, states, current_time, state)
@@ -595,6 +663,7 @@ def _integrate_scipy_stepwise(
breakpoints: Sequence[float] = (),
state_transition_handler: StateTransitionHandler | None = None,
jac_sparsity=None,
jac: JacobianCallable | None = None,
) -> ODESolution:
"""Initial stepwise integration path for breakpoints and state resets.
@@ -617,6 +686,7 @@ def _integrate_scipy_stepwise(
solver_type = solver_types.get(config.method)
if solver_type is None:
raise ValueError(f"Unsupported integration method: {config.method}")
implicit_jac = jac if config.method in {"BDF", "Radau"} else None
times = [float(config.t_start)]
states = [[float(value)] for value in initial_state]
@@ -632,8 +702,13 @@ def _integrate_scipy_stepwise(
def cancellable_rhs(time, state):
if cancel_check():
raise _IntegrationCancelled
return rhs(float(time), [float(value) for value in state])
raise IntegrationCancelled
normalized_state = [float(value) for value in state]
derivative = rhs(float(time), normalized_state)
observer = getattr(implicit_jac, "observe", None)
if observer is not None:
observer(float(time), normalized_state, derivative)
return derivative
status: IntegrationStatus = "completed"
message = "The solver successfully reached the end of the integration interval."
@@ -683,6 +758,7 @@ def _integrate_scipy_stepwise(
segment_solver_starts = 0
segment_state_transitions = 0
segment_recoverable_retries = 0
jacobian_work_start = _jacobian_diagnostic_snapshot(implicit_jac)
while has_integration_interval and last_accepted_time < integration_end:
if cancel_check():
@@ -695,8 +771,11 @@ def _integrate_scipy_stepwise(
"atol": config.atol,
"max_step": segment_max_step,
}
if jac_sparsity is not None and config.method in {"BDF", "Radau"}:
solver_options["jac_sparsity"] = jac_sparsity
if config.method in {"BDF", "Radau"}:
if implicit_jac is not None:
solver_options["jac"] = implicit_jac
elif jac_sparsity is not None:
solver_options["jac_sparsity"] = jac_sparsity
requested_first_step = (
0.1 * segment_max_step
if last_recoverable_error is not None
@@ -709,6 +788,9 @@ def _integrate_scipy_stepwise(
)
try:
start_segment = getattr(implicit_jac, "start_segment", None)
if start_segment is not None:
start_segment()
solver = solver_type(
cancellable_rhs,
last_accepted_time,
@@ -716,7 +798,7 @@ def _integrate_scipy_stepwise(
integration_end,
**solver_options,
)
except _IntegrationCancelled:
except IntegrationCancelled:
status = "cancelled"
message = cancellation_message()
break
@@ -755,7 +837,7 @@ def _integrate_scipy_stepwise(
step_start_state = list(last_accepted_state)
try:
step_message = solver.step()
except _IntegrationCancelled:
except IntegrationCancelled:
status = "cancelled"
message = (
"Simulation was stopped before reaching the requested end time."
@@ -957,6 +1039,11 @@ def _integrate_scipy_stepwise(
if not restart_at_transition:
break
jacobian_work_end = _jacobian_diagnostic_snapshot(implicit_jac)
jacobian_work = {
key: jacobian_work_end[key] - jacobian_work_start[key]
for key in _JACOBIAN_DIAGNOSTIC_KEYS
}
solver_segments.append(
SolverSegmentDiagnostics(
start_time=float(segment_start_time),
@@ -971,6 +1058,34 @@ def _integrate_scipy_stepwise(
solver_start_count=segment_solver_starts,
state_transition_count=segment_state_transitions,
recoverable_retry_count=segment_recoverable_retries,
jacobian_evaluation_count=int(
jacobian_work["jacobianEvaluationCount"]
),
jacobian_full_build_count=int(
jacobian_work["fullBuildCount"]
),
jacobian_secant_reuse_count=int(
jacobian_work["secantReuseCount"]
),
jacobian_audit_failure_count=int(
jacobian_work["auditFailureCount"]
),
finite_difference_rhs_evaluation_count=int(
jacobian_work["finiteDifferenceRhsEvaluationCount"]
),
jacobian_base_rhs_evaluation_count=int(
jacobian_work["baseRhsEvaluationCount"]
),
jacobian_jv_audit_rhs_evaluation_count=int(
jacobian_work["jvAuditEvaluationCount"]
),
exact_column_build_count=int(
jacobian_work["exactColumnBuildCount"]
),
exact_column_fallback_count=int(
jacobian_work["exactColumnFallbackCount"]
),
jacobian_assembly_seconds=jacobian_work["assemblySeconds"],
)
)
if status != "completed":
@@ -1034,6 +1149,7 @@ def integrate_ode(
breakpoints: Sequence[float] | None = None,
state_transition_handler: StateTransitionHandler | None = None,
jac_sparsity=None,
jac: JacobianCallable | None = None,
):
"""Integrate an ODE, optionally restarting at equation discontinuities.
@@ -1104,10 +1220,26 @@ def integrate_ode(
normalized_breakpoints,
state_transition_handler,
jac_sparsity,
jac,
)
implicit_jac = jac if config.method in {"BDF", "Radau"} else None
solve_rhs = rhs
if implicit_jac is not None:
observer = getattr(implicit_jac, "observe", None)
if observer is not None:
def observed_rhs(time, state):
derivative = rhs(time, state)
observer(float(time), state, derivative)
return derivative
solve_rhs = observed_rhs
start_segment = getattr(implicit_jac, "start_segment", None)
if start_segment is not None:
start_segment()
solve_options = {
"fun": rhs,
"fun": solve_rhs,
"t_span": (config.t_start, config.t_stop),
"y0": initial_state,
"method": config.method,
@@ -1118,6 +1250,8 @@ def integrate_ode(
}
if config.first_step is not None:
solve_options["first_step"] = config.first_step
if jac_sparsity is not None and config.method in {"BDF", "Radau"}:
if implicit_jac is not None:
solve_options["jac"] = implicit_jac
elif jac_sparsity is not None and config.method in {"BDF", "Radau"}:
solve_options["jac_sparsity"] = jac_sparsity
return solve_ivp(**solve_options)