1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 \*---------------------------------------------------------------------------*/
29 #include "freeSurface.H"
31 #include "volFields.H"
32 #include "transformField.H"
34 #include "emptyFaPatch.H"
35 #include "wedgeFaPatch.H"
36 #include "wallFvPatch.H"
38 #include "EulerDdtScheme.H"
39 #include "CrankNicholsonDdtScheme.H"
40 #include "backwardDdtScheme.H"
42 #include "tetFemMatrices.H"
43 #include "tetPointFields.H"
44 #include "faceTetPolyPatch.H"
45 #include "tetPolyPatchInterpolation.H"
46 #include "fixedValueTetPolyPatchFields.H"
47 #include "fixedValuePointPatchFields.H"
48 #include "twoDPointCorrector.H"
50 #include "slipFvPatchFields.H"
51 #include "symmetryFvPatchFields.H"
52 #include "fixedGradientFvPatchFields.H"
53 #include "zeroGradientCorrectedFvPatchFields.H"
54 #include "fixedGradientCorrectedFvPatchFields.H"
55 #include "fixedValueCorrectedFvPatchFields.H"
57 #include "primitivePatchInterpolation.H"
59 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
64 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
66 defineTypeNameAndDebug(freeSurface, 0);
69 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
71 void freeSurface::clearOut()
73 deleteDemandDrivenData(interpolatorABPtr_);
74 deleteDemandDrivenData(interpolatorBAPtr_);
75 deleteDemandDrivenData(controlPointsPtr_);
76 deleteDemandDrivenData(motionPointsMaskPtr_);
77 deleteDemandDrivenData(pointsDisplacementDirPtr_);
78 deleteDemandDrivenData(facesDisplacementDirPtr_);
79 deleteDemandDrivenData(totalDisplacementPtr_);
80 deleteDemandDrivenData(aMeshPtr_);
81 deleteDemandDrivenData(UsPtr_);
82 deleteDemandDrivenData(phisPtr_);
83 deleteDemandDrivenData(surfactConcPtr_);
84 deleteDemandDrivenData(surfaceTensionPtr_);
85 deleteDemandDrivenData(surfactantPtr_);
86 deleteDemandDrivenData(fluidIndicatorPtr_);
89 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
91 freeSurface::freeSurface
94 const volScalarField& rho,
97 const surfaceScalarField& sfPhi
104 "freeSurfaceProperties",
105 Ub.mesh().time().constant(),
116 curTimeIndex_(Ub.mesh().time().timeIndex()),
119 this->lookup("twoFluids")
123 this->lookup("normalMotionDir")
128 this->lookup("cleanInterface")
134 this->lookup("muFluidA")
138 this->lookup("muFluidB")
142 this->lookup("rhoFluidA")
146 this->lookup("rhoFluidB")
148 g_(this->lookup("g")),
149 cleanInterfaceSurfTension_
151 this->lookup("surfaceTension")
153 fixedFreeSurfacePatches_
155 this->lookup("fixedFreeSurfacePatches")
157 pointNormalsCorrectionPatches_
159 this->lookup("pointNormalsCorrectionPatches")
163 readInt(this->lookup("nFreeSurfaceCorrectors"))
166 interpolatorABPtr_(NULL),
167 interpolatorBAPtr_(NULL),
168 controlPointsPtr_(NULL),
169 motionPointsMaskPtr_(NULL),
170 pointsDisplacementDirPtr_(NULL),
171 facesDisplacementDirPtr_(NULL),
172 totalDisplacementPtr_(NULL),
176 surfactConcPtr_(NULL),
177 surfaceTensionPtr_(NULL),
178 surfactantPtr_(NULL),
179 fluidIndicatorPtr_(NULL)
181 //Read motion direction
182 if (!normalMotionDir_)
184 motionDir_ = vector(this->lookup("motionDir"));
185 motionDir_ /= mag(motionDir_) + SMALL;
188 // Set point normal correction patches
189 boolList& correction = aMesh().correctPatchPointNormals();
191 forAll(pointNormalsCorrectionPatches_, patchI)
193 word patchName = pointNormalsCorrectionPatches_[patchI];
195 label patchID = aMesh().boundary().findPatchID(patchName);
201 "freeSurface::freeSurface(...)"
202 ) << "Patch name for point normals correction does not exist"
203 << abort(FatalError);
206 correction[patchID] = true;
210 aMesh().movePoints();
213 // Detect the free surface patch
214 forAll (mesh().boundary(), patchI)
216 if(mesh().boundary()[patchI].name() == "freeSurface")
220 Info<< "Found free surface patch. ID: " << aPatchID_
227 FatalErrorIn("freeSurface::freeSurface(...)")
228 << "Free surface patch not defined. Please make sure that "
229 << " the free surface patches is named as freeSurface"
230 << abort(FatalError);
234 // Detect the free surface shadow patch
237 forAll (mesh().boundary(), patchI)
239 if(mesh().boundary()[patchI].name() == "freeSurfaceShadow")
243 Info<< "Found free surface shadow patch. ID: "
244 << bPatchID_ << endl;
250 FatalErrorIn("freeSurface::freeSurface(...)")
251 << "Free surface shadow patch not defined. "
252 << "Please make sure that the free surface shadow patch "
253 << "is named as freeSurfaceShadow."
254 << abort(FatalError);
259 // Mark free surface boundary points
260 // which belonge to processor patches
261 forAll(aMesh().boundary(), patchI)
265 aMesh().boundary()[patchI].type()
266 == processorFaPatch::typeName
269 const labelList& patchPoints =
270 aMesh().boundary()[patchI].pointLabels();
272 forAll(patchPoints, pointI)
274 motionPointsMask()[patchPoints[pointI]] = -1;
280 // Mark fixed free surface boundary points
281 forAll(fixedFreeSurfacePatches_, patchI)
284 aMesh().boundary().findPatchID
286 fixedFreeSurfacePatches_[patchI]
289 if(fixedPatchID == -1)
291 FatalErrorIn("freeSurface::freeSurface(...)")
292 << "Wrong faPatch name in the fixedFreeSurfacePatches list"
293 << " defined in the freeSurfaceProperties dictionary"
294 << abort(FatalError);
297 const labelList& patchPoints =
298 aMesh().boundary()[fixedPatchID].pointLabels();
300 forAll(patchPoints, pointI)
302 motionPointsMask()[patchPoints[pointI]] = 0;
307 // Mark free-surface boundary point
308 // at the axis of 2-D axisymmetic cases
309 forAll(aMesh().boundary(), patchI)
313 aMesh().boundary()[patchI].type()
314 == wedgeFaPatch::typeName
317 const wedgeFaPatch& wedgePatch =
318 refCast<const wedgeFaPatch>(aMesh().boundary()[patchI]);
320 if(wedgePatch.axisPoint() > -1)
322 motionPointsMask()[wedgePatch.axisPoint()] = 0;
324 Info << "Axis point: "
325 << wedgePatch.axisPoint()
327 << aMesh().points()[wedgePatch.axisPoint()] << endl;
333 // Read free-surface points total displacement if present
334 readTotalDisplacement();
337 // Read control points positions if present
341 // Check if smoothing switch is set
342 if (this->found("smoothing"))
344 smoothing_ = Switch(this->lookup("smoothing"));
349 // * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * * //
351 freeSurface::~freeSurface()
357 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
359 void freeSurface::updateDisplacementDirections()
361 if(normalMotionDir())
363 // Update point displacement correction
364 pointsDisplacementDir() = aMesh().pointAreaNormals();
366 // Correcte point displacement direction
367 // at the "centerline" symmetryPlane which represents the axis
368 // of an axisymmetric case
369 forAll(aMesh().boundary(), patchI)
371 if(aMesh().boundary()[patchI].type() == wedgeFaPatch::typeName)
373 const wedgeFaPatch& wedgePatch =
374 refCast<const wedgeFaPatch>(aMesh().boundary()[patchI]);
376 vector axis = wedgePatch.axis();
378 label centerLinePatchID =
379 aMesh().boundary().findPatchID("centerline");
381 if(centerLinePatchID != -1)
383 const labelList& pointLabels =
384 aMesh().boundary()[centerLinePatchID].pointLabels();
386 forAll(pointLabels, pointI)
389 pointsDisplacementDir()[pointLabels[pointI]];
391 dir = (dir&axis)*axis;
394 pointsDisplacementDir()[pointLabels[pointI]] = dir;
399 Info << "Warning: centerline polyPatch does not exist. "
400 << "Free surface points displacement directions "
401 << "will not be corrected at the axis (centerline)"
409 // Update face displacement direction
410 facesDisplacementDir() =
411 aMesh().faceAreaNormals().internalField();
413 // Correction of control points postion
414 const vectorField& Cf = aMesh().areaCentres().internalField();
417 facesDisplacementDir()
418 *(facesDisplacementDir()&(controlPoints() - Cf))
424 bool freeSurface::predictPoints()
430 controlPoints() = aMesh().areaCentres().internalField();
431 movePoints(scalarField(controlPoints().size(), 0));
432 movePoints(-fvc::meshPhi(rho(),U())().boundaryField()[aPatchID()]);
438 freeSurfCorr<nFreeSurfCorr_;
442 movePoints(phi_.boundaryField()[aPatchID()]);
449 bool freeSurface::correctPoints()
454 freeSurfCorr<nFreeSurfCorr_;
458 movePoints(phi_.boundaryField()[aPatchID()]);
465 bool freeSurface::movePoints(const scalarField& interfacePhi)
467 pointField newMeshPoints = mesh().points();
469 scalarField sweptVolCorr =
471 - fvc::meshPhi(rho(),U())().boundaryField()[aPatchID()];
475 mesh().schemesDict().ddtScheme
477 "ddt(" + rho().name() + ',' + U().name()+')'
484 == fv::CrankNicholsonDdtScheme<vector>::typeName
487 sweptVolCorr *= (1.0/2.0)*DB().deltaT().value();
492 == fv::EulerDdtScheme<vector>::typeName
495 sweptVolCorr *= DB().deltaT().value();
500 == fv::backwardDdtScheme<vector>::typeName
503 if (DB().timeIndex() == 1)
505 sweptVolCorr *= DB().deltaT().value();
509 sweptVolCorr *= (2.0/3.0)*DB().deltaT().value();
514 FatalErrorIn("freeSurface::movePoints()")
515 << "Unsupported temporal differencing scheme : "
517 << abort(FatalError);
520 const scalarField& Sf = aMesh().S();
521 const vectorField& Nf = aMesh().faceAreaNormals().internalField();
524 sweptVolCorr/(Sf*(Nf & facesDisplacementDir()));
526 pointField displacement = pointDisplacement(deltaH);
529 // Move only free-surface points
531 const labelList& meshPointsA =
532 mesh().boundaryMesh()[aPatchID()].meshPoints();
534 forAll (displacement, pointI)
536 newMeshPoints[meshPointsA[pointI]] += displacement[pointI];
541 const labelList& meshPointsB =
542 mesh().boundaryMesh()[bPatchID_].meshPoints();
544 pointField displacementB =
545 interpolatorAB().pointInterpolate
550 forAll (displacementB, pointI)
552 newMeshPoints[meshPointsB[pointI]] += displacementB[pointI];
556 // Update total displacement field
558 if(totalDisplacementPtr_ && (curTimeIndex_ < DB().timeIndex()))
560 FatalErrorIn("freeSurface::movePoints()")
561 << "Total displacement of free surface points "
562 << "from previous time step is not absorbed by the mesh."
563 << abort(FatalError);
565 else if (curTimeIndex_ < DB().timeIndex())
567 totalDisplacement() = displacement;
569 curTimeIndex_ = DB().timeIndex();
573 totalDisplacement() += displacement;
576 twoDPointCorrector twoDPointCorr(mesh());
578 twoDPointCorr.correctPoints(newMeshPoints);
580 mesh().movePoints(newMeshPoints);
582 // faMesh motion is done automatically, using meshObject
584 // aMesh().movePoints(mesh().points());
587 // Move correctedFvPatchField fvSubMeshes
589 forAll(U().boundaryField(), patchI)
594 U().boundaryField()[patchI].type()
595 == fixedGradientCorrectedFvPatchField<vector>::typeName
599 U().boundaryField()[patchI].type()
600 == fixedValueCorrectedFvPatchField<vector>::typeName
604 U().boundaryField()[patchI].type()
605 == zeroGradientCorrectedFvPatchField<vector>::typeName
609 correctedFvPatchField<vector>& pU =
610 refCast<correctedFvPatchField<vector> >
612 U().boundaryField()[patchI]
615 pU.movePatchSubMesh();
619 forAll(p().boundaryField(), patchI)
624 p().boundaryField()[patchI].type()
625 == fixedGradientCorrectedFvPatchField<scalar>::typeName
629 p().boundaryField()[patchI].type()
630 == fixedValueCorrectedFvPatchField<scalar>::typeName
634 p().boundaryField()[patchI].type()
635 == zeroGradientCorrectedFvPatchField<scalar>::typeName
639 correctedFvPatchField<scalar>& pP =
640 refCast<correctedFvPatchField<scalar> >
642 p().boundaryField()[patchI]
645 pP.movePatchSubMesh();
653 bool freeSurface::moveMeshPointsForOldFreeSurfDisplacement()
655 if(totalDisplacementPtr_)
657 pointField newPoints = mesh().points();
659 const labelList& meshPointsA =
660 mesh().boundaryMesh()[aPatchID()].meshPoints();
662 forAll (totalDisplacement(), pointI)
664 newPoints[meshPointsA[pointI]] -= totalDisplacement()[pointI];
668 // Check mesh motion solver type
669 bool feMotionSolver =
670 mesh().objectRegistry::foundObject<tetPointVectorField>
674 bool fvMotionSolver =
675 mesh().objectRegistry::foundObject<pointVectorField>
682 tetPointVectorField& motionU =
683 const_cast<tetPointVectorField&>
685 mesh().objectRegistry::
686 lookupObject<tetPointVectorField>
692 fixedValueTetPolyPatchVectorField& motionUaPatch =
693 refCast<fixedValueTetPolyPatchVectorField>
695 motionU.boundaryField()[aPatchID()]
698 tetPolyPatchInterpolation tppiAPatch
700 refCast<const faceTetPolyPatch>
702 motionUaPatch.patch()
707 tppiAPatch.pointToPointInterpolate
709 totalDisplacement()/DB().deltaT().value()
714 const labelList& meshPointsB =
715 mesh().boundaryMesh()[bPatchID()].meshPoints();
717 pointField totDisplacementB =
718 interpolatorAB().pointInterpolate
723 forAll (totDisplacementB, pointI)
725 newPoints[meshPointsB[pointI]] -=
726 totDisplacementB[pointI];
729 fixedValueTetPolyPatchVectorField& motionUbPatch =
730 refCast<fixedValueTetPolyPatchVectorField>
732 motionU.boundaryField()[bPatchID()]
735 tetPolyPatchInterpolation tppiBPatch
737 refCast<const faceTetPolyPatch>(motionUbPatch.patch())
741 tppiBPatch.pointToPointInterpolate
743 totDisplacementB/DB().deltaT().value()
747 else if (fvMotionSolver)
749 pointVectorField& motionU =
750 const_cast<pointVectorField&>
752 mesh().objectRegistry::
753 lookupObject<pointVectorField>
759 fixedValuePointPatchVectorField& motionUaPatch =
760 refCast<fixedValuePointPatchVectorField>
762 motionU.boundaryField()[aPatchID()]
766 totalDisplacement()/DB().deltaT().value();
770 const labelList& meshPointsB =
771 mesh().boundaryMesh()[bPatchID()].meshPoints();
773 pointField totDisplacementB =
774 interpolatorAB().pointInterpolate
779 forAll (totDisplacementB, pointI)
781 newPoints[meshPointsB[pointI]] -=
782 totDisplacementB[pointI];
785 fixedValuePointPatchVectorField& motionUbPatch =
786 refCast<fixedValuePointPatchVectorField>
788 motionU.boundaryField()[bPatchID()]
792 totDisplacementB/DB().deltaT().value();
796 twoDPointCorrector twoDPointCorr(mesh());
798 twoDPointCorr.correctPoints(newPoints);
800 mesh().movePoints(newPoints);
802 deleteDemandDrivenData(totalDisplacementPtr_);
806 // faMesh motion is done automatically, using meshObject
808 // aMesh().movePoints(mesh().points());
810 // Move correctedFvPatchField fvSubMeshes
812 forAll(U().boundaryField(), patchI)
817 U().boundaryField()[patchI].type()
818 == fixedGradientCorrectedFvPatchField<vector>::typeName
822 U().boundaryField()[patchI].type()
823 == fixedValueCorrectedFvPatchField<vector>::typeName
827 U().boundaryField()[patchI].type()
828 == zeroGradientCorrectedFvPatchField<vector>::typeName
832 correctedFvPatchField<vector>& aU =
833 refCast<correctedFvPatchField<vector> >
835 U().boundaryField()[patchI]
838 aU.movePatchSubMesh();
842 forAll(p().boundaryField(), patchI)
847 p().boundaryField()[patchI].type()
848 == fixedGradientCorrectedFvPatchField<scalar>::typeName
852 p().boundaryField()[patchI].type()
853 == fixedValueCorrectedFvPatchField<scalar>::typeName
857 p().boundaryField()[patchI].type()
858 == zeroGradientCorrectedFvPatchField<scalar>::typeName
862 correctedFvPatchField<scalar>& aP =
863 refCast<correctedFvPatchField<scalar> >
865 p().boundaryField()[patchI]
868 aP.movePatchSubMesh();
877 bool freeSurface::moveMeshPoints()
879 scalarField sweptVolCorr =
880 phi_.boundaryField()[aPatchID()]
881 - fvc::meshPhi(rho(),U())().boundaryField()[aPatchID()];
885 mesh().schemesDict().ddtScheme
887 "ddt(" + rho().name() + ',' + U().name()+')'
894 == fv::CrankNicholsonDdtScheme<vector>::typeName
897 sweptVolCorr *= (1.0/2.0)*DB().deltaT().value();
902 == fv::EulerDdtScheme<vector>::typeName
905 sweptVolCorr *= DB().deltaT().value();
910 == fv::backwardDdtScheme<vector>::typeName
913 sweptVolCorr *= (2.0/3.0)*DB().deltaT().value();
917 FatalErrorIn("freeSurface::movePoints()")
918 << "Unsupported temporal differencing scheme : "
920 << abort(FatalError);
924 const scalarField& Sf = aMesh().S();
925 const vectorField& Nf = aMesh().faceAreaNormals().internalField();
928 sweptVolCorr/(Sf*(Nf & facesDisplacementDir()));
931 pointField displacement = pointDisplacement(deltaH);
934 //-- Set mesh motion boundary conditions
936 tetPointVectorField& motionU =
937 const_cast<tetPointVectorField&>
939 mesh().objectRegistry::
940 lookupObject<tetPointVectorField>
946 fixedValueTetPolyPatchVectorField& motionUaPatch =
947 refCast<fixedValueTetPolyPatchVectorField>
949 motionU.boundaryField()[aPatchID()]
952 tetPolyPatchInterpolation tppiAPatch
954 refCast<const faceTetPolyPatch>
956 motionUaPatch.patch()
961 tppiAPatch.pointToPointInterpolate
963 displacement/DB().deltaT().value()
968 fixedValueTetPolyPatchVectorField& motionUbPatch =
969 refCast<fixedValueTetPolyPatchVectorField>
971 motionU.boundaryField()[bPatchID()]
974 tetPolyPatchInterpolation tppiBPatch
976 refCast<const faceTetPolyPatch>(motionUbPatch.patch())
980 tppiBPatch.pointToPointInterpolate
982 interpolatorAB().pointInterpolate
984 displacement/DB().deltaT().value()
991 // faMesh motion is done automatically, using meshObject
993 // aMesh().movePoints(mesh().points());
996 // Move correctedFvPatchField fvSubMeshes
998 forAll(U().boundaryField(), patchI)
1003 U().boundaryField()[patchI].type()
1004 == fixedGradientCorrectedFvPatchField<vector>::typeName
1008 U().boundaryField()[patchI].type()
1009 == fixedValueCorrectedFvPatchField<vector>::typeName
1013 U().boundaryField()[patchI].type()
1014 == zeroGradientCorrectedFvPatchField<vector>::typeName
1018 correctedFvPatchField<vector>& aU =
1019 refCast<correctedFvPatchField<vector> >
1021 U().boundaryField()[patchI]
1024 aU.movePatchSubMesh();
1028 forAll(p().boundaryField(), patchI)
1033 p().boundaryField()[patchI].type()
1034 == fixedGradientCorrectedFvPatchField<scalar>::typeName
1038 p().boundaryField()[patchI].type()
1039 == fixedValueCorrectedFvPatchField<scalar>::typeName
1043 p().boundaryField()[patchI].type()
1044 == zeroGradientCorrectedFvPatchField<scalar>::typeName
1048 correctedFvPatchField<scalar>& aP =
1049 refCast<correctedFvPatchField<scalar> >
1051 p().boundaryField()[patchI]
1054 aP.movePatchSubMesh();
1062 void freeSurface::updateBoundaryConditions()
1065 updateSurfactantConcentration();
1070 void freeSurface::updateVelocity()
1074 vectorField nA = mesh().boundary()[aPatchID()].nf();
1076 vectorField nB = mesh().boundary()[bPatchID()].nf();
1078 scalarField DnB = interpolatorBA().faceInterpolate
1080 mesh().boundary()[bPatchID()].deltaCoeffs()
1083 scalarField DnA = mesh().boundary()[aPatchID()].deltaCoeffs();
1087 U().boundaryField()[aPatchID()].patchInternalField();
1091 U().boundaryField()[aPatchID()].type()
1092 == fixedGradientCorrectedFvPatchField<vector>::typeName
1095 fixedGradientCorrectedFvPatchField<vector>& aU =
1096 refCast<fixedGradientCorrectedFvPatchField<vector> >
1098 U().boundaryField()[aPatchID()]
1101 UtPA += aU.corrVecGrad();
1104 UtPA -= nA*(nA & UtPA);
1107 vectorField UtPB = interpolatorBA().faceInterpolate
1109 U().boundaryField()[bPatchID()].patchInternalField()
1114 U().boundaryField()[bPatchID()].type()
1115 == fixedValueCorrectedFvPatchField<vector>::typeName
1118 fixedValueCorrectedFvPatchField<vector>& bU =
1119 refCast<fixedValueCorrectedFvPatchField<vector> >
1121 U().boundaryField()[bPatchID()]
1124 UtPB += interpolatorBA().faceInterpolate(bU.corrVecGrad());
1127 UtPB -= nA*(nA & UtPB);
1129 vectorField UtFs = muFluidA().value()*DnA*UtPA
1130 + muFluidB().value()*DnB*UtPB;
1133 nA*phi_.boundaryField()[aPatchID()]
1134 /mesh().boundary()[aPatchID()].magSf();
1136 Us().internalField() += UnFs - nA*(nA&Us().internalField());
1137 correctUsBoundaryConditions();
1139 UtFs -= (muFluidA().value() - muFluidB().value())*
1140 (fac::grad(Us())&aMesh().faceAreaNormals())().internalField();
1143 vectorField tangentialSurfaceTensionForce(nA.size(), vector::zero);
1145 if(!cleanInterface())
1147 tangentialSurfaceTensionForce =
1148 surfaceTensionGrad()().internalField();
1152 vectorField surfaceTensionForce =
1153 cleanInterfaceSurfTension().value()
1156 aMesh().Le()*aMesh().edgeLengthCorrection()
1157 )().internalField();
1159 tangentialSurfaceTensionForce =
1161 - cleanInterfaceSurfTension().value()
1162 *aMesh().faceCurvatures().internalField()*nA;
1165 UtFs += tangentialSurfaceTensionForce;
1167 UtFs /= muFluidA().value()*DnA + muFluidB().value()*DnB + VSMALL;
1169 Us().internalField() = UnFs + UtFs;
1170 correctUsBoundaryConditions();
1172 // Store old-time velocity field U()
1175 U().boundaryField()[bPatchID()] ==
1176 interpolatorAB().faceInterpolate(UtFs)
1177 + nB*fvc::meshPhi(rho(),U())().boundaryField()[bPatchID()]/
1178 mesh().boundary()[bPatchID()].magSf();
1182 p().boundaryField()[bPatchID()].type()
1183 == fixedGradientFvPatchField<scalar>::typeName
1186 fixedGradientFvPatchField<scalar>& pB =
1187 refCast<fixedGradientFvPatchField<scalar> >
1189 p().boundaryField()[bPatchID()]
1193 - rhoFluidB().value()
1195 nB&fvc::ddt(U())().boundaryField()[bPatchID()]
1200 // Update fixedGradient boundary condition on patch A
1202 vectorField nGradU =
1203 muFluidB().value()*(UtPB - UtFs)*DnA
1204 + tangentialSurfaceTensionForce
1205 - muFluidA().value()*nA*fac::div(Us())().internalField()
1206 + (muFluidB().value() - muFluidA().value())
1207 *(fac::grad(Us())().internalField()&nA);
1209 nGradU /= muFluidA().value() + VSMALL;
1213 U().boundaryField()[aPatchID()].type()
1214 == fixedGradientCorrectedFvPatchField<vector>::typeName
1217 fixedGradientCorrectedFvPatchField<vector>& aU =
1218 refCast<fixedGradientCorrectedFvPatchField<vector> >
1220 U().boundaryField()[aPatchID()]
1223 aU.gradient() = nGradU;
1227 U().boundaryField()[aPatchID()].type()
1228 == fixedGradientFvPatchField<vector>::typeName
1231 fixedGradientFvPatchField<vector>& aU =
1232 refCast<fixedGradientFvPatchField<vector> >
1234 U().boundaryField()[aPatchID()]
1237 aU.gradient() = nGradU;
1241 FatalErrorIn("freeSurface::updateVelocity()")
1242 << "Bounary condition on " << U().name()
1243 << " for freeSurface patch is "
1244 << U().boundaryField()[aPatchID()].type()
1246 << fixedGradientCorrectedFvPatchField<vector>::typeName
1248 << fixedGradientFvPatchField<vector>::typeName
1249 << abort(FatalError);
1254 vectorField nA = aMesh().faceAreaNormals().internalField();
1257 nA*phi_.boundaryField()[aPatchID()]
1258 /mesh().boundary()[aPatchID()].magSf();
1260 // Correct normal component of free-surface velocity
1261 Us().internalField() += UnFs - nA*(nA&Us().internalField());
1262 correctUsBoundaryConditions();
1264 vectorField tangentialSurfaceTensionForce(nA.size(), vector::zero);
1266 if(!cleanInterface())
1268 tangentialSurfaceTensionForce =
1269 surfaceTensionGrad()().internalField();
1273 vectorField surfaceTensionForce =
1274 cleanInterfaceSurfTension().value()
1277 aMesh().Le()*aMesh().edgeLengthCorrection()
1278 )().internalField();
1280 tangentialSurfaceTensionForce =
1282 - cleanInterfaceSurfTension().value()
1283 *aMesh().faceCurvatures().internalField()*nA;
1285 if (muFluidA().value() < SMALL)
1287 tangentialSurfaceTensionForce = vector::zero;
1291 vectorField tnGradU =
1292 tangentialSurfaceTensionForce/(muFluidA().value() + VSMALL)
1293 - (fac::grad(Us())&aMesh().faceAreaNormals())().internalField();
1296 U().boundaryField()[aPatchID()].patchInternalField();
1297 UtPA -= nA*(nA & UtPA);
1299 scalarField DnA = mesh().boundary()[aPatchID()].deltaCoeffs();
1301 vectorField UtFs = UtPA + tnGradU/DnA;
1303 Us().internalField() = UtFs + UnFs;
1304 correctUsBoundaryConditions();
1306 vectorField nGradU =
1307 tangentialSurfaceTensionForce/(muFluidA().value() + VSMALL)
1308 - nA*fac::div(Us())().internalField()
1309 - (fac::grad(Us())().internalField()&nA);
1313 U().boundaryField()[aPatchID()].type()
1314 == fixedGradientCorrectedFvPatchField<vector>::typeName
1317 fixedGradientCorrectedFvPatchField<vector>& aU =
1318 refCast<fixedGradientCorrectedFvPatchField<vector> >
1320 U().boundaryField()[aPatchID()]
1323 aU.gradient() = nGradU;
1327 U().boundaryField()[aPatchID()].type()
1328 == fixedGradientFvPatchField<vector>::typeName
1331 fixedGradientFvPatchField<vector>& aU =
1332 refCast<fixedGradientFvPatchField<vector> >
1334 U().boundaryField()[aPatchID()]
1337 aU.gradient() = nGradU;
1341 FatalErrorIn("freeSurface::updateVelocity()")
1342 << "Bounary condition on " << U().name()
1343 << " for freeSurface patch is "
1344 << U().boundaryField()[aPatchID()].type()
1346 << fixedGradientCorrectedFvPatchField<vector>::typeName
1348 << fixedGradientFvPatchField<vector>::typeName
1349 << abort(FatalError);
1355 void freeSurface::updatePressure()
1357 // Correct pressure boundary condition at the free-surface
1359 vectorField nA = mesh().boundary()[aPatchID()].nf();
1364 interpolatorBA().faceInterpolate
1366 p().boundaryField()[bPatchID()]
1369 const scalarField& K = aMesh().faceCurvatures().internalField();
1371 Info << "Free surface curvature: min = " << gMin(K)
1372 << ", max = " << gMax(K)
1373 << ", average = " << gAverage(K) << endl << flush;
1375 if(cleanInterface())
1377 // pA -= cleanInterfaceSurfTension().value()*(K - gAverage(K));
1378 pA -= cleanInterfaceSurfTension().value()*K;
1382 scalarField surfTensionK =
1383 surfaceTension().internalField()*K;
1385 pA -= surfTensionK - gAverage(surfTensionK);
1388 pA -= 2.0*(muFluidA().value() - muFluidB().value())
1389 *fac::div(Us())().internalField();
1391 // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]);
1392 vector R0 = vector::zero;
1394 pA -= (rhoFluidA().value() - rhoFluidB().value())*
1398 mesh().C().boundaryField()[aPatchID()]
1403 p().boundaryField()[aPatchID()] == pA;
1407 // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]);
1408 vector R0 = vector::zero;
1411 - rhoFluidA().value()*
1415 mesh().C().boundaryField()[aPatchID()]
1420 const scalarField& K = aMesh().faceCurvatures().internalField();
1422 Info << "Free surface curvature: min = " << gMin(K)
1423 << ", max = " << gMax(K) << ", average = " << gAverage(K)
1426 if(cleanInterface())
1428 // pA -= cleanInterfaceSurfTension().value()*(K - gAverage(K));
1429 pA -= cleanInterfaceSurfTension().value()*K;
1433 scalarField surfTensionK =
1434 surfaceTension().internalField()*K;
1436 pA -= surfTensionK - gAverage(surfTensionK);
1439 pA -= 2.0*muFluidA().value()*fac::div(Us())().internalField();
1441 p().boundaryField()[aPatchID()] == pA;
1445 // Set modified pressure at patches with fixed apsolute
1448 // vector R0 = gAverage(mesh().C().boundaryField()[aPatchID()]);
1449 vector R0 = vector::zero;
1451 for (int patchI=0; patchI < p().boundaryField().size(); patchI++)
1455 p().boundaryField()[patchI].type()
1456 == fixedValueFvPatchScalarField::typeName
1459 if (patchI != aPatchID())
1461 p().boundaryField()[patchI] ==
1462 - rho().boundaryField()[patchI]
1463 *(g_.value()&(mesh().C().boundaryField()[patchI] - R0));
1470 void freeSurface::updateSurfaceFlux()
1472 Phis() = fac::interpolate(Us()) & aMesh().Le();
1476 void freeSurface::updateSurfactantConcentration()
1478 if(!cleanInterface())
1480 Info << "Correct surfactant concentration" << endl << flush;
1482 updateSurfaceFlux();
1484 // Crate and solve the surfactanta transport equation
1485 faScalarMatrix CsEqn
1487 fam::ddt(surfactantConcentration())
1488 + fam::div(Phis(), surfactantConcentration())
1491 surfactant().surfactDiffusion(),
1492 surfactantConcentration()
1497 if(surfactant().soluble())
1499 const scalarField& C =
1500 mesh().boundary()[aPatchID()]
1501 .lookupPatchField<volScalarField, scalar>("C");
1514 dimensioned<scalar>("Cb", dimMoles/dimVolume, 0),
1515 zeroGradientFaPatchScalarField::typeName
1518 Cb.internalField() = C;
1519 Cb.correctBoundaryConditions();
1524 surfactant().surfactAdsorptionCoeff()*Cb
1525 + surfactant().surfactAdsorptionCoeff()
1526 *surfactant().surfactDesorptionCoeff(),
1527 surfactantConcentration()
1529 - surfactant().surfactAdsorptionCoeff()
1530 *Cb*surfactant().surfactSaturatedConc();
1535 Info << "Correct surface tension" << endl;
1538 cleanInterfaceSurfTension()
1539 + surfactant().surfactR()
1540 *surfactant().surfactT()
1541 *surfactant().surfactSaturatedConc()
1542 *log(1.0 - surfactantConcentration()
1543 /surfactant().surfactSaturatedConc());
1545 if(neg(min(surfaceTension().internalField())))
1549 "void freeSurface::correctSurfactantConcentration()"
1550 ) << "Surface tension is negative"
1551 << abort(FatalError);
1557 void freeSurface::correctUsBoundaryConditions()
1559 forAll(Us().boundaryField(), patchI)
1563 Us().boundaryField()[patchI].type()
1564 == calculatedFaPatchVectorField::typeName
1567 vectorField& pUs = Us().boundaryField()[patchI];
1569 pUs = Us().boundaryField()[patchI].patchInternalField();
1571 label ngbPolyPatchID =
1572 aMesh().boundary()[patchI].ngbPolyPatchIndex();
1574 if(ngbPolyPatchID != -1)
1579 isA<slipFvPatchVectorField>
1581 U().boundaryField()[ngbPolyPatchID]
1586 isA<symmetryFvPatchVectorField>
1588 U().boundaryField()[ngbPolyPatchID]
1594 aMesh().boundary()[patchI].ngbPolyPatchFaceNormals();
1602 Us().correctBoundaryConditions();
1606 vector freeSurface::totalPressureForce() const
1608 const scalarField& S = aMesh().S();
1610 const vectorField& n = aMesh().faceAreaNormals().internalField();
1612 const scalarField& P = p().boundaryField()[aPatchID()];
1614 vectorField pressureForces = S*P*n;
1616 return gSum(pressureForces);
1620 vector freeSurface::totalViscousForce() const
1622 const scalarField& S = aMesh().S();
1623 const vectorField& n = aMesh().faceAreaNormals().internalField();
1625 vectorField nGradU =
1626 U().boundaryField()[aPatchID()].snGrad();
1628 vectorField viscousForces =
1629 - muFluidA().value()*S
1632 + (fac::grad(Us())().internalField()&n)
1633 - (n*fac::div(Us())().internalField())
1636 return gSum(viscousForces);
1640 vector freeSurface::totalSurfaceTensionForce() const
1642 const scalarField& S = aMesh().S();
1644 const vectorField& n = aMesh().faceAreaNormals().internalField();
1646 const scalarField& K = aMesh().faceCurvatures().internalField();
1648 vectorField surfTensionForces(n.size(), vector::zero);
1650 if(cleanInterface())
1653 S*cleanInterfaceSurfTension().value()
1656 aMesh().Le()*aMesh().edgeLengthCorrection()
1657 )().internalField();
1661 surfTensionForces *= surfaceTension().internalField()*K;
1664 return gSum(surfTensionForces);
1668 void freeSurface::initializeControlPointsPosition()
1670 scalarField deltaH = scalarField(aMesh().nFaces(), 0.0);
1672 pointField displacement = pointDisplacement(deltaH);
1674 const faceList& faces = aMesh().faces();
1675 const pointField& points = aMesh().points();
1678 pointField newPoints = points + displacement;
1680 scalarField sweptVol(faces.size(), 0.0);
1682 forAll(faces, faceI)
1684 sweptVol[faceI] = -faces[faceI].sweptVol(points, newPoints);
1687 vectorField faceArea(faces.size(), vector::zero);
1689 forAll (faceArea, faceI)
1691 faceArea[faceI] = faces[faceI].normal(newPoints);
1694 forAll(deltaH, faceI)
1696 deltaH[faceI] = sweptVol[faceI]/
1697 (faceArea[faceI] & facesDisplacementDir()[faceI]);
1700 displacement = pointDisplacement(deltaH);
1704 scalar freeSurface::maxCourantNumber()
1708 if(cleanInterface())
1710 const scalarField& dE =aMesh().lPN();
1714 DB().deltaT().value()/
1717 rhoFluidA().value()*dE*dE*dE/
1718 2.0/M_PI/(cleanInterfaceSurfTension().value() + SMALL)
1724 scalarField sigmaE =
1725 linearEdgeInterpolate(surfaceTension())().internalField()
1728 const scalarField& dE =aMesh().lPN();
1732 DB().deltaT().value()/
1735 rhoFluidA().value()*dE*dE*dE/
1745 void freeSurface::updateProperties()
1747 muFluidA_ = dimensionedScalar(this->lookup("muFluidA"));
1749 muFluidB_ = dimensionedScalar(this->lookup("muFluidB"));
1751 rhoFluidA_ = dimensionedScalar(this->lookup("rhoFluidA"));
1753 rhoFluidB_ = dimensionedScalar(this->lookup("rhoFluidB"));
1755 g_ = dimensionedVector(this->lookup("g"));
1757 cleanInterfaceSurfTension_ =
1758 dimensionedScalar(this->lookup("surfaceTension"));
1762 void freeSurface::writeVTK() const
1764 aMesh().patch().writeVTK
1766 DB().timePath()/"freeSurface",
1768 aMesh().patch().points()
1773 void freeSurface::writeVTKControlPoints()
1775 // Write patch and points into VTK
1776 fileName name(DB().timePath()/"freeSurfaceControlPoints");
1777 OFstream mps(name + ".vtk");
1779 mps << "# vtk DataFile Version 2.0" << nl
1780 << name << ".vtk" << nl
1782 << "DATASET POLYDATA" << nl
1783 << "POINTS " << controlPoints().size() << " float" << nl;
1785 forAll(controlPoints(), pointI)
1787 mps << controlPoints()[pointI].x() << ' '
1788 << controlPoints()[pointI].y() << ' '
1789 << controlPoints()[pointI].z() << nl;
1793 mps << "VERTICES " << controlPoints().size() << ' '
1794 << controlPoints().size()*2 << nl;
1796 forAll(controlPoints(), pointI)
1798 mps << 1 << ' ' << pointI << nl;
1803 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1805 } // End namespace Foam
1807 // ************************************************************************* //