Skip to main content

cmtool_core/model/
compartments.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::{
4    model::{CMGeometry, interfaces::AInterfacesInfo},
5    utils::compute_volume,
6};
7
8#[derive(Clone, Copy)]
9pub struct ElementVolumeInfo {
10    pub global_id: usize,
11    pub volume: f64,
12}
13
14impl Default for ElementVolumeInfo {
15    fn default() -> Self {
16        ElementVolumeInfo {
17            global_id: 0,
18            volume: 0.,
19        }
20    }
21}
22
23pub struct CompartmentInfo {
24    pub n_volumes: Vec<usize>,
25    pub volumes: Vec<Vec<ElementVolumeInfo>>,
26}
27
28impl CompartmentInfo {
29    /// Creates a new [`CompartmentInfo`].
30    fn new(volume_per_compartment: Vec<usize>) -> Self {
31        let volumes: Vec<Vec<ElementVolumeInfo>> = volume_per_compartment
32            .iter()
33            .map(|n_volume| vec![ElementVolumeInfo::default(); *n_volume])
34            .collect();
35
36        Self {
37            n_volumes: volume_per_compartment,
38            volumes,
39        }
40    }
41
42    pub fn fill(&mut self, geometry: &CMGeometry) {
43        let mut local_vertices = Vec::new();
44
45        let mut tmp_count_k_element: Vec<usize> = vec![0; geometry.n_zone()];
46
47        for (volume_element_global_id, &n_compartment_in_velem) in
48            geometry.volume_elements.enumerate_number_id()
49        {
50            let (elem_type, n_vertex) = geometry
51                .volume_elements
52                .get_element_and_nvertex(volume_element_global_id);
53
54            local_vertices.clear();
55            local_vertices.resize(n_vertex, Default::default());
56            for i_compartment_in_velem in 0..n_compartment_in_velem {
57                let compartment_id = geometry
58                    .volume_elements
59                    .get_list_compartment_id(volume_element_global_id, i_compartment_in_velem);
60
61                let k_element = tmp_count_k_element[compartment_id];
62                tmp_count_k_element[compartment_id] += 1;
63
64                geometry.fill_vertices(volume_element_global_id, n_vertex, &mut local_vertices);
65                let volume = compute_volume(&local_vertices, elem_type).unwrap()
66                    / (n_compartment_in_velem as f64);
67
68                assert!(volume >= 0., "CMTOOL(\"fill\"): Negative volume");
69                self.volumes[compartment_id][k_element] = ElementVolumeInfo {
70                    global_id: volume_element_global_id,
71                    volume,
72                };
73            }
74        }
75    }
76}
77
78pub struct CountVolumeElement {
79    pub per_compartment: Vec<usize>,
80    pub at_interface: Vec<usize>,
81}
82
83impl CountVolumeElement {
84    pub fn new(n_zone: usize) -> Self {
85        CountVolumeElement {
86            per_compartment: vec![0; n_zone],
87            //Each compartment can technically have one interface with each other
88            //Ahead of time, allocate more than needed. be resized later
89            at_interface: vec![0; n_zone * n_zone],
90        }
91    }
92
93    pub fn into_reduce(self) -> (CompartmentInfo, AInterfacesInfo) {
94        let at_interface: Vec<usize> = self.at_interface.into_iter().filter(|&v| v != 0).collect();
95
96        let per_compartment: Vec<usize> = self
97            .per_compartment
98            .into_iter()
99            .filter(|&v| v != 0)
100            .collect();
101
102        (
103            CompartmentInfo::new(per_compartment),
104            AInterfacesInfo::new(at_interface),
105        )
106    }
107
108    pub fn incr_compartment(&mut self, compartment_id: usize) {
109        self.per_compartment[compartment_id] += 1;
110    }
111    pub fn incr_interface(&mut self, compartment_id_0: usize, compartment_id_k: usize) {
112        self.at_interface[compartment_id_0 * self.per_compartment.len() + compartment_id_k] += 1;
113    }
114
115    pub fn n_interfaces(&self) -> usize {
116        self.at_interface.iter().filter(|&&v| v > 0).count()
117    }
118
119    pub fn n_zone_with_volume_element(&self) -> usize {
120        self.per_compartment.iter().filter(|v| **v != 0).count()
121    }
122}