phaseSurfaceBoiling.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2025-2026 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "phaseSurfaceBoiling.H"
27 
28 #include "multiphaseEuler.H"
29 
30 #include "diameterModel.H"
32 
34 #include "partitioningModel.H"
35 #include "nucleationSiteModel.H"
36 #include "departureDiameterModel.H"
38 
40 
41 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
42 
43 namespace Foam
44 {
45 namespace fv
46 {
49 }
50 }
51 
52 
53 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
54 
55 void Foam::fv::phaseSurfaceBoiling::readCoeffs(const dictionary& dict)
56 {
58 
59  saturationModelPtr_.reset
60  (
62  (
63  "saturationTemperature",
64  dict
65  ).ptr()
66  );
67 
68  partitioningModel_.reset
69  (
71  (
72  dict.subDict("partitioningModel")
73  ).ptr()
74  );
75 
76  nucleationSiteModel_.reset
77  (
79  (
80  dict.subDict("nucleationSiteModel")
81  ).ptr()
82  );
83 
84  departureDiameterModel_.reset
85  (
87  (
88  dict.subDict("departureDiameterModel")
89  ).ptr()
90  );
91 
92  departureFrequencyModel_.reset
93  (
95  (
96  dict.subDict("departureFrequencyModel")
97  ).ptr()
98  );
99 }
100 
101 
102 void Foam::fv::phaseSurfaceBoiling::correctMDot() const
103 {
105 
106  static const dimensionedScalar rooVSmallT(dimTemperature, rootVSmall);
107  static const dimensionedScalar rootVSmallF(dimless/dimTime, rootVSmall);
108  static const dimensionedScalar rootVSmallH
109  (
111  rootVSmall
112  );
113 
114  const rhoThermo& solidThermo = solid_.thermo();
115  const volScalarField::Internal& solidT = solidThermo.T();
116  const rhoThermo& liquidThermo = liquid_.thermo();
117  const volScalarField::Internal& liquidT = liquidThermo.T();
118  const rhoThermo& vapourThermo = vapour_.thermo();
119 
120  // Estimate the surface temperature from the surrounding temperature and
121  // heat transfer coefficients. Note that lagged values of qEvaporative and
122  // qQuenching are used in this calculation.
123  const Pair<tmp<volScalarField>> Hs =
124  solver_.heatTransfer.Hs(liquid_, solid_, scalar(0));
125  const volScalarField::Internal& liquidH = Hs.first();
126  const volScalarField::Internal& solidH = Hs.second();
127  const volScalarField::Internal Tsurface
128  (
129  (solidH*solidT + liquidH*liquidT + qEvaporative_ + qQuenching_)
130  /max(solidH + liquidH, rootVSmallH)
131  );
132 
133  const volScalarField::Internal Tsat
134  (
135  saturationModelPtr_->Tsat(liquid_.fluidThermo().p()())
136  );
137 
138  const volScalarField::Internal L(this->L(Tsat));
139 
140  // Wetted fraction
141  wetFraction_ =
142  partitioningModel_->wetFraction(liquid_()/max(1 - solid_(), small));
143 
144  // Bubble departure diameter
145  dDeparture_ =
146  departureDiameterModel_->dDeparture
147  (
148  liquid_,
149  vapour_,
150  solid_,
151  Tsurface,
152  Tsat,
153  L
154  );
155 
156  // Bubble departure frequency
157  fDeparture_ =
158  departureFrequencyModel_->fDeparture
159  (
160  liquid_,
161  vapour_,
162  solid_,
163  Tsurface,
164  Tsat,
165  L,
166  dDeparture_
167  );
168 
169  // Nucleation site density
170  nucleationSiteDensity_ =
171  nucleationSiteModel_->nucleationSiteDensity
172  (
173  liquid_,
174  vapour_,
175  solid_,
176  Tsurface,
177  Tsat,
178  L,
179  dDeparture_,
180  fDeparture_
181  );
182 
183  const tmp<volScalarField> tliquidRho(liquidThermo.rho());
184  const volScalarField::Internal& liquidRho = tliquidRho();
185  const tmp<volScalarField> tvapourRho(vapourThermo.rho());
186  const volScalarField::Internal& vapourRho = tvapourRho();
187  const volScalarField::Internal& liquidCp = liquidThermo.Cp();
188  const volScalarField::Internal& liquidkappa = liquidThermo.kappa();
189 
190  // Area fractions: Del Valle & Kenning (1985)
191  const volScalarField::Internal Ja
192  (
193  liquidRho*liquidCp*(Tsat - min(Tsurface, Tsat))/(liquidRho*L)
194  );
195  const volScalarField::Internal Al
196  (
197  wetFraction_*4.8*exp(min(-Ja/80, log(vGreat)))
198  );
199  const volScalarField::Internal A2
200  (
201  min(pi*sqr(dDeparture_)*nucleationSiteDensity_*Al/4, scalar(1))
202  );
203  const volScalarField::Internal A2E
204  (
205  min(pi*sqr(dDeparture_)*nucleationSiteDensity_*Al/4, scalar(5))
206  );
207 
208  // Surface area per unit volume
209  const volScalarField Av(solid_.diameter().Av());
210 
211  // Relaxation factor
212  const scalar f = mesh().solution().fieldRelaxationFactor(mDot_.member());
213 
214  // Volumetric mass source in due to the wall boiling and bulk nucleation
215  mDot_ =
216  (1 - f)*mDot_
217  + f*(1.0/6.0)*A2E*dDeparture_*vapourRho*fDeparture_*Av;
218 
219  // Evaporative heat flux
220  qEvaporative_ = mDot_*L;
221 
222  // Quenching heat transfer coefficient
223  const volScalarField::Internal hQuenching
224  (
225  2
226  *liquidkappa
227  *fDeparture_
228  *sqrt
229  (
230  0.8
231  /max(fDeparture_, rootVSmallF)
232  /(pi*(liquidkappa/liquidCp)/liquidRho)
233  )
234  );
235 
236  // Quenching heat flux
237  qQuenching_ =
238  (1 - f)*qQuenching_
239  + f*A2*hQuenching*max(Tsurface - liquidThermo.T(), rooVSmallT)*Av;
240 }
241 
242 
243 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
244 
246 (
247  const word& name,
248  const word& modelType,
249  const fvMesh& mesh,
250  const dictionary& dict
251 )
252 :
254  (
255  name,
256  modelType,
257  mesh,
258  dict,
259  readSpecie(coeffs(modelType, dict), false)
260  ),
261  nucleation(),
262  solver_(mesh().lookupObject<solvers::multiphaseEuler>(solver::typeName)),
263  fluid_(solver_.fluid),
264  liquid_(fluid_.phases()[phaseNames().first()]),
265  vapour_(fluid_.phases()[phaseNames().second()]),
266  solid_(fluid_.phases()[dict.lookup("phase")]),
267  saturationModelPtr_(nullptr),
268  partitioningModel_(nullptr),
269  nucleationSiteModel_(nullptr),
270  departureDiameterModel_(nullptr),
271  departureFrequencyModel_(nullptr),
272  pressureEquationIndex_(-1),
273  wetFraction_
274  (
275  IOobject
276  (
277  name + ":wetFraction",
278  mesh.time().name(),
279  mesh,
280  IOobject::READ_IF_PRESENT,
281  IOobject::AUTO_WRITE
282  ),
283  mesh,
284  dimensionedScalar(dimless, scalar(0))
285  ),
286  dDeparture_
287  (
288  IOobject
289  (
290  name + ":dDeparture",
291  mesh.time().name(),
292  mesh,
293  IOobject::READ_IF_PRESENT,
294  IOobject::AUTO_WRITE
295  ),
296  mesh,
298  ),
299  fDeparture_
300  (
301  IOobject
302  (
303  name + ":fDeparture",
304  mesh.time().name(),
305  mesh,
306  IOobject::READ_IF_PRESENT,
307  IOobject::AUTO_WRITE
308  ),
309  mesh,
310  dimensionedScalar(inv(dimTime), scalar(0))
311  ),
312  nucleationSiteDensity_
313  (
314  IOobject
315  (
316  name + ":nucleationSiteDensity",
317  mesh.time().name(),
318  mesh,
319  IOobject::READ_IF_PRESENT,
320  IOobject::AUTO_WRITE
321  ),
322  mesh,
323  dimensionedScalar(inv(dimArea), scalar(0))
324  ),
325  qQuenching_
326  (
327  IOobject
328  (
329  name + ":qQuenching",
330  mesh.time().name(),
331  mesh,
332  IOobject::READ_IF_PRESENT,
333  IOobject::AUTO_WRITE
334  ),
335  mesh,
337  ),
338  qEvaporative_
339  (
340  IOobject
341  (
342  name + ":qEvaporative",
343  mesh.time().name(),
344  mesh,
345  IOobject::NO_READ,
346  IOobject::NO_WRITE
347  ),
348  mesh,
350  ),
351  mDot_
352  (
353  IOobject
354  (
355  name + ":mDot",
356  mesh.time().name(),
357  mesh,
358  IOobject::READ_IF_PRESENT,
359  IOobject::AUTO_WRITE
360  ),
361  mesh,
363  )
364 {
365  readCoeffs(coeffs(dict));
366 }
367 
368 
369 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
370 
372 {
373  return
374  phaseChange::addsSupToField(fieldName)
375  || fieldName == solid_.thermo().he().name();
376 }
377 
378 
381 {
382  // Put all the latent heat into the liquid
383  return
385  (
386  name() + ":Lfraction",
387  mesh(),
388  dimensionedScalar(dimless, scalar(0))
389  );
390 }
391 
392 
395 {
396  return dDeparture_;
397 }
398 
399 
402 {
403  return fDeparture_;
404 }
405 
406 
409 {
411  return tmp<volScalarField::Internal>(nullptr);
412 }
413 
414 
417 {
418  return mDot_;
419 }
420 
421 
423 (
424  const volScalarField& alpha,
425  const volScalarField& rho,
426  fvMatrix<scalar>& eqn
427 ) const
428 {
429  // Pressure equation (i.e., continuity, linearised in the pressure)
430  if
431  (
432  (&alpha == &liquid_ || &alpha == &vapour_)
433  && (&rho == &liquid_.rho() || &rho == &vapour_.rho())
434  && &eqn.psi() == &solver_.p_rgh
435  )
436  {
437  // Ensure that the source is up-to date if this is the first call in
438  // the current phase loop
439  if (pressureEquationIndex_ % 2 == 0) correctMDot();
440  pressureEquationIndex_ ++;
441  }
442 
443  // Let the base class add the actual source
445 }
446 
447 
449 (
450  const volScalarField& alpha,
451  const volScalarField& rho,
452  const volScalarField& he,
453  fvMatrix<scalar>& eqn
454 ) const
455 {
456  // Liquid or solid energy equation. Apply the additional explicit latent
457  // and quenching heat transfers.
458  if (&he == &liquid_.thermo().he() || &he == &solid_.thermo().he())
459  {
460  const scalar sign = &he == &solid_.thermo().he() ? -1 : +1;
461 
462  eqn += sign*(qEvaporative_ + qQuenching_);
463  }
464 
465  // Let the base class do the other liquid-vapour transfers
466  if (&he != &solid_.thermo().he())
467  {
468  phaseChange::addSup(alpha, rho, he, eqn);
469  }
470 }
471 
472 
474 {
475  // Reset the p_rgh equation solution counter
476  pressureEquationIndex_ = 0;
477 
478  // Correct the total phase change rate
479  correctMDot();
480 }
481 
482 
484 {
485  if (phaseChange::read(dict))
486  {
487  readCoeffs(coeffs(dict));
488  return true;
489  }
490  else
491  {
492  return false;
493  }
494 }
495 
496 
497 // ************************************************************************* //
Macros for easy insertion into run-time selection tables.
static tmp< DimensionedField< Type, GeoMesh, PrimitiveField > > New(const word &name, const GeoMesh &mesh, const dimensionSet &, const PrimitiveField< Type > &)
Return a temporary field constructed from name, mesh,.
Generic GeometricField class.
DimensionedField< Type, GeoMesh, PrimitiveField > Internal
Type of the internal field from which this GeometricField is derived.
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
void reset(T *=nullptr)
If object pointer already set, delete object and set to given.
Definition: autoPtrI.H:114
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
A special matrix type and solver, designed for finite volume solutions of scalar equations....
Definition: fvMatrix.H:118
VolField< Type > & psi()
Definition: fvMatrix.H:289
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:98
const fvSolution & solution() const
Return the fvSolution.
Definition: fvMesh.C:1803
Finite volume model abstract base class.
Definition: fvModel.H:60
static const dictionary & coeffs(const word &modelType, const dictionary &)
Return the coefficients sub-dictionary for a given model type.
Definition: fvModelI.H:31
virtual void addSup(fvMatrix< scalar > &eqn) const
Add a source term to a field-less proxy equation.
Definition: massTransfer.C:223
virtual bool addsSupToField(const word &fieldName) const
Return true if the fvModel adds a source term to the given.
Definition: massTransfer.C:205
Mix-in interface for nucleation models. Provides access to properties of the nucleation process,...
Definition: nucleation.H:53
Base class for phase change models.
Definition: phaseChange.H:61
void reReadSpecie(const dictionary &dict) const
Re-read the names of the transferring specie.
Definition: phaseChange.C:107
virtual bool read(const dictionary &dict)
Read source dictionary.
Definition: phaseChange.C:602
void addSup(const volScalarField &alpha, const volScalarField &rho, const volScalarField &heOrYi, fvMatrix< scalar > &eqn) const
Override the energy equation to add the phase change heat, or.
Definition: phaseChange.C:529
Model for nucleate wall boiling on the surface of a third (solid) phase.
virtual tmp< DimensionedField< scalar, fvMesh > > Lfraction() const
Return the fraction of the latent heat that is transferred into.
virtual void correct()
Correct the fvModel.
virtual tmp< DimensionedField< scalar, fvMesh > > mDot() const
Return the mass transfer rate.
virtual bool read(const dictionary &dict)
Read source dictionary.
virtual tmp< DimensionedField< scalar, fvMesh > > d() const
Return the diameter of nuclei.
virtual tmp< DimensionedField< scalar, fvMesh > > tau() const
Return the nucleation time scale.
void addSup(const volScalarField &alpha, const volScalarField &rho, fvMatrix< scalar > &eqn) const
Override the pressure equation to add the mass transfer rate.
phaseSurfaceBoiling(const word &name, const word &modelType, const fvMesh &mesh, const dictionary &dict)
virtual bool addsSupToField(const word &fieldName) const
Return true if the fvModel adds a source term to the given.
virtual tmp< DimensionedField< scalar, fvMesh > > nDot() const
Return the number rate at which nuclei are generated.
static const dimensionSet dimK
Coefficient dimensions.
static autoPtr< saturationTemperatureModel > New(const word &name, const dictionary &dict)
Select with name within a dictionary.
scalar fieldRelaxationFactor(const word &name) const
Return the relaxation factor for the given field.
Definition: solution.C:174
Abstract base class for run-time selectable region solvers.
Definition: solver.H:56
A class for managing temporary objects.
Definition: tmp.H:55
Template function which returns the un-mangled name of a given type. Useful for types which do not ha...
static autoPtr< departureDiameterModel > New(const dictionary &dict)
Select null constructed.
static autoPtr< departureFrequencyModel > New(const dictionary &dict)
Select null constructed.
static autoPtr< nucleationSiteModel > New(const dictionary &dict)
Select null constructed.
static autoPtr< partitioningModel > New(const dictionary &dict)
Select null constructed.
A class for handling words, derived from string.
Definition: word.H:63
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define NotImplemented
Issue a FatalErrorIn for a function not currently implemented.
Definition: error.H:381
rho
Definition: pEqn.H:1
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
const dimensionSet time
addToRunTimeSelectionTable(fvConstraint, bound, dictionary)
defineTypeNameAndDebug(bound, 0)
const unitSet & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
Namespace for OpenFOAM.
dimensionedScalar exp(const dimensionedScalar &ds)
const dimensionSet & dimless
Definition: dimensions.C:138
dimensionedScalar sign(const dimensionedScalar &ds)
const dimensionSet & dimLength
Definition: dimensions.C:141
const dimensionSet & dimVolume
Definition: dimensions.C:150
labelList second(const UList< labelPair > &p)
Definition: patchToPatch.C:49
dimensionedScalar log(const dimensionedScalar &ds)
labelList first(const UList< labelPair > &p)
Definition: patchToPatch.C:39
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
void inv(pointPatchField< tensor > &, const pointPatchField< tensor > &)
const dimensionSet & dimTime
Definition: dimensions.C:142
VolField< scalar > volScalarField
Definition: volFieldsFwd.H:62
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
const dimensionSet & dimDensity
Definition: dimensions.C:158
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
const dimensionSet & dimEnergy
Definition: dimensions.C:160
const dimensionSet & dimArea
Definition: dimensions.C:149
void sqrt(LagrangianPatchField< scalar > &f, const LagrangianPatchField< scalar > &f1)
const dimensionSet & dimTemperature
Definition: dimensions.C:143
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
labelList f(nPoints)
labelList fv(nPoints)
dictionary dict