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