phaseSurfaceCondensation.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) 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 
27 #include "multiphaseEuler.H"
30 #include "diameterModel.H"
35 
36 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
37 
38 namespace Foam
39 {
40 namespace fv
41 {
44 }
45 }
46 
47 
48 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
49 
50 void Foam::fv::phaseSurfaceCondensation::readCoeffs(const dictionary& dict)
51 {
53 
54  const dictionary& diffusiveMassTransferDict =
55  dict.subDict("diffusiveMassTransfer");
56 
57  checkBlendedInterfacialModelsDict<blendedSidedDiffusiveMassTransferModel>
58  (
59  fluid_,
60  diffusiveMassTransferDict
61  );
62 
63  const phaseInterface interface(vapour_, solid_);
64 
65  diffusiveMassTransferModel_.reset
66  (
68  (
69  modelSubDicts(diffusiveMassTransferDict),
70  interface,
71  blendingDict<blendedSidedDiffusiveMassTransferModel>
72  (
73  fluid_,
74  diffusiveMassTransferDict
75  )
76  ).ptr()
77  );
78 
79  saturationModelPtr_.reset
80  (
82  (
83  "saturationPressure",
84  dict
85  ).ptr()
86  );
87 
88  specieSemiImplicit_ =
89  dict.lookupOrDefault<bool>("specieSemiImplicit", false);
90 }
91 
92 
93 void Foam::fv::phaseSurfaceCondensation::correctMDot() const
94 {
95  Info<< type() << ": " << name() << endl << incrIndent;
96 
97  static const dimensionedScalar rootVSmallH
98  (
100  rootVSmall
101  );
102 
103  const rhoThermo& solidThermo = solid_.thermo();
104  const volScalarField::Internal& solidT = solidThermo.T();
105 
106  const fluidMulticomponentThermo& vapourThermo =
107  fluidMulticomponentThermos(true, false)[0];
108  const volScalarField::Internal& vapourT = vapourThermo.T();
109 
110  const label speciei = specieis()[0];
111 
112  const Pair<tmp<volScalarField>> Hs =
113  solver_.heatTransfer.Hs(vapour_, solid_);
114  const volScalarField::Internal& vapourH = Hs.first();
115  const volScalarField::Internal& solidH = Hs.second();
116  const volScalarField::Internal Tsurface
117  (
118  (solidH*solidT + vapourH*vapourT + q_)
119  /max(solidH + vapourH, rootVSmallH)
120  );
121 
122  const volScalarField::Internal pSat
123  (
124  saturationModelPtr_->pSat(Tsurface)
125  );
126 
127  const volScalarField::Internal L(this->L(Tsurface));
128 
129  const fluidThermophysicalTransportModel& ttmVapour =
130  mesh().lookupType<fluidThermophysicalTransportModel>
131  (
132  vapour_.name()
133  );
134 
135  const volScalarField::Internal freeSurf(vapour_/(1 - solid_));
136 
137  const volScalarField::Internal xc
138  (
139  vapour_.Y(species()[0])/vapourThermo.Wi(speciei)*vapourThermo.W()
140  );
141 
142  const volScalarField::Internal xw(pSat/vapourThermo.p()());
143 
144  // Optional relaxation factor
145  const scalar f = mesh().solution().fieldRelaxationFactor(mDot_.member());
146 
147  mDot_ =
148  (1 - f)*mDot_
149  - f*freeSurf*vapourThermo.Wi(speciei)/vapourThermo.W()()
150  *diffusiveMassTransferModel_->KinThe(vapour_)()
151  *ttmVapour.D(vapour_.Y(species()[0]))()
152  *log(max(1 - xc, scalar(0.001))/max(1 - xw, scalar(0.001)));
153  mDot_.max(scalar(0));
154 
155  infoField("mDot", mDot_);
156 
157  if (specieSemiImplicit_)
158  {
159  mDotDy_ =
160  (1 - f)*mDotDy_
161  + f*freeSurf
162  *diffusiveMassTransferModel_->KinThe(vapour_)()
163  *ttmVapour.D(vapour_.Y(species()[0]))()
164  /(1 - min(xc, scalar(0.999)))*pos(xc - xw);
165  }
166  else
167  {
168  mDotDy_ = Zero;
169  }
170 
171  // Heat flux
172  q_ = mDot_*L;
173 
174  Info<< decrIndent;
175 }
176 
177 
178 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
179 
181 (
182  const word& name,
183  const word& modelType,
184  const fvMesh& mesh,
185  const dictionary& dict
186 )
187 :
189  (
190  name,
191  modelType,
192  mesh,
193  dict,
194  readSpecie(coeffs(modelType, dict), false)
195  ),
196  solver_(mesh().lookupObject<solvers::multiphaseEuler>(solver::typeName)),
197  fluid_(solver_.fluid),
198  liquid_(fluid_.phases()[phaseNames().second()]),
199  vapour_(fluid_.phases()[phaseNames().first()]),
200  solid_(fluid_.phases()[dict.lookup("phase")]),
201  diffusiveMassTransferModel_(nullptr),
202  saturationModelPtr_(nullptr),
203  pressureEquationIndex_(-1),
204  specieSemiImplicit_(false),
205  q_
206  (
207  IOobject
208  (
209  name + ":q",
210  mesh.time().name(),
211  mesh,
212  IOobject::NO_READ,
213  IOobject::NO_WRITE
214  ),
215  mesh,
217  ),
218  mDot_
219  (
220  IOobject
221  (
222  name + ":mDot",
223  mesh.time().name(),
224  mesh,
225  IOobject::READ_IF_PRESENT,
226  IOobject::AUTO_WRITE
227  ),
228  mesh,
230  ),
231  mDotDy_
232  (
233  IOobject
234  (
235  name + ":mDotDy",
236  mesh.time().name(),
237  mesh,
238  IOobject::READ_IF_PRESENT,
239  IOobject::AUTO_WRITE
240  ),
241  mesh,
243  )
244 {
245  readCoeffs(coeffs(dict));
246 }
247 
248 
249 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
250 
251 bool
253 {
254  return
255  phaseChange::addsSupToField(fieldName)
256  || fieldName == solid_.thermo().he().name();
257 }
258 
259 
262 {
263  // Put all the latent heat into the vapour, additional source term
264  // will transfer this to solid phase in addSup
265  return
267  (
268  name() + ":Lfraction",
269  mesh(),
270  dimensionedScalar(dimless, scalar(0))
271  );
272 }
273 
274 
277 {
278  return mDot_;
279 }
280 
281 
284 {
285  return mDotDy_;
286 }
287 
288 
290 (
291  const volScalarField& alpha,
292  const volScalarField& rho,
293  fvMatrix<scalar>& eqn
294 ) const
295 {
296  // Pressure equation (i.e., continuity, linearised in the pressure)
297  if
298  (
299  (&alpha == &liquid_ || &alpha == &vapour_)
300  && (&rho == &liquid_.rho() || &rho == &vapour_.rho())
301  && &eqn.psi() == &solver_.p_rgh
302  )
303  {
304  // Ensure that the source is up-to date if this is the first call in
305  // the current phase loop
306  if (pressureEquationIndex_ % 2 == 0) correctMDot();
307  pressureEquationIndex_ ++;
308  }
309 
310  // Let the base class add the actual source
312 }
313 
314 
316 (
317  const volScalarField& alpha,
318  const volScalarField& rho,
319  const volScalarField& heOrYi,
320  fvMatrix<scalar>& eqn
321 ) const
322 {
323  // Vapour or solid energy equation. Apply the additional explicit latent
324  // heat transfers.
325  if (&heOrYi == &vapour_.thermo().he() || &heOrYi == &solid_.thermo().he())
326  {
327  const scalar sign = &heOrYi == &solid_.thermo().he() ? -1 : +1;
328 
329  eqn += sign*q_;
330  }
331 
332  // For solid nothing more is needed
333  if (&heOrYi == &solid_.thermo().he())
334  {
335  return;
336  }
337 
338  const label i = this->index(phaseNames(), alpha.group());
339 
340  // If implicit treatment is not needed or this is liquid, use normal addSup
341  if (!specieSemiImplicit_ || i != 0)
342  {
343  return phaseChange::addSup(alpha, rho, heOrYi, eqn);
344  }
345 
346  const label s = this->sign(phaseNames(), alpha.group());
347 
349  fluidMulticomponentThermos(true, false)[0];
350 
351  const word specieName = heOrYi.member();
352 
353  // Mass fraction equation
354  if (thermo.containsSpecie(specieName))
355  {
356  // A non-transferring specie. Do not add a source.
357  if (!species().found(specieName)) return;
358 
359  // The transferring specie. Add a linearised source.
360  tmp<volScalarField::Internal> tmDot = this->mDot();
361  tmp<volScalarField::Internal> tmDotDy = this->mDotDy();
362 
363  eqn += s*(tmDot() + correction(fvm::Sp(tmDotDy, eqn.psi())));
364 
365  return;
366  }
367 
368  // Something else. Fall back.
369  phaseChange::addSup(alpha, rho, heOrYi, eqn);
370 }
371 
372 
374 {
375  // Reset the p_rgh equation solution counter
376  pressureEquationIndex_ = 0;
377 
378  // Correct the total phase change rate
379  correctMDot();
380 }
381 
382 
384 {
385  if (phaseChange::read(dict))
386  {
387  readCoeffs(coeffs(dict));
388  return true;
389  }
390  else
391  {
392  return false;
393  }
394 }
395 
396 
397 // ************************************************************************* //
bool found
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
static word member(const word &name)
Return member (name without the extension)
Definition: IOobject.C:146
static autoPtr< blendedSidedDiffusiveMassTransferModel > New(const UPtrList< const dictionary > &subDicts, const phaseInterface &interface, const dictionary &blendingDict)
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
Base-class for multi-component fluid thermodynamic properties.
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
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 mass diffusion limited condensation 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 > > mDotDy() const
Return the derivative of mass transfer rate.
virtual tmp< DimensionedField< scalar, fvMesh > > mDot() const
Return the mass transfer rate.
virtual bool read(const dictionary &dict)
Read source dictionary.
phaseSurfaceCondensation(const word &name, const word &modelType, const fvMesh &mesh, const dictionary &dict)
void addSup(const volScalarField &alpha, const volScalarField &rho, fvMatrix< scalar > &eqn) const
Override the pressure equation to add the mass transfer rate.
virtual bool addsSupToField(const word &fieldName) const
Return true if the fvModel adds a source term to the given.
static const dimensionSet dimK
Coefficient dimensions.
const Type & lookupType(const word &group=word::null) const
Lookup and return the object of the given Type.
static autoPtr< saturationPressureModel > 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...
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)
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), lagrangian::cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
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)
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const VolField< Type > &)
const unitSet & lookup(const word &unitName)
Lookup and return the named unit from the table.
Definition: units.C:346
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
dimensionedScalar pos(const dimensionedScalar &ds)
const dimensionSet & dimless
Definition: dimensions.C:138
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:272
dimensionedScalar sign(const dimensionedScalar &ds)
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
UPtrList< const dictionary > modelSubDicts(const dictionary &dict, const wordHashSet &ignoreKeys=wordHashSet())
messageStream Info
const dimensionSet & dimVolume
Definition: dimensions.C:150
labelList second(const UList< labelPair > &p)
Definition: patchToPatch.C:49
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:265
dimensionedScalar log(const dimensionedScalar &ds)
labelList first(const UList< labelPair > &p)
Definition: patchToPatch.C:39
const dimensionSet & dimTime
Definition: dimensions.C:142
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
const dimensionSet & dimDensity
Definition: dimensions.C:158
tmp< fvMatrix< Type > > correction(const fvMatrix< Type > &)
Return the correction form of the given matrix.
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
const dimensionSet & dimEnergy
Definition: dimensions.C:160
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
labelList f(nPoints)
labelList fv(nPoints)
dictionary dict
fluidMulticomponentThermo & thermo
Definition: createFields.H:15