Skip to main content

conspire/geometry/mesh/write/exodus/
mod.rs

1#[cfg(test)]
2mod test;
3
4use crate::{
5    geometry::mesh::{Mesh, connectivity::base::FlatConnectivity},
6    io::{DefineVariable, NetCDF, PutVariable},
7};
8use std::{ffi::NulError, path::Path};
9
10/// Which on-disk Exodus flavour to write.
11pub enum ExodusFormat<P>
12where
13    P: AsRef<Path>,
14{
15    /// Classic netCDF (CDF-5).
16    Classic(P),
17    /// netCDF-4 (HDF5): chunked, shuffle + deflate compressed. `threads` bounds
18    /// the pool used to compress chunks (`0` or `1` compresses serially).
19    Netcdf4 { path: P, threads: usize },
20}
21
22impl<P> AsRef<Path> for ExodusFormat<P>
23where
24    P: AsRef<Path>,
25{
26    fn as_ref(&self) -> &Path {
27        match self {
28            ExodusFormat::Classic(path) => path.as_ref(),
29            ExodusFormat::Netcdf4 { path, .. } => path.as_ref(),
30        }
31    }
32}
33
34pub(crate) trait WriteExodus<P>
35where
36    P: AsRef<Path>,
37{
38    fn write_exodus(&self, output: P) -> Result<(), NulError>;
39    fn write_exodus_compressed(&self, output: P, threads: usize) -> Result<(), NulError>;
40}
41
42impl<const D: usize, P> WriteExodus<P> for Mesh<D>
43where
44    P: AsRef<Path>,
45{
46    fn write_exodus(&self, output: P) -> Result<(), NulError> {
47        self.write_exodus_format(output, None)
48    }
49    fn write_exodus_compressed(&self, output: P, threads: usize) -> Result<(), NulError> {
50        self.write_exodus_format(output, Some(threads))
51    }
52}
53
54impl<const D: usize> Mesh<D> {
55    fn write_exodus_format<P: AsRef<Path>>(
56        &self,
57        output: P,
58        netcdf4: Option<usize>,
59    ) -> Result<(), NulError> {
60        let path = output.as_ref().to_str().unwrap();
61        let mut netcdf = match netcdf4 {
62            Some(threads) => NetCDF::create_netcdf4(path, threads)?,
63            None => NetCDF::create(path)?,
64        };
65        netcdf.global();
66        let element_numbers: Option<Vec<i32>> = self
67            .connectivities()
68            .iter()
69            .map(|connectivity| {
70                connectivity.element_numbers().map(|numbers| {
71                    numbers
72                        .iter()
73                        .map(|&number| number as i32)
74                        .collect::<Vec<_>>()
75                })
76            })
77            .collect::<Option<Vec<Vec<i32>>>>()
78            .map(|per_block| per_block.into_iter().flatten().collect());
79        let node_numbers: Option<Vec<i32>> = self
80            .coordinates
81            .numbers()
82            .map(|numbers| numbers.iter().map(|&number| number as i32).collect());
83        netcdf.define_dimension("num_dim", D)?;
84        netcdf.define_dimension("num_elem", self.number_of_elements())?;
85        netcdf.define_dimension("num_el_blk", self.number_of_element_blocks())?;
86        netcdf.define_variable::<i32>("eb_prop1", 1, &["num_el_blk"])?;
87        netcdf.put_variable_attribute_text("eb_prop1", "name", "ID")?;
88        if let Some(num_fa_blk) = self.number_of_face_blocks() {
89            netcdf.define_dimension("num_fa_blk", num_fa_blk)?;
90            netcdf.define_variable::<i32>("fa_prop1", 1, &["num_fa_blk"])?;
91            netcdf.put_variable_attribute_text("fa_prop1", "name", "ID")?;
92        }
93        if let Some(num_face) = self.number_of_faces() {
94            netcdf.define_dimension("num_face", num_face)?;
95        }
96        if element_numbers.is_some() {
97            netcdf.define_variable::<i32>("elem_num_map", 1, &["num_elem"])?;
98        }
99        self.iter()
100            .enumerate()
101            .try_for_each(|(block, connectivity)| {
102                let block = block + 1;
103                netcdf.define_dimension(
104                    &format!("num_el_in_blk{}", block),
105                    connectivity.number_of_elements(),
106                )?;
107                if let Some(num) = connectivity.number_of_nodes_per_element() {
108                    netcdf.define_dimension(&format!("num_nod_per_el{}", block), num)?;
109                    netcdf.define_variable::<i32>(
110                        &format!("connect{}", block),
111                        2,
112                        &[
113                            &format!("num_el_in_blk{}", block),
114                            &format!("num_nod_per_el{}", block),
115                        ],
116                    )?;
117                    netcdf.put_variable_attribute_text(
118                        &format!("connect{}", block),
119                        "elem_type",
120                        connectivity.exodus_element_type(),
121                    )
122                } else {
123                    if let Some(ebepecnt) = connectivity.number_of_faces_per_element::<i32>() {
124                        netcdf.define_dimension(
125                            &format!("num_fac_per_el{}", block),
126                            ebepecnt.into_iter().map(|c| c as usize).sum(),
127                        )?;
128                        netcdf.define_variable::<i32>(
129                            &format!("facconn{}", block),
130                            1,
131                            &[&format!("num_fac_per_el{}", block)],
132                        )?;
133                        netcdf.put_variable_attribute_text(
134                            &format!("facconn{}", block),
135                            "elem_type",
136                            connectivity.exodus_element_type(),
137                        )?;
138                        netcdf.define_variable::<i32>(
139                            &format!("ebepecnt{}", block),
140                            1,
141                            &[&format!("num_el_in_blk{}", block)],
142                        )?;
143                        netcdf.put_variable_attribute_text(
144                            &format!("ebepecnt{}", block),
145                            "entity_type1",
146                            "FACE",
147                        )?;
148                        netcdf.put_variable_attribute_text(
149                            &format!("ebepecnt{}", block),
150                            "entity_type2",
151                            "ELEM",
152                        )?;
153                    } else {
154                        panic!()
155                    }
156                    if let Some(num_fa_in_blk) = connectivity.number_of_faces() {
157                        netcdf
158                            .define_dimension(&format!("num_fa_in_blk{}", block), num_fa_in_blk)?;
159                    } else {
160                        panic!()
161                    }
162                    if let Some(fbepecnt) = connectivity.number_of_nodes_per_face::<i32>() {
163                        netcdf.define_dimension(
164                            &format!("num_nod_per_fa{}", block),
165                            fbepecnt.into_iter().map(|c| c as usize).sum(),
166                        )?;
167                        netcdf.define_variable::<i32>(
168                            &format!("fbconn{}", block),
169                            1,
170                            &[&format!("num_nod_per_fa{}", block)],
171                        )?;
172                        netcdf.put_variable_attribute_text(
173                            &format!("fbconn{}", block),
174                            "elem_type",
175                            "nsided",
176                        )?;
177                        netcdf.define_variable::<i32>(
178                            &format!("fbepecnt{}", block),
179                            1,
180                            &[&format!("num_fa_in_blk{}", block)],
181                        )?;
182                        netcdf.put_variable_attribute_text(
183                            &format!("fbepecnt{}", block),
184                            "entity_type1",
185                            "NODE",
186                        )?;
187                        netcdf.put_variable_attribute_text(
188                            &format!("fbepecnt{}", block),
189                            "entity_type2",
190                            "FACE",
191                        )
192                    } else {
193                        panic!()
194                    }
195                }
196            })?;
197        netcdf.define_dimension("num_nodes", self.number_of_nodes())?;
198        if node_numbers.is_some() {
199            netcdf.define_variable::<i32>("node_num_map", 1, &["num_nodes"])?;
200        }
201        if !self.node_sets().is_empty() {
202            netcdf.define_dimension("num_node_sets", self.node_sets().len())?;
203            netcdf.define_variable::<i32>("ns_prop1", 1, &["num_node_sets"])?;
204            netcdf.put_variable_attribute_text("ns_prop1", "name", "ID")?;
205            self.node_sets()
206                .iter()
207                .enumerate()
208                .try_for_each(|(set, nodes)| {
209                    let set = set + 1;
210                    netcdf.define_dimension(&format!("num_nod_ns{}", set), nodes.len())?;
211                    netcdf.define_variable::<i32>(
212                        &format!("node_ns{}", set),
213                        1,
214                        &[&format!("num_nod_ns{}", set)],
215                    )
216                })?;
217        }
218        if !self.side_sets().is_empty() {
219            netcdf.define_dimension("num_side_sets", self.side_sets().len())?;
220            netcdf.define_variable::<i32>("ss_prop1", 1, &["num_side_sets"])?;
221            netcdf.put_variable_attribute_text("ss_prop1", "name", "ID")?;
222            self.side_sets()
223                .iter()
224                .enumerate()
225                .try_for_each(|(set, sides)| {
226                    let set = set + 1;
227                    netcdf.define_dimension(&format!("num_side_ss{}", set), sides.len())?;
228                    netcdf.define_variable::<i32>(
229                        &format!("elem_ss{}", set),
230                        1,
231                        &[&format!("num_side_ss{}", set)],
232                    )?;
233                    netcdf.define_variable::<i32>(
234                        &format!("side_ss{}", set),
235                        1,
236                        &[&format!("num_side_ss{}", set)],
237                    )
238                })?;
239        }
240        netcdf.define_dimension("time_step", 0)?;
241        netcdf.define_variable::<f64>("coordx", 1, &["num_nodes"])?;
242        netcdf.define_variable::<f64>("coordy", 1, &["num_nodes"])?;
243        match D {
244            2 => {}
245            3 => {
246                netcdf.define_variable::<f64>("coordz", 1, &["num_nodes"])?;
247            }
248            _ => unimplemented!(),
249        }
250        netcdf.end_definition();
251        let block_ids: Vec<i32> = match self.connectivities.numbers() {
252            Some(numbers) => numbers.iter().map(|&number| number as i32).collect(),
253            None => (1..=self.number_of_element_blocks() as i32).collect(),
254        };
255        netcdf.put_variable("eb_prop1", &block_ids)?;
256        if self.number_of_face_blocks().is_some() {
257            netcdf.put_variable("fa_prop1", &block_ids)?;
258        }
259        if let Some(element_numbers) = &element_numbers {
260            netcdf.put_variable("elem_num_map", element_numbers)?;
261        }
262        if let Some(node_numbers) = &node_numbers {
263            netcdf.put_variable("node_num_map", node_numbers)?;
264        }
265        if !self.node_sets().is_empty() {
266            let node_set_ids: Vec<i32> = match self.node_set_numbers() {
267                Some(numbers) => numbers.iter().map(|&number| number as i32).collect(),
268                None => (1..=self.node_sets().len() as i32).collect(),
269            };
270            netcdf.put_variable("ns_prop1", &node_set_ids)?;
271            self.node_sets()
272                .iter()
273                .enumerate()
274                .try_for_each(|(set, nodes)| {
275                    let set = set + 1;
276                    let node_ids: Vec<i32> = nodes.iter().map(|&node| node as i32 + 1).collect();
277                    netcdf.put_variable(&format!("node_ns{}", set), &node_ids)
278                })?;
279        }
280        if !self.side_sets().is_empty() {
281            let side_set_ids: Vec<i32> = match self.side_set_numbers() {
282                Some(numbers) => numbers.iter().map(|&number| number as i32).collect(),
283                None => (1..=self.side_sets().len() as i32).collect(),
284            };
285            netcdf.put_variable("ss_prop1", &side_set_ids)?;
286            self.side_sets()
287                .iter()
288                .enumerate()
289                .try_for_each(|(set, sides)| {
290                    let set = set + 1;
291                    let elem_ids: Vec<i32> = sides
292                        .iter()
293                        .map(|&(element, _)| match &element_numbers {
294                            Some(numbers) => numbers[element],
295                            None => element as i32 + 1,
296                        })
297                        .collect();
298                    let side_ids: Vec<i32> =
299                        sides.iter().map(|&(_, side)| side as i32 + 1).collect();
300                    netcdf.put_variable(&format!("elem_ss{}", set), &elem_ids)?;
301                    netcdf.put_variable(&format!("side_ss{}", set), &side_ids)
302                })?;
303        }
304        self.iter()
305            .enumerate()
306            .try_for_each(|(block, connectivity)| {
307                let block = block + 1;
308                match connectivity.flat_connectivity::<i32>() {
309                    FlatConnectivity::Primitive(flat) => {
310                        netcdf.put_variable(&format!("connect{}", block), &flat)
311                    }
312                    FlatConnectivity::Polytopal(elements_faces, faces_nodes) => {
313                        netcdf.put_variable(&format!("facconn{}", block), &elements_faces)?;
314                        netcdf.put_variable(&format!("fbconn{}", block), &faces_nodes)?;
315                        if let Some(ebepecnt) = connectivity.number_of_faces_per_element::<i32>() {
316                            netcdf.put_variable(&format!("ebepecnt{}", block), &ebepecnt)?;
317                        } else {
318                            panic!()
319                        }
320                        if let Some(fbepecnt) = connectivity.number_of_nodes_per_face::<i32>() {
321                            netcdf.put_variable(&format!("fbepecnt{}", block), &fbepecnt)
322                        } else {
323                            panic!()
324                        }
325                    }
326                }
327            })?;
328        let coordinates: [_; D] = self.coordinates().into();
329        netcdf.put_variable("coordx", &coordinates[0])?;
330        netcdf.put_variable("coordy", &coordinates[1])?;
331        match D {
332            2 => {}
333            3 => {
334                netcdf.put_variable("coordz", &coordinates[2])?;
335            }
336            _ => unimplemented!(),
337        }
338        Ok(())
339    }
340}