use super::CellSelection;
use crate::fields::VolScalarField;
use crate::ldu_matrix::{FvMatrix, FvVectorMatrix};
use crate::primitives::Vector3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SolidificationMeltingCoefficients {
pub solidus: f64,
pub liquidus: f64,
pub latent_heat: f64,
pub eutectic_fraction: f64,
pub darcy_coefficient: f64,
pub darcy_regularisation: f64,
pub thermal_expansion: f64,
pub reference_density: f64,
pub relaxation: f64,
pub specific_heat: f64,
}
impl SolidificationMeltingCoefficients {
#[must_use]
pub fn new(
solidus: f64,
liquidus: f64,
latent_heat: f64,
specific_heat: f64,
reference_density: f64,
thermal_expansion: f64,
) -> Self {
Self {
solidus,
liquidus,
latent_heat,
eutectic_fraction: 0.0,
darcy_coefficient: 1.0e5,
darcy_regularisation: 1.0e-3,
thermal_expansion,
reference_density,
relaxation: 0.9,
specific_heat,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SolidificationMelting {
name: String,
velocity_name: String,
energy_name: String,
energy_is_temperature: bool,
selection: CellSelection,
coefficients: SolidificationMeltingCoefficients,
gravity: Vector3,
liquid_fraction: Vec<f64>,
liquid_fraction_old: Vec<f64>,
delta_temperature: Vec<f64>,
updated_this_step: bool,
}
impl SolidificationMelting {
#[must_use]
pub fn new(
name: impl Into<String>,
velocity_name: impl Into<String>,
energy_name: impl Into<String>,
energy_is_temperature: bool,
selection: CellSelection,
coefficients: SolidificationMeltingCoefficients,
gravity: Vector3,
n_cells: usize,
) -> Self {
let n = selection.len(n_cells);
Self {
name: name.into(),
velocity_name: velocity_name.into(),
energy_name: energy_name.into(),
energy_is_temperature,
selection,
coefficients,
gravity,
liquid_fraction: vec![0.0; n],
liquid_fraction_old: vec![0.0; n],
delta_temperature: vec![0.0; n],
updated_this_step: false,
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn velocity_name(&self) -> &str {
&self.velocity_name
}
#[must_use]
pub fn energy_name(&self) -> &str {
&self.energy_name
}
#[must_use]
pub fn selection(&self) -> &CellSelection {
&self.selection
}
#[must_use]
pub fn liquid_fraction(&self) -> &[f64] {
&self.liquid_fraction
}
#[must_use]
pub fn effective_liquidus(&self, liquid_fraction: f64) -> f64 {
let c = &self.coefficients;
let linear = c.solidus
+ (c.liquidus - c.solidus) * (liquid_fraction - c.eutectic_fraction)
/ (1.0 - c.eutectic_fraction);
c.solidus.max(linear)
}
pub fn update(&mut self, temperature: &VolScalarField) {
if self.updated_this_step {
return;
}
self.updated_this_step = true;
let c = self.coefficients;
let cells = self.selection.cells(temperature.internal.len());
for (i, &cell) in cells.iter().enumerate() {
let t = temperature.internal[cell];
let alpha = self.liquid_fraction[i];
let new = alpha
+ c.relaxation * c.specific_heat * (t - self.effective_liquidus(alpha))
/ c.latent_heat;
self.liquid_fraction[i] = new.clamp(0.0, 1.0);
self.delta_temperature[i] = t - self.effective_liquidus(self.liquid_fraction[i]);
}
}
pub fn advance_time(&mut self) {
self.liquid_fraction_old
.copy_from_slice(&self.liquid_fraction);
self.updated_this_step = false;
}
#[must_use]
pub fn darcy_coefficient(&self, liquid_fraction: f64) -> f64 {
let c = &self.coefficients;
let solid = 1.0 - liquid_fraction;
-c.darcy_coefficient * solid * solid
/ (liquid_fraction * liquid_fraction * liquid_fraction + c.darcy_regularisation)
}
pub fn add_momentum_source(&mut self, temperature: &VolScalarField, eqn: &mut FvVectorMatrix) {
self.update(temperature);
let c = self.coefficients;
let mesh = eqn.mesh.clone();
let cells = self.selection.cells(mesh.n_cells);
for (i, &cell) in cells.iter().enumerate() {
let v = mesh.cell_volumes[cell];
let drag = self.darcy_coefficient(self.liquid_fraction[i]);
let buoyancy = self.gravity
* (-c.reference_density * c.thermal_expansion * self.delta_temperature[i]);
eqn.ldu.diag[cell] -= v * drag;
eqn.source[cell] += buoyancy * v;
}
}
pub fn add_energy_source(
&mut self,
rho: &VolScalarField,
temperature: &VolScalarField,
dt: f64,
eqn: &mut FvMatrix,
) {
if dt <= 0.0 {
return;
}
self.update(temperature);
let c = self.coefficients;
let scale = if self.energy_is_temperature {
c.latent_heat / c.specific_heat
} else {
c.latent_heat
};
let mesh = eqn.mesh.clone();
let cells = self.selection.cells(mesh.n_cells);
for (i, &cell) in cells.iter().enumerate() {
let v = mesh.cell_volumes[cell];
let rate =
rho.internal[cell] * (self.liquid_fraction[i] - self.liquid_fraction_old[i]) / dt;
eqn.source[cell] -= v * scale * rate;
}
}
}
#[cfg(test)]
mod tests;