1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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.
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
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/>.
24 \*---------------------------------------------------------------------------*/
26 #include "kinematicSingleLayer.H"
29 #include "fvcLaplacian.H"
30 #include "fvcSnGrad.H"
31 #include "fvcReconstruct.H"
32 #include "fvcVolumeIntegrate.H"
33 #include "addToRunTimeSelectionTable.H"
34 #include "directMappedWallPolyPatch.H"
35 #include "mapDistribute.H"
37 #include "cachedRandom.H"
39 #include "mathematicalConstants.H"
41 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
45 namespace regionModels
47 namespace surfaceFilmModels
50 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
52 defineTypeNameAndDebug(kinematicSingleLayer, 0);
54 addToRunTimeSelectionTable(surfaceFilmModel, kinematicSingleLayer, mesh);
56 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
58 bool kinematicSingleLayer::read()
60 if (surfaceFilmModel::read())
62 const dictionary& solution = this->solution().subDict("PISO");
63 solution.lookup("momentumPredictor") >> momentumPredictor_;
64 solution.lookup("nOuterCorr") >> nOuterCorr_;
65 solution.lookup("nCorr") >> nCorr_;
66 solution.lookup("nNonOrthCorr") >> nNonOrthCorr_;
77 void kinematicSingleLayer::correctThermoFields()
79 if (thermoModel_ == tmConstant)
81 const dictionary& constDict(coeffs_.subDict("constantThermoCoeffs"));
82 rho_ == dimensionedScalar(constDict.lookup("rho0"));
83 mu_ == dimensionedScalar(constDict.lookup("mu0"));
84 sigma_ == dimensionedScalar(constDict.lookup("sigma0"));
90 "void Foam::surfaceFilmModels::kinematicSingleLayer::"
92 ) << "Kinematic surface film must use "
93 << thermoModelTypeNames_[thermoModel_] << "thermodynamics" << endl;
98 void kinematicSingleLayer::resetPrimaryRegionSourceTerms()
102 Info<< "kinematicSingleLayer::resetPrimaryRegionSourceTerms()" << endl;
105 rhoSpPrimary_ == dimensionedScalar("zero", rhoSp_.dimensions(), 0.0);
106 USpPrimary_ == dimensionedVector("zero", USp_.dimensions(), vector::zero);
107 pSpPrimary_ == dimensionedScalar("zero", pSp_.dimensions(), 0.0);
111 void kinematicSingleLayer::transferPrimaryRegionThermoFields()
115 Info<< "kinematicSingleLayer::"
116 << "transferPrimaryRegionThermoFields()" << endl;
118 // Update fields from primary region via direct mapped
119 // (coupled) boundary conditions
120 UPrimary_.correctBoundaryConditions();
121 pPrimary_.correctBoundaryConditions();
122 rhoPrimary_.correctBoundaryConditions();
123 muPrimary_.correctBoundaryConditions();
127 void kinematicSingleLayer::transferPrimaryRegionSourceFields()
131 Info<< "kinematicSingleLayer::"
132 << "transferPrimaryRegionSourceFields()" << endl;
135 // Retrieve the source fields from the primary region via direct mapped
136 // (coupled) boundary conditions
137 // - fields require transfer of values for both patch AND to push the
138 // values into the first layer of internal cells
139 rhoSp_.correctBoundaryConditions();
140 USp_.correctBoundaryConditions();
141 pSp_.correctBoundaryConditions();
143 // Convert accummulated source terms into per unit area per unit time
144 // Note: boundary values will still have original (neat) values
145 const scalar deltaT = time_.deltaTValue();
146 rhoSp_.field() /= magSf()*deltaT;
147 USp_.field() /= magSf()*deltaT;
148 pSp_.field() /= magSf()*deltaT;
152 tmp<volScalarField> kinematicSingleLayer::pu()
154 return tmp<volScalarField>
166 pPrimary_ // pressure (mapped from primary region)
167 - pSp_ // accumulated particle impingement
168 - fvc::laplacian(sigma_, delta_) // surface tension
174 tmp<volScalarField> kinematicSingleLayer::pp()
176 return tmp<volScalarField>
188 -rho_*gNormClipped() // hydrostatic effect only
194 void kinematicSingleLayer::updateSubmodels()
198 Info<< "kinematicSingleLayer::updateSubmodels()" << endl;
201 // Update injection model - mass returned is mass available for injection
202 injection_.correct(availableMass_, cloudMassTrans_, cloudDiameterTrans_);
204 // Update source fields
205 const dimensionedScalar deltaT = time().deltaT();
206 rhoSp_ += cloudMassTrans_/magSf()/deltaT;
210 void kinematicSingleLayer::continuityCheck()
212 const volScalarField deltaRho0(deltaRho_);
218 const volScalarField mass(deltaRho_*magSf());
219 const dimensionedScalar totalMass =
220 fvc::domainIntegrate(mass)
221 + dimensionedScalar("SMALL", dimMass*dimVolume, ROOTVSMALL);
223 const scalar sumLocalContErr =
225 fvc::domainIntegrate(mag(mass - magSf()*deltaRho0))/totalMass
228 const scalar globalContErr =
230 fvc::domainIntegrate(mass - magSf()*deltaRho0)/totalMass
233 cumulativeContErr_ += globalContErr;
235 Info<< "Surface film: " << type() << nl
236 << " time step continuity errors: sum local = "
237 << sumLocalContErr << ", global = " << globalContErr
238 << ", cumulative = " << cumulativeContErr_ << endl;
243 void kinematicSingleLayer::solveContinuity()
247 Info<< "kinematicSingleLayer::solveContinuity()" << endl;
260 void kinematicSingleLayer::updateSurfaceVelocities()
262 // Push boundary film velocity values into internal field
263 for (label i=0; i<intCoupledPatchIDs_.size(); i++)
265 label patchI = intCoupledPatchIDs_[i];
266 const polyPatch& pp = regionMesh().boundaryMesh()[patchI];
267 UIndirectList<vector>(Uw_, pp.faceCells()) =
268 U_.boundaryField()[patchI];
270 Uw_ -= nHat()*(Uw_ & nHat());
271 Uw_.correctBoundaryConditions();
273 // TODO: apply quadratic profile to determine surface velocity
275 Us_.correctBoundaryConditions();
279 tmp<Foam::fvVectorMatrix> kinematicSingleLayer::solveMomentum
281 const volScalarField& pu,
282 const volScalarField& pp
287 Info<< "kinematicSingleLayer::solveMomentum()" << endl;
290 updateSurfaceVelocities();
293 tmp<fvVectorMatrix> tUEqn
295 fvm::ddt(deltaRho_, U_)
299 - fvm::SuSp(rhoSp_, U_)
300 + forces_.correct(U_)
303 fvVectorMatrix& UEqn = tUEqn();
307 if (momentumPredictor_)
315 - fvc::interpolate(delta_)
319 fvc::snGrad(pu, "snGrad(p)")
320 + fvc::snGrad(pp, "snGrad(p)")*fvc::interpolate(delta_)
321 + fvc::snGrad(delta_)*fvc::interpolate(pp)
323 - (fvc::interpolate(rho_*gTan()) & regionMesh().Sf())
328 // Remove any patch-normal components of velocity
329 U_ -= nHat()*(nHat() & U_);
330 U_.correctBoundaryConditions();
337 void kinematicSingleLayer::solveThickness
339 const volScalarField& pu,
340 const volScalarField& pp,
341 const fvVectorMatrix& UEqn
346 Info<< "kinematicSingleLayer::solveThickness()" << endl;
349 volScalarField rUA(1.0/UEqn.A());
352 surfaceScalarField deltarUAf(fvc::interpolate(delta_*rUA));
353 surfaceScalarField rhof(fvc::interpolate(rho_));
355 surfaceScalarField phiAdd
360 fvc::snGrad(pu, "snGrad(p)")
361 + fvc::snGrad(pp, "snGrad(p)")*fvc::interpolate(delta_)
363 - (fvc::interpolate(rho_*gTan()) & regionMesh().Sf())
365 constrainFilmField(phiAdd, 0.0);
367 surfaceScalarField phid
370 (fvc::interpolate(U_*rho_) & regionMesh().Sf())
371 - deltarUAf*phiAdd*rhof
373 constrainFilmField(phid, 0.0);
375 surfaceScalarField ddrhorUAppf
378 fvc::interpolate(delta_)*deltarUAf*rhof*fvc::interpolate(pp)
380 // constrainFilmField(ddrhorUAppf, 0.0);
382 for (int nonOrth=0; nonOrth<=nNonOrthCorr_; nonOrth++)
384 // Film thickness equation
385 fvScalarMatrix deltaEqn
387 fvm::ddt(rho_, delta_)
388 + fvm::div(phid, delta_)
389 - fvm::laplacian(ddrhorUAppf, delta_)
396 if (nonOrth == nNonOrthCorr_)
400 * fvc::snGrad(delta_)
401 * regionMesh().magSf();
403 phi_ == deltaEqn.flux();
407 // Bound film thickness by a minimum of zero
411 U_ -= fvc::reconstruct(deltarUAf*phiAdd);
413 // Remove any patch-normal components of velocity
414 U_ -= nHat()*(nHat() & U_);
416 U_.correctBoundaryConditions();
423 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
425 kinematicSingleLayer::kinematicSingleLayer
427 const word& modelType,
429 const dimensionedVector& g,
430 const bool readFields
433 surfaceFilmModel(modelType, mesh, g),
435 momentumPredictor_(solution().subDict("PISO").lookup("momentumPredictor")),
436 nOuterCorr_(readLabel(solution().subDict("PISO").lookup("nOuterCorr"))),
437 nCorr_(readLabel(solution().subDict("PISO").lookup("nCorr"))),
440 readLabel(solution().subDict("PISO").lookup("nNonOrthCorr"))
443 cumulativeContErr_(0.0),
456 dimensionedScalar("zero", dimDensity, 0.0),
457 zeroGradientFvPatchScalarField::typeName
470 dimensionedScalar("zero", dimPressure*dimTime, 0.0),
471 zeroGradientFvPatchScalarField::typeName
484 dimensionedScalar("zero", dimMass/sqr(dimTime), 0.0),
485 zeroGradientFvPatchScalarField::typeName
523 zeroGradientFvPatchScalarField::typeName
536 zeroGradientFvPatchScalarField::typeName
542 delta_.name() + "*" + rho_.name(),
549 dimensionedScalar("zero", delta_.dimensions()*rho_.dimensions(), 0.0),
550 zeroGradientFvPatchScalarField::typeName
560 IOobject::READ_IF_PRESENT,
564 dimLength*dimMass/dimTime
578 dimensionedScalar("zero", dimMass, 0.0),
579 zeroGradientFvPatchScalarField::typeName
592 dimensionedScalar("zero", dimMass, 0.0),
593 zeroGradientFvPatchScalarField::typeName
599 "cloudDiameterTrans",
606 dimensionedScalar("zero", dimLength, -1.0),
607 zeroGradientFvPatchScalarField::typeName
623 "zero", dimMass*dimVelocity/dimArea/dimTime, vector::zero
625 this->mappedPushedFieldPatchTypes<vector>()
638 dimensionedScalar("zero", dimPressure, 0.0),
639 this->mappedPushedFieldPatchTypes<scalar>()
652 dimensionedScalar("zero", dimMass/dimTime/dimArea, 0.0),
653 this->mappedPushedFieldPatchTypes<scalar>()
660 USp_.name(), // must have same name as USp_ to enable mapping
667 dimensionedVector("zero", USp_.dimensions(), vector::zero)
673 pSp_.name(), // must have same name as pSp_ to enable mapping
680 dimensionedScalar("zero", pSp_.dimensions(), 0.0)
686 rhoSp_.name(), // must have same name as rhoSp_ to enable mapping
693 dimensionedScalar("zero", rhoSp_.dimensions(), 0.0)
700 "U", // must have same name as U to enable mapping
707 dimensionedVector("zero", dimVelocity, vector::zero),
708 this->mappedFieldAndInternalPatchTypes<vector>()
714 "p", // must have same name as p to enable mapping
721 dimensionedScalar("zero", dimPressure, 0.0),
722 this->mappedFieldAndInternalPatchTypes<scalar>()
728 "rho", // must have same name as rho to enable mapping
735 dimensionedScalar("zero", dimDensity, 0.0),
736 this->mappedFieldAndInternalPatchTypes<scalar>()
742 "mu", // must have same name as mu to enable mapping
749 dimensionedScalar("zero", dimPressure*dimTime, 0.0),
750 this->mappedFieldAndInternalPatchTypes<scalar>()
753 availableMass_(regionMesh().nCells(), 0.0),
755 injection_(*this, coeffs_),
757 forces_(*this, coeffs_),
763 transferPrimaryRegionThermoFields();
765 correctThermoFields();
767 deltaRho_ == delta_*rho_;
768 phi_ = fvc::interpolate(deltaRho_*U_) & regionMesh().Sf();
773 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
775 kinematicSingleLayer::~kinematicSingleLayer()
779 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
781 void kinematicSingleLayer::addSources
785 const scalar massSource,
786 const vector& momentumSource,
787 const scalar pressureSource,
788 const scalar energySource
793 Info<< "\nSurface film: " << type() << ": adding to film source:" << nl
794 << " mass = " << massSource << nl
795 << " momentum = " << momentumSource << nl
796 << " pressure = " << pressureSource << endl;
799 rhoSpPrimary_.boundaryField()[patchI][faceI] -= massSource;
800 USpPrimary_.boundaryField()[patchI][faceI] -= momentumSource;
801 pSpPrimary_.boundaryField()[patchI][faceI] -= pressureSource;
803 addedMassTotal_ += massSource;
807 void kinematicSingleLayer::preEvolveRegion()
811 Info<< "kinematicSingleLayer::preEvolveRegion()" << endl;
814 transferPrimaryRegionThermoFields();
816 correctThermoFields();
818 transferPrimaryRegionSourceFields();
820 // Reset transfer fields
821 // availableMass_ = mass();
822 availableMass_ = netMass();
823 cloudMassTrans_ == dimensionedScalar("zero", dimMass, 0.0);
824 cloudDiameterTrans_ == dimensionedScalar("zero", dimLength, -1.0);
828 void kinematicSingleLayer::evolveRegion()
832 Info<< "kinematicSingleLayer::evolveRegion()" << endl;
837 // Solve continuity for deltaRho_
840 // Implicit pressure source coefficient - constant
841 tmp<volScalarField> tpp(this->pp());
843 for (int oCorr=0; oCorr<nOuterCorr_; oCorr++)
845 // Explicit pressure source contribution - varies with delta_
846 tmp<volScalarField> tpu(this->pu());
848 // Solve for momentum for U_
849 tmp<fvVectorMatrix> UEqn = solveMomentum(tpu(), tpp());
851 // Film thickness correction loop
852 for (int corr=1; corr<=nCorr_; corr++)
854 // Solve thickness for delta_
855 solveThickness(tpu(), tpp(), UEqn());
859 // Update deltaRho_ with new delta_
860 deltaRho_ == delta_*rho_;
862 // Update film wall and surface velocities
863 updateSurfaceVelocities();
865 // Reset source terms for next time integration
866 resetPrimaryRegionSourceTerms();
870 scalar kinematicSingleLayer::CourantNumber() const
874 if (regionMesh().nInternalFaces() > 0)
876 const scalar deltaT = time_.deltaTValue();
878 const surfaceScalarField SfUfbyDelta
880 regionMesh().surfaceInterpolation::deltaCoeffs()*mag(phi_)
882 const surfaceScalarField rhoDelta(fvc::interpolate(rho_*delta_));
883 const surfaceScalarField& magSf = regionMesh().magSf();
887 if (rhoDelta[i] > ROOTVSMALL)
889 CoNum = max(CoNum, SfUfbyDelta[i]/rhoDelta[i]/magSf[i]*deltaT);
894 reduce(CoNum, maxOp<scalar>());
896 Info<< "Film max Courant number: " << CoNum << endl;
902 const volVectorField& kinematicSingleLayer::U() const
908 const volVectorField& kinematicSingleLayer::Us() const
914 const volVectorField& kinematicSingleLayer::Uw() const
920 const surfaceScalarField& kinematicSingleLayer::phi() const
926 const volScalarField& kinematicSingleLayer::rho() const
932 const volScalarField& kinematicSingleLayer::T() const
936 "const volScalarField& kinematicSingleLayer::T() const"
937 ) << "T field not available for " << type() << abort(FatalError);
939 return volScalarField::null();
943 const volScalarField& kinematicSingleLayer::Ts() const
947 "const volScalarField& kinematicSingleLayer::Ts() const"
948 ) << "Ts field not available for " << type() << abort(FatalError);
950 return volScalarField::null();
954 const volScalarField& kinematicSingleLayer::Tw() const
958 "const volScalarField& kinematicSingleLayer::Tw() const"
959 ) << "Tw field not available for " << type() << abort(FatalError);
961 return volScalarField::null();
965 const volScalarField& kinematicSingleLayer::Cp() const
969 "const volScalarField& kinematicSingleLayer::Cp() const"
970 ) << "Cp field not available for " << type() << abort(FatalError);
972 return volScalarField::null();
976 const volScalarField& kinematicSingleLayer::kappa() const
980 "const volScalarField& kinematicSingleLayer::kappa() const"
981 ) << "kappa field not available for " << type() << abort(FatalError);
983 return volScalarField::null();
987 tmp<volScalarField> kinematicSingleLayer::primaryMassTrans() const
989 return tmp<volScalarField>
995 "kinematicSingleLayer::primaryMassTrans",
1003 dimensionedScalar("zero", dimMass/dimVolume/dimTime, 0.0)
1009 const volScalarField& kinematicSingleLayer::cloudMassTrans() const
1011 return cloudMassTrans_;
1015 const volScalarField& kinematicSingleLayer::cloudDiameterTrans() const
1017 return cloudDiameterTrans_;
1021 void kinematicSingleLayer::info() const
1023 Info<< "\nSurface film: " << type() << endl;
1025 Info<< indent << "added mass = "
1026 << returnReduce<scalar>(addedMassTotal_, sumOp<scalar>()) << nl
1027 << indent << "current mass = "
1028 << gSum((deltaRho_*magSf())()) << nl
1029 << indent << "min/max(mag(U)) = " << min(mag(U_)).value() << ", "
1030 << max(mag(U_)).value() << nl
1031 << indent << "min/max(delta) = " << min(delta_).value() << ", "
1032 << max(delta_).value() << nl;
1034 injection_.info(Info);
1038 tmp<DimensionedField<scalar, volMesh> > kinematicSingleLayer::Srho() const
1040 return tmp<DimensionedField<scalar, volMesh> >
1042 new DimensionedField<scalar, volMesh>
1046 "kinematicSingleLayer::Srho",
1054 dimensionedScalar("zero", dimMass/dimVolume/dimTime, 0.0)
1060 tmp<DimensionedField<scalar, volMesh> > kinematicSingleLayer::Srho
1065 return tmp<DimensionedField<scalar, volMesh> >
1067 new DimensionedField<scalar, volMesh>
1071 "kinematicSingleLayer::Srho(" + Foam::name(i) + ")",
1079 dimensionedScalar("zero", dimMass/dimVolume/dimTime, 0.0)
1085 tmp<DimensionedField<scalar, volMesh> > kinematicSingleLayer::Sh() const
1087 return tmp<DimensionedField<scalar, volMesh> >
1089 new DimensionedField<scalar, volMesh>
1093 "kinematicSingleLayer::Sh",
1101 dimensionedScalar("zero", dimEnergy/dimVolume/dimTime, 0.0)
1107 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1109 } // End namespace surfaceFilmModels
1110 } // End namespace regionModels
1111 } // End namespace Foam
1113 // ************************************************************************* //