use crate::mesh::Mesh;
use crate::Real;
use nalgebra::{DefaultAllocator, DimName, Scalar};
use vtkio::model::{Attribute, CellType, Cells, DataSet, UnstructuredGridPiece, VertexNumbers};
use crate::connectivity::{
Connectivity, Hex20Connectivity, Hex27Connectivity, Hex8Connectivity, Quad4d2Connectivity, Quad9d2Connectivity,
Segment2d2Connectivity, Segment2d3Connectivity, Tet10Connectivity, Tet20Connectivity, Tet4Connectivity,
Tri3d2Connectivity, Tri3d3Connectivity, Tri6d2Connectivity,
};
use nalgebra::allocator::Allocator;
use std::convert::TryInto;
use crate::vtkio::model::{Attributes, ByteOrder, DataArray, Piece, Version, Vtk};
use num::{ToPrimitive, Zero};
use std::fs::create_dir_all;
use std::path::Path;
pub trait VtkCellConnectivity: Connectivity {
fn num_nodes(&self) -> usize {
self.vertex_indices().len()
}
fn cell_type(&self) -> vtkio::model::CellType;
fn write_vtk_connectivity(&self, connectivity: &mut [usize]) {
assert_eq!(connectivity.len(), self.vertex_indices().len());
connectivity.clone_from_slice(self.vertex_indices());
}
}
impl VtkCellConnectivity for Segment2d2Connectivity {
fn cell_type(&self) -> CellType {
CellType::Line
}
}
impl VtkCellConnectivity for Segment2d3Connectivity {
fn cell_type(&self) -> CellType {
CellType::Line
}
}
impl VtkCellConnectivity for Tri3d2Connectivity {
fn cell_type(&self) -> CellType {
CellType::Triangle
}
}
impl VtkCellConnectivity for Tri6d2Connectivity {
fn cell_type(&self) -> CellType {
CellType::QuadraticTriangle
}
}
impl VtkCellConnectivity for Quad4d2Connectivity {
fn cell_type(&self) -> CellType {
CellType::Quad
}
}
impl VtkCellConnectivity for Quad9d2Connectivity {
fn cell_type(&self) -> CellType {
CellType::QuadraticQuad
}
}
impl VtkCellConnectivity for Tet4Connectivity {
fn cell_type(&self) -> CellType {
CellType::Tetra
}
}
impl VtkCellConnectivity for Hex8Connectivity {
fn cell_type(&self) -> CellType {
CellType::Hexahedron
}
}
impl VtkCellConnectivity for Tri3d3Connectivity {
fn cell_type(&self) -> CellType {
CellType::Triangle
}
}
impl VtkCellConnectivity for Tet10Connectivity {
fn cell_type(&self) -> CellType {
CellType::QuadraticTetra
}
fn write_vtk_connectivity(&self, connectivity: &mut [usize]) {
assert_eq!(connectivity.len(), self.vertex_indices().len());
connectivity.clone_from_slice(self.vertex_indices());
connectivity.swap(8, 9);
}
}
impl VtkCellConnectivity for Tet20Connectivity {
fn num_nodes(&self) -> usize {
4
}
fn cell_type(&self) -> CellType {
CellType::Tetra
}
fn write_vtk_connectivity(&self, connectivity: &mut [usize]) {
assert_eq!(connectivity.len(), 4); connectivity[0..4].copy_from_slice(&self.0[0..4]);
}
}
impl VtkCellConnectivity for Hex20Connectivity {
fn cell_type(&self) -> CellType {
CellType::QuadraticHexahedron
}
fn write_vtk_connectivity(&self, connectivity: &mut [usize]) {
assert_eq!(connectivity.len(), self.num_nodes());
let v = self.vertex_indices();
connectivity[0..8].clone_from_slice(&v[0..8]);
connectivity[8] = v[8];
connectivity[9] = v[11];
connectivity[10] = v[13];
connectivity[11] = v[9];
connectivity[12] = v[16];
connectivity[13] = v[18];
connectivity[14] = v[19];
connectivity[15] = v[17];
connectivity[16] = v[10];
connectivity[17] = v[12];
connectivity[18] = v[14];
connectivity[19] = v[15];
}
}
impl VtkCellConnectivity for Hex27Connectivity {
fn num_nodes(&self) -> usize {
20
}
fn cell_type(&self) -> CellType {
CellType::QuadraticHexahedron
}
fn write_vtk_connectivity(&self, connectivity: &mut [usize]) {
assert_eq!(connectivity.len(), self.num_nodes());
let v = self.vertex_indices();
connectivity[0..8].clone_from_slice(&v[0..8]);
connectivity[8] = v[8];
connectivity[9] = v[11];
connectivity[10] = v[13];
connectivity[11] = v[9];
connectivity[12] = v[16];
connectivity[13] = v[18];
connectivity[14] = v[19];
connectivity[15] = v[17];
connectivity[16] = v[10];
connectivity[17] = v[12];
connectivity[18] = v[14];
connectivity[19] = v[15];
}
}
pub struct FiniteElementMeshDataSetBuilder<'a, T, D, C>
where
T: Scalar,
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
mesh: &'a Mesh<T, D, C>,
attributes: Attributes,
title: Option<String>, }
impl<'a, T, D, C> FiniteElementMeshDataSetBuilder<'a, T, D, C>
where
T: Scalar,
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
pub fn from_mesh(mesh: &'a Mesh<T, D, C>) -> Self {
Self {
mesh,
attributes: Attributes::new(),
title: None,
}
}
}
impl<'a, T, D, C> FiniteElementMeshDataSetBuilder<'a, T, D, C>
where
T: Real + ToPrimitive,
D: DimName,
DefaultAllocator: Allocator<T, D>,
{
pub fn with_title(self, title: impl Into<String>) -> Self {
Self {
mesh: self.mesh,
title: Some(title.into()),
attributes: self.attributes,
}
}
pub fn with_point_vector_attributes<S: Scalar + Zero + ToPrimitive>(
self,
name: impl Into<String>,
num_components: usize,
attributes: &[S],
) -> Self {
let num_points = self.mesh.vertices().len();
assert_eq!(
attributes.len(),
num_components * num_points,
"Number of attribute entries incompatible with mesh and number of components."
);
assert!(num_components <= 3, "Each vector must not have more than 3 components.");
let mut attribute_vec = Vec::new();
attribute_vec.reserve(3 * num_points);
for i in 0..num_points {
for j in 0..num_components {
attribute_vec.push(attributes[num_components * i + j].clone());
}
for _ in num_components..3 {
attribute_vec.push(S::zero());
}
}
let mut attribs = self.attributes;
let data_array = DataArray::vectors(name).with_data(attribute_vec);
attribs.point.push(Attribute::DataArray(data_array));
Self {
mesh: self.mesh,
attributes: attribs,
title: self.title,
}
}
pub fn with_point_scalar_attributes<S: Scalar + ToPrimitive>(
self,
name: impl Into<String>,
num_components: usize,
attributes: &[S],
) -> Self {
let num_points = self.mesh.vertices().len();
assert_eq!(
attributes.len(),
num_components * num_points,
"Number of attribute entries incompatible with mesh and number of components."
);
let mut attribs = self.attributes;
let num_comp = num_components
.try_into()
.expect("Number of components is ridiculously huge, stop it!");
let data_array = DataArray::scalars(name, num_comp).with_data(attributes.to_vec());
attribs.point.push(Attribute::DataArray(data_array));
Self {
mesh: self.mesh,
attributes: attribs,
title: self.title,
}
}
pub fn with_cell_scalar_attributes<S: Scalar + ToPrimitive>(
self,
name: impl Into<String>,
num_components: usize,
attributes: &[S],
) -> Self {
let num_cells = self.mesh.connectivity().len();
assert_eq!(
attributes.len(),
num_components * num_cells,
"Number of attribute entries incompatible with mesh and number of components."
);
let mut attribs = self.attributes;
let num_comp = num_components
.try_into()
.expect("Number of components is ridiculously huge, stop it!");
let data_array = DataArray::scalars(name, num_comp).with_data(attributes.to_vec());
attribs.cell.push(Attribute::DataArray(data_array));
Self {
mesh: self.mesh,
attributes: attribs,
title: self.title,
}
}
pub fn try_build(&self) -> eyre::Result<DataSet>
where
C: VtkCellConnectivity,
{
assert!(D::dim() <= 3, "Unable to support dimensions larger than 3.");
let points: Vec<_> = {
let mut points: Vec<T> = Vec::new();
for v in self.mesh.vertices() {
points.extend_from_slice(v.coords.as_slice());
for _ in v.coords.len()..3 {
points.push(T::zero());
}
}
points
};
let mut vertices = Vec::new();
let mut cell_types = Vec::new();
let mut vertex_indices = Vec::new();
for cell in self.mesh.connectivity() {
vertices.push(cell.num_nodes().try_into()?);
vertex_indices.clear();
vertex_indices.resize(cell.num_nodes(), 0);
cell.write_vtk_connectivity(&mut vertex_indices);
for &idx in &vertex_indices {
vertices.push(idx.try_into()?);
}
cell_types.push(cell.cell_type());
}
let piece = UnstructuredGridPiece {
points: points.into(),
cells: Cells {
cell_verts: VertexNumbers::Legacy {
num_cells: self.mesh.connectivity().len() as u32,
vertices,
},
types: cell_types,
},
data: self.attributes.clone(),
};
Ok(DataSet::UnstructuredGrid {
meta: None,
pieces: vec![Piece::Inline(Box::new(piece))],
})
}
pub fn try_export(&self, filename: impl AsRef<Path>) -> eyre::Result<()>
where
C: VtkCellConnectivity,
{
let filepath = filename.as_ref();
let fallback_title = filepath
.file_stem()
.map(|os_str| os_str.to_string_lossy().to_string())
.unwrap_or_else(|| "untitled".to_string());
let dataset = self.try_build()?;
if let Some(parent) = filepath.parent() {
create_dir_all(parent)?;
}
let extension = filepath
.extension()
.map(|os_str| os_str.to_string_lossy().to_ascii_lowercase());
let version = match extension.as_ref().map(|s| s.as_str()) {
Some("vtu") => Version { major: 1, minor: 0 },
Some("vtk") | _ => Version { major: 4, minor: 1 },
};
Vtk {
version,
title: self.title.clone().unwrap_or(fallback_title),
byte_order: ByteOrder::BigEndian,
data: dataset,
file_path: None,
}
.export(filepath)?;
Ok(())
}
}