/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2018-2020 OpenCFD Ltd ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField.H" #include "addToRunTimeSelectionTable.H" #include "fvPatchFieldMapper.H" #include "volFields.H" #include "phaseSystem.H" #include "mappedPatchBase.H" #include "solidThermo.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // const Foam::Enum < Foam::compressible:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField::regionType > Foam::compressible:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField::regionTypeNames_ { { regionType::solid, "solid" }, { regionType::fluid, "fluid" }, }; const Foam::Enum < Foam::compressible:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField::KMethodType > Foam::compressible:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField::KMethodTypeNames_ { { KMethodType::mtSolidThermo, "solidThermo" }, { KMethodType::mtLookup, "lookup" }, { KMethodType::mtPhaseSystem, "phaseSystem" } }; // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // Foam::tmp Foam::compressible:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: kappa ( const scalarField& Tp ) const { const polyMesh& mesh = patch().boundaryMesh().mesh(); const label patchi = patch().index(); switch (method_) { case mtSolidThermo: { const solidThermo& thermo = mesh.lookupObject(basicThermo::dictName); return thermo.kappa(patchi); break; } case mtLookup: { { const auto* ptr = mesh.cfindObject(kappaName_); if (ptr) { return patch().patchField(*ptr); } } { const auto* ptr = mesh.cfindObject(kappaName_); if (ptr) { const symmTensorField& KWall = patch().patchField(*ptr); const vectorField n(patch().nf()); return n & KWall & n; } } { FatalErrorInFunction << "Did not find field " << kappaName_ << " on mesh " << mesh.name() << " patch " << patch().name() << nl << " Please set 'kappa' to the name of a volScalarField" << " or volSymmTensorField." << exit(FatalError); } break; } case mtPhaseSystem: { // Lookup the fluid model const phaseSystem& fluid = ( mesh.lookupObject("phaseProperties") ); auto tkappaEff = tmp::New(patch().size(), Zero); auto& kappaEff = tkappaEff.ref(); forAll(fluid.phases(), phasei) { const phaseModel& phase = fluid.phases()[phasei]; const fvPatchScalarField& alpha = phase.boundaryField()[patchi]; kappaEff += alpha*phase.kappaEff(patchi)(); } return tkappaEff; break; } default: { FatalErrorInFunction << "Unimplemented method " << KMethodTypeNames_[method_] << nl << "Please set 'kappaMethod' to one of " << flatOutput(KMethodTypeNames_.sortedToc()) << nl << "and 'kappa' to the name of the volScalar" << exit(FatalError); } } // Return zero-sized (not nullptr) return tmp::New(); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { namespace compressible { // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField ( const fvPatch& p, const DimensionedField& iF ) : mixedFvPatchScalarField(p, iF), regionType_(fluid), method_(mtLookup), kappaName_("none"), otherPhaseName_("vapor"), TnbrName_("undefined-Tnbr"), qrNbrName_("undefined-qrNbr"), qrName_("undefined-qr") { this->refValue() = 0.0; this->refGrad() = 0.0; this->valueFraction() = 1.0; } turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField ( const turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField& psf, const fvPatch& p, const DimensionedField& iF, const fvPatchFieldMapper& mapper ) : mixedFvPatchScalarField(psf, p, iF, mapper), regionType_(psf.regionType_), method_(psf.method_), kappaName_(psf.kappaName_), otherPhaseName_(psf.otherPhaseName_), TnbrName_(psf.TnbrName_), qrNbrName_(psf.qrNbrName_), qrName_(psf.qrName_) {} turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField ( const fvPatch& p, const DimensionedField& iF, const dictionary& dict ) : mixedFvPatchScalarField(p, iF), regionType_(regionTypeNames_.get("region", dict)), method_(KMethodTypeNames_.get("kappaMethod", dict)), kappaName_(dict.getOrDefault("kappa", "none")), otherPhaseName_(dict.get("otherPhase")), TnbrName_(dict.getOrDefault("Tnbr", "T")), qrNbrName_(dict.getOrDefault("qrNbr", "none")), qrName_(dict.getOrDefault("qr", "none")) { if (!isA(this->patch().patch())) { FatalErrorInFunction << "' not type '" << mappedPatchBase::typeName << "'" << "\n for patch " << p.name() << " of field " << internalField().name() << " in file " << internalField().objectPath() << exit(FatalError); } this->readValueEntry(dict, IOobjectOption::MUST_READ); if (this->readMixedEntries(dict)) { // Full restart } else { // Start from user entered data. Assume fixedValue. refValue() = *this; refGrad() = 0.0; valueFraction() = 1.0; } } turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField ( const turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField& psf, const DimensionedField& iF ) : mixedFvPatchScalarField(psf, iF), regionType_(psf.regionType_), method_(psf.method_), kappaName_(psf.kappaName_), otherPhaseName_(psf.otherPhaseName_), TnbrName_(psf.TnbrName_), qrNbrName_(psf.qrNbrName_), qrName_(psf.qrName_) {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // void turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField:: updateCoeffs() { if (updated()) { return; } const polyMesh& mesh = patch().boundaryMesh().mesh(); // Since we're inside initEvaluate/evaluate there might be processor // comms underway. Change the tag we use. const int oldTag = UPstream::incrMsgType(); // Get the coupling information from the mappedPatchBase const label patchi = patch().index(); const mappedPatchBase& mpp = refCast(patch().patch()); const polyMesh& nbrMesh = mpp.sampleMesh(); const label samplePatchi = mpp.samplePolyPatch().index(); const fvPatch& nbrPatch = refCast(nbrMesh).boundary()[samplePatchi]; scalarField& Tp = *this; const auto& nbrField = refCast < const turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField >(nbrPatch.lookupPatchField(TnbrName_)); // Swap to obtain full local values of neighbour internal field scalarField TcNbr(nbrField.patchInternalField()); mpp.distribute(TcNbr); // Swap to obtain full local values of neighbour K*delta scalarField KDeltaNbr; KDeltaNbr = nbrField.kappa(nbrField)*nbrPatch.deltaCoeffs(); mpp.distribute(KDeltaNbr); scalarField KDelta(kappa(Tp)*patch().deltaCoeffs()); scalarField qr(Tp.size(), 0.0); if (qrName_ != "none") { qr = patch().lookupPatchField(qrName_); } scalarField qrNbr(Tp.size(), 0.0); if (qrNbrName_ != "none") { qrNbr = nbrPatch.lookupPatchField(qrNbrName_); mpp.distribute(qrNbr); } if (regionType_ == solid) { // Lookup the fluid model in the nbrFvMesh const phaseSystem& fluid = ( nbrMesh.lookupObject("phaseProperties") ); // The BC is applied to the liquid phase of the fluid const phaseModel& liquid ( fluid.phases()[nbrField.internalField().group()] ); const phaseModel& vapor(fluid.phases()[otherPhaseName_]); scalarField KDeltaLiqNbr; const fvPatchScalarField& alphal = liquid.boundaryField()[samplePatchi]; KDeltaLiqNbr = alphal*(liquid.kappaEff(samplePatchi))*nbrPatch.deltaCoeffs(); mpp.distribute(KDeltaLiqNbr); scalarField KDeltaVapNbr; const fvPatchScalarField& alphav = vapor.boundaryField()[samplePatchi]; KDeltaVapNbr = alphav*(vapor.kappaEff(samplePatchi))*nbrPatch.deltaCoeffs(); mpp.distribute(KDeltaVapNbr); scalarField TvNbr; const fvPatchScalarField& Tv = vapor.thermo().T().boundaryField()[samplePatchi]; TvNbr = Tv.patchInternalField(); mpp.distribute(TvNbr); // TcNbr: liquid Tp // TvNbr: vapour Tp scalarField c(TcNbr*KDeltaLiqNbr + TvNbr*KDeltaVapNbr); //valueFraction() = KDeltaNbr/(KDeltaNbr + KDelta); //refValue() = c/KDeltaNbr; scalarField KDeltaLiqVapNbr(KDeltaLiqNbr + KDeltaVapNbr); valueFraction() = KDeltaLiqVapNbr/(KDeltaLiqVapNbr + KDelta); refValue() = c/KDeltaLiqVapNbr; refGrad() = (qr + qrNbr)/kappa(Tp); if (debug) { scalar Q = gSum(kappa(Tp)*patch().magSf()*snGrad()); auto limits = gMinMax(Tp); auto avg = gAverage(Tp); Info<< "T solid : " << nl << endl; Info << " heat transfer rate from solid:" << Q << " walltemperature " << " min:" << limits.min() << " max:" << limits.max() << " avg:" << avg << nl << endl; } } else if (regionType_ == fluid) { const phaseSystem& fluid = ( mesh.lookupObject("phaseProperties") ); const phaseModel& liquid ( fluid.phases()[internalField().group()] ); const phaseModel& vapor(fluid.phases()[otherPhaseName_]); const fvPatchScalarField& Tv = vapor.thermo().T().boundaryField()[patchi]; const fvPatchScalarField& alphav = vapor.boundaryField()[patchi]; const scalarField KdeltaVap ( alphav*(vapor.kappaEff(patchi))*patch().deltaCoeffs() ); const fvPatchScalarField& alphal = liquid.boundaryField()[patchi]; const scalarField KdeltaLiq ( alphal*(liquid.kappaEff(patchi))*patch().deltaCoeffs() ); // TcNbr: solid Tp // Tv: vapour Tp const scalarField c(TcNbr*KDeltaNbr + Tv.patchInternalField()*KdeltaVap); const scalarField a(KdeltaVap + KDeltaNbr); valueFraction() = a/(a + KdeltaLiq); refValue() = c/a; refGrad() = (qr + qrNbr)/kappa(Tp); if (debug) { scalarField Tc(patchInternalField()); scalarField qLiq((Tp - Tc)*KdeltaLiq); scalarField qVap((Tp - Tv.patchInternalField())*KdeltaVap); auto infoT = gMinMax(Tp); auto avgT = gAverage(Tp); auto infoLiq = gMinMax(qLiq); auto infoVap = gMinMax(qVap); Info<< "T flow : " << nl << endl; Info<< " qLiq: " << infoLiq.min() << " - " << infoLiq.max() << nl << " qVap: " << infoVap.min() << " - " << infoVap.max() << nl; scalar QLiq = gSum(qLiq*patch().magSf()); scalar QVap = gSum(qVap*patch().magSf()); Info<< " Heat transfer to Liq: " << QLiq << endl; Info<< " Heat transfer to Vap: " << QVap << endl; Info<< " walltemperature " << " min:" << infoT.min() << " max:" << infoT.max() << " avg:" << avgT << endl; } } else { FatalErrorInFunction << "Unknown phase type. Valid types are: " << regionTypeNames_ << nl << exit(FatalError); } UPstream::msgType(oldTag); // Restore tag mixedFvPatchScalarField::updateCoeffs(); } void turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField::write ( Ostream& os ) const { mixedFvPatchField::write(os); os.writeEntry("kappaMethod", KMethodTypeNames_[method_]); os.writeEntryIfDifferent("kappa","none", kappaName_); os.writeEntry("Tnbr", TnbrName_); os.writeEntryIfDifferent("qrNbr", "none", qrNbrName_); os.writeEntryIfDifferent("qr", "none", qrName_); os.writeEntry("region", regionTypeNames_[regionType_]); os.writeEntry("otherPhase", otherPhaseName_); } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // makePatchTypeField ( fvPatchScalarField, turbulentTemperatureTwoPhaseRadCoupledMixedFvPatchScalarField ); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace compressible } // End namespace Foam // ************************************************************************* //