runmat_meshing/visualization/
field_mapping.rs1use 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;