use std::ops::{Add, Div, Mul, Neg, Sub};
use std::sync::Arc;
use super::boundary::bc::PatchField;
use super::field::Field;
use crate::mesh::fv_mesh::FvMesh;
use crate::primitives::{SymmTensor, Tensor, Vector3};
#[derive(Debug, Clone)]
pub struct VolField<T: Clone> {
pub name: String,
pub mesh: Arc<FvMesh>,
pub internal: Field<T>,
pub boundary: Vec<PatchField<T>>,
}
pub type VolScalarField = VolField<f64>;
pub type VolVectorField = VolField<Vector3>;
pub type VolTensorField = VolField<Tensor>;
pub type VolSymmTensorField = VolField<SymmTensor>;
impl<T: Clone + Default> VolField<T> {
pub fn new(
name: impl Into<String>,
mesh: Arc<FvMesh>,
internal: Field<T>,
boundary: Vec<PatchField<T>>,
) -> Self {
debug_assert_eq!(
internal.len(),
mesh.n_cells,
"VolField internal field length must equal n_cells"
);
debug_assert_eq!(
boundary.len(),
mesh.patches.len(),
"VolField boundary length must equal number of patches"
);
Self {
name: name.into(),
mesh,
internal,
boundary,
}
}
}
impl VolScalarField {
pub fn uniform(name: impl Into<String>, mesh: Arc<FvMesh>, value: f64) -> Self {
let n_cells = mesh.n_cells;
let boundary = mesh
.patches
.iter()
.map(|p| PatchField::zero_gradient(p.size))
.collect();
Self::new(name, mesh, Field::uniform(n_cells, value), boundary)
}
pub fn zeros(name: impl Into<String>, mesh: Arc<FvMesh>) -> Self {
Self::uniform(name, mesh, 0.0)
}
}
impl VolVectorField {
pub fn uniform(name: impl Into<String>, mesh: Arc<FvMesh>, value: Vector3) -> Self {
let n_cells = mesh.n_cells;
let boundary = mesh
.patches
.iter()
.map(|p| PatchField::zero_gradient_vec(p.size))
.collect();
Self::new(name, mesh, Field::uniform(n_cells, value), boundary)
}
pub fn zero(name: impl Into<String>, mesh: Arc<FvMesh>) -> Self {
Self::uniform(name, mesh, Vector3::ZERO)
}
}
impl<T> Add for VolField<T>
where
T: Add<Output = T> + Clone + Default,
{
type Output = VolField<T>;
fn add(mut self, rhs: Self) -> Self::Output {
self.internal = self.internal + rhs.internal;
for (l, r) in self.boundary.iter_mut().zip(rhs.boundary) {
l.values = l.values.clone() + r.values;
}
self
}
}
impl<T> Sub for VolField<T>
where
T: Sub<Output = T> + Clone + Default,
{
type Output = VolField<T>;
fn sub(mut self, rhs: Self) -> Self::Output {
self.internal = self.internal - rhs.internal;
for (l, r) in self.boundary.iter_mut().zip(rhs.boundary) {
l.values = l.values.clone() - r.values;
}
self
}
}
impl<T> Neg for VolField<T>
where
T: Neg<Output = T> + Clone,
{
type Output = VolField<T>;
fn neg(mut self) -> Self::Output {
self.internal = -self.internal;
for p in self.boundary.iter_mut() {
p.values = -p.values.clone();
}
self
}
}
impl<T> Mul<f64> for VolField<T>
where
T: Mul<f64, Output = T> + Clone,
{
type Output = VolField<T>;
fn mul(mut self, s: f64) -> Self::Output {
self.internal = self.internal * s;
for p in self.boundary.iter_mut() {
p.values = p.values.clone() * s;
}
self
}
}
impl<T> Div<f64> for VolField<T>
where
T: Mul<f64, Output = T> + Clone,
{
type Output = VolField<T>;
fn div(self, s: f64) -> Self::Output {
self * (1.0 / s)
}
}
impl Mul for VolScalarField {
type Output = Self;
fn mul(mut self, rhs: Self) -> Self {
self.internal = self.internal * rhs.internal;
for (l, r) in self.boundary.iter_mut().zip(rhs.boundary) {
l.values = l.values.clone() * r.values;
}
self
}
}
impl Mul<VolVectorField> for VolScalarField {
type Output = VolVectorField;
fn mul(self, mut rhs: VolVectorField) -> VolVectorField {
rhs.internal = rhs.internal * self.internal;
for (r, l) in rhs.boundary.iter_mut().zip(self.boundary) {
r.values = r.values.clone() * l.values;
}
rhs
}
}
impl<T: Mul<f64, Output = T> + Clone> Mul<VolField<T>> for f64 {
type Output = VolField<T>;
fn mul(self, mut rhs: VolField<T>) -> VolField<T> {
rhs.internal = rhs.internal * self;
for p in rhs.boundary.iter_mut() {
p.values = p.values.clone() * self;
}
rhs
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mesh::fv_mesh::{BoundaryPatch, FvMeshBuilder, PatchKind};
fn unit_mesh() -> Arc<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![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()
.unwrap(),
)
}
#[test]
fn uniform_vol_scalar_field() {
let m = unit_mesh();
let p = VolScalarField::uniform("p", m, 101325.0);
assert_eq!(p.internal.len(), 2);
assert!((p.internal[0] - 101325.0).abs() < 1e-8);
assert_eq!(p.boundary.len(), 2);
}
#[test]
fn add_vol_scalar_fields() {
let m = unit_mesh();
let a = VolScalarField::uniform("a", m.clone(), 1.0);
let b = VolScalarField::uniform("b", m, 2.0);
let c = a + b;
assert!((c.internal[0] - 3.0).abs() < 1e-15);
assert!((c.internal[1] - 3.0).abs() < 1e-15);
}
#[test]
fn mul_scalar_field_by_f64() {
let m = unit_mesh();
let a = VolScalarField::uniform("a", m, 3.0);
let b = a * 2.0;
assert!((b.internal[0] - 6.0).abs() < 1e-15);
}
#[test]
fn neg_vol_vector_field() {
let m = unit_mesh();
let u = VolVectorField::uniform("U", m, Vector3::new(1.0, 2.0, 3.0));
let neg_u = -u;
assert_eq!(neg_u.internal[0], Vector3::new(-1.0, -2.0, -3.0));
}
}