use super::*;
use crate::mesh::FvMesh;
use std::sync::Arc;
const T_SOLID: f64 = 330.0;
const T_LIQUID: f64 = 330.5;
const D_SOLID: f64 = 10_000.0;
const N_CELLS: usize = 4;
const CELL_VOLUME: f64 = 0.5;
fn mesh() -> Arc<FvMesh> {
Arc::new(FvMesh::periodic_1d(N_CELLS, 4.0, 0.5))
}
fn table() -> TemperatureTable {
TemperatureTable::two_knot(T_SOLID, T_LIQUID, D_SOLID, 0.0)
}
fn model(form: MomentumEquationForm) -> SolidificationPorosity {
SolidificationPorosity::new("iceZone", "U", CellSelection::All, table(), form)
}
fn uniform(name: &str, m: &Arc<FvMesh>, value: f64) -> VolScalarField {
VolScalarField::uniform(name, m.clone(), value)
}
#[test]
fn drag_table_clamps_at_both_ends_instead_of_extrapolating() {
let t = table();
let far_cold = t.value(200.0);
let at_solidus = t.value(T_SOLID);
let at_liquidus = t.value(T_LIQUID);
let far_hot = t.value(500.0);
println!(
"D(200) = {far_cold}, D({T_SOLID}) = {at_solidus}, \
D({T_LIQUID}) = {at_liquidus}, D(500) = {far_hot}"
);
assert_eq!(far_cold, D_SOLID);
assert_eq!(at_solidus, D_SOLID);
assert_eq!(at_liquidus, 0.0);
assert_eq!(far_hot, 0.0);
}
#[test]
fn drag_table_is_linear_between_the_knots() {
let t = table();
let mid = 0.5 * (T_SOLID + T_LIQUID);
let d = t.value(mid);
let closed_form = 0.5 * D_SOLID;
println!(
"D({mid}) = {d}, closed form = {closed_form}, diff = {}",
d - closed_form
);
assert!((d - closed_form).abs() < 1e-12 * D_SOLID);
}
#[test]
#[should_panic(expected = "strictly ascending")]
fn drag_table_rejects_descending_temperatures() {
let _ = TemperatureTable::new(vec![330.5, 330.0], vec![0.0, D_SOLID]);
}
#[test]
fn kinematic_drag_adds_volume_times_d_to_the_diagonal() {
let m = mesh();
let model = model(MomentumEquationForm::Kinematic);
let t = uniform("T", &m, 300.0); let rho = uniform("rho", &m, 6093.0); let mut eqn = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, None, &mut eqn);
let closed_form = CELL_VOLUME * D_SOLID;
println!("diag = {:?}, closed form = {closed_form}", eqn.ldu.diag);
for c in 0..N_CELLS {
assert_eq!(eqn.ldu.diag[c], closed_form);
}
}
#[test]
fn density_weighted_form_scales_the_drag_by_rho() {
let m = mesh();
let rho_value = 6093.0;
let t = uniform("T", &m, 300.0);
let rho = uniform("rho", &m, rho_value);
let mut kin = FvVectorMatrix::new(m.clone());
model(MomentumEquationForm::Kinematic).add_momentum_source(&t, &rho, None, &mut kin);
let mut dw = FvVectorMatrix::new(m.clone());
model(MomentumEquationForm::DensityWeighted).add_momentum_source(&t, &rho, None, &mut dw);
let ratio = dw.ldu.diag[0] / kin.ldu.diag[0];
println!(
"kinematic diag[0] = {}, density-weighted diag[0] = {}, ratio = {ratio}, rho = {rho_value}",
kin.ldu.diag[0], dw.ldu.diag[0]
);
assert!((ratio - rho_value).abs() < 1e-12 * rho_value);
}
#[test]
fn fully_molten_cells_gain_exactly_no_drag() {
let m = mesh();
let model = model(MomentumEquationForm::Kinematic);
let t = uniform("T", &m, 400.0);
let rho = uniform("rho", &m, 1.0);
let mut eqn = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, None, &mut eqn);
println!("diag = {:?}", eqn.ldu.diag);
for c in 0..N_CELLS {
assert_eq!(eqn.ldu.diag[c], 0.0);
}
}
#[test]
fn phase_fraction_scales_the_drag_linearly() {
let m = mesh();
let model = model(MomentumEquationForm::Kinematic).with_phase_fraction("alpha.solid");
let t = uniform("T", &m, 300.0);
let rho = uniform("rho", &m, 1.0);
let alpha = uniform("alpha.solid", &m, 0.25);
let mut without = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, None, &mut without);
let mut with = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, Some(&alpha), &mut with);
let ratio = with.ldu.diag[0] / without.ldu.diag[0];
println!(
"no alpha: diag[0] = {}, alpha=0.25: diag[0] = {}, ratio = {ratio}",
without.ldu.diag[0], with.ldu.diag[0]
);
assert_eq!(model.phase_fraction_name(), Some("alpha.solid"));
assert!((ratio - 0.25).abs() < 1e-12);
}
#[test]
fn a_cell_zone_restricts_the_drag_to_its_own_cells() {
let m = mesh();
let model = SolidificationPorosity::new(
"iceZone",
"U",
CellSelection::zone(vec![1, 2]),
table(),
MomentumEquationForm::Kinematic,
);
let t = uniform("T", &m, 300.0);
let rho = uniform("rho", &m, 1.0);
let mut eqn = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, None, &mut eqn);
println!("diag = {:?}", eqn.ldu.diag);
assert_eq!(eqn.ldu.diag[0], 0.0);
assert_eq!(eqn.ldu.diag[1], CELL_VOLUME * D_SOLID);
assert_eq!(eqn.ldu.diag[2], CELL_VOLUME * D_SOLID);
assert_eq!(eqn.ldu.diag[3], 0.0);
}
#[test]
fn force_diagonal_matches_the_assembled_momentum_diagonal() {
let m = mesh();
let model = model(MomentumEquationForm::Kinematic);
let t = uniform("T", &m, 300.0);
let rho = uniform("rho", &m, 1.0);
let mut eqn = FvVectorMatrix::new(m.clone());
model.add_momentum_source(&t, &rho, None, &mut eqn);
let force = model.force_diagonal(&t, &rho, None, &m.cell_volumes);
println!(
"force_diagonal = {force:?}, assembled diag = {:?}",
eqn.ldu.diag
);
for c in 0..N_CELLS {
assert_eq!(force[c], eqn.ldu.diag[c]);
}
}