pub struct VofSolidificationMelting { /* private fields */ }Expand description
Solidification and melting of the condensed phase of a VoF simulation.
§How this differs from the enthalpy-porosity model
SolidificationMelting derives its liquid
fraction by integrating an under-relaxed enthalpy update, so latent heat
feeds back on the temperature and holds a melting cell at its melting point.
This model does not integrate anything: the solid fraction is read
straight off a table alphaSolid(T), capped by however much of the cell
the VoF phase occupies. It is the cruder of the two, and the right one only
when the interface tracking — not the phase-change thermodynamics — is what
the calculation is about.
It also carries no buoyancy term. The enthalpy-porosity model supplies
its own Boussinesq force; here the host VoF solver already carries gravity
through its p_rgh formulation, so adding one would double-count it.
§The two meanings of relax — an upstream trap
Both models take a relax coefficient defaulting to 0.9, and they mean
different things:
- Enthalpy-porosity:
relaxscales the increment,α₁ ← α₁ + relax·Cp·(T − T_liq_eff)/L. Raising it makes the update larger. - Here:
relaxis a convex blend between the new and old value,αₛ ← min(relax·α_vof·αₛ(T) + (1 − relax)·αₛ_old, α_vof). Raising it makes the update approach the table value faster, andrelax = 1discards the history entirely.
Transplanting a tuned relax from one model to the other is therefore not
meaningful. This port keeps both upstream behaviours exactly as written
rather than unifying them.
§State
Stateful, like the enthalpy-porosity model: it holds the solid fraction and its previous-timestep value, because the latent-heat source is a rate.
Implementations§
Source§impl VofSolidificationMelting
impl VofSolidificationMelting
Sourcepub fn new(
name: impl Into<String>,
velocity_name: impl Into<String>,
energy_name: impl Into<String>,
selection: CellSelection,
solid_fraction_table: TemperatureTable,
latent_heat: f64,
n_cells: usize,
) -> Self
pub fn new( name: impl Into<String>, velocity_name: impl Into<String>, energy_name: impl Into<String>, selection: CellSelection, solid_fraction_table: TemperatureTable, latent_heat: f64, n_cells: usize, ) -> Self
Build the model with upstream’s numerical defaults.
name— identifier for diagnostics, upstream’s dictionary key.velocity_name/energy_name— the equations this contributes to, upstream’saddSupFieldsreturning{"U", thermo1().he().name()}.selection— the cells acted on, upstream’scellZone.solid_fraction_table— upstream’salphaSolidT, solid fraction [-] against temperature [K]. It descends: 1 when cold, 0 when hot. That is the opposite sense to the drag tableSolidificationPorositygives the sameTemperatureTabletype; the type is shared because the interpolation and end-clamping are identical, not because the quantity is, and it carries no unit of its own.latent_heat—L[J/kg].
Defaults applied: relax = 0.9, Cu = 1e5, q = 1e-3, matching
upstream’s documented defaults.
n_cells is the mesh cell count, needed to size the internal state.
Sourcepub fn with_coefficients(
self,
relaxation: f64,
darcy_coefficient: f64,
darcy_regularisation: f64,
) -> Self
pub fn with_coefficients( self, relaxation: f64, darcy_coefficient: f64, darcy_regularisation: f64, ) -> Self
Override the numerical coefficients.
relaxation [-] in [0, 1] — the convex blend described on the type,
not the enthalpy-porosity model’s increment scale.
darcy_coefficient Cu and darcy_regularisation q have the same
meaning as in
SolidificationMeltingCoefficients:
Cu sets the drag magnitude, q bounds it in a fully solid cell.
Sourcepub fn velocity_name(&self) -> &str
pub fn velocity_name(&self) -> &str
The velocity field this adds a momentum sink to.
Sourcepub fn energy_name(&self) -> &str
pub fn energy_name(&self) -> &str
The energy field this adds latent heat to.
Sourcepub fn selection(&self) -> &CellSelection
pub fn selection(&self) -> &CellSelection
The cells acted on.
Sourcepub fn solid_fraction(&self) -> &[f64]
pub fn solid_fraction(&self) -> &[f64]
The current solid fraction [-], indexed by position within the selection (not by mesh cell index).
Sourcepub fn correct(
&mut self,
temperature: &VolScalarField,
vof_phase_fraction: &VolScalarField,
)
pub fn correct( &mut self, temperature: &VolScalarField, vof_phase_fraction: &VolScalarField, )
Advance the solid fraction from the current temperature and VoF phase
fraction — upstream’s correct().
αₛ ← min(relax·α_vof·αₛ(T) + (1 − relax)·αₛ_old, α_vof)
The min against α_vof is what stops the model claiming more solid
than there is condensed phase in the cell: with relax < 1 the blend
can otherwise carry a stale value forward past a receding interface.
§No once-per-timestep guard
Unlike SolidificationMelting::update,
this has none, because upstream’s correct() is called by the solver at
a defined point in the timestep rather than lazily from addSup. Call
it exactly once per timestep, before the equations that use it. Calling
it twice applies the relaxation twice, exactly as it would upstream.
Sourcepub fn advance_time(&mut self)
pub fn advance_time(&mut self)
Roll the solid-fraction history forward.
Upstream’s alphaSolid_.oldTime() at the top of correct(). Call once
per completed timestep; the latent-heat source is proportional to
∂(ρα_s)/∂t and is silently wrong without it.
Sourcepub fn darcy_coefficient(&self, fluid_fraction: f64) -> f64
pub fn darcy_coefficient(&self, fluid_fraction: f64) -> f64
The Darcy drag coefficient at a given fluid fraction [1/s] per unit density.
Cu (1 − α_fluid)² / (α_fluid³ + q), where α_fluid = 1 − αₛ.
Positive here, because upstream writes the sink as Sp -= Vc·ρ·S with
a positive S; see add_momentum_source
for why that ends up the same drag as the enthalpy-porosity model’s
negative one.
Sourcepub fn add_momentum_source(
&self,
rho: &VolScalarField,
eqn: &mut FvVectorMatrix,
)
pub fn add_momentum_source( &self, rho: &VolScalarField, eqn: &mut FvVectorMatrix, )
Add the momentum sink to a velocity equation.
Upstream’s addSup(rho, U, eqn):
const scalar alphaFluid = 1 - alphaSolid_[celli];
const scalar S = Cu_*sqr(1 - alphaFluid)/(pow3(alphaFluid) + q_);
Sp[celli] -= Vc*rho[celli]*S;§Why the sign looks opposite to the enthalpy-porosity model
It is not. Upstream writes the base fvModel as Sp += Vc·S with
S = −Cu(1−α₁)²/(α₁³+q) and this one as Sp −= Vc·ρ·S with a
positive S; the two products are the same negative number, modulo
the density weighting. Both then pass through the
solve(UEqn == fvModels.source(...)) negation described on
SolidificationMelting::add_momentum_source,
reaching the solved system as a positive diagonal contribution — a
stabilising sink. This port therefore places it the same way:
diag += V·ρ·Cu(1−α_fluid)²/(α_fluid³+q).
Sourcepub fn add_enthalpy_source(
&self,
rho: &VolScalarField,
dt: f64,
eqn: &mut FvMatrix,
)
pub fn add_enthalpy_source( &self, rho: &VolScalarField, dt: f64, eqn: &mut FvMatrix, )
Add the latent-heat source to an enthalpy equation.
Upstream’s addSup(alpha, rho, he, eqn), which is the single line
eqn += L*(fvc::ddt(rho, alphaSolid_)). Following it through the
solve(hEqn == fvModels.source(...)) negation, the term reaching the
solved right-hand side is −L·∂(ρα_s)/∂t: freezing (∂α_s/∂t > 0)
releases energy, so it appears as a negative contribution to an
equation whose sources are subtracted.
§Enthalpy only — an upstream limitation kept, not corrected
SolidificationMelting::add_energy_source
branches on whether the equation is solved in temperature or enthalpy
and divides by Cp in the temperature form. This model has no such
branch upstream — it is only ever attached to thermo1().he(), an
enthalpy — so none is added here. Attaching it to a temperature equation
would over-source it by a factor of Cp, typically several hundred.
That restriction is upstream’s and is reproduced rather than papered
over.
dt is the timestep [s]; a non-positive dt contributes nothing.