Skip to main content

runmat_meshing_size/adaptive/
convergence.rs

1use serde::{Deserialize, Serialize};
2
3use crate::refinement::{MeshRefinementOptions, RefinementStrategy};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum AdaptiveConvergenceStatus {
8    NotStarted,
9    Disabled,
10    Pending,
11    Converged,
12    MaxIterationsReached,
13    ElementBudgetReached,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Default)]
17pub struct AdaptiveConvergenceMetrics {
18    pub completed_iterations: usize,
19    pub element_budget_reached: bool,
20    pub previous_node_count: Option<usize>,
21    pub current_node_count: Option<usize>,
22    pub previous_element_count: Option<usize>,
23    pub current_element_count: Option<usize>,
24    pub field_change: Option<f64>,
25    pub energy_change: Option<f64>,
26    pub residual: Option<f64>,
27}
28
29pub fn evaluate_adaptive_convergence(
30    options: &MeshRefinementOptions,
31    metrics: AdaptiveConvergenceMetrics,
32) -> AdaptiveConvergenceStatus {
33    if matches!(
34        options.strategy,
35        RefinementStrategy::None | RefinementStrategy::Uniform
36    ) {
37        return AdaptiveConvergenceStatus::Disabled;
38    }
39    if metrics.element_budget_reached {
40        return AdaptiveConvergenceStatus::ElementBudgetReached;
41    }
42    if metrics.completed_iterations >= options.max_iterations {
43        return AdaptiveConvergenceStatus::MaxIterationsReached;
44    }
45    if metrics.completed_iterations > 0
46        && matches!(
47            (
48                metrics.previous_node_count,
49                metrics.current_node_count,
50                metrics.previous_element_count,
51                metrics.current_element_count,
52            ),
53            (Some(previous_nodes), Some(current_nodes), Some(previous_elements), Some(current_elements))
54                if current_nodes <= previous_nodes && current_elements <= previous_elements
55        )
56    {
57        return AdaptiveConvergenceStatus::Converged;
58    }
59
60    let mut considered_metric = false;
61    let mut converged = true;
62
63    if let Some(field_change) = metrics.field_change {
64        considered_metric = true;
65        converged &=
66            field_change.is_finite() && field_change <= options.convergence.field_change_tolerance;
67    }
68    if let Some(energy_change) = metrics.energy_change {
69        considered_metric = true;
70        converged &= energy_change.is_finite()
71            && energy_change <= options.convergence.energy_change_tolerance;
72    }
73    if let (Some(residual), Some(tolerance)) =
74        (metrics.residual, options.convergence.residual_tolerance)
75    {
76        considered_metric = true;
77        converged &= residual.is_finite() && residual <= tolerance;
78    }
79
80    if considered_metric && converged {
81        AdaptiveConvergenceStatus::Converged
82    } else {
83        AdaptiveConvergenceStatus::Pending
84    }
85}