use crate::fields::boundary::bc::BoundaryCondition;
use crate::fields::surface_field::SurfaceScalarField;
use crate::fields::vol_field::VolScalarField;
use crate::ldu_matrix::fv_matrix::FvMatrix;
pub fn div(phi: &SurfaceScalarField, psi: &VolScalarField) -> FvMatrix {
let mesh = psi.mesh.clone();
let mut mat = FvMatrix::new(mesh.clone());
for f in 0..mesh.n_internal_faces {
let o = mesh.owner[f];
let n = mesh.neighbour[f];
let phi_f = phi.internal[f];
mat.ldu.diag[o] += phi_f.max(0.0);
mat.ldu.upper[f] += phi_f.min(0.0);
mat.ldu.diag[n] -= phi_f.min(0.0);
mat.ldu.lower[f] -= phi_f.max(0.0);
}
for (pi, patch) in mesh.patches.iter().enumerate() {
for fi in 0..patch.size {
let owner = mesh.owner[patch.start + fi];
let phi_f = phi.boundary[pi].values[fi];
match &psi.boundary[pi].bc {
BoundaryCondition::ZeroGradient | BoundaryCondition::Symmetry => {
mat.ldu.diag[owner] += phi_f;
}
BoundaryCondition::FixedValue(v) => {
if phi_f >= 0.0 {
mat.ldu.diag[owner] += phi_f;
} else {
mat.source[owner] -= phi_f * v;
}
}
BoundaryCondition::FixedField(ff) => {
if phi_f >= 0.0 {
mat.ldu.diag[owner] += phi_f;
} else {
mat.source[owner] -= phi_f * ff[fi];
}
}
_ => {
if phi_f >= 0.0 {
mat.ldu.diag[owner] += phi_f;
}
}
}
}
}
mat
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fields::boundary::bc::{BoundaryCondition, PatchField};
use crate::fields::field::Field;
use crate::fields::surface_field::SurfaceScalarField;
use crate::fields::vol_field::VolScalarField;
use crate::mesh::fv_mesh::{BoundaryPatch, FvMeshBuilder, PatchKind};
use crate::primitives::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 make_phi(
m: Arc<crate::mesh::fv_mesh::FvMesh>,
internal: f64,
bnd: f64,
) -> SurfaceScalarField {
let n_int = m.n_internal_faces;
let bnd_vals: Vec<_> = m
.patches
.iter()
.map(|p| PatchField {
bc: BoundaryCondition::ZeroGradient,
values: Field::uniform(p.size, bnd),
})
.collect();
SurfaceScalarField::new("phi", m, Field::uniform(n_int, internal), bnd_vals)
}
#[test]
fn upwind_positive_flux_on_diagonal() {
let m = unit_mesh();
let phi = make_phi(m.clone(), 1.0, 0.0);
let psi = VolScalarField::uniform("psi", m.clone(), 0.0);
let mat = div(&phi, &psi);
assert!(
(mat.ldu.diag[0] - 1.0).abs() < 1e-12,
"diag[0]={}",
mat.ldu.diag[0]
);
assert!((mat.ldu.upper[0] - 0.0).abs() < 1e-12);
assert!((mat.ldu.diag[1] - 0.0).abs() < 1e-12);
assert!(
(mat.ldu.lower[0] - (-1.0)).abs() < 1e-12,
"lower[0]={}",
mat.ldu.lower[0]
);
}
#[test]
fn upwind_negative_flux_on_upper() {
let m = unit_mesh();
let phi = make_phi(m.clone(), -1.0, 0.0);
let psi = VolScalarField::uniform("psi", m.clone(), 0.0);
let mat = div(&phi, &psi);
assert!((mat.ldu.upper[0] - (-1.0)).abs() < 1e-12);
assert!((mat.ldu.diag[0] - 0.0).abs() < 1e-12);
assert!((mat.ldu.diag[1] - 1.0).abs() < 1e-12);
}
}