use super::interpolate;
use crate::fields::boundary::bc::PatchField;
use crate::fields::field::Field;
use crate::fields::vol_field::{VolSymmTensorField, VolTensorField, VolVectorField};
use crate::primitives::{Tensor, Vector3};
pub fn div_tensor(sigma: &VolTensorField) -> VolVectorField {
let mesh = &sigma.mesh;
let s_f = interpolate(sigma);
let mut d = vec![Vector3::ZERO; mesh.n_cells];
for f in 0..mesh.n_internal_faces {
let flux = Tensor::vec_mat(mesh.face_area_vectors[f], s_f.internal[f]);
d[mesh.owner[f]] += flux;
d[mesh.neighbour[f]] -= flux;
}
for (pi, patch) in mesh.patches.iter().enumerate() {
for fi in 0..patch.size {
let gf = patch.start + fi;
let flux = Tensor::vec_mat(mesh.face_area_vectors[gf], s_f.boundary[pi].values[fi]);
d[mesh.owner[gf]] += flux;
}
}
let boundary = mesh
.patches
.iter()
.map(|p| PatchField::zero_gradient_vec(p.size))
.collect();
VolVectorField::new(
crate::fv_operators::naming::derived_name("div", &sigma.name),
sigma.mesh.clone(),
Field::from_fn(mesh.n_cells, |c| d[c] * (1.0 / mesh.cell_volumes[c])),
boundary,
)
}
pub fn div_symm_tensor(sigma: &VolSymmTensorField) -> VolVectorField {
let mesh = &sigma.mesh;
let s_f = interpolate(sigma);
let mut d = vec![Vector3::ZERO; mesh.n_cells];
for f in 0..mesh.n_internal_faces {
let flux = s_f.internal[f].mat_vec(mesh.face_area_vectors[f]);
d[mesh.owner[f]] += flux;
d[mesh.neighbour[f]] -= flux;
}
for (pi, patch) in mesh.patches.iter().enumerate() {
for fi in 0..patch.size {
let gf = patch.start + fi;
let flux = s_f.boundary[pi].values[fi].mat_vec(mesh.face_area_vectors[gf]);
d[mesh.owner[gf]] += flux;
}
}
let boundary = mesh
.patches
.iter()
.map(|p| PatchField::zero_gradient_vec(p.size))
.collect();
VolVectorField::new(
crate::fv_operators::naming::derived_name("div", &sigma.name),
sigma.mesh.clone(),
Field::from_fn(mesh.n_cells, |c| d[c] * (1.0 / mesh.cell_volumes[c])),
boundary,
)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fields::boundary::bc::{BoundaryCondition, PatchField};
use crate::mesh::fv_mesh::{BoundaryPatch, FvMeshBuilder, PatchKind};
use crate::primitives::{SymmTensor, 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(),
)
}
#[test]
fn linear_tensor_div_is_x_row() {
let mesh = unit_mesh();
let m_t = Tensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
let boundary = vec![
PatchField {
bc: BoundaryCondition::FixedValue(m_t), values: Field::new(vec![m_t]),
},
PatchField {
bc: BoundaryCondition::FixedValue(Tensor::ZERO), values: Field::new(vec![Tensor::ZERO]),
},
];
let mut sigma = VolTensorField::new(
"sigma",
mesh.clone(),
Field::new(vec![Tensor::ZERO; 2]),
boundary,
);
sigma.internal[0] = m_t * 0.25;
sigma.internal[1] = m_t * 0.75;
let d = div_tensor(&sigma);
for c in 0..2 {
assert!(
(d.internal[c].x - 1.0).abs() < 1e-10,
"cell {c} x = {}",
d.internal[c].x
);
assert!(
(d.internal[c].y - 2.0).abs() < 1e-10,
"cell {c} y = {}",
d.internal[c].y
);
assert!(
(d.internal[c].z - 3.0).abs() < 1e-10,
"cell {c} z = {}",
d.internal[c].z
);
}
}
#[test]
fn linear_symm_tensor_div_is_x_row() {
let mesh = unit_mesh();
let m_s = SymmTensor::new(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
let boundary = vec![
PatchField {
bc: BoundaryCondition::FixedValue(m_s),
values: Field::new(vec![m_s]),
},
PatchField {
bc: BoundaryCondition::FixedValue(SymmTensor::ZERO),
values: Field::new(vec![SymmTensor::ZERO]),
},
];
let mut sigma = VolSymmTensorField::new(
"sigma",
mesh.clone(),
Field::new(vec![SymmTensor::ZERO; 2]),
boundary,
);
sigma.internal[0] = m_s * 0.25;
sigma.internal[1] = m_s * 0.75;
let d = div_symm_tensor(&sigma);
for c in 0..2 {
assert!(
(d.internal[c].x - 1.0).abs() < 1e-10,
"cell {c} x = {}",
d.internal[c].x
);
assert!(
(d.internal[c].y - 2.0).abs() < 1e-10,
"cell {c} y = {}",
d.internal[c].y
);
assert!(
(d.internal[c].z - 3.0).abs() < 1e-10,
"cell {c} z = {}",
d.internal[c].z
);
}
}
}