sandbox/ecipriano/src/multicomponent-properties.h
- Update
Properties
- update_properties_constant(): update variable property fields setting constant properties (for debug purposes)
- update_properties_initial(): update variable properties with initial conditions
- Defaults
- Initialization
- update_properties(): update variable properties as a function of the thermodynamic state
- update_divergence(): update density material derivative
Update Properties
Update the thermodynamic and transport properties for the multicomponent phase change model, and compute the density material derivative which is used as a source term for the velocity divergence in order to describe low-Mach compressibility effects.
#ifndef T_PROP
# define T_PROP 0.1
#endif
#ifdef VARPROP
scalar DTDt1[], DTDt2[];
scalar * DYDt1 = NULL; // [NLS]
scalar * DYDt2 = NULL; // [NGS]
scalar Hcheck[];
scalar betaexp1[], betaexp2[];
scalar rho1vInt[], rho2vInt[];
update_properties_constant(): update variable property fields setting constant properties (for debug purposes)
void update_properties_constant (void) {
foreach() {
[] = rho1;
rho1v[] = rho1;
rho1v0[] = rho1;
rho1vInt[] = mu1;
mu1v[] = cp1;
cp1v[] = lambda1;
lambda1v
for (int jj=0; jj<NLS; jj++) {
scalar dhevjj = dhevList[jj];
[] = dhev;
dhevjj
scalar Dmix1v = Dmix1List[jj];
[] = inDmix1[jj];
Dmix1v
scalar Cp1v = Cp1List[jj];
[] = cp1;
Cp1v}
[] = rho2;
rho2v[] = rho2;
rho2v0[] = mu2;
mu2v[] = cp2;
cp2v[] = lambda2;
lambda2v
for (int jj=0; jj<NGS; jj++) {
scalar Dmix2v = Dmix2List[jj];
[] = inDmix2[jj];
Dmix2v
scalar Cp2v = Cp2List[jj];
[] = cp2;
Cp2v}
}
Do not remove the following boundary function calls: some of the property fields require boundaries to be calculated “manually” as a function of the thermodynamic state.
boundary ({rho1v,rho1v0,rho1vInt,mu1v,cp1v,lambda1v,
, rho2v0, mu2v, cp2v, lambda2v});
rho2vboundary (dhevList);
boundary (Dmix1List);
boundary (Cp1List);
boundary (Dmix2List);
boundary (Cp2List);
}
update_properties_initial(): update variable properties with initial conditions
void update_properties_initial (void) {
foreach() {
, ts2h;
ThermoState ts1h.T = TL0;
ts1h.T = TG0;
ts2h.P = Pref;
ts1h.P = Pref;
ts2h.x = liq_start;
ts1h.x = gas_start;
ts2h
[] = tp1.rhov (&ts1h);
rho1v[] = rho1v[];
rho1vInt[] = rho1v[];
rho1v0[] = tp1.muv (&ts1h);
mu1v[] = tp1.cpv (&ts1h);
cp1v[] = tp1.lambdav (&ts1h);
lambda1v
for (int jj=0; jj<NLS; jj++) {
scalar dhevjj = dhevList[jj];
[] = tp1.dhev (&ts1h, jj);
dhevjj
scalar Dmix1v = Dmix1List[jj];
[] = tp1.diff (&ts1h, jj);
Dmix1v
scalar Cp1v = Cp1List[jj];
[] = tp1.cps (&ts1h, jj);
Cp1v}
[] = tp2.rhov (&ts2h);
rho2v[] = rho2v[];
rho2v0[] = tp2.muv (&ts2h);
mu2v[] = tp2.cpv (&ts2h);
cp2v[] = tp2.lambdav (&ts2h);
lambda2v
for (int jj=0; jj<NGS; jj++) {
scalar Dmix2v = Dmix2List[jj];
[] = tp2.diff (&ts2h, jj);
Dmix2v
scalar Cp2v = Cp2List[jj];
[] = tp2.cps (&ts2h, jj);
Cp2v}
}
Do not remove the following boundary function calls: some of the property fields require boundaries to be calculated “manually” as a function of the thermodynamic state.
boundary ({rho1v,rho1v0,rho1vInt,mu1v,cp1v,lambda1v,
, rho2v0, mu2v, cp2v, lambda2v});
rho2vboundary (dhevList);
boundary (Dmix1List);
boundary (Cp1List);
boundary (Dmix2List);
boundary (Cp2List);
}
Defaults
In the defaults
event, we create the fields for the
density material derivative.
event defaults (i = 0)
{
for (int jj=0; jj<NLS; jj++) {
scalar a = new scalar;
free (a.name);
char name[20];
sprintf (name, "DYDt1_%s", liq_species[jj]);
.name = strdup (name);
a.nodump = true;
a= list_append (DYDt1, a);
DYDt1 }
for (int jj=0; jj<NGS; jj++) {
scalar a = new scalar;
free (a.name);
char name[20];
sprintf (name, "DYDt2_%s", gas_species[jj]);
.name = strdup (name);
a.nodump = true;
a= list_append (DYDt2, a);
DYDt2 }
}
double mLiq0 = 0.;
Initialization
We set update the variable property fields as a function of the initial conditions.
event init (i = 0)
{
update_properties_initial();
= 0.;
mLiq0 foreach(reduction(+:mLiq0))
+= rho1v[]*f[]*dv();
mLiq0
//MW1mix.dirty = false;
//MW2mix.dirty = false;
.dirty = true;
MW1mix.dirty = true;
MW2mix
#if TREE
for (scalar s in {drhodt, drhodtext}) {
#if EMBED
.refine = s.prolongation = refine_embed_linear;
s#else
.refine = refine_linear;
s#endif
.restriction = restriction_volume_average;
s.dirty = true; // boundary conditions need to be updated
s}
#endif
}
event cleanup (t = end)
{
delete (DYDt1), free (DYDt1), DYDt1 = NULL;
delete (DYDt2), free (DYDt2), DYDt2 = NULL;
}
The following event resets the material derivatives, so that they can be modified by external modules by adding source terms which contribute to the density variations.
event reset_sources (i++)
{
foreach() {
[] = 0.;
DTDt1[] = 0.;
DTDt2
for (int jj=0; jj<NLS; jj++) {
scalar DYDt1jj = DYDt1[jj];
[] = 0.;
DYDt1jj}
for (int jj=0; jj<NGS; jj++) {
scalar DYDt2jj = DYDt2[jj];
[] = 0.;
DYDt2jj}
}
}
update_properties(): update variable properties as a function of the thermodynamic state
Every phase property is calculated from expressions and correlations as a function of the thermodynamic state of the mixture (in this case temperature, thermodynamic pressure, and mole fractions):
\phi_k = f(T_k, P , x_{i,k})
These functions can be easily overwritten by assigning the
ThermoProp
functions in variable-properties.h.
void update_properties (void)
{
foreach() {
[] = rho1v[];
rho1v0[] = rho2v[];
rho2v0}
double MW1[NLS], MW2[NGS];
for (int jj=0; jj<NLS; jj++)
[jj] = inMW[LSI[jj]];
MW1for (int jj=0; jj<NGS; jj++)
[jj] = inMW[jj];
MW2
foreach() {
//MW1mix[] = 0.;
//MW2mix[] = 0.;
if (f[] > T_PROP) {
double x1[NLS], y1[NLS];
for (int jj=0; jj<NLS; jj++) {
scalar YL = YLList[jj];
[jj] = (NLS == 1) ? 1. : YL[];
y1}
(y1, NLS);
correctfrac (x1, y1, MW1, NLS);
mass2molefrac //MW1mix[] = mass2mw (y1, MW1, NLS);
;
ThermoState ts1h.T = TL[];
ts1h.P = Pref;
ts1h.x = x1;
ts1h
[] = tp1.rhov (&ts1h);
rho1v[] = tp1.muv (&ts1h);
mu1v[] = tp1.cpv (&ts1h);
cp1v[] = tp1.lambdav (&ts1h);
lambda1v[] = liqprop_thermal_expansion (&tp1, &ts1h);
betaexp1
for (int jj=0; jj<NLS; jj++) {
// Enthalpy of evaporation
scalar dhevjj = dhevList[jj];
[] = tp1.dhev (&ts1h, jj);
dhevjj
// Liquid phase diffusivity
scalar Dmix1v = Dmix1List[jj];
[] = tp1.diff (&ts1h, jj);
Dmix1v
// Liquid phase species heat capacity
scalar Cp1v = Cp1List[jj];
[] = tp1.cps (&ts1h, jj);
Cp1v}
}
//else {
// //rho1v[] = 0.;
// rho1v0[] = 0.;
// mu1v[] = 0.;
// cp1v[] = 0.;
// cp1v[] = 0.;
// lambda1v[] = 0.;
// betaexp1[] = 0.;
// for (int jj=0; jj<NLS; jj++) {
// scalar Dmix1v = Dmix1List[jj];
// scalar dhevjj = dhevList[jj];
// scalar Cp1v = Cp1List[jj];
// Dmix1v[] = 0.;
// dhevjj[] = 0.;
// Cp1v[] = 0.;
// }
//}
if ((1. - f[]) > T_PROP) {
double x2[NGS], y2[NGS];
for (int jj=0; jj<NGS; jj++) {
scalar YG = YGList[jj];
[jj] = YG[];
y2}
(y2, NGS);
correctfrac (x2, y2, MW2, NGS);
mass2molefrac //MW2mix[] = mass2mw (y2, MW2, NGS);
;
ThermoState ts2h.T = TG[];
ts2h.P = Pref;
ts2h.x = x2;
ts2h
[] = tp2.rhov (&ts2h);
rho2v[] = tp2.muv (&ts2h);
mu2v[] = tp2.cpv (&ts2h);
cp2v[] = tp2.lambdav (&ts2h);
lambda2v[] = gasprop_thermal_expansion (&ts2h);
betaexp2
for (int jj=0; jj<NGS; jj++) {
scalar Dmix2v = Dmix2List[jj];
[] = tp2.diff (&ts2h, jj);
Dmix2v
scalar Cp2v = Cp2List[jj];
[] = tp2.cps (&ts2h, jj);
Cp2v}
}
//else {
// //rho2v[] = 0.;
// rho2v0[] = 0.;
// mu2v[] = 0.;
// cp2v[] = 0.;
// betaexp2[] = 0.;
// for (int jj=0; jj<NGS; jj++) {
// scalar Dmix2v = Dmix2List[jj];
// scalar Cp2v = Cp2List[jj];
// Dmix2v[] = 0.;
// Cp2v[] = 0.;
// }
//}
}
We extend the properties around the interfacial cells, in order to avoid problems due to spurious values.
//// Update interface properties
//foreach() {
// if (f[] > F_ERR && f[] < 1.-F_ERR) {
// // Liquid interface side
// double x1[NLS], y1[NLS];
// for (int jj=0; jj<NLS; jj++) {
// scalar YLInt = YLIntList[jj];
// y1[jj] = YLInt[];
// }
// correctfrac (y1, NLS);
// mass2molefrac (x1, y1, MW1, NLS);
// ThermoState ts1h;
// ts1h.T = TInt[];
// ts1h.P = Pref;
// ts1h.x = x1;
// //rho1v[] = tp1.rhov (&ts1h);
// //mu1v[] = tp1.muv (&ts1h);
// //cp1v[] = tp1.cpv (&ts1h);
// //lambda1v[] = tp1.lambdav (&ts1h);
// //betaexp1[] = liqprop_thermal_expansion (&tp1, &ts1h);
// //for (int jj=0; jj<NLS; jj++) {
// // scalar dhevjj = dhevList[jj];
// // dhevjj[] = tp1.dhev (&ts1h, jj);
// // scalar Dmix1v = Dmix1List[jj];
// // Dmix1v[] = tp1.diff (&ts1h, jj);
// //}
// // Gas interface side
// double x2[NGS], y2[NGS];
// for (int jj=0; jj<NGS; jj++) {
// scalar YGInt = YGIntList[jj];
// y2[jj] = YGInt[];
// }
// correctfrac (y2, NGS);
// mass2molefrac (x2, y2, MW2, NGS);
// ThermoState ts2h;
// ts2h.T = TInt[];
// ts2h.P = Pref;
// ts2h.x = x2;
// rho2vInt[] = tp2.rhov (&ts2h);
// //mu2v[] = tp2.muv (&ts2h);
// //cp2v[] = tp2.cpv (&ts2h);
// //lambda2v[] = tp2.lambdav (&ts2h);
// //betaexp2[] = gasprop_thermal_expansion (&ts2h);
// //for (int jj=0; jj<NGS; jj++) {
// // scalar Dmix2v = Dmix2List[jj];
// // Dmix2v[] = tp2.diff (&ts2h, jj);
// //}
// }
//}
foreach() {
if (f[] <= T_PROP) {
double rho1vgh = 0.;
double mu1vgh = 0.;
double cp1vgh = 0.;
double lambda1vgh = 0.;
double dhevgh[NLS];
double beta1vgh = 0.;
for (int jj=0; jj<NLS; jj++)
[jj] = 0.;
dhevgh
int counter = 0;
foreach_neighbor(1) {
if (f[] > T_PROP) {
++;
counter+= rho1v[];
rho1vgh += mu1v[];
mu1vgh += cp1v[];
cp1vgh += lambda1v[];
lambda1vgh += betaexp1[];
beta1vgh
for (int jj=0; jj<NLS; jj++) {
scalar dhevjj = dhevList[jj];
[jj] += dhevjj[];
dhevgh}
}
}
[] = (counter != 0) ? rho1vgh/counter : 0.;
rho1v[] = (counter != 0) ? mu1vgh/counter : 0.;
mu1v[] = (counter != 0) ? cp1vgh/counter : 0.;
cp1v[] = (counter != 0) ? lambda1vgh/counter : 0.;
lambda1v[] = (counter != 0) ? beta1vgh/counter : 0.;
betaexp1
for (int jj=0; jj<NLS; jj++) {
scalar dhevjj = dhevList[jj];
[] = (counter != 0) ? dhevgh[jj]/counter : 0.;
dhevjj}
}
if ((1. - f[]) <= T_PROP) {
double rho2vgh = 0.;
double mu2vgh = 0.;
double cp2vgh = 0.;
double lambda2vgh = 0.;
double Dmix2vgh[NGS];
for (int jj=0; jj<NGS; jj++)
[jj] = 0.;
Dmix2vgh
int counter = 0;
foreach_neighbor(1) {
if ((1. - f[]) > T_PROP) {
++;
counter+= rho2v[];
rho2vgh += mu2v[];
mu2vgh += cp2v[];
cp2vgh += lambda2v[];
lambda2vgh
for (int jj=0; jj<NGS; jj++) {
scalar Dmix2jj = Dmix2List[jj];
[jj] += Dmix2jj[];
Dmix2vgh}
}
}
[] = (counter != 0) ? rho2vgh/counter : 0.;
rho2v[] = (counter != 0) ? mu2vgh/counter : 0.;
mu2v[] = (counter != 0) ? cp2vgh/counter : 0.;
cp2v[] = (counter != 0) ? lambda2vgh/counter : 0.;
lambda2v
for (int jj=0; jj<NGS; jj++) {
scalar Dmix2jj = Dmix2List[jj];
[] = (counter != 0) ? Dmix2vgh[jj]/counter : 0.;
Dmix2jj}
}
}
Do not remove the following boundary function calls: some of the property fields require boundaries to be calculated “manually” as a function of the thermodynamic state.
boundary ({rho1v,rho1v0,rho1vInt,mu1v,cp1v,lambda1v,
, rho2v0, mu2v, cp2v, lambda2v});
rho2vboundary (dhevList);
boundary (Dmix1List);
boundary (Cp1List);
boundary (Dmix2List);
boundary (Cp2List);
We update the mixture molecular weights at the boundaries manually, on each boundary face.
//for (int b = 0; b < nboundary; b++) {
// foreach_boundary(b) {
// // liquid phase
// double x1[NLS], y1[NLS];
// for (int jj=0; jj<NLS; jj++) {
// scalar YL = YLList[jj];
// double YLf = 0.5*(YL[] + get_ghost (point, YL, b));
// y1[jj] = (NLS == 1.) ? 1. : YLf;
// }
// correctfrac (y1, NLS);
// mass2molefrac (x1, y1, MW1, NLS);
// double MW1mixf = mass2mw (y1, MW1, NLS);
// set_ghost (point, MW1mix, b, MW1mixf);
// // gas phase
// double x2[NGS], y2[NGS];
// for (int jj=0; jj<NGS; jj++) {
// scalar YG = YGList[jj];
// double YGf = 0.5*(YG[] + get_ghost (point, YG, b));
// y2[jj] = YGf;
// }
// correctfrac (y2, NGS);
// mass2molefrac (x2, y2, MW2, NGS);
// double MW2mixf = mass2mw (y2, MW2, NGS);
// set_ghost (point, MW2mix, b, MW2mixf);
// }
//}
}
update_divergence(): update density material derivative
According to the low-Mach approximation, the density material derivative is computed by considering that temperature and composition gradients dominate over thermodynamic pressure gradients. The final expression of the velocity divergence reads:
\nabla\cdot\mathbf{u}_k = \left. \dfrac{1}{\rho}\dfrac{D\rho}{D t} \right\vert_k = \beta_k\dfrac{D T_k}{D t} + M_{w,k} \sum_{i=1}^{NS} \dfrac{1}{M_{w,i}}\dfrac{D\omega_{i,k}}{D t}
which is used both for the liquid and for the gas phase. However, in liquid phase we assume that the density changes due to the temperature gradients dominate over composition effects. This is always true for pure liquid droplets, but it is generally valid also for multicomponent droplets. Relaxing this hypotesis is not complex, it requires the last term on the RHS to be computed without the ideal gas approximation.
void update_divergence (void) {
We define the variables used to compute the lagrangian derivative on each level.
({T,TL,TG});
restriction (YLList);
restriction (YGList);
restriction
scalar dYdt[];
foreach()
[] = 0.; dYdt
We compute the chemical species diffusive fluxes, including corrections to Fick’s law, and molar-based diffusivities.
face vector phicGtot[];
foreach_face() {
.x[] = 0.;
phicGtotfor (int jj=0; jj<NGS; jj++) {
#ifdef FICK_CORRECTED
scalar Dmix2v = Dmix2List[jj];
double rho2f = 0.5*(rho2v[] + rho2v[-1]);
double Dmix2f = 0.5*(Dmix2v[] + Dmix2v[-1]);
# ifdef MOLAR_DIFFUSION
scalar XG = XGList[jj];
double MW2mixf = 0.5*(MW2mix[] + MW2mix[-1]);
.x[] += (MW2mixf > 0.) ?
phicGtot*Dmix2f*inMW[jj]/MW2mixf*face_gradient_x (XG, 0)*fsG.x[]*fm.x[] : 0.;
rho2f# else
scalar YG = YGList[jj];
.x[] += rho2f*Dmix2f*face_gradient_x (YG, 0)*fsG.x[]*fm.x[];
phicGtot#endif // MOLAR_DIFFUSION
#else
.x[] = 0.;
phicGtot#endif // FICK_CORRECTED
}
}
for (int jj=0; jj<NGS; jj++) {
face vector phicGjj[];
scalar YG = YGList[jj];
//scalar DYDt2jj = DYDt2[jj];
scalar Dmix2v = Dmix2List[jj];
foreach_face() {
double rho2f = 0.5*(rho2v[] + rho2v[-1]);
double Dmix2f = 0.5*(Dmix2v[] + Dmix2v[-1]);
#ifdef MOLAR_DIFFUSION
scalar XG = XGList[jj];
double MW2mixf = 0.5*(MW2mix[] + MW2mix[-1]);
.x[] = (MW2mixf > 0.) ?
phicGjj*Dmix2f*inMW[jj]/MW2mixf*face_gradient_x (XG, 0)*fsG.x[]*fm.x[] : 0.;
rho2f#else
.x[] = rho2f*Dmix2f*face_gradient_x (YG, 0)*fsG.x[]*fm.x[];
phicGjj#endif // MOLAR_DIFFUSION
double YGf = 0.5*(YG[] + YG[-1]);
.x[] -= YGf*phicGtot.x[];
phicGjj}
scalar sgexp = sgexpList[jj];
scalar sgimp = sgimpList[jj];
foreach() {
foreach_dimension()
[] += (phicGjj.x[1] - phicGjj.x[])/Delta;
dYdt[] += (sgexp[] + sgimp[]*YG[]);
dYdt[] *= 1./inMW[jj];
dYdt}
}
scalar DrhoDt1[], DrhoDt2[];
foreach() {
Calculate the sum of the material derivative for each chemical species.
double DYDt2sum = dYdt[];
for (int jj=0; jj<NGS; jj++) {
scalar DYDt2jj = DYDt2[jj];
+= 1./inMW[jj]*DYDt2jj[];
DYDt2sum }
*= (rho2v[] > 0.) ? MW2mix[]/rho2v[] : 0.; DYDt2sum
Calculate the material derivative for the temperature field in liquid phase.
double laplT1 = 0.;
foreach_dimension() {
double lambdafr = 0.5*(lambda1v[1] + lambda1v[]);
double lambdafl = 0.5*(lambda1v[] + lambda1v[-1]);
+= (fm.x[1]*fsL.x[1]*lambdafr*face_gradient_x (TL, 1) -
laplT1 .x[]*fsL.x[]*lambdafl*face_gradient_x (TL, 0));
fm}
/= Delta; laplT1
Calculate the material derivative for the temperature field in gas phase.
double laplT2 = 0.;
foreach_dimension() {
double lambdafr = 0.5*(lambda2v[1] + lambda2v[]);
double lambdafl = 0.5*(lambda2v[] + lambda2v[-1]);
+= (fm.x[1]*fsG.x[1]*lambdafr*face_gradient_x (TG, 1) -
laplT2 .x[]*fsG.x[]*lambdafl*face_gradient_x (TG, 0));
fm}
/= Delta; laplT2
Add source terms from multicomponent.h.
[] += (laplT1 + slT[]);
DTDt1[] += (laplT2 + sgT[]);
DTDt2
//double DrhoDt1 = 0.;
//double DrhoDt2 = 0.;
[] = 0.;
DrhoDt1[] = 0.;
DrhoDt2
// Add liquid compressibility due to temperature
[] += (rho1v[]*cp1v[] > 0.) ?
DrhoDt1-betaexp1[]/(rho1v[]*cp1v[])*DTDt1[] : 0.;
[] += (TG[]*rho2v[]*cp2v[] > 0.) ?
DrhoDt2-1./(TG[]*rho2v[]*cp2v[])*DTDt2[] : 0.;
// Add gas compressibility due to composition
//DrhoDt2 += ((1. - f[]) > F_ERR) ? -DYDt2sum : 0.;
[] += (f[] == 0.) ? -DYDt2sum : 0.;
DrhoDt2
//drhodt[] = DrhoDt1*f[] + DrhoDt2*(1. - f[])*(f[] < F_ERR);
//drhodtext[] = DrhoDt1;
}
//shift_field (DrhoDt1, f, 1);
//shift_field (DrhoDt2, f, 0);
foreach() {
[] = DrhoDt1[]*f[] + DrhoDt2[]*(1. - f[]);
drhodt[] = DrhoDt1[];
drhodtext}
boundary ({drhodt, drhodtext});
}
void update_divergence_density (void) {
vector grho1[], grho2[];
({rho1v, rho2v}, {grho1, grho2});
gradients
scalar DrhoDt1[], DrhoDt2[];
foreach() {
[] = (rho1v[] - rho1v0[])/dt;
DrhoDt1[] = (rho2v[] - rho2v0[])/dt;
DrhoDt2
foreach_dimension() {
#ifdef VELOCITY_JUMP
[] += u1.x[]*grho1.x[];
DrhoDt1[] += u2.x[]*grho2.x[];
DrhoDt2#else
[] += uext.x[]*grho1.x[];
DrhoDt1[] += u.x[]*grho2.x[];
DrhoDt2#endif
}
[] = DrhoDt1[]*cm[];
DrhoDt1[] = DrhoDt2[]*cm[];
DrhoDt2
double one_over_rho1 = (rho1v[] > 0.) ? 1./rho1v[] : 0.;
double one_over_rho2 = (rho2v[] > 0.) ? 1./rho2v[] : 0.;
if (iter > 1) {
[] = (one_over_rho1*DrhoDt1[]*f[] + one_over_rho2*DrhoDt2[]*(1. - f[]));
drhodt[] = one_over_rho1*DrhoDt1[]*f[];
drhodtext}
}
}
#endif