chore: remove deprecated flow_boundary and update docs to match new architecture

This commit is contained in:
Sepehr
2026-03-01 20:00:09 +01:00
parent 20700afce8
commit d88914a44f
105 changed files with 11222 additions and 2994 deletions

View File

@@ -7,7 +7,10 @@ use std::time::{Duration, Instant};
use crate::criteria::ConvergenceCriteria;
use crate::metadata::SimulationMetadata;
use crate::solver::{ConvergedState, ConvergenceStatus, Solver, SolverError, TimeoutConfig};
use crate::solver::{
ConvergedState, ConvergenceDiagnostics, ConvergenceStatus, IterationDiagnostics, Solver,
SolverError, SolverType, TimeoutConfig, VerboseConfig,
};
use crate::system::System;
/// Configuration for the Sequential Substitution (Picard iteration) solver.
@@ -38,6 +41,8 @@ pub struct PicardConfig {
pub initial_state: Option<Vec<f64>>,
/// Multi-circuit convergence criteria.
pub convergence_criteria: Option<ConvergenceCriteria>,
/// Verbose mode configuration for diagnostics.
pub verbose_config: VerboseConfig,
}
impl Default for PicardConfig {
@@ -54,6 +59,7 @@ impl Default for PicardConfig {
previous_residual: None,
initial_state: None,
convergence_criteria: None,
verbose_config: VerboseConfig::default(),
}
}
}
@@ -78,6 +84,12 @@ impl PicardConfig {
self
}
/// Enables verbose mode for diagnostics.
pub fn with_verbose(mut self, config: VerboseConfig) -> Self {
self.verbose_config = config;
self
}
/// Computes the residual norm (L2 norm of the residual vector).
fn residual_norm(residuals: &[f64]) -> f64 {
residuals.iter().map(|r| r * r).sum::<f64>().sqrt()
@@ -194,12 +206,21 @@ impl Solver for PicardConfig {
fn solve(&mut self, system: &mut System) -> Result<ConvergedState, SolverError> {
let start_time = Instant::now();
// Initialize diagnostics collection if verbose mode enabled
let verbose_enabled = self.verbose_config.enabled && self.verbose_config.is_any_enabled();
let mut diagnostics = if verbose_enabled {
Some(ConvergenceDiagnostics::with_capacity(self.max_iterations))
} else {
None
};
tracing::info!(
max_iterations = self.max_iterations,
tolerance = self.tolerance,
relaxation_factor = self.relaxation_factor,
divergence_threshold = self.divergence_threshold,
divergence_patience = self.divergence_patience,
verbose = verbose_enabled,
"Sequential Substitution (Picard) solver starting"
);
@@ -328,6 +349,13 @@ impl Solver for PicardConfig {
previous_norm = current_norm;
current_norm = Self::residual_norm(&residuals);
// Compute delta norm for diagnostics
let delta_norm: f64 = state.iter()
.zip(prev_iteration_state.iter())
.map(|(s, p)| (s - p).powi(2))
.sum::<f64>()
.sqrt();
// Update best state if residual improved (Story 4.5 - AC: #2)
if current_norm < best_residual {
@@ -340,6 +368,29 @@ impl Solver for PicardConfig {
);
}
// Verbose mode: Log iteration residuals
if verbose_enabled && self.verbose_config.log_residuals {
tracing::info!(
iteration,
residual_norm = current_norm,
delta_norm = delta_norm,
relaxation_factor = self.relaxation_factor,
"Picard iteration"
);
}
// Collect iteration diagnostics
if let Some(ref mut diag) = diagnostics {
diag.push_iteration(IterationDiagnostics {
iteration,
residual_norm: current_norm,
delta_norm,
alpha: None, // Picard doesn't use line search
jacobian_frozen: false, // Picard doesn't use Jacobian
jacobian_condition: None, // No Jacobian in Picard
});
}
tracing::debug!(
iteration = iteration,
residual_norm = current_norm,
@@ -352,20 +403,37 @@ impl Solver for PicardConfig {
let report =
criteria.check(&state, Some(&prev_iteration_state), &residuals, system);
if report.is_globally_converged() {
// Finalize diagnostics
if let Some(ref mut diag) = diagnostics {
diag.iterations = iteration;
diag.final_residual = current_norm;
diag.best_residual = best_residual;
diag.converged = true;
diag.timing_ms = start_time.elapsed().as_millis() as u64;
diag.final_solver = Some(SolverType::SequentialSubstitution);
if self.verbose_config.log_residuals {
tracing::info!("{}", diag.summary());
}
}
tracing::info!(
iterations = iteration,
final_residual = current_norm,
relaxation_factor = self.relaxation_factor,
"Sequential Substitution converged (criteria)"
);
return Ok(ConvergedState::with_report(
let result = ConvergedState::with_report(
state,
iteration,
current_norm,
ConvergenceStatus::Converged,
report,
SimulationMetadata::new(system.input_hash()),
));
);
return Ok(if let Some(d) = diagnostics {
ConvergedState { diagnostics: Some(d), ..result }
} else { result });
}
false
} else {
@@ -373,19 +441,36 @@ impl Solver for PicardConfig {
};
if converged {
// Finalize diagnostics
if let Some(ref mut diag) = diagnostics {
diag.iterations = iteration;
diag.final_residual = current_norm;
diag.best_residual = best_residual;
diag.converged = true;
diag.timing_ms = start_time.elapsed().as_millis() as u64;
diag.final_solver = Some(SolverType::SequentialSubstitution);
if self.verbose_config.log_residuals {
tracing::info!("{}", diag.summary());
}
}
tracing::info!(
iterations = iteration,
final_residual = current_norm,
relaxation_factor = self.relaxation_factor,
"Sequential Substitution converged"
);
return Ok(ConvergedState::new(
let result = ConvergedState::new(
state,
iteration,
current_norm,
ConvergenceStatus::Converged,
SimulationMetadata::new(system.input_hash()),
));
);
return Ok(if let Some(d) = diagnostics {
ConvergedState { diagnostics: Some(d), ..result }
} else { result });
}
// Check divergence (AC: #5)
@@ -401,6 +486,27 @@ impl Solver for PicardConfig {
}
}
// Non-convergence: dump diagnostics if enabled
if let Some(ref mut diag) = diagnostics {
diag.iterations = self.max_iterations;
diag.final_residual = current_norm;
diag.best_residual = best_residual;
diag.converged = false;
diag.timing_ms = start_time.elapsed().as_millis() as u64;
diag.final_solver = Some(SolverType::SequentialSubstitution);
if self.verbose_config.dump_final_state {
diag.final_state = Some(state.clone());
let json_output = diag.dump_diagnostics(self.verbose_config.output_format);
tracing::warn!(
iterations = self.max_iterations,
final_residual = current_norm,
"Non-convergence diagnostics:\n{}",
json_output
);
}
}
// Max iterations exceeded
tracing::warn!(
max_iterations = self.max_iterations,