cmtool_core/model/
interfaces.rs1use crate::coordinates::*;
4use crate::grid::NeighborDirection;
5use crate::model::CMGeometry;
6use crate::utils::{compute_intersection_area, is_curved_face, tangent_plane_at};
7
8const REL_TOLERANCE_AREA: f64 = 0.1;
9
10fn is_area_mismatch(total_area: f64, theoretical_area: f64) -> bool {
13 if theoretical_area.abs() < f64::EPSILON {
14 return total_area.abs() > f64::EPSILON;
15 }
16
17 (total_area - theoretical_area).abs() / theoretical_area > REL_TOLERANCE_AREA
18}
19
20#[derive(Default, Clone)]
21pub struct InterfaceInfo {
22 pub source_id: usize,
23 pub target_id: usize,
24}
25
26#[derive(Clone, Copy, Debug)]
27pub struct InterfaceFlow {
28 pub source_flow: f64,
29 pub target_flow: f64,
30}
31
32pub struct AInterfacesInfo {
33 n_facet: Vec<usize>,
34 pub ids: Vec<InterfaceInfo>,
35 pub area: Vec<Vec<f64>>,
36 pub normal_axis: Vec<usize>,
37 pub global_id_from_interface: Vec<Vec<usize>>,
38 pub interface_theta: Vec<f64>,
39 }
42
43impl AInterfacesInfo {
44 pub fn new(at_interface: Vec<usize>) -> Self {
45 let n_interfaces = at_interface.len();
46
47 Self {
48 n_facet: at_interface,
49 ids: vec![Default::default(); n_interfaces],
50 area: vec![Default::default(); n_interfaces],
51 normal_axis: vec![Default::default(); n_interfaces],
52 global_id_from_interface: vec![Default::default(); n_interfaces],
53 interface_theta: vec![Default::default(); n_interfaces],
54 }
57 }
58}
59
60impl Default for InterfaceFlow {
79 fn default() -> Self {
80 InterfaceFlow {
81 source_flow: 0.0,
82 target_flow: 0.0,
83 }
84 }
85}
86
87impl AInterfacesInfo {
88 pub fn n_interfaces(&self) -> usize {
89 self.n_facet.len()
90 }
91
92 pub fn fill(&mut self, geometry: &CMGeometry, interface_count_raw: &[usize]) {
93 let mut planes: Vec<BoundedPlane> = Vec::with_capacity(self.n_interfaces());
94
95 let grid = geometry.get_grid().unwrap();
96 let n_zones = geometry.n_zone();
97 let mut interfaces_id_from_cells = vec![0; n_zones * n_zones];
98 let mut interface_counter = 0;
99
100 for source_id in 0..n_zones {
101 for target_id in 0..n_zones {
102 if interface_count_raw[source_id * n_zones + target_id] == 0 {
103 continue;
104 }
105
106 let interface_id = interface_counter;
107 interface_counter += 1;
108 self.ids[interface_id] = InterfaceInfo {
109 source_id,
110 target_id,
111 };
112 interfaces_id_from_cells[source_id * n_zones + target_id] = interface_id;
113 interfaces_id_from_cells[target_id * n_zones + source_id] = interface_id;
114
115 let (plane, direction_neighbors) = grid.get_interface_plane(source_id, target_id);
116
117 let origin = plane.origin.0;
118 let theta = origin[1].atan2(origin[0]);
119 self.interface_theta[interface_id] = theta;
120 planes.push(plane);
121 self.normal_axis[interface_id] = direction_neighbors;
122 }
123 }
124 self.global_id_from_interface =
125 self.count_interfaces_second_pass(geometry, &interfaces_id_from_cells);
126 self.fill_area(geometry, &planes);
127
128 }
143 #[allow(unused)]
144 fn check_areas(&self, geometry: &CMGeometry) {
145 let grid = geometry.get_grid().unwrap();
146 let n_zones = geometry.n_zone();
147 for cell_id in 0..n_zones {
148 for axis_idx in 0..3 {
149 let axis = crate::grid::index_to_oriented(axis_idx);
150 let theoretical_area = grid.cell_surface(cell_id, axis);
151 let total_area: f64 = self
152 .ids
153 .iter()
154 .enumerate()
155 .filter(|(i, id)| {
156 (id.source_id == cell_id || id.target_id == cell_id)
157 && self.normal_axis[*i] == axis_idx
158 })
159 .map(|(i, _)| self.area[i].iter().sum::<f64>())
160 .sum();
161
162 if is_area_mismatch(total_area, theoretical_area) {
163 println!(
164 "(areas): area incorrect : axis: {}\r\n -cell_id:{}\r\n -total_area: {}\r\n -theoretical: {}",
165 axis_idx, cell_id, total_area, theoretical_area
166 );
167 }
168 }
169 }
170 }
171
172 fn count_interfaces_second_pass(
173 &mut self,
174 geometry: &CMGeometry,
175 interfaces_id_from_cells: &[usize],
176 ) -> Vec<Vec<usize>> {
177 let mut tmp_element_counter = vec![0; self.n_facet.len()];
178 let mut global_id_from_interface: Vec<Vec<usize>> = vec![Vec::new(); self.n_facet.len()];
179 for (element_id, n_element) in global_id_from_interface.iter_mut().zip(self.n_facet.iter())
180 {
181 *element_id = vec![0; *n_element];
182 }
183 let grid = geometry.get_grid().unwrap();
184 let n_zones = geometry.n_zone();
185
186 for (vol_element_global_id, _, interface_cid_k, k_vertex) in geometry.interface_iter() {
187 for i in 0..k_vertex {
189 let cid_i = geometry
190 .volume_elements
191 .get_list_compartment_id(vol_element_global_id, i);
192 let neighbors = grid.are_cell_neighbor(cid_i, interface_cid_k);
193 if neighbors != NeighborDirection::NotNeighbors {
194 let interface_global_id =
195 interfaces_id_from_cells[cid_i * n_zones + interface_cid_k];
196 let k_element = tmp_element_counter[interface_global_id];
197 tmp_element_counter[interface_global_id] += 1;
198 global_id_from_interface[interface_global_id][k_element] =
199 vol_element_global_id;
200 }
201 }
202 }
204
205 global_id_from_interface
206 }
207
208 fn fill_area(&mut self, geometry: &CMGeometry, planes: &[BoundedPlane]) {
209 for (i, n) in self.n_facet.iter().enumerate() {
211 self.area[i].resize(*n, 0.);
212 }
213
214 let mut local_vertices: Vec<CartesianCoordinates> = Vec::new();
215
216 for (interface_id, cn_facet) in self.n_facet.iter().enumerate() {
217 let plane = &planes[interface_id];
218 for i_facet in 0..*cn_facet {
219 let volume_element_global_id = self.global_id_from_interface[interface_id][i_facet];
220
221 let (elem_type, n_vertex) = geometry
222 .volume_elements
223 .get_element_and_nvertex(volume_element_global_id);
224
225 geometry.fill_vertices(volume_element_global_id, n_vertex, &mut local_vertices);
226
227 let element_plane = is_curved_face(plane).then(|| {
229 tangent_plane_at(
230 plane,
231 geometry.volume_elements.xyz[volume_element_global_id],
232 )
233 });
234
235 let area = compute_intersection_area(
236 &local_vertices,
237 elem_type,
238 element_plane.as_ref().unwrap_or(plane),
239 )
240 .expect("Area between element");
241
242 self.area[interface_id][i_facet] = area;
243 }
244 }
245 }
246}
247
248#[cfg(test)]
249mod test {
250 use super::*;
251
252 #[test]
253 fn test_matching_area_is_not_reported() {
254 assert!(!is_area_mismatch(10., 10.));
255 assert!(!is_area_mismatch(10.5, 10.));
257 }
258
259 #[test]
260 fn test_mismatching_area_is_reported() {
261 assert!(is_area_mismatch(5., 10.));
262 assert!(is_area_mismatch(0., 10.));
263 assert!(is_area_mismatch(20., 10.));
264 }
265
266 #[test]
268 fn test_area_without_geometric_surface() {
269 assert!(is_area_mismatch(1., 0.));
270 assert!(!is_area_mismatch(0., 0.));
271 }
272}