#[derive(Debug, Clone)]
pub struct ConvergenceHistory {
pub residuals: Vec<f64>,
pub du_norms: Vec<f64>,
}
impl ConvergenceHistory {
pub fn new() -> Self {
Self {
residuals: Vec::new(),
du_norms: Vec::new(),
}
}
pub fn record(&mut self, residual: f64, du_norm: f64) {
self.residuals.push(residual);
self.du_norms.push(du_norm);
}
pub fn is_monotone_decreasing(&self) -> bool {
self.residuals.windows(2).all(|w| w[1] <= w[0])
}
pub fn convergence_rate(&self) -> Option<f64> {
let n = self.residuals.len();
if n < 2 || self.residuals[n - 2].abs() < 1e-60 {
return None;
}
Some(self.residuals[n - 1] / self.residuals[n - 2])
}
}
pub struct RiksState {
pub u: Vec<f64>,
pub lambda: f64,
pub du: Vec<f64>,
pub dlambda: f64,
pub arc_length: f64,
}
impl RiksState {
pub fn new(n_dof: usize, arc_length: f64) -> Self {
Self {
u: vec![0.0; n_dof],
lambda: 0.0,
du: vec![0.0; n_dof],
dlambda: 0.01,
arc_length,
}
}
pub fn current_arc(&self) -> f64 {
let du_sq: f64 = self.du.iter().map(|d| d * d).sum::<f64>();
(du_sq + self.dlambda * self.dlambda).sqrt()
}
}
pub struct SnapThroughDetector {
pub det_signs: Vec<f64>,
pub critical_loads: Vec<f64>,
}
impl SnapThroughDetector {
pub fn new() -> Self {
Self {
det_signs: Vec::new(),
critical_loads: Vec::new(),
}
}
pub fn check_2x2(&mut self, k: [[f64; 2]; 2], lambda: f64) -> bool {
let det = k[0][0] * k[1][1] - k[0][1] * k[1][0];
let sign = if det >= 0.0 { 1.0 } else { -1.0 };
let snap = if let Some(&prev_sign) = self.det_signs.last() {
prev_sign * sign < 0.0
} else {
false
};
if snap {
self.critical_loads.push(lambda);
}
self.det_signs.push(sign);
snap
}
pub fn has_snap_through(&self) -> bool {
!self.critical_loads.is_empty()
}
}
pub struct EnergyConvergenceCriteria {
pub max_iter: usize,
pub energy_tol: f64,
}
impl EnergyConvergenceCriteria {
pub fn new(max_iter: usize, energy_tol: f64) -> Self {
Self {
max_iter,
energy_tol,
}
}
}
pub struct LoadStepper {
pub total_load: f64,
pub current_lambda: f64,
pub step_size: f64,
pub min_step: f64,
pub max_step: f64,
pub target_iters: usize,
}
impl LoadStepper {
pub fn new(total_load: f64, n_steps: usize) -> Self {
let step = 1.0 / (n_steps.max(1) as f64);
Self {
total_load,
current_lambda: 0.0,
step_size: step,
min_step: step * 0.1,
max_step: step * 4.0,
target_iters: 5,
}
}
pub fn advance(&mut self) -> Option<f64> {
if self.current_lambda >= 1.0 - 1e-14 {
return None;
}
self.current_lambda = (self.current_lambda + self.step_size).min(1.0);
Some(self.current_lambda * self.total_load)
}
pub fn adapt_step(&mut self, actual_iters: usize) {
let ratio = if actual_iters == 0 {
2.0
} else {
(self.target_iters as f64 / actual_iters as f64).sqrt()
};
self.step_size = (self.step_size * ratio).clamp(self.min_step, self.max_step);
}
pub fn is_complete(&self) -> bool {
self.current_lambda >= 1.0 - 1e-14
}
}
pub struct ArcLengthControl {
pub arc_length: f64,
pub lambda: f64,
pub d_lambda: f64,
}
impl ArcLengthControl {
pub fn new(arc_length: f64) -> Self {
Self {
arc_length,
lambda: 0.0,
d_lambda: 0.0,
}
}
pub fn step_size(&self) -> f64 {
self.arc_length
}
}
#[derive(Debug, Clone)]
pub struct ConvergenceCriteria {
pub max_iter: usize,
pub residual_tol: f64,
pub displacement_tol: f64,
}
pub struct ContinuationStepResult {
pub displacement: Vec<f64>,
pub lambda: f64,
pub converged: bool,
pub iterations: usize,
}
pub struct NrResult {
pub displacement: Vec<f64>,
pub converged: bool,
pub iterations: usize,
pub final_residual: f64,
}