use crate::fields::boundary::bc::{BoundaryCondition, PatchField};
use crate::fields::vol_field::{VolField, VolScalarField, VolSymmTensorField, VolTensorField};
fn map_vol_field<T, U, F>(vol: &VolField<T>, f: F) -> VolField<U>
where
T: Clone,
U: Clone + Default,
F: Fn(&T) -> U,
{
let internal = vol.internal.map(&f);
let boundary = vol
.boundary
.iter()
.map(|pf| PatchField {
bc: BoundaryCondition::ZeroGradient,
values: pf.values.map(&f),
})
.collect();
VolField::new(vol.name.clone(), vol.mesh.clone(), internal, boundary)
}
pub fn tr(vol: &VolTensorField) -> VolScalarField {
map_vol_field(vol, |t| t.tr())
}
pub fn tr_of_symm(vol: &VolSymmTensorField) -> VolScalarField {
map_vol_field(vol, |s| s.tr())
}
pub fn symm(vol: &VolTensorField) -> VolSymmTensorField {
map_vol_field(vol, |t| t.symm())
}
pub fn two_symm(vol: &VolTensorField) -> VolSymmTensorField {
map_vol_field(vol, |t| t.two_symm())
}
pub fn two_symm_of_symm(vol: &VolSymmTensorField) -> VolSymmTensorField {
map_vol_field(vol, |s| *s * 2.0)
}
pub fn dev(vol: &VolTensorField) -> VolTensorField {
map_vol_field(vol, |t| t.dev())
}
pub fn dev2(vol: &VolTensorField) -> VolTensorField {
map_vol_field(vol, |t| t.dev2())
}
pub fn dev_of_symm(vol: &VolSymmTensorField) -> VolSymmTensorField {
map_vol_field(vol, |s| s.dev())
}
pub fn dev2_of_symm(vol: &VolSymmTensorField) -> VolSymmTensorField {
map_vol_field(vol, |s| s.dev2())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fields::field::Field;
use crate::mesh::fv_mesh::{BoundaryPatch, FvMeshBuilder, PatchKind};
use crate::primitives::{SymmTensor, Tensor, Vector3};
use std::sync::Arc;
fn unit_mesh() -> Arc<crate::mesh::fv_mesh::FvMesh> {
Arc::new(
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![0.5, 0.5])
.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()
.unwrap(),
)
}
fn tensor_field(m: Arc<crate::mesh::fv_mesh::FvMesh>, t: Tensor) -> VolTensorField {
let boundary = m
.patches
.iter()
.map(|p| PatchField::fixed_value_tensor(p.size, t))
.collect();
VolTensorField::new("T", m.clone(), Field::uniform(m.n_cells, t), boundary)
}
#[test]
fn field_algebra_matches_primitive() {
let m = unit_mesh();
let t = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
let vt = tensor_field(m.clone(), t);
let tr_f = tr(&vt);
assert!((tr_f.internal[0] - 15.0).abs() < 1e-13);
assert!((tr_f.boundary[0].values[0] - 15.0).abs() < 1e-13);
let s = symm(&vt);
assert!((s.internal[0].xy - 3.0).abs() < 1e-13); assert!((s.internal[0].xz - 5.0).abs() < 1e-13); assert!((s.internal[0].yz - 7.0).abs() < 1e-13);
let ts = two_symm(&vt);
assert!((ts.internal[0].xy - 6.0).abs() < 1e-13);
assert!((ts.internal[0].xx - 2.0).abs() < 1e-13);
let d = dev(&vt);
assert!(d.internal[0].tr().abs() < 1e-13, "dev not trace-free");
let d2 = dev2(&vt);
assert!((d2.internal[0].tr() - (-15.0)).abs() < 1e-13);
}
#[test]
fn symm_field_algebra_matches_primitive() {
let m = unit_mesh();
let s = SymmTensor::new(2.0, 1.0, 0.0, 4.0, 0.0, 6.0);
let boundary = m
.patches
.iter()
.map(|p| PatchField::zero_gradient_symm_tensor(p.size))
.collect();
let vs = VolSymmTensorField::new("S", m.clone(), Field::uniform(m.n_cells, s), boundary);
assert!((tr_of_symm(&vs).internal[0] - 12.0).abs() < 1e-13);
let ts = two_symm_of_symm(&vs);
assert!((ts.internal[0].xx - 4.0).abs() < 1e-13);
assert!((ts.internal[0].zz - 12.0).abs() < 1e-13);
assert!(dev_of_symm(&vs).internal[0].tr().abs() < 1e-13);
assert!((dev2_of_symm(&vs).internal[0].tr() - (-12.0)).abs() < 1e-13);
}
}