/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | Website: https://openfoam.org \\ / A nd | Copyright (C) 2015-2023 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- 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 "MomentumTransferPhaseSystem.H" #include "dragModel.H" #include "virtualMassModel.H" #include "liftModel.H" #include "wallLubricationModel.H" #include "turbulentDispersionModel.H" #include "scalarMatrices.H" #include "fvmDdt.H" #include "fvmDiv.H" #include "fvmSup.H" #include "fvcDdt.H" #include "fvcDiv.H" #include "fvcFlux.H" #include "fvcSnGrad.H" #include "fvcMeshPhi.H" #include "fvcReconstruct.H" #include "pimpleNoLoopControl.H" // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // template void Foam::MomentumTransferPhaseSystem::addDmdtUfs ( const phaseSystem::dmdtfTable& dmdtfs, phaseSystem::momentumTransferTable& eqns ) { forAllConstIter(phaseSystem::dmdtfTable, dmdtfs, dmdtfIter) { const phaseInterface interface(*this, dmdtfIter.key()); const volScalarField& dmdtf = *dmdtfIter(); const volScalarField dmdtf21(posPart(dmdtf)); const volScalarField dmdtf12(negPart(dmdtf)); phaseModel& phase1 = this->phases()[interface.phase1().name()]; phaseModel& phase2 = this->phases()[interface.phase2().name()]; if (!phase1.stationary()) { *eqns[phase1.name()] += dmdtf21*phase2.U() + fvm::Sp(dmdtf12, phase1.URef()); } if (!phase2.stationary()) { *eqns[phase2.name()] -= dmdtf12*phase1.U() + fvm::Sp(dmdtf21, phase2.URef()); } } } template void Foam::MomentumTransferPhaseSystem::addTmpField ( tmp& result, const tmp& field ) const { if (result.valid()) { result.ref() += field; } else { if (field.isTmp()) { result = field; } else { result = field().clone(); } } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // template Foam::MomentumTransferPhaseSystem:: MomentumTransferPhaseSystem ( const fvMesh& mesh ) : BasePhaseSystem(mesh) { this->generateInterfacialModels(dragModels_); this->generateInterfacialModels(virtualMassModels_); this->generateInterfacialModels(liftModels_); this->generateInterfacialModels(wallLubricationModels_); this->generateInterfacialModels(turbulentDispersionModels_); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // template Foam::MomentumTransferPhaseSystem:: ~MomentumTransferPhaseSystem() {} // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // template Foam::autoPtr Foam::MomentumTransferPhaseSystem::momentumTransfer() { // Create a momentum transfer matrix for each phase autoPtr eqnsPtr ( new phaseSystem::momentumTransferTable() ); phaseSystem::momentumTransferTable& eqns = eqnsPtr(); forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; eqns.insert ( phase.name(), new fvVectorMatrix(phase.U(), dimMass*dimVelocity/dimTime) ); } return eqnsPtr; } template Foam::autoPtr Foam::MomentumTransferPhaseSystem::momentumTransferf() { // Create a momentum transfer matrix for each phase autoPtr eqnsPtr ( new phaseSystem::momentumTransferTable() ); phaseSystem::momentumTransferTable& eqns = eqnsPtr(); forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; eqns.insert ( phase.name(), new fvVectorMatrix(phase.U(), dimMass*dimVelocity/dimTime) ); } return eqnsPtr; } template Foam::PtrList Foam::MomentumTransferPhaseSystem::Fs() const { PtrList Fs(this->phaseModels_.size()); // Add the lift force forAllConstIter ( liftModelTable, liftModels_, liftModelIter ) { const phaseInterface& interface = liftModelIter()->interface(); const volVectorField F(liftModelIter()->F()); addField ( interface.phase1(), "F", fvc::flux(F), Fs ); addField ( interface.phase2(), "F", -fvc::flux(F), Fs ); } // Add the wall lubrication force forAllConstIter ( wallLubricationModelTable, wallLubricationModels_, wallLubricationModelIter ) { const phaseInterface& interface = wallLubricationModelIter()->interface(); const volVectorField F(wallLubricationModelIter()->F()); addField ( interface.phase1(), "F", fvc::flux(F), Fs ); addField ( interface.phase2(), "F", -fvc::flux(F), Fs ); } // Add the phase pressure forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; addField ( phase, "F", phase.pPrimef() *fvc::snGrad(phase)*this->mesh_.magSf(), Fs ); } // Add the turbulent dispersion force forAllConstIter ( turbulentDispersionModelTable, turbulentDispersionModels_, turbulentDispersionModelIter ) { const phaseInterface& interface = turbulentDispersionModelIter()->interface(); const surfaceScalarField Df ( fvc::interpolate(turbulentDispersionModelIter()->D()) ); const volScalarField alpha12(interface.phase1() + interface.phase2()); const surfaceScalarField snGradAlpha1By12 ( fvc::snGrad ( interface.phase1() /max(alpha12, interface.phase1().residualAlpha()) )*this->mesh_.magSf() ); const surfaceScalarField snGradAlpha2By12 ( fvc::snGrad ( interface.phase2() /max(alpha12, interface.phase2().residualAlpha()) )*this->mesh_.magSf() ); addField(interface.phase1(), "F", Df*snGradAlpha1By12, Fs); addField(interface.phase2(), "F", Df*snGradAlpha2By12, Fs); } return Fs; } template Foam::PtrList Foam::MomentumTransferPhaseSystem::Ffs() const { PtrList Ffs(this->phaseModels_.size()); // Add the lift force forAllConstIter ( liftModelTable, liftModels_, liftModelIter ) { const phaseInterface& interface = liftModelIter()->interface(); const surfaceScalarField Ff(liftModelIter()->Ff()); addField ( interface.phase1(), "Ff", Ff, Ffs ); addField ( interface.phase2(), "Ff", -Ff, Ffs ); } // Add the wall lubrication force forAllConstIter ( wallLubricationModelTable, wallLubricationModels_, wallLubricationModelIter ) { const phaseInterface& interface = wallLubricationModelIter()->interface(); const surfaceScalarField Ff(wallLubricationModelIter()->Ff()); addField ( interface.phase1(), "Ff", Ff, Ffs ); addField ( interface.phase2(), "Ff", -Ff, Ffs ); } // Add the phase pressure forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; addField ( phase, "Ff", phase.pPrimef() *fvc::snGrad(phase)*this->mesh_.magSf(), Ffs ); } // Add the turbulent dispersion force forAllConstIter ( turbulentDispersionModelTable, turbulentDispersionModels_, turbulentDispersionModelIter ) { const phaseInterface& interface = turbulentDispersionModelIter()->interface(); const surfaceScalarField Df ( fvc::interpolate(turbulentDispersionModelIter()->D()) ); const volScalarField alpha12(interface.phase1() + interface.phase2()); const surfaceScalarField snGradAlpha1By12 ( fvc::snGrad ( interface.phase1() /max(alpha12, interface.phase1().residualAlpha()) )*this->mesh_.magSf() ); const surfaceScalarField snGradAlpha2By12 ( fvc::snGrad ( interface.phase2() /max(alpha12, interface.phase2().residualAlpha()) )*this->mesh_.magSf() ); addField(interface.phase1(), "F", Df*snGradAlpha1By12, Ffs); addField(interface.phase2(), "F", Df*snGradAlpha2By12, Ffs); } return Ffs; } template void Foam::MomentumTransferPhaseSystem::invADVs ( List>& ADVs ) const { const label n = ADVs.size(); scalarSquareMatrix AD(n); scalarField source(n); labelList pivotIndices(n); forAll(ADVs[0][0], ci) { for (label i=0; i template class PatchField, class GeoMesh> void Foam::MomentumTransferPhaseSystem::invADVs ( PtrList>>& ADVs ) const { const label n = ADVs.size(); List> ADps(n); for (label i=0; i void Foam::MomentumTransferPhaseSystem::invADVs ( const PtrList& As, PtrList& HVms, PtrList>& invADVs, PtrList>& invADVfs ) const { const label n = As.size(); invADVs.setSize(n); invADVfs.setSize(n); forAll(invADVs, i) { invADVs.set(i, new PtrList(n)); invADVs[i].set(i, As[i].clone()); invADVfs.set(i, new PtrList(n)); invADVfs[i].set(i, fvc::interpolate(As[i])); } labelList movingPhases(this->phases().size(), -1); forAll(this->movingPhases(), movingPhasei) { movingPhases[this->movingPhases()[movingPhasei].index()] = movingPhasei; } Kds_.clear(); // Update the drag coefficients forAllConstIter ( dragModelTable, dragModels_, dragModelIter ) { const phaseInterface& interface = dragModelIter()->interface(); tmp tKd(dragModelIter()->K()); volScalarField& Kd = tKd.ref(); Kds_.insert(dragModelIter.key(), tKd.ptr()); // Zero-gradient the drag coefficient to boundaries with fixed velocity forAll(Kd.boundaryField(), patchi) { if ( ( !interface.phase1().stationary() && interface.phase1().U()() .boundaryField()[patchi].fixesValue() ) && ( !interface.phase2().stationary() && interface.phase2().U()() .boundaryField()[patchi].fixesValue() ) ) { Kd.boundaryFieldRef()[patchi] = Kd.boundaryField()[patchi].patchInternalField(); } } forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const phaseModel& otherPhase = iter.otherPhase(); const label i = movingPhases[phase.index()]; if (i != -1) { const volScalarField Kdij ( (otherPhase/max(otherPhase, otherPhase.residualAlpha()))*Kd ); const surfaceScalarField Kdijf(fvc::interpolate(Kdij)); invADVs[i][i] += Kdij; invADVfs[i][i] += Kdijf; const label j = movingPhases[otherPhase.index()]; if (j != -1) { invADVs[i].set(j, -Kdij); invADVfs[i].set(j, -Kdijf); } } } } // Clear the Kds_ if they are not needed for the optional dragCorrection const pimpleNoLoopControl& pimple = this->pimple(); if (!pimple.dict().lookupOrDefault("dragCorrection", false)) { Kds_.clear(); } for (label i=0; imesh(), dimensionedScalar(As[0].dimensions(), 0) ) ); invADVfs[i].set ( j, surfaceScalarField::New ( "0", this->mesh(), dimensionedScalar(As[0].dimensions(), 0) ) ); } } } // Cache the phase acceleration As and Hs PtrList ADUDts(movingPhases.size()); PtrList HDUDts(movingPhases.size()); forAllConstIter ( virtualMassModelTable, virtualMassModels_, VmIter ) { const phaseInterface& interface = VmIter()->interface(); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const label i = movingPhases[phase.index()]; if (i != -1 && !ADUDts.set(i)) { const fvVectorMatrix DUDt(phase.DUDt()); ADUDts.set(i, DUDt.A()); HDUDts.set(i, DUDt.H()); } } } // Add the virtual mass contributions forAllConstIter ( virtualMassModelTable, virtualMassModels_, VmIter ) { const phaseInterface& interface = VmIter()->interface(); const volScalarField Vm(VmIter()->K()); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const phaseModel& otherPhase = iter.otherPhase(); const label i = movingPhases[phase.index()]; if (i != -1) { const volScalarField VmPhase ( (otherPhase/max(otherPhase, otherPhase.residualAlpha()))*Vm ); { const volScalarField AVm(VmPhase*ADUDts[i]); invADVs[i][i] += AVm; invADVfs[i][i] += fvc::interpolate(AVm); addField ( i, "HVm", VmPhase*HDUDts[i], HVms ); } const label j = movingPhases[otherPhase.index()]; if (j != -1) { const volScalarField AVm(VmPhase*ADUDts[j]); invADVs[i][j] -= AVm; invADVfs[i][j] -= fvc::interpolate(AVm); addField ( i, "HVm", -VmPhase*HDUDts[j], HVms ); } } } } MomentumTransferPhaseSystem::invADVs(invADVs); MomentumTransferPhaseSystem::invADVs(invADVfs); } template Foam::PtrList> Foam::MomentumTransferPhaseSystem::invADVfs ( const PtrList& Afs, PtrList& HVmfs ) const { const label n = Afs.size(); PtrList> invADVfs(n); forAll(invADVfs, i) { invADVfs.set(i, new PtrList(n)); invADVfs[i].set(i, Afs[i].clone()); } labelList movingPhases(this->phases().size(), -1); forAll(this->movingPhases(), movingPhasei) { movingPhases[this->movingPhases()[movingPhasei].index()] = movingPhasei; } forAllConstIter ( dragModelTable, dragModels_, dragModelIter ) { const phaseInterface& interface = dragModelIter()->interface(); const surfaceScalarField Kdf(dragModelIter()->Kf()); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const phaseModel& otherPhase = iter.otherPhase(); const label i = movingPhases[phase.index()]; if (i != -1) { const surfaceScalarField alphaf ( fvc::interpolate(otherPhase) ); const surfaceScalarField Kdfij ( (alphaf/max(alphaf, otherPhase.residualAlpha()))*Kdf ); invADVfs[i][i] += Kdfij; const label j = movingPhases[otherPhase.index()]; if (j != -1) { invADVfs[i].set(j, -Kdfij); } } } } for (label i=0; imesh(), dimensionedScalar(Afs[0].dimensions(), 0) ) ); } } } // Cache the phase acceleration Afs and Hs PtrList AUgradUs(movingPhases.size()); PtrList HUgradUs(movingPhases.size()); forAllConstIter ( virtualMassModelTable, virtualMassModels_, VmIter ) { const phaseInterface& interface = VmIter()->interface(); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const label i = movingPhases[phase.index()]; if (i != -1 && !AUgradUs.set(i)) { const fvVectorMatrix UgradU(phase.UgradU()); AUgradUs.set(i, UgradU.A()); HUgradUs.set(i, UgradU.H()); } } } // Add the virtual mass contributions forAllConstIter ( virtualMassModelTable, virtualMassModels_, VmIter ) { const phaseInterface& interface = VmIter()->interface(); const volScalarField Vm(VmIter()->K()); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const phaseModel& otherPhase = iter.otherPhase(); const label i = movingPhases[phase.index()]; if (i != -1) { const volScalarField VmPhase ( (otherPhase/max(otherPhase, otherPhase.residualAlpha())) *Vm ); const surfaceScalarField VmPhasef(fvc::interpolate(VmPhase)); invADVfs[i][i] += byDt(VmPhasef) + fvc::interpolate(VmPhase*AUgradUs[i]); addField ( i, "HVmf", VmPhasef *byDt ( fvc::absolute ( this->MRF().absolute(phase.phi()().oldTime()), phase.U() ) ) + fvc::flux(VmPhase*HUgradUs[i]), HVmfs ); const label j = movingPhases[otherPhase.index()]; if (j != -1) { invADVfs[i][j] -= byDt(VmPhasef) + fvc::interpolate(VmPhase*AUgradUs[j]); addField ( i, "HVmf", -VmPhasef *byDt ( fvc::absolute ( this->MRF().absolute ( otherPhase.phi()().oldTime() ), otherPhase.U() ) ) - fvc::flux(VmPhase*HUgradUs[j]), HVmfs ); } } } } invADVs(invADVfs); return invADVfs; } template bool Foam::MomentumTransferPhaseSystem:: implicitPhasePressure(const phaseModel& phase) const { return this->mesh_.solution().solverDict(phase.volScalarField::name()). template lookupOrDefault ( "implicitPhasePressure", false ); } template bool Foam::MomentumTransferPhaseSystem:: implicitPhasePressure() const { bool implicitPressure = false; forAll(this->phaseModels_, phasei) { const phaseModel& phase = this->phaseModels_[phasei]; implicitPressure = implicitPressure || implicitPhasePressure(phase); } return implicitPressure; } template Foam::tmp Foam::MomentumTransferPhaseSystem::alphaDByAf ( const PtrList& rAs ) const { tmp alphaDByAf; // Add the phase pressure forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; addTmpField ( alphaDByAf, fvc::interpolate(max(phase, scalar(0))) *fvc::interpolate(rAs[phase.index()]) *phase.pPrimef() ); } // Add the turbulent dispersion forAllConstIter ( turbulentDispersionModelTable, turbulentDispersionModels_, turbulentDispersionModelIter ) { const phaseInterface& interface = turbulentDispersionModelIter()->interface(); const surfaceScalarField alpha1f ( fvc::interpolate(max(interface.phase1(), scalar(0))) ); const surfaceScalarField alpha2f ( fvc::interpolate(max(interface.phase2(), scalar(0))) ); addTmpField ( alphaDByAf, alpha1f*alpha2f /max(alpha1f + alpha2f, interface.phase1().residualAlpha()) *fvc::interpolate ( max ( rAs[interface.phase1().index()], rAs[interface.phase2().index()] ) *turbulentDispersionModelIter()->D() ) ); } return alphaDByAf; } template Foam::PtrList Foam::MomentumTransferPhaseSystem::ddtCorrs() const { PtrList ddtCorrs(this->phaseModels_.size()); // Add correction forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; addField ( phase, "ddtCorr", fvc::ddtCorr ( phase, phase.rho(), phase.U()(), phase.phi()(), phase.Uf() ), ddtCorrs ); } const pimpleNoLoopControl& pimple = this->pimple(); const Switch VmDdtCorr ( pimple.dict().lookupOrDefault("VmDdtCorrection", false) ); // Optionally ddd virtual mass correction if (VmDdtCorr) { PtrList VmDdtCoeffs(this->phaseModels_.size()); PtrList VmDdtCorrs(this->phaseModels_.size()); forAll(this->movingPhases(), movingPhasei) { const phaseModel& phase = this->movingPhases()[movingPhasei]; const label phasei = phase.index(); VmDdtCorrs.set ( phasei, fvc::ddtCorr ( phase.U()(), phase.phi()(), phase.Uf() ) ); } forAllConstIter ( virtualMassModelTable, virtualMassModels_, VmIter ) { const phaseInterface& interface = VmIter()->interface(); const volScalarField Vm(VmIter()->K()); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const label phasei = phase.index(); const phaseModel& otherPhase = iter.otherPhase(); const label otherPhasei = otherPhase.index(); const volScalarField VmPhase ( (otherPhase/max(otherPhase, otherPhase.residualAlpha())) *Vm ); addField ( phase, "ddtCorr", fvc::interpolate(VmPhase) *(VmDdtCorrs[phasei] - VmDdtCorrs[otherPhasei]), ddtCorrs ); } } } return ddtCorrs; } template void Foam::MomentumTransferPhaseSystem::dragCorrs ( PtrList& dragCorrs, PtrList& dragCorrfs ) const { labelList movingPhases(this->phases().size(), -1); PtrList Uphis(this->movingPhases().size()); forAll(this->movingPhases(), movingPhasei) { movingPhases[this->movingPhases()[movingPhasei].index()] = movingPhasei; Uphis.set ( movingPhasei, fvc::reconstruct(this->movingPhases()[movingPhasei].phi()) ); } forAllConstIter(KdTable, Kds_, KdIter) { const volScalarField& K(*KdIter()); const phaseInterface interface(*this, KdIter.key()); forAllConstIter(phaseInterface, interface, iter) { const phaseModel& phase = iter(); const phaseModel& otherPhase = iter.otherPhase(); const label i = movingPhases[phase.index()]; const label j = movingPhases[otherPhase.index()]; if (i != -1) { const volScalarField K1 ( (otherPhase/max(otherPhase, otherPhase.residualAlpha()))*K ); addField ( i, "dragCorr", K1*(j == -1 ? -Uphis[i] : (Uphis[j] - Uphis[i])), dragCorrs ); addField ( i, "dragCorrf", fvc::interpolate(K1) *(j == -1 ? -phase.phi() : (otherPhase.phi() - phase.phi())), dragCorrfs ); } } } } template bool Foam::MomentumTransferPhaseSystem::read() { if (BasePhaseSystem::read()) { bool readOK = true; // Read models ... return readOK; } else { return false; } } // ************************************************************************* //