ThermalPhaseChangePhaseSystem.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) 2015-2021 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 
28 #include "fvcVolumeIntegrate.H"
29 #include "fvmSup.H"
30 #include "rhoReactionThermo.H"
31 
32 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
33 
34 template<class BasePhaseSystem>
36 (
37  PtrList<volScalarField>& dmdts
38 ) const
39 {
40  forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
41  {
42  const phasePair& pair = this->phasePairs_[dmdtfIter.key()];
43  const volScalarField& dmdtf = *dmdtfIter();
44 
45  addField(pair.phase1(), "dmdt", dmdtf, dmdts);
46  addField(pair.phase2(), "dmdt", - dmdtf, dmdts);
47  }
48 
49  forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
50  {
51  const phasePair& pair = this->phasePairs_[nDmdtfIter.key()];
52  const volScalarField& nDmdtf = *nDmdtfIter();
53 
54  addField(pair.phase1(), "dmdt", nDmdtf, dmdts);
55  addField(pair.phase2(), "dmdt", - nDmdtf, dmdts);
56  }
57 }
58 
59 
60 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
61 
62 template<class BasePhaseSystem>
65 (
66  const fvMesh& mesh
67 )
68 :
69  BasePhaseSystem(mesh),
70  volatile_(this->template lookupOrDefault<word>("volatile", "none")),
71  dmdt0s_(this->phases().size()),
72  pressureImplicit_
73  (
74  this->template lookupOrDefault<Switch>("pressureImplicit", true)
75  )
76 {
77  this->generatePairsAndSubModels
78  (
79  "saturation",
80  saturationModels_
81  );
82 
83  // Check that models have been specified in the correct combinations
85  (
86  saturationModelTable,
87  saturationModels_,
88  saturationModelIter
89  )
90  {
91  const phasePair& pair =
92  this->phasePairs_[saturationModelIter.key()];
93 
94  this->template validateMassTransfer<saturationModel>(pair);
95 
96  if
97  (
98  !this->heatTransferModels_.found(pair)
99  || !this->heatTransferModels_[pair].first().valid()
100  || !this->heatTransferModels_[pair].second().valid()
101  )
102  {
104  << "A heat transfer model for both sides of the " << pair
105  << "pair is not specified. This is required by the "
106  << "corresponding saturation model"
107  << exit(FatalError);
108  }
109  }
110 
111  // Generate interfacial mass transfer fields, initially assumed to be zero
113  (
114  saturationModelTable,
115  saturationModels_,
116  saturationModelIter
117  )
118  {
119  const phasePair& pair =
120  this->phasePairs_[saturationModelIter.key()];
121 
122  dmdtfs_.insert
123  (
124  pair,
125  new volScalarField
126  (
127  IOobject
128  (
130  (
131  "thermalPhaseChange:dmdtf",
132  pair.name()
133  ),
134  this->mesh().time().timeName(),
135  this->mesh(),
138  ),
139  this->mesh(),
141  )
142  );
143 
144  d2mdtdpfs_.insert
145  (
146  pair,
147  new volScalarField
148  (
149  IOobject
150  (
152  (
153  "thermalPhaseChange:d2mdtdpf",
154  pair.name()
155  ),
156  this->mesh().time().timeName(),
157  this->mesh(),
160  ),
161  this->mesh(),
163  )
164  );
165 
166  Tfs_.insert
167  (
168  pair,
169  new volScalarField
170  (
171  IOobject
172  (
174  (
175  "thermalPhaseChange:Tf",
176  pair.name()
177  ),
178  this->mesh().time().timeName(),
179  this->mesh(),
182  ),
183  (pair.phase1().thermo().T() + pair.phase2().thermo().T())/2
184  )
185  );
186 
187  nDmdtfs_.insert
188  (
189  pair,
190  new volScalarField
191  (
192  IOobject
193  (
195  (
196  "thermalPhaseChange:nucleation:dmdtf",
197  pair.name()
198  ),
199  this->mesh().time().timeName(),
200  this->mesh(),
203  ),
204  this->mesh(),
206  )
207  );
208  }
209 }
210 
211 
212 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
213 
214 template<class BasePhaseSystem>
217 {}
218 
219 
220 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
221 
222 template<class BasePhaseSystem>
225 (
226  const phasePairKey& key
227 ) const
228 {
229  return saturationModels_[key];
230 }
231 
232 
233 template<class BasePhaseSystem>
236 (
237  const phasePairKey& key
238 ) const
239 {
240  tmp<volScalarField> tDmdtf = BasePhaseSystem::dmdtf(key);
241 
242  const scalar dmdtfSign =
243  Pair<word>::compare(this->phasePairs_[key], key);
244 
245  if (dmdtfs_.found(key))
246  {
247  tDmdtf.ref() += dmdtfSign**dmdtfs_[key];
248  }
249 
250  if (nDmdtfs_.found(key))
251  {
252  tDmdtf.ref() += dmdtfSign**nDmdtfs_[key];
253  }
254 
255  return tDmdtf;
256 }
257 
258 
259 template<class BasePhaseSystem>
262 {
263  PtrList<volScalarField> dmdts(BasePhaseSystem::dmdts());
264 
265  addDmdts(dmdts);
266 
267  return dmdts;
268 }
269 
270 
271 template<class BasePhaseSystem>
274 {
275  PtrList<volScalarField> d2mdtdps(BasePhaseSystem::d2mdtdps());
276 
277  forAllConstIter(phaseSystem::dmdtfTable, d2mdtdpfs_, d2mdtdpfIter)
278  {
279  const phasePair& pair = this->phasePairs_[d2mdtdpfIter.key()];
280  const volScalarField& d2mdtdpf = *d2mdtdpfIter();
281 
282  addField(pair.phase1(), "d2mdtdp", d2mdtdpf, d2mdtdps);
283  addField(pair.phase2(), "d2mdtdp", - d2mdtdpf, d2mdtdps);
284  }
285 
286  return d2mdtdps;
287 }
288 
289 
290 template<class BasePhaseSystem>
294 {
295  autoPtr<phaseSystem::momentumTransferTable> eqnsPtr =
297 
298  phaseSystem::momentumTransferTable& eqns = eqnsPtr();
299 
300  this->addDmdtUfs(dmdtfs_, eqns);
301  this->addDmdtUfs(nDmdtfs_, eqns);
302 
303  return eqnsPtr;
304 }
305 
306 
307 template<class BasePhaseSystem>
311 {
312  autoPtr<phaseSystem::momentumTransferTable> eqnsPtr =
313  BasePhaseSystem::momentumTransferf();
314 
315  phaseSystem::momentumTransferTable& eqns = eqnsPtr();
316 
317  this->addDmdtUfs(dmdtfs_, eqns);
318  this->addDmdtUfs(nDmdtfs_, eqns);
319 
320  return eqnsPtr;
321 }
322 
323 
324 template<class BasePhaseSystem>
327 {
328  autoPtr<phaseSystem::heatTransferTable> eqnsPtr =
329  BasePhaseSystem::heatTransfer();
330 
331  phaseSystem::heatTransferTable& eqns = eqnsPtr();
332 
333  // Create temperatures at which to evaluate nucleation mass transfers
335  forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
336  {
337  const phasePair& pair = this->phasePairs_[nDmdtfIter.key()];
338  const saturationModel& satModel = this->saturation(nDmdtfIter.key());
339  Tns.insert(pair, satModel.Tsat(pair.phase1().thermo().p()).ptr());
340  }
341 
342  // Mass transfer terms
343  if (volatile_ != "none")
344  {
345  {
346  phaseSystem::dmidtfTable dmidtfs;
347 
348  forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
349  {
350  const phasePair& pair = this->phasePairs_[dmdtfIter.key()];
351  const volScalarField& dmdtf = *dmdtfIter();
352 
353  dmidtfs.insert(pair, new HashPtrTable<volScalarField>());
354  dmidtfs[pair]->insert(volatile_, new volScalarField(dmdtf));
355  }
356 
357  this->addDmidtHefs
358  (
359  dmidtfs,
360  Tfs_,
361  latentHeatScheme::upwind,
362  latentHeatTransfer::heat,
363  eqns
364  );
365  }
366  {
367  phaseSystem::dmidtfTable nDmidtfs;
368 
369  forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
370  {
371  const phasePair& pair = this->phasePairs_[nDmdtfIter.key()];
372  const volScalarField& nDmdtf = *nDmdtfIter();
373 
374  nDmidtfs.insert(pair, new HashPtrTable<volScalarField>());
375  nDmidtfs[pair]->insert(volatile_, new volScalarField(nDmdtf));
376  }
377 
378  this->addDmidtHefs
379  (
380  nDmidtfs,
381  Tns,
382  0,
383  latentHeatScheme::upwind,
384  eqns
385  );
386  }
387  }
388  else
389  {
390  this->addDmdtHefs
391  (
392  dmdtfs_,
393  Tfs_,
394  latentHeatScheme::upwind,
395  latentHeatTransfer::heat,
396  eqns
397  );
398  this->addDmdtHefs
399  (
400  nDmdtfs_,
401  Tns,
402  0,
403  latentHeatScheme::upwind,
404  eqns
405  );
406  }
407 
408  // Lagging
409  {
410  PtrList<volScalarField> dmdts(this->phases().size());
411 
412  addDmdts(dmdts);
413 
415  {
416  const phaseModel& phase = phaseIter();
417 
418  if (dmdt0s_.set(phase.index()))
419  {
420  *eqns[phase.name()] +=
421  fvm::Sp
422  (
423  dmdt0s_[phase.index()] - dmdts[phase.index()],
424  phase.thermo().he()
425  );
426  }
427  }
428  }
429 
430  return eqnsPtr;
431 }
432 
433 
434 template<class BasePhaseSystem>
437 {
438  autoPtr<phaseSystem::specieTransferTable> eqnsPtr =
440 
441  phaseSystem::specieTransferTable& eqns = eqnsPtr();
442 
443  if (volatile_ != "none")
444  {
445  {
446  phaseSystem::dmidtfTable dmidtfs;
447 
448  forAllConstIter(phaseSystem::dmdtfTable, dmdtfs_, dmdtfIter)
449  {
450  const phasePair& pair = this->phasePairs_[dmdtfIter.key()];
451  const volScalarField& dmdtf = *dmdtfIter();
452 
453  dmidtfs.insert(pair, new HashPtrTable<volScalarField>());
454  dmidtfs[pair]->insert(volatile_, new volScalarField(dmdtf));
455  }
456 
457  this->addDmidtYf(dmidtfs, eqns);
458  }
459 
460  {
461  phaseSystem::dmidtfTable nDmidtfs;
462 
463  forAllConstIter(phaseSystem::dmdtfTable, nDmdtfs_, nDmdtfIter)
464  {
465  const phasePair& pair = this->phasePairs_[nDmdtfIter.key()];
466  const volScalarField& nDmdtf = *nDmdtfIter();
467 
468  nDmidtfs.insert(pair, new HashPtrTable<volScalarField>());
469  nDmidtfs[pair]->insert(volatile_, new volScalarField(nDmdtf));
470  }
471 
472  this->addDmidtYf(nDmidtfs, eqns);
473  }
474  }
475  else
476  {
477  this->addDmdtYfs(dmdtfs_, eqns);
478  this->addDmdtYfs(nDmdtfs_, eqns);
479  }
480 
481  return eqnsPtr;
482 }
483 
484 
485 template<class BasePhaseSystem>
488 {
489  dmdt0s_ = PtrList<volScalarField>(this->phases().size());
490 
491  addDmdts(dmdt0s_);
492 
493  BasePhaseSystem::correctContinuityError();
494 }
495 
496 
497 template<class BasePhaseSystem>
498 void
500 {
501  typedef compressible::alphatPhaseChangeWallFunctionFvPatchScalarField
502  alphatPhaseChangeWallFunction;
503 
505  (
506  saturationModelTable,
507  saturationModels_,
508  saturationModelIter
509  )
510  {
511  const phasePair& pair = this->phasePairs_[saturationModelIter.key()];
512 
513  const phaseModel& phase1 = pair.phase1();
514  const phaseModel& phase2 = pair.phase2();
515  const rhoThermo& thermo1 = phase1.thermo();
516  const rhoThermo& thermo2 = phase2.thermo();
517  const volScalarField& T1(thermo1.T());
518  const volScalarField& T2(thermo2.T());
519 
520  // Interfacial mass transfer update
521  {
522  volScalarField& dmdtf(*this->dmdtfs_[pair]);
523  volScalarField& Tf(*this->Tfs_[pair]);
524 
525  const volScalarField Tsat(saturationModelIter()->Tsat(thermo1.p()));
526 
527  const volScalarField L
528  (
529  volatile_ != "none"
530  ? this->Li(pair, volatile_, dmdtf, Tsat, latentHeatScheme::upwind)
531  : this->L(pair, dmdtf, Tsat, latentHeatScheme::upwind)
532  );
533 
534  volScalarField H1(this->heatTransferModels_[pair].first()->K(0));
535  volScalarField H2(this->heatTransferModels_[pair].second()->K(0));
536 
537  volScalarField dmdtfNew((H1*(Tsat - T1) + H2*(Tsat - T2))/L);
538 
539  if (volatile_ != "none")
540  {
541  dmdtfNew *=
542  neg0(dmdtfNew)*phase1.Y(volatile_)
543  + pos(dmdtfNew)*phase2.Y(volatile_);
544  }
545 
546  if (pressureImplicit_)
547  {
548  volScalarField& d2mdtdpf(*this->d2mdtdpfs_[pair]);
549 
550  const dimensionedScalar dp(rootSmall*thermo1.p().average());
551 
552  const volScalarField dTsatdp
553  (
554  (
555  saturationModelIter()->Tsat(thermo1.p() + dp/2)
556  - saturationModelIter()->Tsat(thermo1.p() - dp/2)
557  )/dp
558  );
559 
560  d2mdtdpf = (H1 + H2)*dTsatdp/L;
561 
562  if (volatile_ != "none")
563  {
564  d2mdtdpf *=
565  neg0(dmdtfNew)*phase1.Y(volatile_)
566  + pos(dmdtfNew)*phase2.Y(volatile_);
567  }
568  }
569 
570  H1 = this->heatTransferModels_[pair].first()->K();
571  H2 = this->heatTransferModels_[pair].second()->K();
572 
573  // Limit the H[12] to avoid /0
574  H1.max(small);
575  H2.max(small);
576 
577  Tf = (H1*T1 + H2*T2 + dmdtfNew*L)/(H1 + H2);
578 
579  Info<< Tf.name()
580  << ": min = " << min(Tf.primitiveField())
581  << ", mean = " << average(Tf.primitiveField())
582  << ", max = " << max(Tf.primitiveField())
583  << endl;
584 
585  const scalar dmdtfRelax =
586  this->mesh().fieldRelaxationFactor(dmdtf.member());
587 
588  dmdtf = (1 - dmdtfRelax)*dmdtf + dmdtfRelax*dmdtfNew;
589 
590  Info<< dmdtf.name()
591  << ": min = " << min(dmdtf.primitiveField())
592  << ", mean = " << average(dmdtf.primitiveField())
593  << ", max = " << max(dmdtf.primitiveField())
594  << ", integral = " << fvc::domainIntegrate(dmdtf).value()
595  << endl;
596  }
597 
598  // Nucleation mass transfer update
599  {
600  volScalarField& nDmdtf(*this->nDmdtfs_[pair]);
601 
602  nDmdtf = Zero;
603 
604  bool wallBoilingActive = false;
605 
606  forAllConstIter(phasePair, pair, pairIter)
607  {
608  const phaseModel& phase = pairIter();
609 
610  const word alphatName =
611  IOobject::groupName("alphat", phase.name());
612 
613  if (phase.mesh().foundObject<volScalarField>(alphatName))
614  {
615  const volScalarField& alphat =
616  phase.mesh().lookupObject<volScalarField>(alphatName);
617 
618  forAll(alphat.boundaryField(), patchi)
619  {
620  const fvPatchScalarField& alphatp =
621  alphat.boundaryField()[patchi];
622 
623  if (isA<alphatPhaseChangeWallFunction>(alphatp))
624  {
625  const alphatPhaseChangeWallFunction& alphatw =
626  refCast<const alphatPhaseChangeWallFunction>
627  (
628  alphatp
629  );
630 
631  if (alphatw.activePhasePair(pair))
632  {
633  wallBoilingActive = true;
634 
635  const scalarField& patchDmdtf =
636  alphatw.dmdtf(pair);
637 
638  const scalar sign =
639  pairIter.index() == 0 ? +1 : -1;
640 
641  forAll(patchDmdtf, facei)
642  {
643  const label celli =
644  alphatw.patch().faceCells()[facei];
645  nDmdtf[celli] -= sign*patchDmdtf[facei];
646  }
647  }
648  }
649  }
650  }
651  }
652 
653  if (wallBoilingActive)
654  {
655  Info<< nDmdtf.name()
656  << ": min = " << min(nDmdtf.primitiveField())
657  << ", mean = " << average(nDmdtf.primitiveField())
658  << ", max = " << max(nDmdtf.primitiveField())
659  << ", integral = " << fvc::domainIntegrate(nDmdtf).value()
660  << endl;
661  }
662  }
663  }
664 }
665 
666 
667 template<class BasePhaseSystem>
669 {
670  if (BasePhaseSystem::read())
671  {
672  bool readOK = true;
673 
674  // Models ...
675 
676  return readOK;
677  }
678  else
679  {
680  return false;
681  }
682 }
683 
684 
685 // ************************************************************************* //
dimensionedScalar sign(const dimensionedScalar &ds)
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
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
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
error FatalError
virtual PtrList< volScalarField > dmdts() const
Return the mass transfer rates for each phase.
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:323
ThermalPhaseChangePhaseSystem(const fvMesh &)
Construct from fvMesh.
HashPtrTable< fvVectorMatrix > momentumTransferTable
Definition: phaseSystem.H:78
const dimensionSet dimPressure
static int compare(const Pair< Type > &a, const Pair< Type > &b)
Compare Pairs.
Definition: Pair.H:153
virtual autoPtr< phaseSystem::specieTransferTable > specieTransfer() const
Return the specie transfer matrices.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
virtual PtrList< volScalarField > dmdts() const
Return the mass transfer rates for each phase.
HashPtrTable< fvScalarMatrix > heatTransferTable
Definition: phaseSystem.H:80
dimensioned< Type > domainIntegrate(const GeometricField< Type, fvPatchField, volMesh > &vf)
HashPtrTable< fvScalarMatrix > specieTransferTable
Definition: phaseSystem.H:82
void read(Istream &, label &, const dictionary &)
In-place read with dictionary lookup.
const saturationModel & saturation(const phasePairKey &key) const
Return the saturationModel.
tmp< fvMatrix< Type > > Sp(const volScalarField::Internal &, const GeometricField< Type, fvPatchField, volMesh > &)
CGAL::Exact_predicates_exact_constructions_kernel K
Class to provide interfacial heat and mass transfer between a number of phases according the interfac...
virtual tmp< volScalarField > dmdtf(const phasePairKey &key) const
Return the mass transfer rate for an interface.
HashPtrTable< HashPtrTable< volScalarField >, phasePairKey, phasePairKey::hash > dmidtfTable
Definition: phaseSystem.H:103
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:57
dimensionedScalar pos(const dimensionedScalar &ds)
const dimensionSet dimTime
virtual PtrList< volScalarField > d2mdtdps() const
Return the mass transfer linearisation coeffs for each phase.
virtual void correctInterfaceThermo()
Correct the interface thermodynamics.
dynamicFvMesh & mesh
bool found(const Key &) const
Return true if hashedEntry is found in table.
Definition: HashTable.C:113
fvPatchField< scalar > fvPatchScalarField
virtual tmp< volScalarField > dmdtf(const phasePairKey &key) const
Return the mass transfer rate for an interface.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
static word groupName(Name name, const word &group)
HashPtrTable< volScalarField, phasePairKey, phasePairKey::hash > dmdtfTable
Definition: phaseSystem.H:94
const dimensionSet dimDensity
dimensionedScalar neg0(const dimensionedScalar &ds)
PtrListDictionary< phaseModel > phaseModelList
Definition: phaseSystem.H:84
virtual autoPtr< phaseSystem::heatTransferTable > heatTransfer() const
Return the heat transfer matrices.
virtual autoPtr< phaseSystem::momentumTransferTable > momentumTransferf()
Return the momentum transfer matrices for the face-based algorithm.
Volume integrate volField creating a volField.
static const zero Zero
Definition: zero.H:97
dimensioned< Type > average(const DimensionedField< Type, GeoMesh > &df)
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
phaseSystem::specieTransferTable & specieTransfer(specieTransferPtr())
const Mesh & mesh() const
Return mesh.
virtual bool read()
Read base phaseProperties dictionary.
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
phaseSystem::momentumTransferTable & momentumTransfer(momentumTransferPtr())
label patchi
virtual ~ThermalPhaseChangePhaseSystem()
Destructor.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
A templated 1D list of pointers to objects of type <T>, where the size of the array is known and used...
Definition: List.H:70
virtual void correctContinuityError()
Store phase dmdts at the during the continuity error update.
phaseSystem::phaseModelList & phases
Definition: createFields.H:12
messageStream Info
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:52
A class for managing temporary objects.
Definition: PtrList.H:53
virtual autoPtr< phaseSystem::momentumTransferTable > momentumTransfer()
Return the momentum transfer matrices for the cell-based algorithm.
Calculate the matrix for implicit and explicit sources.
PtrList< volScalarField > d2mdtdps(fluid.d2mdtdps())