#[cfg(test)]
mod test;
use crate::{
geometry::mesh::{Mesh, connectivity::base::FlatConnectivity},
io::{DefineVariable, NetCDF, PutVariable},
};
use std::{ffi::NulError, path::Path};
pub trait WriteExodus<P>
where
P: AsRef<Path>,
{
fn write_exodus(&self, output: P) -> Result<(), NulError>;
}
impl<const D: usize, P> WriteExodus<P> for Mesh<D>
where
P: AsRef<Path>,
{
fn write_exodus(&self, output: P) -> Result<(), NulError> {
let path = output.as_ref().to_str().unwrap();
let mut netcdf = NetCDF::create(path)?;
netcdf.global();
let element_numbers: Option<Vec<i32>> = self
.connectivities()
.iter()
.map(|connectivity| {
connectivity.element_numbers().map(|numbers| {
numbers
.iter()
.map(|&number| number as i32)
.collect::<Vec<_>>()
})
})
.collect::<Option<Vec<Vec<i32>>>>()
.map(|per_block| per_block.into_iter().flatten().collect());
let node_numbers: Option<Vec<i32>> = self
.coordinates
.numbers()
.map(|numbers| numbers.iter().map(|&number| number as i32).collect());
netcdf.define_dimension("num_dim", D)?;
netcdf.define_dimension("num_elem", self.number_of_elements())?;
netcdf.define_dimension("num_el_blk", self.number_of_element_blocks())?;
netcdf.define_variable::<i32>("eb_prop1", 1, &["num_el_blk"])?;
netcdf.put_variable_attribute_text("eb_prop1", "name", "ID")?;
if let Some(num_fa_blk) = self.number_of_face_blocks() {
netcdf.define_dimension("num_fa_blk", num_fa_blk)?;
netcdf.define_variable::<i32>("fa_prop1", 1, &["num_fa_blk"])?;
netcdf.put_variable_attribute_text("fa_prop1", "name", "ID")?;
}
if let Some(num_face) = self.number_of_faces() {
netcdf.define_dimension("num_face", num_face)?;
}
if element_numbers.is_some() {
netcdf.define_variable::<i32>("elem_num_map", 1, &["num_elem"])?;
}
self.iter()
.enumerate()
.try_for_each(|(block, connectivity)| {
let block = block + 1;
netcdf.define_dimension(
&format!("num_el_in_blk{}", block),
connectivity.number_of_elements(),
)?;
if let Some(num) = connectivity.number_of_nodes_per_element() {
netcdf.define_dimension(&format!("num_nod_per_el{}", block), num)?;
netcdf.define_variable::<i32>(
&format!("connect{}", block),
2,
&[
&format!("num_el_in_blk{}", block),
&format!("num_nod_per_el{}", block),
],
)?;
netcdf.put_variable_attribute_text(
&format!("connect{}", block),
"elem_type",
connectivity.exodus_element_type(),
)
} else {
if let Some(ebepecnt) = connectivity.number_of_faces_per_element::<i32>() {
netcdf.define_dimension(
&format!("num_fac_per_el{}", block),
ebepecnt.into_iter().map(|c| c as usize).sum(),
)?;
netcdf.define_variable::<i32>(
&format!("facconn{}", block),
1,
&[&format!("num_fac_per_el{}", block)],
)?;
netcdf.put_variable_attribute_text(
&format!("facconn{}", block),
"elem_type",
connectivity.exodus_element_type(),
)?;
netcdf.define_variable::<i32>(
&format!("ebepecnt{}", block),
1,
&[&format!("num_el_in_blk{}", block)],
)?;
netcdf.put_variable_attribute_text(
&format!("ebepecnt{}", block),
"entity_type1",
"FACE",
)?;
netcdf.put_variable_attribute_text(
&format!("ebepecnt{}", block),
"entity_type2",
"ELEM",
)?;
} else {
panic!()
}
if let Some(num_fa_in_blk) = connectivity.number_of_faces() {
netcdf
.define_dimension(&format!("num_fa_in_blk{}", block), num_fa_in_blk)?;
} else {
panic!()
}
if let Some(fbepecnt) = connectivity.number_of_nodes_per_face::<i32>() {
netcdf.define_dimension(
&format!("num_nod_per_fa{}", block),
fbepecnt.into_iter().map(|c| c as usize).sum(),
)?;
netcdf.define_variable::<i32>(
&format!("fbconn{}", block),
1,
&[&format!("num_nod_per_fa{}", block)],
)?;
netcdf.put_variable_attribute_text(
&format!("fbconn{}", block),
"elem_type",
"nsided",
)?;
netcdf.define_variable::<i32>(
&format!("fbepecnt{}", block),
1,
&[&format!("num_fa_in_blk{}", block)],
)?;
netcdf.put_variable_attribute_text(
&format!("fbepecnt{}", block),
"entity_type1",
"NODE",
)?;
netcdf.put_variable_attribute_text(
&format!("fbepecnt{}", block),
"entity_type2",
"FACE",
)
} else {
panic!()
}
}
})?;
netcdf.define_dimension("num_nodes", self.number_of_nodes())?;
if node_numbers.is_some() {
netcdf.define_variable::<i32>("node_num_map", 1, &["num_nodes"])?;
}
netcdf.define_dimension("time_step", 0)?;
netcdf.define_variable::<f64>("coordx", 1, &["num_nodes"])?;
netcdf.define_variable::<f64>("coordy", 1, &["num_nodes"])?;
match D {
2 => {}
3 => {
netcdf.define_variable::<f64>("coordz", 1, &["num_nodes"])?;
}
_ => unimplemented!(),
}
netcdf.end_definition();
let block_ids: Vec<i32> = match self.connectivities.numbers() {
Some(numbers) => numbers.iter().map(|&number| number as i32).collect(),
None => (1..=self.number_of_element_blocks() as i32).collect(),
};
netcdf.put_variable("eb_prop1", &block_ids)?;
if self.number_of_face_blocks().is_some() {
netcdf.put_variable("fa_prop1", &block_ids)?;
}
if let Some(element_numbers) = &element_numbers {
netcdf.put_variable("elem_num_map", element_numbers)?;
}
if let Some(node_numbers) = &node_numbers {
netcdf.put_variable("node_num_map", node_numbers)?;
}
self.iter()
.enumerate()
.try_for_each(|(block, connectivity)| {
let block = block + 1;
match connectivity.flat_connectivity::<i32>() {
FlatConnectivity::Primitive(flat) => {
netcdf.put_variable(&format!("connect{}", block), &flat)
}
FlatConnectivity::Polytopal(elements_faces, faces_nodes) => {
netcdf.put_variable(&format!("facconn{}", block), &elements_faces)?;
netcdf.put_variable(&format!("fbconn{}", block), &faces_nodes)?;
if let Some(ebepecnt) = connectivity.number_of_faces_per_element::<i32>() {
netcdf.put_variable(&format!("ebepecnt{}", block), &ebepecnt)?;
} else {
panic!()
}
if let Some(fbepecnt) = connectivity.number_of_nodes_per_face::<i32>() {
netcdf.put_variable(&format!("fbepecnt{}", block), &fbepecnt)
} else {
panic!()
}
}
}
})?;
let coordinates: [_; D] = self.coordinates().into();
netcdf.put_variable("coordx", &coordinates[0])?;
netcdf.put_variable("coordy", &coordinates[1])?;
match D {
2 => {}
3 => {
netcdf.put_variable("coordz", &coordinates[2])?;
}
_ => unimplemented!(),
}
Ok(())
}
}