use crate::mesh::error::MeshError;
use crate::primitives::Vector3;
#[derive(Debug, Clone)]
pub struct BoundaryPatch {
pub name: String,
pub start: usize,
pub size: usize,
pub kind: PatchKind,
}
impl BoundaryPatch {
pub fn new(name: impl Into<String>, start: usize, size: usize, kind: PatchKind) -> Self {
Self {
name: name.into(),
start,
size,
kind,
}
}
pub fn end(&self) -> usize {
self.start + self.size
}
pub fn contains_face(&self, f: usize) -> bool {
f >= self.start && f < self.end()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatchKind {
Patch, Wall, Symmetry, Empty, Wedge, Cyclic, Processor, }
#[derive(Debug, Clone)]
pub struct FvMesh {
pub n_cells: usize,
pub n_internal_faces: usize,
pub n_faces: usize,
pub owner: Vec<usize>,
pub neighbour: Vec<usize>,
pub patches: Vec<BoundaryPatch>,
pub cell_volumes: Vec<f64>,
pub cell_centres: Vec<Vector3>,
pub face_area_vectors: Vec<Vector3>,
pub face_areas: Vec<f64>,
pub face_centres: Vec<Vector3>,
}
impl FvMesh {
pub fn n_boundary_faces(&self) -> usize {
self.n_faces - self.n_internal_faces
}
pub fn n_patches(&self) -> usize {
self.patches.len()
}
pub fn is_internal_face(&self, f: usize) -> bool {
f < self.n_internal_faces
}
pub fn patch_for_face(&self, f: usize) -> Option<(usize, usize)> {
if self.is_internal_face(f) {
return None;
}
for (pi, patch) in self.patches.iter().enumerate() {
if patch.contains_face(f) {
return Some((pi, f - patch.start));
}
}
None
}
pub fn validate(&self) -> Result<(), MeshError> {
let check_len = |array, got, expected| {
if got != expected {
Err(MeshError::ArrayLengthMismatch {
array,
expected,
got,
})
} else {
Ok(())
}
};
check_len("owner", self.owner.len(), self.n_faces)?;
check_len("neighbour", self.neighbour.len(), self.n_internal_faces)?;
check_len("cell_volumes", self.cell_volumes.len(), self.n_cells)?;
check_len("cell_centres", self.cell_centres.len(), self.n_cells)?;
check_len(
"face_area_vectors",
self.face_area_vectors.len(),
self.n_faces,
)?;
check_len("face_areas", self.face_areas.len(), self.n_faces)?;
check_len("face_centres", self.face_centres.len(), self.n_faces)?;
let mut covered = self.n_internal_faces;
for p in &self.patches {
if p.start != covered {
return Err(MeshError::PatchStartMismatch {
name: p.name.clone(),
expected: covered,
got: p.start,
});
}
covered += p.size;
}
if covered != self.n_faces {
return Err(MeshError::PatchCoverageMismatch {
covered,
n_faces: self.n_faces,
});
}
Ok(())
}
}
#[derive(Default)]
pub struct FvMeshBuilder {
n_cells: usize,
n_internal_faces: usize,
owner: Vec<usize>,
neighbour: Vec<usize>,
patches: Vec<BoundaryPatch>,
cell_volumes: Vec<f64>,
cell_centres: Vec<Vector3>,
face_area_vectors: Vec<Vector3>,
face_areas: Vec<f64>,
face_centres: Vec<Vector3>,
}
impl FvMeshBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn n_cells(mut self, n: usize) -> Self {
self.n_cells = n;
self
}
pub fn n_internal_faces(mut self, n: usize) -> Self {
self.n_internal_faces = n;
self
}
pub fn owner(mut self, v: Vec<usize>) -> Self {
self.owner = v;
self
}
pub fn neighbour(mut self, v: Vec<usize>) -> Self {
self.neighbour = v;
self
}
pub fn patches(mut self, v: Vec<BoundaryPatch>) -> Self {
self.patches = v;
self
}
pub fn cell_volumes(mut self, v: Vec<f64>) -> Self {
self.cell_volumes = v;
self
}
pub fn cell_centres(mut self, v: Vec<Vector3>) -> Self {
self.cell_centres = v;
self
}
pub fn face_area_vectors(mut self, v: Vec<Vector3>) -> Self {
self.face_area_vectors = v;
self
}
pub fn face_areas(mut self, v: Vec<f64>) -> Self {
self.face_areas = v;
self
}
pub fn face_centres(mut self, v: Vec<Vector3>) -> Self {
self.face_centres = v;
self
}
fn ensure_face_areas(&mut self) {
if self.face_areas.is_empty() && !self.face_area_vectors.is_empty() {
self.face_areas = self.face_area_vectors.iter().map(|sf| sf.mag()).collect();
}
}
pub fn build(mut self) -> Result<FvMesh, MeshError> {
self.ensure_face_areas();
let n_faces = self.owner.len();
let mesh = FvMesh {
n_cells: self.n_cells,
n_internal_faces: self.n_internal_faces,
n_faces,
owner: self.owner,
neighbour: self.neighbour,
patches: self.patches,
cell_volumes: self.cell_volumes,
cell_centres: self.cell_centres,
face_area_vectors: self.face_area_vectors,
face_areas: self.face_areas,
face_centres: self.face_centres,
};
mesh.validate()?;
Ok(mesh)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn two_cell_mesh() -> FvMesh {
FvMeshBuilder::new()
.n_cells(2)
.n_internal_faces(1)
.owner(vec![0, 1, 0])
.neighbour(vec![1]) .patches(vec![
BoundaryPatch::new("right", 1, 1, PatchKind::Wall),
BoundaryPatch::new("left", 2, 1, PatchKind::Wall),
])
.cell_volumes(vec![1.0, 1.0])
.cell_centres(vec![
Vector3::new(0.25, 0.0, 0.0),
Vector3::new(0.75, 0.0, 0.0),
])
.face_area_vectors(vec![
Vector3::new(1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0), Vector3::new(-1.0, 0.0, 0.0), ])
.face_centres(vec![
Vector3::new(0.5, 0.0, 0.0),
Vector3::new(1.0, 0.0, 0.0),
Vector3::new(0.0, 0.0, 0.0),
])
.build()
.expect("two_cell_mesh should be valid")
}
#[test]
fn validate_two_cell_mesh() {
let m = two_cell_mesh();
assert_eq!(m.n_cells, 2);
assert_eq!(m.n_internal_faces, 1);
assert_eq!(m.n_faces, 3);
assert_eq!(m.n_boundary_faces(), 2);
}
#[test]
fn is_internal_face() {
let m = two_cell_mesh();
assert!(m.is_internal_face(0));
assert!(!m.is_internal_face(1));
assert!(!m.is_internal_face(2));
}
#[test]
fn patch_for_face() {
let m = two_cell_mesh();
assert_eq!(m.patch_for_face(0), None); assert_eq!(m.patch_for_face(1), Some((0, 0))); assert_eq!(m.patch_for_face(2), Some((1, 0))); }
#[test]
fn face_areas_derived() {
let m = two_cell_mesh();
assert!((m.face_areas[0] - 1.0).abs() < 1e-15);
}
}