runmat_meshing_size/adaptive/
convergence.rs1use 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}