/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | foam-extend: Open Source CFD \\ / O peration | Version: 4.1 \\ / A nd | Web: http://www.foam-extend.org \\/ M anipulation | For copyright notice see file Copyright ------------------------------------------------------------------------------- License This file is part of foam-extend. foam-extend 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. foam-extend 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 foam-extend. If not, see . Description \*---------------------------------------------------------------------------*/ #include "freeSurface.H" #include "volFields.H" #include "transformField.H" #include "emptyFaPatch.H" #include "wedgeFaPatch.H" #include "wallFvPatch.H" #include "EulerDdtScheme.H" #include "CrankNicolsonDdtScheme.H" #include "backwardDdtScheme.H" #include "tetFemMatrices.H" #include "tetPointFields.H" #include "faceTetPolyPatch.H" #include "tetPolyPatchInterpolation.H" #include "fixedValueTetPolyPatchFields.H" #include "fixedValuePointPatchFields.H" #include "twoDPointCorrector.H" #include "slipFvPatchFields.H" #include "symmetryFvPatchFields.H" #include "fixedGradientFvPatchFields.H" #include "zeroGradientCorrectedFvPatchFields.H" #include "fixedGradientCorrectedFvPatchFields.H" #include "fixedValueCorrectedFvPatchFields.H" #include "primitivePatchInterpolation.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // defineTypeNameAndDebug(freeSurface, 0); // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // void freeSurface::clearOut() { deleteDemandDrivenData(interpolatorABPtr_); deleteDemandDrivenData(interpolatorBAPtr_); deleteDemandDrivenData(controlPointsPtr_); deleteDemandDrivenData(motionPointsMaskPtr_); deleteDemandDrivenData(pointsDisplacementDirPtr_); deleteDemandDrivenData(facesDisplacementDirPtr_); deleteDemandDrivenData(totalDisplacementPtr_); deleteDemandDrivenData(aMeshPtr_); deleteDemandDrivenData(UsPtr_); deleteDemandDrivenData(phisPtr_); deleteDemandDrivenData(surfactConcPtr_); deleteDemandDrivenData(surfaceTensionPtr_); deleteDemandDrivenData(surfactantPtr_); deleteDemandDrivenData(fluidIndicatorPtr_); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // freeSurface::freeSurface ( dynamicFvMesh& m, const volScalarField& rho, volVectorField& Ub, volScalarField& Pb, const surfaceScalarField& sfPhi ) : IOdictionary ( IOobject ( "freeSurfaceProperties", Ub.mesh().time().constant(), Ub.mesh(), IOobject::MUST_READ, IOobject::NO_WRITE ) ), mesh_(m), rho_(rho), U_(Ub), p_(Pb), phi_(sfPhi), curTimeIndex_(Ub.mesh().time().timeIndex()), twoFluids_ ( this->lookup("twoFluids") ), normalMotionDir_ ( this->lookup("normalMotionDir") ), motionDir_(0, 0, 0), cleanInterface_ ( this->lookup("cleanInterface") ), aPatchID_(-1), bPatchID_(-1), muFluidA_ ( this->lookup("muFluidA") ), muFluidB_ ( this->lookup("muFluidB") ), rhoFluidA_ ( this->lookup("rhoFluidA") ), rhoFluidB_ ( this->lookup("rhoFluidB") ), g_(this->lookup("g")), cleanInterfaceSurfTension_ ( this->lookup("surfaceTension") ), fixedFreeSurfacePatches_ ( this->lookup("fixedFreeSurfacePatches") ), pointNormalsCorrectionPatches_ ( this->lookup("pointNormalsCorrectionPatches") ), nFreeSurfCorr_ ( readInt(this->lookup("nFreeSurfaceCorrectors")) ), smoothing_(false), interpolatorABPtr_(NULL), interpolatorBAPtr_(NULL), controlPointsPtr_(NULL), motionPointsMaskPtr_(NULL), pointsDisplacementDirPtr_(NULL), facesDisplacementDirPtr_(NULL), totalDisplacementPtr_(NULL), aMeshPtr_(NULL), UsPtr_(NULL), phisPtr_(NULL), surfactConcPtr_(NULL), surfaceTensionPtr_(NULL), surfactantPtr_(NULL), fluidIndicatorPtr_(NULL) { //Read motion direction if (!normalMotionDir_) { motionDir_ = vector(this->lookup("motionDir")); motionDir_ /= mag(motionDir_) + SMALL; } // Set point normal correction patches boolList& correction = aMesh().correctPatchPointNormals(); forAll (pointNormalsCorrectionPatches_, patchI) { word patchName = pointNormalsCorrectionPatches_[patchI]; label patchID = aMesh().boundary().findPatchID(patchName); if (patchID == -1) { FatalErrorIn ( "freeSurface::freeSurface(...)" ) << "Patch name for point normals correction does not exist" << abort(FatalError); } correction[patchID] = true; } // Clear geometry aMesh().movePoints(); // Detect the free surface patch forAll (mesh().boundary(), patchI) { if (mesh().boundary()[patchI].name() == "freeSurface") { aPatchID_ = patchI; Info<< "Found free surface patch. ID: " << aPatchID_ << endl; } } if (aPatchID() == -1) { FatalErrorIn("freeSurface::freeSurface(...)") << "Free surface patch not defined. Please make sure that " << " the free surface patches is named as freeSurface" << abort(FatalError); } // Detect the free surface shadow patch if (twoFluids()) { forAll (mesh().boundary(), patchI) { if (mesh().boundary()[patchI].name() == "freeSurfaceShadow") { bPatchID_ = patchI; Info<< "Found free surface shadow patch. ID: " << bPatchID_ << endl; } } if (bPatchID() == -1) { FatalErrorIn("freeSurface::freeSurface(...)") << "Free surface shadow patch not defined. " << "Please make sure that the free surface shadow patch " << "is named as freeSurfaceShadow." << abort(FatalError); } } // Mark free surface boundary points // which belonge to processor patches forAll (aMesh().boundary(), patchI) { if ( aMesh().boundary()[patchI].type() == processorFaPatch::typeName ) { const labelList& patchPoints = aMesh().boundary()[patchI].pointLabels(); forAll (patchPoints, pointI) { motionPointsMask()[patchPoints[pointI]] = -1; } } } // Mark fixed free surface boundary points forAll (fixedFreeSurfacePatches_, patchI) { label fixedPatchID = aMesh().boundary().findPatchID ( fixedFreeSurfacePatches_[patchI] ); if (fixedPatchID == -1) { FatalErrorIn("freeSurface::freeSurface(...)") << "Wrong faPatch name in the fixedFreeSurfacePatches list" << " defined in the freeSurfaceProperties dictionary" << abort(FatalError); } const labelList& patchPoints = aMesh().boundary()[fixedPatchID].pointLabels(); forAll (patchPoints, pointI) { motionPointsMask()[patchPoints[pointI]] = 0; } } // Mark free-surface boundary point // at the axis of 2-D axisymmetic cases forAll (aMesh().boundary(), patchI) { if ( aMesh().boundary()[patchI].type() == wedgeFaPatch::typeName ) { const wedgeFaPatch& wedgePatch = refCast(aMesh().boundary()[patchI]); if (wedgePatch.axisPoint() > -1) { motionPointsMask()[wedgePatch.axisPoint()] = 0; Info << "Axis point: " << wedgePatch.axisPoint() << "vector: " << aMesh().points()[wedgePatch.axisPoint()] << endl; } } } // Read free-surface points total displacement if present readTotalDisplacement(); // Read control points positions if present controlPoints(); // Check if smoothing switch is set if (this->found("smoothing")) { smoothing_ = Switch(this->lookup("smoothing")); } } // * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * * // freeSurface::~freeSurface() { clearOut(); } // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // void freeSurface::updateDisplacementDirections() { if (normalMotionDir()) { // Update point displacement correction pointsDisplacementDir() = aMesh().pointAreaNormals(); // Correcte point displacement direction // at the "centerline" symmetryPlane which represents the axis // of an axisymmetric case forAll (aMesh().boundary(), patchI) { if (aMesh().boundary()[patchI].type() == wedgeFaPatch::typeName) { const wedgeFaPatch& wedgePatch = refCast(aMesh().boundary()[patchI]); vector axis = wedgePatch.axis(); label centerLinePatchID = aMesh().boundary().findPatchID("centerline"); if (centerLinePatchID != -1) { const labelList& pointLabels = aMesh().boundary()[centerLinePatchID].pointLabels(); forAll (pointLabels, pointI) { vector dir = pointsDisplacementDir()[pointLabels[pointI]]; dir = (dir&axis)*axis; dir /= mag(dir); pointsDisplacementDir()[pointLabels[pointI]] = dir; } } else { Info << "Warning: centerline polyPatch does not exist. " << "Free surface points displacement directions " << "will not be corrected at the axis (centerline)" << endl; } break; } } // Update face displacement direction facesDisplacementDir() = aMesh().faceAreaNormals().internalField(); // Correction of control points postion const vectorField& Cf = aMesh().areaCentres().internalField(); controlPoints() = facesDisplacementDir() *(facesDisplacementDir()&(controlPoints() - Cf)) + Cf; } } bool freeSurface::predictPoints() { // Smooth interface if (smoothing_) { controlPoints() = aMesh().areaCentres().internalField(); movePoints(scalarField(controlPoints().size(), 0)); movePoints(-fvc::meshPhi(rho(),U())().boundaryField()[aPatchID()]); } for ( int freeSurfCorr=0; freeSurfCorr::typeName ) { sweptVolCorr *= (1.0/2.0)*DB().deltaT().value(); } else if ( ddtScheme == fv::EulerDdtScheme::typeName ) { sweptVolCorr *= DB().deltaT().value(); } else if ( ddtScheme == fv::backwardDdtScheme::typeName ) { if (DB().timeIndex() == 1) { sweptVolCorr *= DB().deltaT().value(); } else { sweptVolCorr *= (2.0/3.0)*DB().deltaT().value(); } } else { FatalErrorIn("freeSurface::movePoints()") << "Unsupported temporal differencing scheme : " << ddtScheme << abort(FatalError); } const scalarField& Sf = aMesh().S(); const vectorField& Nf = aMesh().faceAreaNormals().internalField(); scalarField deltaH = sweptVolCorr/(Sf*(Nf & facesDisplacementDir())); pointField displacement = pointDisplacement(deltaH); // Move only free-surface points const labelList& meshPointsA = mesh().boundaryMesh()[aPatchID()].meshPoints(); forAll (displacement, pointI) { newMeshPoints[meshPointsA[pointI]] += displacement[pointI]; } if (twoFluids_) { const labelList& meshPointsB = mesh().boundaryMesh()[bPatchID_].meshPoints(); pointField displacementB = interpolatorAB().pointInterpolate ( displacement ); forAll (displacementB, pointI) { newMeshPoints[meshPointsB[pointI]] += displacementB[pointI]; } } // Update total displacement field if (totalDisplacementPtr_ && (curTimeIndex_ < DB().timeIndex())) { FatalErrorIn("freeSurface::movePoints()") << "Total displacement of free surface points " << "from previous time step is not absorbed by the mesh." << abort(FatalError); } else if (curTimeIndex_ < DB().timeIndex()) { totalDisplacement() = displacement; curTimeIndex_ = DB().timeIndex(); } else { totalDisplacement() += displacement; } twoDPointCorrector twoDPointCorr(mesh()); twoDPointCorr.correctPoints(newMeshPoints); mesh().movePoints(newMeshPoints); // faMesh motion is done automatically, using meshObject // HJ, 8/Aug/2011 // aMesh().movePoints(mesh().points()); // Move correctedFvPatchField fvSubMeshes forAll (U().boundaryField(), patchI) { if ( ( U().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& pU = refCast > ( U().boundaryField()[patchI] ); pU.movePatchSubMesh(); } } forAll (p().boundaryField(), patchI) { if ( ( p().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& pP = refCast > ( p().boundaryField()[patchI] ); pP.movePatchSubMesh(); } } return true; } bool freeSurface::moveMeshPointsForOldFreeSurfDisplacement() { if (totalDisplacementPtr_) { pointField newPoints = mesh().points(); const labelList& meshPointsA = mesh().boundaryMesh()[aPatchID()].meshPoints(); forAll (totalDisplacement(), pointI) { newPoints[meshPointsA[pointI]] -= totalDisplacement()[pointI]; } // Check mesh motion solver type bool feMotionSolver = mesh().objectRegistry::foundObject ( "motionU" ); bool fvMotionSolver = mesh().objectRegistry::foundObject ( "pointMotionU" ); if (feMotionSolver) { tetPointVectorField& motionU = const_cast ( mesh().objectRegistry:: lookupObject ( "motionU" ) ); fixedValueTetPolyPatchVectorField& motionUaPatch = refCast ( motionU.boundaryField()[aPatchID()] ); tetPolyPatchInterpolation tppiAPatch ( refCast ( motionUaPatch.patch() ) ); motionUaPatch == tppiAPatch.pointToPointInterpolate ( totalDisplacement()/DB().deltaT().value() ); if (twoFluids_) { const labelList& meshPointsB = mesh().boundaryMesh()[bPatchID()].meshPoints(); pointField totDisplacementB = interpolatorAB().pointInterpolate ( totalDisplacement() ); forAll (totDisplacementB, pointI) { newPoints[meshPointsB[pointI]] -= totDisplacementB[pointI]; } fixedValueTetPolyPatchVectorField& motionUbPatch = refCast ( motionU.boundaryField()[bPatchID()] ); tetPolyPatchInterpolation tppiBPatch ( refCast(motionUbPatch.patch()) ); motionUbPatch == tppiBPatch.pointToPointInterpolate ( totDisplacementB/DB().deltaT().value() ); } } else if (fvMotionSolver) { pointVectorField& motionU = const_cast ( mesh().objectRegistry:: lookupObject ( "pointMotionU" ) ); fixedValuePointPatchVectorField& motionUaPatch = refCast ( motionU.boundaryField()[aPatchID()] ); motionUaPatch == totalDisplacement()/DB().deltaT().value(); if (twoFluids_) { const labelList& meshPointsB = mesh().boundaryMesh()[bPatchID()].meshPoints(); pointField totDisplacementB = interpolatorAB().pointInterpolate ( totalDisplacement() ); forAll (totDisplacementB, pointI) { newPoints[meshPointsB[pointI]] -= totDisplacementB[pointI]; } fixedValuePointPatchVectorField& motionUbPatch = refCast ( motionU.boundaryField()[bPatchID()] ); motionUbPatch == totDisplacementB/DB().deltaT().value(); } } twoDPointCorrector twoDPointCorr(mesh()); twoDPointCorr.correctPoints(newPoints); mesh().movePoints(newPoints); deleteDemandDrivenData(totalDisplacementPtr_); mesh().update(); // faMesh motion is done automatically, using meshObject // HJ, 8/Aug/2011 // aMesh().movePoints(mesh().points()); // Move correctedFvPatchField fvSubMeshes forAll (U().boundaryField(), patchI) { if ( ( U().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& aU = refCast > ( U().boundaryField()[patchI] ); aU.movePatchSubMesh(); } } forAll (p().boundaryField(), patchI) { if ( ( p().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& aP = refCast > ( p().boundaryField()[patchI] ); aP.movePatchSubMesh(); } } } return true; } bool freeSurface::moveMeshPoints() { scalarField sweptVolCorr = phi_.boundaryField()[aPatchID()] - fvc::meshPhi(rho(),U())().boundaryField()[aPatchID()]; word ddtScheme ( mesh().schemesDict().ddtScheme ( "ddt(" + rho().name() + ',' + U().name()+')' ) ); if ( ddtScheme == fv::CrankNicolsonDdtScheme::typeName ) { sweptVolCorr *= (1.0/2.0)*DB().deltaT().value(); } else if ( ddtScheme == fv::EulerDdtScheme::typeName ) { sweptVolCorr *= DB().deltaT().value(); } else if ( ddtScheme == fv::backwardDdtScheme::typeName ) { sweptVolCorr *= (2.0/3.0)*DB().deltaT().value(); } else { FatalErrorIn("freeSurface::movePoints()") << "Unsupported temporal differencing scheme : " << ddtScheme << abort(FatalError); } const scalarField& Sf = aMesh().S(); const vectorField& Nf = aMesh().faceAreaNormals().internalField(); scalarField deltaH = sweptVolCorr/(Sf*(Nf & facesDisplacementDir())); pointField displacement = pointDisplacement(deltaH); //-- Set mesh motion boundary conditions tetPointVectorField& motionU = const_cast ( mesh().objectRegistry:: lookupObject ( "motionU" ) ); fixedValueTetPolyPatchVectorField& motionUaPatch = refCast ( motionU.boundaryField()[aPatchID()] ); tetPolyPatchInterpolation tppiAPatch ( refCast ( motionUaPatch.patch() ) ); motionUaPatch == tppiAPatch.pointToPointInterpolate ( displacement/DB().deltaT().value() ); if (twoFluids()) { fixedValueTetPolyPatchVectorField& motionUbPatch = refCast ( motionU.boundaryField()[bPatchID()] ); tetPolyPatchInterpolation tppiBPatch ( refCast(motionUbPatch.patch()) ); motionUbPatch == tppiBPatch.pointToPointInterpolate ( interpolatorAB().pointInterpolate ( displacement/DB().deltaT().value() ) ); } mesh().update(); // faMesh motion is done automatically, using meshObject // HJ, 8/Aug/2011 // aMesh().movePoints(mesh().points()); // Move correctedFvPatchField fvSubMeshes forAll (U().boundaryField(), patchI) { if ( ( U().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( U().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& aU = refCast > ( U().boundaryField()[patchI] ); aU.movePatchSubMesh(); } } forAll (p().boundaryField(), patchI) { if ( ( p().boundaryField()[patchI].type() == fixedGradientCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == fixedValueCorrectedFvPatchField::typeName ) || ( p().boundaryField()[patchI].type() == zeroGradientCorrectedFvPatchField::typeName ) ) { correctedFvPatchField& aP = refCast > ( p().boundaryField()[patchI] ); aP.movePatchSubMesh(); } } return true; } void freeSurface::updateBoundaryConditions() { updateVelocity(); updateSurfactantConcentration(); updatePressure(); } void freeSurface::updateVelocity() { if (twoFluids()) { vectorField nA = mesh().boundary()[aPatchID()].nf(); vectorField nB = mesh().boundary()[bPatchID()].nf(); scalarField DnB = interpolatorBA().faceInterpolate ( mesh().boundary()[bPatchID()].deltaCoeffs() ); scalarField DnA = mesh().boundary()[aPatchID()].deltaCoeffs(); vectorField UtPA = U().boundaryField()[aPatchID()].patchInternalField(); if ( U().boundaryField()[aPatchID()].type() == fixedGradientCorrectedFvPatchField::typeName ) { fixedGradientCorrectedFvPatchField& aU = refCast > ( U().boundaryField()[aPatchID()] ); UtPA += aU.corrVecGrad(); } UtPA -= nA*(nA & UtPA); vectorField UtPB = interpolatorBA().faceInterpolate ( U().boundaryField()[bPatchID()].patchInternalField() ); if ( U().boundaryField()[bPatchID()].type() == fixedValueCorrectedFvPatchField::typeName ) { fixedValueCorrectedFvPatchField& bU = refCast > ( U().boundaryField()[bPatchID()] ); UtPB += interpolatorBA().faceInterpolate(bU.corrVecGrad()); } UtPB -= nA*(nA & UtPB); vectorField UtFs = muFluidA().value()*DnA*UtPA + muFluidB().value()*DnB*UtPB; vectorField UnFs = nA*phi_.boundaryField()[aPatchID()] /mesh().boundary()[aPatchID()].magSf(); Us().internalField() += UnFs - nA*(nA&Us().internalField()); correctUsBoundaryConditions(); UtFs -= (muFluidA().value() - muFluidB().value())* (fac::grad(Us())&aMesh().faceAreaNormals())().internalField(); vectorField tangentialSurfaceTensionForce(nA.size(), vector::zero); if (!cleanInterface()) { tangentialSurfaceTensionForce = surfaceTensionGrad()().internalField(); } else { vectorField surfaceTensionForce = cleanInterfaceSurfTension().value() *fac::edgeIntegrate ( aMesh().Le()*aMesh().edgeLengthCorrection() )().internalField(); tangentialSurfaceTensionForce = surfaceTensionForce - cleanInterfaceSurfTension().value() *aMesh().faceCurvatures().internalField()*nA; } UtFs += tangentialSurfaceTensionForce; UtFs /= muFluidA().value()*DnA + muFluidB().value()*DnB + VSMALL; Us().internalField() = UnFs + UtFs; correctUsBoundaryConditions(); // Store old-time velocity field U() U().oldTime(); U().boundaryField()[bPatchID()] == interpolatorAB().faceInterpolate(UtFs) + nB*fvc::meshPhi(rho(),U())().boundaryField()[bPatchID()]/ mesh().boundary()[bPatchID()].magSf(); if ( p().boundaryField()[bPatchID()].type() == fixedGradientFvPatchField::typeName ) { fixedGradientFvPatchField& pB = refCast > ( p().boundaryField()[bPatchID()] ); pB.gradient() = - rhoFluidB().value() *( nB&fvc::ddt(U())().boundaryField()[bPatchID()] ); } // Update fixedGradient boundary condition on patch A vectorField nGradU = muFluidB().value()*(UtPB - UtFs)*DnA + tangentialSurfaceTensionForce - muFluidA().value()*nA*fac::div(Us())().internalField() + (muFluidB().value() - muFluidA().value()) *(fac::grad(Us())().internalField()&nA); nGradU /= muFluidA().value() + VSMALL; if ( U().boundaryField()[aPatchID()].type() == fixedGradientCorrectedFvPatchField::typeName ) { fixedGradientCorrectedFvPatchField& aU = refCast > ( U().boundaryField()[aPatchID()] ); aU.gradient() = nGradU; } else if ( U().boundaryField()[aPatchID()].type() == fixedGradientFvPatchField::typeName ) { fixedGradientFvPatchField& aU = refCast > ( U().boundaryField()[aPatchID()] ); aU.gradient() = nGradU; } else { FatalErrorIn("freeSurface::updateVelocity()") << "Bounary condition on " << U().name() << " for freeSurface patch is " << U().boundaryField()[aPatchID()].type() << ", instead " << fixedGradientCorrectedFvPatchField::typeName << " or " << fixedGradientFvPatchField::typeName << abort(FatalError); } } else { vectorField nA = aMesh().faceAreaNormals().internalField(); vectorField UnFs = nA*phi_.boundaryField()[aPatchID()] /mesh().boundary()[aPatchID()].magSf(); // Correct normal component of free-surface velocity Us().internalField() += UnFs - nA*(nA&Us().internalField()); correctUsBoundaryConditions(); vectorField tangentialSurfaceTensionForce(nA.size(), vector::zero); if (!cleanInterface()) { tangentialSurfaceTensionForce = surfaceTensionGrad()().internalField(); } else { vectorField surfaceTensionForce = cleanInterfaceSurfTension().value() *fac::edgeIntegrate ( aMesh().Le()*aMesh().edgeLengthCorrection() )().internalField(); tangentialSurfaceTensionForce = surfaceTensionForce - cleanInterfaceSurfTension().value() *aMesh().faceCurvatures().internalField()*nA; if (muFluidA().value() < SMALL) { tangentialSurfaceTensionForce = vector::zero; } } vectorField tnGradU = tangentialSurfaceTensionForce/(muFluidA().value() + VSMALL) - (fac::grad(Us())&aMesh().faceAreaNormals())().internalField(); vectorField UtPA = U().boundaryField()[aPatchID()].patchInternalField(); UtPA -= nA*(nA & UtPA); scalarField DnA = mesh().boundary()[aPatchID()].deltaCoeffs(); vectorField UtFs = UtPA + tnGradU/DnA; Us().internalField() = UtFs + UnFs; correctUsBoundaryConditions(); vectorField nGradU = tangentialSurfaceTensionForce/(muFluidA().value() + VSMALL) - nA*fac::div(Us())().internalField() - (fac::grad(Us())().internalField()&nA); if ( U().boundaryField()[aPatchID()].type() == fixedGradientCorrectedFvPatchField::typeName ) { fixedGradientCorrectedFvPatchField& aU = refCast > ( U().boundaryField()[aPatchID()] ); aU.gradient() = nGradU; } else if ( U().boundaryField()[aPatchID()].type() == fixedGradientFvPatchField::typeName ) { fixedGradientFvPatchField& aU = refCast > ( U().boundaryField()[aPatchID()] ); aU.gradient() = nGradU; } else { FatalErrorIn("freeSurface::updateVelocity()") << "Bounary condition on " << U().name() << " for freeSurface patch is " << U().boundaryField()[aPatchID()].type() << ", instead " << fixedGradientCorrectedFvPatchField::typeName << " or " << fixedGradientFvPatchField::typeName << abort(FatalError); } } } void freeSurface::updatePressure() { // Correct pressure boundary condition at the free-surface vectorField nA = mesh().boundary()[aPatchID()].nf(); if (twoFluids()) { scalarField pA = interpolatorBA().faceInterpolate ( p().boundaryField()[bPatchID()] ); const scalarField& K = aMesh().faceCurvatures().internalField(); Info << "Free surface curvature: min = " << gMin(K) << ", max = " << gMax(K) << ", average = " << gAverage(K) << endl << flush; if (cleanInterface()) { // pA -= cleanInterfaceSurfTension().value()*(K - gAverage(K)); pA -= cleanInterfaceSurfTension().value()*K; } else { scalarField surfTensionK = surfaceTension().internalField()*K; pA -= surfTensionK - gAverage(surfTensionK); } pA -= 2.0*(muFluidA().value() - muFluidB().value()) *fac::div(Us())().internalField(); // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]); vector R0 = vector::zero; pA -= (rhoFluidA().value() - rhoFluidB().value())* ( g_.value() & ( mesh().C().boundaryField()[aPatchID()] - R0 ) ); p().boundaryField()[aPatchID()] == pA; } else { // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]); vector R0 = vector::zero; scalarField pA = - rhoFluidA().value()* ( g_.value() & ( mesh().C().boundaryField()[aPatchID()] - R0 ) ); const scalarField& K = aMesh().faceCurvatures().internalField(); Info << "Free surface curvature: min = " << gMin(K) << ", max = " << gMax(K) << ", average = " << gAverage(K) << endl; if (cleanInterface()) { // pA -= cleanInterfaceSurfTension().value()*(K - gAverage(K)); pA -= cleanInterfaceSurfTension().value()*K; } else { scalarField surfTensionK = surfaceTension().internalField()*K; pA -= surfTensionK - gAverage(surfTensionK); } pA -= 2.0*muFluidA().value()*fac::div(Us())().internalField(); p().boundaryField()[aPatchID()] == pA; } // Set modified pressure at patches with fixed apsolute // pressure // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]); vector R0 = vector::zero; for (int patchI=0; patchI < p().boundaryField().size(); patchI++) { if ( p().boundaryField()[patchI].type() == fixedValueFvPatchScalarField::typeName ) { if (patchI != aPatchID()) { p().boundaryField()[patchI] == - rho().boundaryField()[patchI] *(g_.value()&(mesh().C().boundaryField()[patchI] - R0)); } } } } void freeSurface::updateSurfaceFlux() { Phis() = fac::interpolate(Us()) & aMesh().Le(); } void freeSurface::updateSurfactantConcentration() { if (!cleanInterface()) { Info << "Correct surfactant concentration" << endl << flush; updateSurfaceFlux(); // Crate and solve the surfactanta transport equation faScalarMatrix CsEqn ( fam::ddt(surfactantConcentration()) + fam::div(Phis(), surfactantConcentration()) - fam::laplacian ( surfactant().surfactDiffusion(), surfactantConcentration() ) ); if (surfactant().soluble()) { const scalarField& C = mesh().boundary()[aPatchID()] .lookupPatchField("C"); areaScalarField Cb ( IOobject ( "Cb", DB().timeName(), mesh(), IOobject::NO_READ, IOobject::NO_WRITE ), aMesh(), dimensioned("Cb", dimMoles/dimVolume, 0), zeroGradientFaPatchScalarField::typeName ); Cb.internalField() = C; Cb.correctBoundaryConditions(); CsEqn += fam::Sp ( surfactant().surfactAdsorptionCoeff()*Cb + surfactant().surfactAdsorptionCoeff() *surfactant().surfactDesorptionCoeff(), surfactantConcentration() ) - surfactant().surfactAdsorptionCoeff() *Cb*surfactant().surfactSaturatedConc(); } CsEqn.solve(); Info << "Correct surface tension" << endl; surfaceTension() = cleanInterfaceSurfTension() + surfactant().surfactR() *surfactant().surfactT() *surfactant().surfactSaturatedConc() *log(1.0 - surfactantConcentration() /surfactant().surfactSaturatedConc()); if (neg(min(surfaceTension().internalField()))) { FatalErrorIn ( "void freeSurface::correctSurfactantConcentration()" ) << "Surface tension is negative" << abort(FatalError); } } } void freeSurface::correctUsBoundaryConditions() { forAll (Us().boundaryField(), patchI) { if ( Us().boundaryField()[patchI].type() == calculatedFaPatchVectorField::typeName ) { vectorField& pUs = Us().boundaryField()[patchI]; pUs = Us().boundaryField()[patchI].patchInternalField(); label ngbPolyPatchID = aMesh().boundary()[patchI].ngbPolyPatchIndex(); if (ngbPolyPatchID != -1) { if ( ( isA ( U().boundaryField()[ngbPolyPatchID] ) ) || ( isA ( U().boundaryField()[ngbPolyPatchID] ) ) ) { vectorField N = aMesh().boundary()[patchI].ngbPolyPatchFaceNormals(); pUs -= N*(N&pUs); } } } } Us().correctBoundaryConditions(); } vector freeSurface::totalPressureForce() const { const scalarField& S = aMesh().S(); const vectorField& n = aMesh().faceAreaNormals().internalField(); const scalarField& P = p().boundaryField()[aPatchID()]; vectorField pressureForces = S*P*n; return gSum(pressureForces); } vector freeSurface::totalViscousForce() const { const scalarField& S = aMesh().S(); const vectorField& n = aMesh().faceAreaNormals().internalField(); vectorField nGradU = U().boundaryField()[aPatchID()].snGrad(); vectorField viscousForces = - muFluidA().value()*S *( nGradU + (fac::grad(Us())().internalField()&n) - (n*fac::div(Us())().internalField()) ); return gSum(viscousForces); } vector freeSurface::totalSurfaceTensionForce() const { const scalarField& S = aMesh().S(); const vectorField& n = aMesh().faceAreaNormals().internalField(); const scalarField& K = aMesh().faceCurvatures().internalField(); vectorField surfTensionForces(n.size(), vector::zero); if (cleanInterface()) { surfTensionForces = S*cleanInterfaceSurfTension().value() *fac::edgeIntegrate ( aMesh().Le()*aMesh().edgeLengthCorrection() )().internalField(); } else { surfTensionForces *= surfaceTension().internalField()*K; } return gSum(surfTensionForces); } void freeSurface::initializeControlPointsPosition() { scalarField deltaH = scalarField(aMesh().nFaces(), 0.0); pointField displacement = pointDisplacement(deltaH); const faceList& faces = aMesh().faces(); const pointField& points = aMesh().points(); pointField newPoints = points + displacement; scalarField sweptVol(faces.size(), 0.0); forAll (faces, faceI) { sweptVol[faceI] = -faces[faceI].sweptVol(points, newPoints); } vectorField faceArea(faces.size(), vector::zero); forAll (faceArea, faceI) { faceArea[faceI] = faces[faceI].normal(newPoints); } forAll (deltaH, faceI) { deltaH[faceI] = sweptVol[faceI]/ (faceArea[faceI] & facesDisplacementDir()[faceI]); } displacement = pointDisplacement(deltaH); } scalar freeSurface::maxCourantNumber() { scalar CoNum = 0; if (cleanInterface()) { const scalarField& dE = aMesh().lPN(); CoNum = gMax ( DB().deltaT().value()/ sqrt ( rhoFluidA().value()*dE*dE*dE/ 2.0/M_PI/(cleanInterfaceSurfTension().value() + SMALL) ) ); } else { scalarField sigmaE = linearEdgeInterpolate(surfaceTension())().internalField() + SMALL; const scalarField& dE =aMesh().lPN(); CoNum = gMax ( DB().deltaT().value()/ sqrt ( rhoFluidA().value()*dE*dE*dE/ 2.0/M_PI/sigmaE ) ); } return CoNum; } void freeSurface::updateProperties() { muFluidA_ = dimensionedScalar(this->lookup("muFluidA")); muFluidB_ = dimensionedScalar(this->lookup("muFluidB")); rhoFluidA_ = dimensionedScalar(this->lookup("rhoFluidA")); rhoFluidB_ = dimensionedScalar(this->lookup("rhoFluidB")); g_ = dimensionedVector(this->lookup("g")); cleanInterfaceSurfTension_ = dimensionedScalar(this->lookup("surfaceTension")); } void freeSurface::writeVTK() const { aMesh().patch().writeVTK ( DB().timePath()/"freeSurface", aMesh().patch(), aMesh().patch().points() ); } void freeSurface::writeVTKControlPoints() { // Write patch and points into VTK fileName name(DB().timePath()/"freeSurfaceControlPoints"); OFstream mps(name + ".vtk"); mps << "# vtk DataFile Version 2.0" << nl << name << ".vtk" << nl << "ASCII" << nl << "DATASET POLYDATA" << nl << "POINTS " << controlPoints().size() << " float" << nl; forAll (controlPoints(), pointI) { mps << controlPoints()[pointI].x() << ' ' << controlPoints()[pointI].y() << ' ' << controlPoints()[pointI].z() << nl; } // Write vertices mps << "VERTICES " << controlPoints().size() << ' ' << controlPoints().size()*2 << nl; forAll (controlPoints(), pointI) { mps << 1 << ' ' << pointI << nl; } } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam // ************************************************************************* //