Skip to main content

runmat_meshing_size/adaptive/
indicator_plan.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::refinement::{MeshRefinementOptions, RefinementIndicatorMode, RefinementStrategy};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum RefinementIndicatorStatus {
10    Used,
11    SkippedMissingField,
12    SkippedNotApplicable,
13    SkippedBudget,
14    SkippedQuality,
15}
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct RefinementIndicatorSummary {
19    pub namespace: String,
20    pub name: String,
21    pub requested_mode: RefinementIndicatorMode,
22    pub status: RefinementIndicatorStatus,
23    #[serde(default)]
24    pub detail: Option<String>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
28pub struct RefinementIndicatorKey {
29    pub namespace: String,
30    pub name: String,
31}
32
33impl RefinementIndicatorKey {
34    pub fn new(namespace: impl Into<String>, name: impl Into<String>) -> Self {
35        Self {
36            namespace: namespace.into(),
37            name: name.into(),
38        }
39    }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43pub struct RefinementIndicatorAvailability {
44    pub key: RefinementIndicatorKey,
45    pub applicable: bool,
46    pub field_available: bool,
47}
48
49pub fn plan_refinement_indicators(
50    options: &MeshRefinementOptions,
51    defaults: &[RefinementIndicatorKey],
52    availability: &[RefinementIndicatorAvailability],
53    element_budget_reached: bool,
54    quality_blocked: bool,
55) -> Vec<RefinementIndicatorSummary> {
56    if matches!(
57        options.strategy,
58        RefinementStrategy::None | RefinementStrategy::Uniform
59    ) {
60        return Vec::new();
61    }
62
63    let availability_by_key = availability
64        .iter()
65        .map(|item| (item.key.clone(), item))
66        .collect::<BTreeMap<_, _>>();
67    let overrides = options
68        .indicators
69        .namespaces
70        .iter()
71        .flat_map(|(namespace, names)| {
72            names.iter().map(|(name, mode)| {
73                (
74                    RefinementIndicatorKey::new(namespace.clone(), name.clone()),
75                    *mode,
76                )
77            })
78        })
79        .collect::<BTreeMap<_, _>>();
80
81    let mut keys = defaults.iter().cloned().collect::<BTreeSet<_>>();
82    keys.extend(overrides.keys().cloned());
83
84    keys.into_iter()
85        .map(|key| {
86            let requested_mode = overrides
87                .get(&key)
88                .copied()
89                .unwrap_or(RefinementIndicatorMode::Auto);
90            let (status, detail) = if matches!(requested_mode, RefinementIndicatorMode::Off) {
91                (
92                    RefinementIndicatorStatus::SkippedNotApplicable,
93                    Some("indicator disabled by override".to_string()),
94                )
95            } else if element_budget_reached {
96                (
97                    RefinementIndicatorStatus::SkippedBudget,
98                    Some("element budget reached".to_string()),
99                )
100            } else if quality_blocked {
101                (
102                    RefinementIndicatorStatus::SkippedQuality,
103                    Some("mesh quality constraint blocked refinement".to_string()),
104                )
105            } else if let Some(available) = availability_by_key.get(&key) {
106                if !available.applicable {
107                    (
108                        RefinementIndicatorStatus::SkippedNotApplicable,
109                        Some("indicator does not apply to the active analysis".to_string()),
110                    )
111                } else if !available.field_available {
112                    (
113                        RefinementIndicatorStatus::SkippedMissingField,
114                        Some("required recovered field is unavailable".to_string()),
115                    )
116                } else {
117                    (RefinementIndicatorStatus::Used, None)
118                }
119            } else if matches!(requested_mode, RefinementIndicatorMode::On) {
120                (
121                    RefinementIndicatorStatus::SkippedMissingField,
122                    Some("required recovered field is unavailable".to_string()),
123                )
124            } else {
125                (
126                    RefinementIndicatorStatus::SkippedNotApplicable,
127                    Some("indicator was not selected by the active analysis".to_string()),
128                )
129            };
130
131            RefinementIndicatorSummary {
132                namespace: key.namespace,
133                name: key.name,
134                requested_mode,
135                status,
136                detail,
137            }
138        })
139        .collect()
140}