use crate::fields::vol_field::{VolScalarField, VolVectorField};
use crate::ldu_matrix::fv_matrix::FvMatrix;
use crate::ldu_matrix::fv_vector_matrix::FvVectorMatrix;
pub fn sp(sp: &VolScalarField, phi: &VolScalarField) -> FvMatrix {
let mesh = phi.mesh.clone();
let mut mat = FvMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
mat.ldu.diag[c] += mesh.cell_volumes[c] * sp.internal[c];
}
mat
}
pub fn su(su: &VolScalarField, phi: &VolScalarField) -> FvMatrix {
let mesh = phi.mesh.clone();
let mut mat = FvMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
mat.source[c] -= mesh.cell_volumes[c] * su.internal[c];
}
mat
}
pub fn su_sp(susp: &VolScalarField, phi: &VolScalarField) -> FvMatrix {
let mesh = phi.mesh.clone();
let mut mat = FvMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
let v = mesh.cell_volumes[c];
let coeff = susp.internal[c];
mat.ldu.diag[c] += v * coeff.max(0.0);
mat.source[c] -= v * coeff.min(0.0) * phi.internal[c];
}
mat
}
pub fn sp_vec(sp: &VolScalarField, u: &VolVectorField) -> FvVectorMatrix {
let mesh = u.mesh.clone();
let mut mat = FvVectorMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
mat.ldu.diag[c] += mesh.cell_volumes[c] * sp.internal[c];
}
mat
}
pub fn su_vec(su: &VolVectorField, u: &VolVectorField) -> FvVectorMatrix {
let mesh = u.mesh.clone();
let mut mat = FvVectorMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
mat.source[c] -= su.internal[c] * mesh.cell_volumes[c];
}
mat
}
pub fn su_sp_vec(susp: &VolScalarField, u: &VolVectorField) -> FvVectorMatrix {
let mesh = u.mesh.clone();
let mut mat = FvVectorMatrix::new(mesh.clone());
for c in 0..mesh.n_cells {
let v = mesh.cell_volumes[c];
let coeff = susp.internal[c];
mat.ldu.diag[c] += v * coeff.max(0.0);
mat.source[c] -= u.internal[c] * (v * coeff.min(0.0));
}
mat
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ldu_matrix::fv_matrix::SolverSettings;
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![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 sp_su_scalar_solve_k_phi_equals_s() {
let m = unit_mesh();
let phi = VolScalarField::zeros("phi", m.clone());
let k = VolScalarField::uniform("k", m.clone(), 2.0);
let s = VolScalarField::uniform("S", m.clone(), 6.0);
let sp_m = sp(&k, &phi);
assert!((sp_m.ldu.diag[0] - 2.0).abs() < 1e-12);
let su_m = su(&s, &phi);
assert!((su_m.source[0] - (-6.0)).abs() < 1e-12);
let eqn = sp_m - su_m; assert!((eqn.source[0] - 6.0).abs() < 1e-12);
let (x, perf) = eqn.solve("phi", SolverSettings::default());
assert!(perf.converged, "residual = {}", perf.final_residual);
assert!(
(x.internal[0] - 3.0).abs() < 1e-8,
"phi = {}",
x.internal[0]
);
}
#[test]
fn su_sp_scalar_routes_by_sign() {
let m = unit_mesh();
let mut phi = VolScalarField::uniform("phi", m.clone(), 5.0);
phi.internal[1] = 5.0;
let pos = VolScalarField::uniform("cp", m.clone(), 4.0);
let mp = su_sp(&pos, &phi);
assert!((mp.ldu.diag[0] - 4.0).abs() < 1e-12);
assert!(mp.source[0].abs() < 1e-12);
let neg = VolScalarField::uniform("cn", m.clone(), -4.0);
let mn = su_sp(&neg, &phi);
assert!(mn.ldu.diag[0].abs() < 1e-12);
assert!(
(mn.source[0] - 20.0).abs() < 1e-12,
"source = {}",
mn.source[0]
);
}
#[test]
fn sp_su_vector_solve_componentwise() {
let m = unit_mesh();
let u = VolVectorField::zero("U", m.clone());
let k = VolScalarField::uniform("k", m.clone(), 2.0);
let s = VolVectorField::uniform("S", m.clone(), Vector3::new(6.0, -4.0, 10.0));
let eqn = sp_vec(&k, &u) - su_vec(&s, &u);
let (x, perf) = eqn.solve("U", SolverSettings::default());
assert!(perf.converged, "residual = {}", perf.final_residual);
assert!(
(x.internal[0].x - 3.0).abs() < 1e-8,
"x = {}",
x.internal[0].x
);
assert!(
(x.internal[0].y - (-2.0)).abs() < 1e-8,
"y = {}",
x.internal[0].y
);
assert!(
(x.internal[0].z - 5.0).abs() < 1e-8,
"z = {}",
x.internal[0].z
);
}
#[test]
fn su_sp_vector_negative_goes_explicit() {
let m = unit_mesh();
let u = VolVectorField::uniform("U", m.clone(), Vector3::new(2.0, 0.0, 0.0));
let neg = VolScalarField::uniform("cn", m.clone(), -3.0);
let mn = su_sp_vec(&neg, &u);
assert!(mn.ldu.diag[0].abs() < 1e-12);
assert!(
(mn.source[0].x - 6.0).abs() < 1e-12,
"source.x = {}",
mn.source[0].x
);
}
}