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
10pub enum ExodusFormat<P>
12where
13 P: AsRef<Path>,
14{
15 Classic(P),
17 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}