Skip to main content

runmat_meshing/visualization/
field_mapping.rs

1use std::collections::BTreeMap;
2
3use runmat_meshing_core::contracts::AnalysisMeshArtifact;
4
5mod error;
6mod types;
7mod validation;
8
9pub use error::FieldMappingError;
10pub use types::{BoundaryFaceScalarValue, BoundaryFaceVectorValue, BoundaryNodeVectorValue};
11
12use validation::{
13    validate_nodal_vector_field, validate_nodal_vector_to_boundary_face_topology,
14    validate_nodal_vector_to_boundary_node_topology,
15    validate_volume_scalar_to_boundary_face_topology,
16};
17
18pub fn map_volume_scalar_field_to_boundary_faces(
19    mesh: &AnalysisMeshArtifact,
20    element_values: &[f64],
21) -> Result<Vec<BoundaryFaceScalarValue>, FieldMappingError> {
22    validate_volume_scalar_to_boundary_face_topology(mesh)?;
23    if element_values.len() != mesh.volume_elements.len() {
24        return Err(FieldMappingError::ElementFieldLengthMismatch {
25            element_value_count: element_values.len(),
26            volume_element_count: mesh.volume_elements.len(),
27        });
28    }
29    for (element_index, value) in element_values.iter().enumerate() {
30        if !value.is_finite() {
31            return Err(FieldMappingError::NonFiniteElementValue { element_index });
32        }
33    }
34
35    let element_values_by_id = mesh
36        .volume_elements
37        .iter()
38        .zip(element_values.iter().copied())
39        .map(|(element, value)| (element.element_id.as_str(), value))
40        .collect::<BTreeMap<_, _>>();
41
42    mesh.boundary_faces
43        .iter()
44        .map(|face| {
45            if face.adjacent_volume_element_ids.is_empty() {
46                return Err(FieldMappingError::BoundaryFaceMissingAdjacentVolume {
47                    face_id: face.face_id.clone(),
48                });
49            }
50            let mut value_sum = 0.0_f64;
51            for volume_element_id in &face.adjacent_volume_element_ids {
52                let Some(value) = element_values_by_id
53                    .get(volume_element_id.as_str())
54                    .copied()
55                else {
56                    return Err(FieldMappingError::BoundaryFaceReferencesUnknownVolume {
57                        face_id: face.face_id.clone(),
58                        volume_element_id: volume_element_id.clone(),
59                    });
60                };
61                value_sum += value;
62            }
63            Ok(BoundaryFaceScalarValue {
64                face_id: face.face_id.clone(),
65                value: value_sum / face.adjacent_volume_element_ids.len() as f64,
66            })
67        })
68        .collect()
69}
70
71pub fn map_nodal_vector_field_to_boundary_nodes(
72    mesh: &AnalysisMeshArtifact,
73    node_values: &[[f64; 3]],
74) -> Result<Vec<BoundaryNodeVectorValue>, FieldMappingError> {
75    validate_nodal_vector_to_boundary_node_topology(mesh)?;
76    let node_values_by_id = validate_nodal_vector_field(mesh, node_values)?;
77    let mut boundary_node_ids = BTreeMap::<u32, ()>::new();
78
79    for face in &mesh.boundary_faces {
80        for node_id in &face.node_ids {
81            if !node_values_by_id.contains_key(node_id) {
82                return Err(FieldMappingError::BoundaryFaceReferencesUnknownNode {
83                    face_id: face.face_id.clone(),
84                    node_id: *node_id,
85                });
86            }
87            boundary_node_ids.insert(*node_id, ());
88        }
89    }
90    for edge in &mesh.boundary_edges {
91        for node_id in edge.node_ids {
92            if !node_values_by_id.contains_key(&node_id) {
93                return Err(FieldMappingError::BoundaryEdgeReferencesUnknownNode {
94                    edge_id: edge.edge_id.clone(),
95                    node_id,
96                });
97            }
98            boundary_node_ids.insert(node_id, ());
99        }
100    }
101
102    Ok(boundary_node_ids
103        .keys()
104        .map(|node_id| BoundaryNodeVectorValue {
105            node_id: *node_id,
106            value: node_values_by_id[node_id],
107        })
108        .collect())
109}
110
111pub fn map_nodal_vector_field_to_boundary_faces(
112    mesh: &AnalysisMeshArtifact,
113    node_values: &[[f64; 3]],
114) -> Result<Vec<BoundaryFaceVectorValue>, FieldMappingError> {
115    validate_nodal_vector_to_boundary_face_topology(mesh)?;
116    let node_values_by_id = validate_nodal_vector_field(mesh, node_values)?;
117
118    mesh.boundary_faces
119        .iter()
120        .map(|face| {
121            if face.node_ids.is_empty() {
122                return Err(FieldMappingError::BoundaryFaceHasNoNodes {
123                    face_id: face.face_id.clone(),
124                });
125            }
126            let mut value_sum = [0.0_f64; 3];
127            for node_id in &face.node_ids {
128                let Some(value) = node_values_by_id.get(node_id).copied() else {
129                    return Err(FieldMappingError::BoundaryFaceReferencesUnknownNode {
130                        face_id: face.face_id.clone(),
131                        node_id: *node_id,
132                    });
133                };
134                for component in 0..3 {
135                    value_sum[component] += value[component];
136                }
137            }
138            let node_count = face.node_ids.len() as f64;
139            Ok(BoundaryFaceVectorValue {
140                face_id: face.face_id.clone(),
141                value: [
142                    value_sum[0] / node_count,
143                    value_sum[1] / node_count,
144                    value_sum[2] / node_count,
145                ],
146            })
147        })
148        .collect()
149}
150
151#[cfg(test)]
152mod tests;