mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
Merge branch 'develop' of develop.openfoam.com:Development/OpenFOAM-plus into develop
Conflicts: tutorials/basic/overLaplacianDyMFoam/heatTransfer/0.orig/T tutorials/basic/overLaplacianDyMFoam/heatTransfer/0.orig/zoneID
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -234,25 +234,16 @@ void Foam::epsilonWallFunctionFvPatchScalarField::calculate
|
||||
{
|
||||
const label celli = patch.faceCells()[facei];
|
||||
|
||||
const scalar yPlus = Cmu25*y[facei]*sqrt(k[celli])/nuw[facei];
|
||||
|
||||
const scalar w = cornerWeights[facei];
|
||||
|
||||
if (yPlus > yPlusLam_)
|
||||
{
|
||||
epsilon0[celli] += w*Cmu75*pow(k[celli], 1.5)/(kappa_*y[facei]);
|
||||
epsilon0[celli] += w*Cmu75*pow(k[celli], 1.5)/(kappa_*y[facei]);
|
||||
|
||||
G0[celli] +=
|
||||
w
|
||||
*(nutw[facei] + nuw[facei])
|
||||
*magGradUw[facei]
|
||||
*Cmu25*sqrt(k[celli])
|
||||
/(kappa_*y[facei]);
|
||||
}
|
||||
else
|
||||
{
|
||||
epsilon0[celli] += w*2.0*k[celli]*nuw[facei]/sqr(y[facei]);
|
||||
}
|
||||
G0[celli] +=
|
||||
w
|
||||
*(nutw[facei] + nuw[facei])
|
||||
*magGradUw[facei]
|
||||
*Cmu25*sqrt(k[celli])
|
||||
/(kappa_*y[facei]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
|
||||
\\/ M anipulation |
|
||||
\\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd.
|
||||
-------------------------------------------------------------------------------
|
||||
License
|
||||
This file is part of OpenFOAM.
|
||||
@ -238,8 +238,6 @@ void omegaWallFunctionFvPatchScalarField::calculate
|
||||
{
|
||||
const label celli = patch.faceCells()[facei];
|
||||
|
||||
const scalar yPlus = Cmu25*y[facei]*sqrt(k[celli])/nuw[facei];
|
||||
|
||||
const scalar w = cornerWeights[facei];
|
||||
|
||||
const scalar omegaVis = 6*nuw[facei]/(beta1_*sqr(y[facei]));
|
||||
@ -257,27 +255,17 @@ void omegaWallFunctionFvPatchScalarField::calculate
|
||||
omega0[celli] += w*sqrt(sqr(omegaVis) + sqr(omegaLog));
|
||||
}
|
||||
|
||||
if (yPlus > yPlusLam_)
|
||||
if (!blended_)
|
||||
{
|
||||
if (!blended_)
|
||||
{
|
||||
omega0[celli] += w*omegaLog;
|
||||
}
|
||||
omega0[celli] += w*omegaLog;
|
||||
}
|
||||
|
||||
G0[celli] +=
|
||||
w
|
||||
*(nutw[facei] + nuw[facei])
|
||||
*magGradUw[facei]
|
||||
*Cmu25*sqrt(k[celli])
|
||||
/(kappa_*y[facei]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!blended_)
|
||||
{
|
||||
omega0[celli] += w*omegaVis;
|
||||
}
|
||||
}
|
||||
G0[celli] +=
|
||||
w
|
||||
*(nutw[facei] + nuw[facei])
|
||||
*magGradUw[facei]
|
||||
*Cmu25*sqrt(k[celli])
|
||||
/(kappa_*y[facei]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -238,6 +238,9 @@ fvMatrices/fvMatrices.C
|
||||
fvMatrices/fvScalarMatrix/fvScalarMatrix.C
|
||||
fvMatrices/solvers/MULES/MULES.C
|
||||
fvMatrices/solvers/MULES/CMULES.C
|
||||
fvMatrices/solvers/isoAdvection/isoCutCell/isoCutCell.C
|
||||
fvMatrices/solvers/isoAdvection/isoCutFace/isoCutFace.C
|
||||
fvMatrices/solvers/isoAdvection/isoAdvection/isoAdvection.C
|
||||
fvMatrices/solvers/GAMGSymSolver/GAMGAgglomerations/faceAreaPairGAMGAgglomeration/faceAreaPairGAMGAgglomeration.C
|
||||
|
||||
interpolation = interpolation/interpolation
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,388 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016-2017 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016-2017 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::isoAdvection
|
||||
|
||||
Description
|
||||
Calculates the new VOF (alpha) field after time step dt given the initial
|
||||
VOF field and a velocity field U and face fluxes phi. The fluid transport
|
||||
calculation is based on an idea of using isosurfaces to estimate the
|
||||
internal distribution of fluid in cells and advecting such isosurfaces
|
||||
across the mesh faces with the velocity field interpolated to the
|
||||
isosurfaces.
|
||||
|
||||
Reference:
|
||||
\verbatim
|
||||
Roenby, J., Bredmose, H. and Jasak, H. (2016).
|
||||
A computational method for sharp interface advection
|
||||
Royal Society Open Science, 3
|
||||
doi 10.1098/rsos.160405
|
||||
\endverbatim
|
||||
|
||||
Original code supplied by Johan Roenby, DHI (2016)
|
||||
|
||||
SourceFiles
|
||||
isoAdvection.C
|
||||
isoAdvectionTemplates.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef isoAdvection_H
|
||||
#define isoAdvection_H
|
||||
|
||||
#include "fvMesh.H"
|
||||
#include "volFieldsFwd.H"
|
||||
#include "surfaceFields.H"
|
||||
#include "className.H"
|
||||
#include "isoCutCell.H"
|
||||
#include "isoCutFace.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class isoAdvection Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class isoAdvection
|
||||
{
|
||||
// Private data types
|
||||
|
||||
typedef DynamicList<label> DynamicLabelList;
|
||||
typedef DynamicList<scalar> DynamicScalarList;
|
||||
typedef DynamicList<vector> DynamicVectorList;
|
||||
typedef DynamicList<point> DynamicPointList;
|
||||
|
||||
|
||||
// Private data
|
||||
|
||||
//- Reference to mesh
|
||||
const fvMesh& mesh_;
|
||||
|
||||
//- Dictionary for isoAdvection controls
|
||||
const dictionary dict_;
|
||||
|
||||
//- VOF field
|
||||
volScalarField& alpha1_;
|
||||
|
||||
//- Often used reference to alpha1 internal field
|
||||
scalarField& alpha1In_;
|
||||
|
||||
//- Reference to flux field
|
||||
const surfaceScalarField& phi_;
|
||||
|
||||
//- Reference to velocity field
|
||||
const volVectorField& U_;
|
||||
|
||||
//- Face volumetric water transport
|
||||
surfaceScalarField dVf_;
|
||||
|
||||
//- Time spent performing interface advection
|
||||
scalar advectionTime_;
|
||||
|
||||
|
||||
// Point interpolation data
|
||||
|
||||
//- VOF field interpolated to mesh points
|
||||
scalarField ap_;
|
||||
|
||||
|
||||
// Switches and tolerances. Tolerances need to go into toleranceSwitches
|
||||
|
||||
//- Number of alpha bounding steps
|
||||
label nAlphaBounds_;
|
||||
|
||||
//- Tolerance for search of isoFace giving specified VOF value
|
||||
scalar vof2IsoTol_;
|
||||
|
||||
//- Tolerance for marking of surface cells:
|
||||
// Those with surfCellTol_ < alpha1 < 1 - surfCellTol_
|
||||
scalar surfCellTol_;
|
||||
|
||||
//- Switch controlling whether to use isoface normals for interface
|
||||
// orientation (default corresponding to false) to base it on
|
||||
// a smoothed gradient of alpha calculation (giving better results
|
||||
// on tri on tet meshes).
|
||||
bool gradAlphaBasedNormal_;
|
||||
|
||||
//- Print isofaces in a <case>/isoFaces/isoFaces_#N.vtk files.
|
||||
// Intended for debugging
|
||||
bool writeIsoFacesToFile_;
|
||||
|
||||
// Cell and face cutting
|
||||
|
||||
//- List of surface cells
|
||||
DynamicLabelList surfCells_;
|
||||
|
||||
//- Cell cutting object
|
||||
isoCutCell isoCutCell_;
|
||||
|
||||
//- Face cutting object
|
||||
isoCutFace isoCutFace_;
|
||||
|
||||
//- Bool list for cells that have been touched by the bounding step
|
||||
boolList cellIsBounded_;
|
||||
|
||||
//- True for all surface cells and their neighbours
|
||||
boolList checkBounding_;
|
||||
|
||||
//- Storage for boundary faces downwind to a surface cell
|
||||
DynamicLabelList bsFaces_;
|
||||
|
||||
//- Storage for boundary surface iso face centre
|
||||
DynamicVectorList bsx0_;
|
||||
|
||||
//- Storage for boundary surface iso face normal
|
||||
DynamicVectorList bsn0_;
|
||||
|
||||
//- Storage for boundary surface iso face speed
|
||||
DynamicScalarList bsUn0_;
|
||||
|
||||
//- Storage for boundary surface iso value
|
||||
DynamicScalarList bsf0_;
|
||||
|
||||
|
||||
// Additional data for parallel runs
|
||||
|
||||
//- List of processor patch labels
|
||||
DynamicLabelList procPatchLabels_;
|
||||
|
||||
//- For each patch if it is a processor patch this is a list of the
|
||||
// face labels on this patch that are downwind to a surface cell.
|
||||
// For non-processor patches the list will be empty.
|
||||
List<DynamicLabelList> surfaceCellFacesOnProcPatches_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
//- Disallow default bitwise copy construct
|
||||
isoAdvection(const isoAdvection&);
|
||||
|
||||
//- Disallow default bitwise copy assignment
|
||||
void operator=(const isoAdvection&);
|
||||
|
||||
|
||||
// Advection functions
|
||||
|
||||
//- For each face calculate volumetric face transport during dt
|
||||
void timeIntegratedFlux();
|
||||
|
||||
//- Calculate volumetric face transport during dt given the isoFace
|
||||
// data provided as input for face facei
|
||||
scalar timeIntegratedFaceFlux
|
||||
(
|
||||
const label facei,
|
||||
const vector& x0,
|
||||
const vector& n0,
|
||||
const scalar Un0,
|
||||
const scalar f0,
|
||||
const scalar dt,
|
||||
const scalar phi,
|
||||
const scalar magSf
|
||||
);
|
||||
|
||||
//- For a given cell return labels of faces fluxing out of this cell
|
||||
// (based on sign of phi)
|
||||
void setDownwindFaces
|
||||
(
|
||||
const label celli,
|
||||
DynamicLabelList& downwindFaces
|
||||
) const;
|
||||
|
||||
// Limit fluxes
|
||||
void limitFluxes();
|
||||
|
||||
// Bound fluxes
|
||||
void boundFromAbove
|
||||
(
|
||||
const scalarField& alpha1,
|
||||
surfaceScalarField& dVfcorrected,
|
||||
DynamicLabelList& correctedFaces
|
||||
);
|
||||
|
||||
//- Given the face volume transport dVf calculates the total volume
|
||||
// leaving a given cell. Note: cannot use dVf member because
|
||||
// netFlux is called also for corrected dVf
|
||||
scalar netFlux
|
||||
(
|
||||
const surfaceScalarField& dVf,
|
||||
const label celli
|
||||
) const;
|
||||
|
||||
//- Determine if a cell is a surface cell
|
||||
bool isASurfaceCell(const label celli) const
|
||||
{
|
||||
return
|
||||
(
|
||||
surfCellTol_ < alpha1In_[celli]
|
||||
&& alpha1In_[celli] < 1 - surfCellTol_
|
||||
);
|
||||
}
|
||||
|
||||
//- Clear out isoFace data
|
||||
void clearIsoFaceData()
|
||||
{
|
||||
surfCells_.clear();
|
||||
bsFaces_.clear();
|
||||
bsx0_.clear();
|
||||
bsn0_.clear();
|
||||
bsUn0_.clear();
|
||||
bsf0_.clear();
|
||||
}
|
||||
|
||||
// Face value functions needed for random face access where the face
|
||||
// can be either internal or boundary face
|
||||
|
||||
//- Return face value for a given Geometric surface field
|
||||
template<typename Type>
|
||||
Type faceValue
|
||||
(
|
||||
const GeometricField<Type, fvsPatchField, surfaceMesh>& f,
|
||||
const label facei
|
||||
) const;
|
||||
|
||||
//- Set face value for a given Geometric surface field
|
||||
template<typename Type>
|
||||
void setFaceValue
|
||||
(
|
||||
GeometricField<Type, fvsPatchField, surfaceMesh>& f,
|
||||
const label facei,
|
||||
const Type& value
|
||||
) const;
|
||||
|
||||
|
||||
// Parallel run handling functions
|
||||
|
||||
//- Synchronize dVf across processor boundaries using upwind value
|
||||
void syncProcPatches
|
||||
(
|
||||
surfaceScalarField& dVf,
|
||||
const surfaceScalarField& phi
|
||||
);
|
||||
|
||||
//- Check if the face is on processor patch and append it to the
|
||||
// list of surface cell faces on processor patches
|
||||
void checkIfOnProcPatch(const label facei);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("isoAdvection");
|
||||
|
||||
//- Constructors
|
||||
|
||||
//- Construct given alpha, phi and velocity field. Note: phi should be
|
||||
// divergence free up to a sufficient tolerance
|
||||
isoAdvection
|
||||
(
|
||||
volScalarField& alpha1,
|
||||
const surfaceScalarField& phi,
|
||||
const volVectorField& U
|
||||
);
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~isoAdvection()
|
||||
{}
|
||||
|
||||
|
||||
// Member functions
|
||||
|
||||
//- Advect the free surface. Updates alpha field, taking into account
|
||||
// multiple calls within a single time step.
|
||||
void advect();
|
||||
|
||||
//- Apply the bounding based on user inputs
|
||||
void applyBruteForceBounding();
|
||||
|
||||
// Access functions
|
||||
|
||||
//- Return alpha field
|
||||
const volScalarField& alpha() const
|
||||
{
|
||||
return alpha1_;
|
||||
}
|
||||
|
||||
//- Return the controls dictionary
|
||||
const dictionary& dict() const
|
||||
{
|
||||
return dict_;
|
||||
}
|
||||
|
||||
//- Return cellSet of surface cells
|
||||
void writeSurfaceCells() const;
|
||||
|
||||
//- Return cellSet of bounded cells
|
||||
void writeBoundedCells() const;
|
||||
|
||||
//- Return mass flux
|
||||
tmp<surfaceScalarField> getRhoPhi
|
||||
(
|
||||
const dimensionedScalar rho1,
|
||||
const dimensionedScalar rho2
|
||||
) const
|
||||
{
|
||||
return tmp<surfaceScalarField>
|
||||
(
|
||||
new surfaceScalarField
|
||||
(
|
||||
"rhoPhi",
|
||||
(rho1 - rho2)*dVf_/mesh_.time().deltaT() + rho2*phi_
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
scalar advectionTime() const
|
||||
{
|
||||
return advectionTime_;
|
||||
}
|
||||
|
||||
//- Write isoface points to .obj file
|
||||
void writeIsoFaces
|
||||
(
|
||||
const DynamicList<List<point>>& isoFacePts
|
||||
) const;
|
||||
};
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#ifdef NoRepository
|
||||
# include "isoAdvectionTemplates.C"
|
||||
#endif
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,111 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016-2017 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016-2017 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "isoAdvection.H"
|
||||
|
||||
// ************************************************************************* //
|
||||
|
||||
template<typename Type>
|
||||
Type Foam::isoAdvection::faceValue
|
||||
(
|
||||
const GeometricField<Type, fvsPatchField, surfaceMesh>& f,
|
||||
const label facei
|
||||
) const
|
||||
{
|
||||
if (mesh_.isInternalFace(facei))
|
||||
{
|
||||
return f.primitiveField()[facei];
|
||||
}
|
||||
else
|
||||
{
|
||||
const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
|
||||
|
||||
// Boundary face. Find out which face of which patch
|
||||
const label patchi = pbm.patchID()[facei - mesh_.nInternalFaces()];
|
||||
|
||||
if (patchi < 0 || patchi >= pbm.size())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Cannot find patch for face " << facei
|
||||
<< abort(FatalError);
|
||||
}
|
||||
|
||||
// Handle empty patches
|
||||
const polyPatch& pp = pbm[patchi];
|
||||
if (isA<emptyPolyPatch>(pp) || pp.empty())
|
||||
{
|
||||
return pTraits<Type>::zero;
|
||||
}
|
||||
|
||||
const label patchFacei = pp.whichFace(facei);
|
||||
return f.boundaryField()[patchi][patchFacei];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Type>
|
||||
void Foam::isoAdvection::setFaceValue
|
||||
(
|
||||
GeometricField<Type, fvsPatchField, surfaceMesh>& f,
|
||||
const label facei,
|
||||
const Type& value
|
||||
) const
|
||||
{
|
||||
if (mesh_.isInternalFace(facei))
|
||||
{
|
||||
f.primitiveFieldRef()[facei] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
|
||||
|
||||
// Boundary face. Find out which face of which patch
|
||||
const label patchi = pbm.patchID()[facei - mesh_.nInternalFaces()];
|
||||
|
||||
if (patchi < 0 || patchi >= pbm.size())
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Cannot find patch for face " << facei
|
||||
<< abort(FatalError);
|
||||
}
|
||||
|
||||
// Handle empty patches
|
||||
const polyPatch& pp = pbm[patchi];
|
||||
if (isA<emptyPolyPatch>(pp) || pp.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const label patchFacei = pp.whichFace(facei);
|
||||
|
||||
f.boundaryFieldRef()[patchi][patchFacei] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,709 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "isoCutCell.H"
|
||||
#include "scalarMatrices.H"
|
||||
#include "volFields.H"
|
||||
#include "surfaceFields.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
int Foam::isoCutCell::debug = 0;
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::isoCutCell::isoCutCell(const fvMesh& mesh, scalarField& f)
|
||||
:
|
||||
mesh_(mesh),
|
||||
cellI_(-1),
|
||||
f_(f),
|
||||
isoValue_(0),
|
||||
isoCutFace_(isoCutFace(mesh_, f_)),
|
||||
isoCutFaces_(10),
|
||||
isoCutFacePoints_(10),
|
||||
isoCutFaceCentres_(10),
|
||||
isoCutFaceAreas_(10),
|
||||
isoFaceEdges_(10),
|
||||
isoFacePoints_(10),
|
||||
isoFaceCentre_(vector::zero),
|
||||
isoFaceArea_(vector::zero),
|
||||
subCellCentre_(vector::zero),
|
||||
subCellVolume_(-10),
|
||||
VOF_(-10),
|
||||
fullySubFaces_(10),
|
||||
cellStatus_(-1),
|
||||
subCellCentreAndVolumeCalculated_(false),
|
||||
isoFaceCentreAndAreaCalculated_(false)
|
||||
{
|
||||
clearStorage();
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
void Foam::isoCutCell::calcSubCellCentreAndVolume()
|
||||
{
|
||||
if (cellStatus_ == 0)
|
||||
{
|
||||
subCellCentre_ = vector::zero;
|
||||
subCellVolume_ = 0.0;
|
||||
|
||||
// Estimate the approximate cell centre as the average of face centres
|
||||
label nCellFaces(1 + isoCutFaceCentres_.size() + fullySubFaces_.size());
|
||||
vector cEst = isoFaceCentre_ + sum(isoCutFaceCentres_);
|
||||
forAll(fullySubFaces_, facei)
|
||||
{
|
||||
cEst += mesh_.faceCentres()[fullySubFaces_[facei]];
|
||||
}
|
||||
cEst /= scalar(nCellFaces);
|
||||
|
||||
|
||||
// Contribution to subcell centre and volume from isoface
|
||||
const scalar pyr3Vol0 =
|
||||
max(mag(isoFaceArea_ & (isoFaceCentre_ - cEst)), VSMALL);
|
||||
|
||||
// Calculate face-pyramid centre
|
||||
const vector pc0 = 0.75*isoFaceCentre_ + 0.25*cEst;
|
||||
|
||||
// Accumulate volume-weighted face-pyramid centre
|
||||
subCellCentre_ += pyr3Vol0*pc0;
|
||||
|
||||
// Accumulate face-pyramid volume
|
||||
subCellVolume_ += pyr3Vol0;
|
||||
|
||||
// Contribution to subcell centre and volume from cut faces
|
||||
forAll(isoCutFaceCentres_, facei)
|
||||
{
|
||||
// Calculate 3*face-pyramid volume
|
||||
scalar pyr3Vol =
|
||||
max
|
||||
(
|
||||
mag
|
||||
(
|
||||
isoCutFaceAreas_[facei]
|
||||
& (isoCutFaceCentres_[facei] - cEst)
|
||||
),
|
||||
VSMALL
|
||||
);
|
||||
|
||||
// Calculate face-pyramid centre
|
||||
vector pc = 0.75*isoCutFaceCentres_[facei] + 0.25*cEst;
|
||||
|
||||
// Accumulate volume-weighted face-pyramid centre
|
||||
subCellCentre_ += pyr3Vol*pc;
|
||||
|
||||
// Accumulate face-pyramid volume
|
||||
subCellVolume_ += pyr3Vol;
|
||||
}
|
||||
|
||||
// Contribution to subcell centre and volume from fully submerged faces
|
||||
forAll(fullySubFaces_, i)
|
||||
{
|
||||
const label facei = fullySubFaces_[i];
|
||||
const point& fCentre = mesh_.faceCentres()[facei];
|
||||
const vector& fArea = mesh_.faceAreas()[facei];
|
||||
|
||||
// Calculate 3*face-pyramid volume
|
||||
scalar pyr3Vol = max(mag(fArea & (fCentre - cEst)), VSMALL);
|
||||
|
||||
// Calculate face-pyramid centre
|
||||
vector pc = 0.75*fCentre + 0.25*cEst;
|
||||
|
||||
// Accumulate volume-weighted face-pyramid centre
|
||||
subCellCentre_ += pyr3Vol*pc;
|
||||
|
||||
// Accumulate face-pyramid volume
|
||||
subCellVolume_ += pyr3Vol;
|
||||
}
|
||||
|
||||
subCellCentre_ /= subCellVolume_;
|
||||
subCellVolume_ /= scalar(3);
|
||||
VOF_ = subCellVolume_/mesh_.cellVolumes()[cellI_];
|
||||
|
||||
subCellCentreAndVolumeCalculated_ = true;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
vector sumSf = isoFaceArea_;
|
||||
scalar sumMagSf = mag(isoFaceArea_);
|
||||
forAll(isoCutFaceCentres_, facei)
|
||||
{
|
||||
sumSf += isoCutFaceAreas_[facei];
|
||||
sumMagSf += mag(isoCutFaceAreas_[facei]);
|
||||
}
|
||||
forAll(fullySubFaces_, facei)
|
||||
{
|
||||
sumSf += mesh_.faceAreas()[fullySubFaces_[facei]];
|
||||
sumMagSf += mag(isoCutFaceAreas_[facei]);
|
||||
}
|
||||
if (mag(sumSf) > 1e-10)
|
||||
{
|
||||
Pout<< "Warninig: mag(sumSf)/magSumSf = "
|
||||
<< mag(sumSf)/sumMagSf << " for surface cell"
|
||||
<< cellI_ << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cellStatus_ == 1)
|
||||
{
|
||||
// Cell fully above isosurface
|
||||
subCellCentre_ = vector::zero;
|
||||
subCellVolume_ = 0;
|
||||
VOF_ = 0;
|
||||
}
|
||||
else if (cellStatus_ == -1)
|
||||
{
|
||||
// Cell fully below isosurface
|
||||
subCellCentre_ = mesh_.cellCentres()[cellI_];
|
||||
subCellVolume_ = mesh_.cellVolumes()[cellI_];
|
||||
VOF_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutCell::calcIsoFaceCentreAndArea()
|
||||
{
|
||||
// Initial guess of face centre from edge points
|
||||
point fCentre = vector::zero;
|
||||
label nEdgePoints = 0;
|
||||
forAll(isoFaceEdges_, ei)
|
||||
{
|
||||
DynamicList<point>& edgePoints = isoFaceEdges_[ei];
|
||||
forAll(edgePoints, pi)
|
||||
{
|
||||
fCentre += edgePoints[pi];
|
||||
nEdgePoints++;
|
||||
}
|
||||
}
|
||||
|
||||
if (nEdgePoints > 0)
|
||||
{
|
||||
fCentre /= nEdgePoints;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugPout << "Warning: nEdgePoints = 0 for cell " << cellI_ << endl;
|
||||
}
|
||||
|
||||
vector sumN = vector::zero;
|
||||
scalar sumA = 0;
|
||||
vector sumAc = vector::zero;
|
||||
|
||||
forAll(isoFaceEdges_, ei)
|
||||
{
|
||||
const DynamicList<point>& edgePoints = isoFaceEdges_[ei];
|
||||
const label nPoints = edgePoints.size();
|
||||
for (label pi = 0; pi < nPoints-1; pi++)
|
||||
{
|
||||
const point& nextPoint = edgePoints[pi + 1];
|
||||
|
||||
vector c = edgePoints[pi] + nextPoint + fCentre;
|
||||
vector n = (nextPoint - edgePoints[pi])^(fCentre - edgePoints[pi]);
|
||||
scalar a = mag(n);
|
||||
|
||||
// Edges may have different orientation
|
||||
sumN += Foam::sign(n & sumN)*n;
|
||||
sumA += a;
|
||||
sumAc += a*c;
|
||||
}
|
||||
}
|
||||
|
||||
// This is to deal with zero-area faces. Mark very small faces
|
||||
// to be detected in e.g., processorPolyPatch.
|
||||
if (sumA < ROOTVSMALL)
|
||||
{
|
||||
isoFaceCentre_ = fCentre;
|
||||
isoFaceArea_ = vector::zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
isoFaceCentre_ = sumAc/sumA/scalar(3);
|
||||
isoFaceArea_ = 0.5*sumN;
|
||||
}
|
||||
|
||||
|
||||
// Check isoFaceArea_ direction and change if not pointing out of subcell
|
||||
if ((isoFaceArea_ & (isoFaceCentre_ - subCellCentre())) < 0)
|
||||
{
|
||||
isoFaceArea_ *= (-1);
|
||||
}
|
||||
|
||||
isoFaceCentreAndAreaCalculated_ = true;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutCell::calcIsoFacePointsFromEdges()
|
||||
{
|
||||
DebugPout
|
||||
<< "Enter calcIsoFacePointsFromEdges() with isoFaceArea_ = "
|
||||
<< isoFaceArea_ << " and isoFaceCentre_ = " << isoFaceCentre_
|
||||
<< " and isoFaceEdges_ = " << isoFaceEdges_ << endl;
|
||||
|
||||
// Defining local coordinates with zhat along isoface normal and xhat from
|
||||
// isoface centre to first point in isoFaceEdges_
|
||||
const vector zhat = isoFaceArea_/mag(isoFaceArea_);
|
||||
vector xhat = isoFaceEdges_[0][0] - isoFaceCentre_;
|
||||
xhat = (xhat - (xhat & zhat)*zhat);
|
||||
xhat /= mag(xhat);
|
||||
vector yhat = zhat^xhat;
|
||||
yhat /= mag(yhat);
|
||||
|
||||
DebugPout << "Calculated local coordinates" << endl;
|
||||
|
||||
// Calculating isoface point angles in local coordinates
|
||||
DynamicList<point> unsortedIsoFacePoints(3*isoFaceEdges_.size());
|
||||
DynamicList<scalar> unsortedIsoFacePointAngles(3*isoFaceEdges_.size());
|
||||
forAll(isoFaceEdges_, ei)
|
||||
{
|
||||
const DynamicList<point>& edgePoints = isoFaceEdges_[ei];
|
||||
forAll(edgePoints, pi)
|
||||
{
|
||||
const point& p = edgePoints[pi];
|
||||
unsortedIsoFacePoints.append(p);
|
||||
unsortedIsoFacePointAngles.append
|
||||
(
|
||||
Foam::atan2
|
||||
(
|
||||
((p - isoFaceCentre_) & yhat),
|
||||
((p - isoFaceCentre_) & xhat)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DebugPout<< "Calculated isoFace point angles" << endl;
|
||||
|
||||
// Sorting isoface points by angle and inserting into isoFacePoints_
|
||||
labelList order(unsortedIsoFacePointAngles.size());
|
||||
Foam::sortedOrder(unsortedIsoFacePointAngles, order);
|
||||
isoFacePoints_.append(unsortedIsoFacePoints[order[0]]);
|
||||
for (label pi = 1; pi < order.size(); pi++)
|
||||
{
|
||||
if
|
||||
(
|
||||
mag
|
||||
(
|
||||
unsortedIsoFacePointAngles[order[pi]]
|
||||
-unsortedIsoFacePointAngles[order[pi-1]]
|
||||
) > 1e-8
|
||||
)
|
||||
{
|
||||
isoFacePoints_.append(unsortedIsoFacePoints[order[pi]]);
|
||||
}
|
||||
}
|
||||
|
||||
DebugPout<< "Sorted isoface points by angle" << endl;
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::isoCutCell::calcSubCell
|
||||
(
|
||||
const label celli,
|
||||
const scalar isoValue
|
||||
)
|
||||
{
|
||||
// Populate isoCutFaces_, isoCutFacePoints_, fullySubFaces_, isoFaceCentres_
|
||||
// and isoFaceArea_.
|
||||
|
||||
clearStorage();
|
||||
cellI_ = celli;
|
||||
isoValue_ = isoValue;
|
||||
const cell& c = mesh_.cells()[celli];
|
||||
|
||||
forAll(c, fi)
|
||||
{
|
||||
const label facei = c[fi];
|
||||
|
||||
const label faceStatus = isoCutFace_.calcSubFace(facei, isoValue_);
|
||||
|
||||
if (faceStatus == 0)
|
||||
{
|
||||
// Face is cut
|
||||
isoCutFacePoints_.append(isoCutFace_.subFacePoints());
|
||||
isoCutFaceCentres_.append(isoCutFace_.subFaceCentre());
|
||||
isoCutFaceAreas_.append(isoCutFace_.subFaceArea());
|
||||
isoFaceEdges_.append(isoCutFace_.surfacePoints());
|
||||
}
|
||||
else if (faceStatus == -1)
|
||||
{
|
||||
// Face fully below
|
||||
fullySubFaces_.append(facei);
|
||||
}
|
||||
}
|
||||
|
||||
if (isoCutFacePoints_.size())
|
||||
{
|
||||
// Cell cut at least at one face
|
||||
cellStatus_ = 0;
|
||||
calcIsoFaceCentreAndArea();
|
||||
}
|
||||
else if (fullySubFaces_.empty())
|
||||
{
|
||||
// Cell fully above isosurface
|
||||
cellStatus_ = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cell fully below isosurface
|
||||
cellStatus_ = -1;
|
||||
}
|
||||
|
||||
return cellStatus_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::point& Foam::isoCutCell::subCellCentre()
|
||||
{
|
||||
if (!subCellCentreAndVolumeCalculated_)
|
||||
{
|
||||
calcSubCellCentreAndVolume();
|
||||
}
|
||||
|
||||
return subCellCentre_;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::isoCutCell::subCellVolume()
|
||||
{
|
||||
if (!subCellCentreAndVolumeCalculated_)
|
||||
{
|
||||
calcSubCellCentreAndVolume();
|
||||
}
|
||||
|
||||
return subCellVolume_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::DynamicList<Foam::point>& Foam::isoCutCell::isoFacePoints()
|
||||
{
|
||||
if (cellStatus_ == 0 && isoFacePoints_.size() == 0)
|
||||
{
|
||||
calcIsoFacePointsFromEdges();
|
||||
}
|
||||
|
||||
return isoFacePoints_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::point& Foam::isoCutCell::isoFaceCentre()
|
||||
{
|
||||
if (!isoFaceCentreAndAreaCalculated_)
|
||||
{
|
||||
calcIsoFaceCentreAndArea();
|
||||
}
|
||||
|
||||
return isoFaceCentre_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::vector& Foam::isoCutCell::isoFaceArea()
|
||||
{
|
||||
if (!isoFaceCentreAndAreaCalculated_)
|
||||
{
|
||||
calcIsoFaceCentreAndArea();
|
||||
}
|
||||
|
||||
return isoFaceArea_;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::isoCutCell::volumeOfFluid()
|
||||
{
|
||||
if (!subCellCentreAndVolumeCalculated_)
|
||||
{
|
||||
calcSubCellCentreAndVolume();
|
||||
}
|
||||
|
||||
return VOF_;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::isoCutCell::isoValue() const
|
||||
{
|
||||
return isoValue_;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutCell::clearStorage()
|
||||
{
|
||||
cellI_ = -1;
|
||||
isoValue_ = 0;
|
||||
isoCutFace_.clearStorage();
|
||||
isoCutFaces_.clear();
|
||||
isoCutFacePoints_.clear();
|
||||
isoCutFaceCentres_.clear();
|
||||
isoCutFaceAreas_.clear();
|
||||
isoFaceEdges_.clear();
|
||||
isoFacePoints_.clear();
|
||||
isoFaceCentre_ = vector::zero;
|
||||
isoFaceArea_ = vector::zero;
|
||||
subCellCentre_ = vector::zero;
|
||||
subCellVolume_ = -10;
|
||||
VOF_ = -10;
|
||||
fullySubFaces_.clear();
|
||||
cellStatus_ = -1;
|
||||
subCellCentreAndVolumeCalculated_ = false;
|
||||
isoFaceCentreAndAreaCalculated_ = false;
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::isoCutCell::vofCutCell
|
||||
(
|
||||
const label celli,
|
||||
const scalar alpha1,
|
||||
const scalar tol,
|
||||
const label maxIter
|
||||
)
|
||||
{
|
||||
DebugInFunction
|
||||
<< "vofCutCell for cell " << celli << " with alpha1 = "
|
||||
<< alpha1 << " ------" << endl;
|
||||
|
||||
// Finding cell vertex extrema values
|
||||
const labelList& pLabels = mesh_.cellPoints(celli);
|
||||
scalarField fvert(pLabels.size());
|
||||
forAll(pLabels, pi)
|
||||
{
|
||||
fvert[pi] = f_[pLabels[pi]];
|
||||
}
|
||||
labelList order(fvert.size());
|
||||
sortedOrder(fvert, order);
|
||||
scalar f1 = fvert[order.first()];
|
||||
scalar f2 = fvert[order.last()];
|
||||
|
||||
DebugPout << "fvert = " << fvert << ", and order = " << order << endl;
|
||||
|
||||
// Handling special case where method is handed an almost full/empty cell
|
||||
if (alpha1 < tol)
|
||||
{
|
||||
return calcSubCell(celli, f2);
|
||||
}
|
||||
else if (1 - alpha1 < tol)
|
||||
{
|
||||
return calcSubCell(celli, f1);
|
||||
}
|
||||
|
||||
// Finding the two vertices inbetween which the isovalue giving alpha1 lies
|
||||
label L1 = 0;
|
||||
label L2 = fvert.size() - 1;
|
||||
scalar a1 = 1;
|
||||
scalar a2 = 0;
|
||||
scalar L3, f3, a3;
|
||||
|
||||
while (L2 - L1 > 1)
|
||||
{
|
||||
L3 = round(0.5*(L1 + L2));
|
||||
f3 = fvert[order[L3]];
|
||||
calcSubCell(celli, f3);
|
||||
a3 = volumeOfFluid();
|
||||
if (a3 > alpha1)
|
||||
{
|
||||
L1 = L3; f1 = f3; a1 = a3;
|
||||
}
|
||||
else if (a3 < alpha1)
|
||||
{
|
||||
L2 = L3; f2 = f3; a2 = a3;
|
||||
}
|
||||
}
|
||||
|
||||
if (mag(f1 - f2) < 10*SMALL)
|
||||
{
|
||||
DebugPout<< "Warning: mag(f1 - f2) < 10*SMALL" << endl;
|
||||
return calcSubCell(celli, f1);
|
||||
}
|
||||
|
||||
if (mag(a1 - a2) < tol)
|
||||
{
|
||||
DebugPout<< "Warning: mag(a1 - a2) < tol for cell " << celli << endl;
|
||||
return calcSubCell(celli, 0.5*(f1 + f2));
|
||||
}
|
||||
|
||||
// Now we know that a(f) = alpha1 is to be found on the f interval
|
||||
// [f1, f2], i.e. alpha1 will be in the interval [a2,a1]
|
||||
DebugPout
|
||||
<< "L1 = " << L1 << ", f1 = " << f1 << ", a1 = " << a1 << nl
|
||||
<< "L2 = " << L2 << ", f2 = " << f2 << ", a2 = " << a2 << endl;
|
||||
|
||||
|
||||
// Finding coefficients in 3 deg polynomial alpha(f) from 4 solutions
|
||||
|
||||
// Finding 2 additional points on 3 deg polynomial
|
||||
f3 = f1 + (f2 - f1)/scalar(3);
|
||||
calcSubCell(celli, f3);
|
||||
a3 = volumeOfFluid();
|
||||
|
||||
scalar f4 = f1 + 2*(f2 - f1)/3;
|
||||
calcSubCell(celli, f4);
|
||||
scalar a4 = volumeOfFluid();
|
||||
|
||||
// Building and solving Vandermonde matrix equation
|
||||
scalarField a(4), f(4), C(4);
|
||||
{
|
||||
a[0] = a1, a[1] = a3, a[2] = a4, a[3] = a2;
|
||||
f[0] = 0, f[1] = (f3-f1)/(f2-f1), f[2] = (f4-f1)/(f2-f1), f[3] = 1;
|
||||
scalarSquareMatrix M(4);
|
||||
forAll(f, i)
|
||||
{
|
||||
forAll(f, j)
|
||||
{
|
||||
M[i][j] = pow(f[i], 3 - j);
|
||||
}
|
||||
}
|
||||
|
||||
// C holds the 4 polynomial coefficients
|
||||
C = a;
|
||||
LUsolve(M, C);
|
||||
}
|
||||
|
||||
// Finding root with Newton method
|
||||
|
||||
f3 = f[1]; a3 = a[1];
|
||||
label nIter = 0;
|
||||
scalar res = mag(a3 - alpha1);
|
||||
while (res > tol && nIter < 10*maxIter)
|
||||
{
|
||||
f3 -=
|
||||
(C[0]*pow3(f3) + C[1]*sqr(f3) + C[2]*f3 + C[3] - alpha1)
|
||||
/(3*C[0]*sqr(f3) + 2*C[1]*f3 + C[2]);
|
||||
a3 = C[0]*pow3(f3) + C[1]*sqr(f3) + C[2]*f3 + C[3];
|
||||
res = mag(a3 - alpha1);
|
||||
nIter++;
|
||||
}
|
||||
// Scaling back to original range
|
||||
f3 = f3*(f2 - f1) + f1;
|
||||
|
||||
// Check result
|
||||
calcSubCell(celli, f3);
|
||||
const scalar VOF = volumeOfFluid();
|
||||
res = mag(VOF - alpha1);
|
||||
|
||||
if (res > tol)
|
||||
{
|
||||
DebugPout
|
||||
<< "Newton obtained f3 = " << f3 << " and a3 = " << a3
|
||||
<< " with mag(a3-alpha1) = " << mag(a3-alpha1)
|
||||
<< " but calcSubCell(celli,f3) gives VOF = " << VOF << nl
|
||||
<< "M(f)*C = a with " << nl
|
||||
<< "f_scaled = " << f << nl
|
||||
<< "f = " << f*(f2 - f1) + f1 << nl
|
||||
<< "a = " << a << nl
|
||||
<< "C = " << C << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugPout<< "Newton did the job" << endl;
|
||||
return cellStatus_;
|
||||
}
|
||||
|
||||
// If tolerance not met use the secant method with f3 as a hopefully very
|
||||
// good initial guess to crank res the last piece down below tol
|
||||
|
||||
scalar x2 = f3;
|
||||
scalar g2 = VOF - alpha1;
|
||||
scalar x1 = max(1e-3*(f2 - f1), 100*SMALL);
|
||||
x1 = min(max(x1, f1), f2);
|
||||
calcSubCell(celli, x1);
|
||||
scalar g1 = volumeOfFluid() - alpha1;
|
||||
|
||||
nIter = 0;
|
||||
scalar g0(0), x0(0);
|
||||
while (res > tol && nIter < maxIter && g1 != g2)
|
||||
{
|
||||
x0 = (x2*g1 - x1*g2)/(g1 - g2);
|
||||
calcSubCell(celli, x0);
|
||||
g0 = volumeOfFluid() - alpha1;
|
||||
res = mag(g0);
|
||||
x2 = x1; g2 = g1;
|
||||
x1 = x0; g1 = g0;
|
||||
nIter++;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
{
|
||||
if (res < tol)
|
||||
{
|
||||
Pout<< "Bisection finished the job in " << nIter << " iterations."
|
||||
<< endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
Pout<< "Warning: Bisection not converged " << endl;
|
||||
Pout<< "Leaving vofCutCell with f3 = " << f3 << " giving a3 = "
|
||||
<< a3 << " so alpha1 - a3 = " << alpha1 - a3 << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return cellStatus_;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutCell::volumeOfFluid
|
||||
(
|
||||
volScalarField& alpha1,
|
||||
const scalar f0
|
||||
)
|
||||
{
|
||||
// Setting internal field
|
||||
scalarField& alphaIn = alpha1;
|
||||
forAll(alphaIn, celli)
|
||||
{
|
||||
const label cellStatus = calcSubCell(celli, f0);
|
||||
if (cellStatus != 1)
|
||||
{
|
||||
// If cell not entirely above isosurface
|
||||
alphaIn[celli] = volumeOfFluid();
|
||||
}
|
||||
}
|
||||
|
||||
// Setting boundary alpha1 values
|
||||
forAll(mesh_.boundary(), patchi)
|
||||
{
|
||||
if (mesh_.boundary()[patchi].size() > 0)
|
||||
{
|
||||
const label start = mesh_.boundary()[patchi].patch().start();
|
||||
scalarField& alphap = alpha1.boundaryFieldRef()[patchi];
|
||||
const scalarField& magSfp = mesh_.magSf().boundaryField()[patchi];
|
||||
|
||||
forAll(alphap, patchFacei)
|
||||
{
|
||||
const label facei = patchFacei + start;
|
||||
const label faceStatus = isoCutFace_.calcSubFace(facei, f0);
|
||||
|
||||
if (faceStatus != 1)
|
||||
{
|
||||
// Face not entirely above isosurface
|
||||
alphap[patchFacei] =
|
||||
mag(isoCutFace_.subFaceArea())/magSfp[patchFacei];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,200 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::isoCutCell
|
||||
|
||||
Description
|
||||
Class for cutting a cell, celli, of an fvMesh, mesh_, at its intersection
|
||||
with an isosurface defined by the mesh point values f_ and the isovalue,
|
||||
isoValue_.
|
||||
|
||||
Reference:
|
||||
\verbatim
|
||||
Roenby, J., Bredmose, H. and Jasak, H. (2016).
|
||||
A computational method for sharp interface advection
|
||||
Royal Society Open Science, 3
|
||||
doi 10.1098/rsos.160405
|
||||
\endverbatim
|
||||
|
||||
Original code supplied by Johan Roenby, DHI (2016)
|
||||
|
||||
SourceFiles
|
||||
isoCutCell.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef isoCutCell_H
|
||||
#define isoCutCell_H
|
||||
|
||||
#include "fvMesh.H"
|
||||
#include "volFieldsFwd.H"
|
||||
#include "isoCutFace.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class isoCutCell Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class isoCutCell
|
||||
{
|
||||
// Private data
|
||||
|
||||
//- Mesh whose cells and faces to cut at their intersection with an
|
||||
// isosurface.
|
||||
const fvMesh& mesh_;
|
||||
|
||||
//- Cell to cut
|
||||
label cellI_;
|
||||
|
||||
//- Isofunction values at mesh points. f_size() = mesh_.nPoints().
|
||||
scalarField& f_;
|
||||
|
||||
//- Isovalue used to cut cell
|
||||
scalar isoValue_;
|
||||
|
||||
//- An isoCutFace object to get access to its face cutting functionality
|
||||
isoCutFace isoCutFace_;
|
||||
|
||||
//- List of face labels of isoCutFaces
|
||||
DynamicList<label> isoCutFaces_;
|
||||
|
||||
//- List of point lists each defining an isoCutFace
|
||||
DynamicList<DynamicList<point>> isoCutFacePoints_;
|
||||
|
||||
//- List of face centres for isoCutFaces
|
||||
DynamicList<point> isoCutFaceCentres_;
|
||||
|
||||
//- List of face area vectors for isoCutFaces
|
||||
DynamicList<vector> isoCutFaceAreas_;
|
||||
|
||||
//- Storage for subFace edges belonging to isoFace
|
||||
DynamicList<DynamicList<point>> isoFaceEdges_;
|
||||
|
||||
//- Points constituting the cell-isosurface intersection (isoface)
|
||||
DynamicList<point> isoFacePoints_;
|
||||
|
||||
//- Face centre of the isoface
|
||||
point isoFaceCentre_;
|
||||
|
||||
//- Face normal of the isoface by convention pointing from high to low
|
||||
// values (i.e. opposite of the gradient vector).
|
||||
vector isoFaceArea_;
|
||||
|
||||
//- Cell centre of the subcell of celli which is "fully submerged", i.e.
|
||||
// where the function value is higher than the isoValue_
|
||||
point subCellCentre_;
|
||||
|
||||
//- Volume of fully submerged subcell
|
||||
scalar subCellVolume_;
|
||||
|
||||
//- Volume of Fluid for celli (subCellVolume_/mesh_.V()[celli])
|
||||
scalar VOF_;
|
||||
|
||||
//- List of fully submerged faces
|
||||
DynamicList<label> fullySubFaces_;
|
||||
|
||||
//- A cell status label taking one of the values:
|
||||
//
|
||||
// - -1: cell is fully below the isosurface
|
||||
// - 0: cell is cut
|
||||
// - +1: cell is fully above the isosurface
|
||||
label cellStatus_;
|
||||
|
||||
//- Boolean telling if subcell centre and volume have been calculated
|
||||
bool subCellCentreAndVolumeCalculated_;
|
||||
|
||||
//- Boolean telling if isoface centre and area have been calculated
|
||||
bool isoFaceCentreAndAreaCalculated_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
void calcSubCellCentreAndVolume();
|
||||
|
||||
void calcIsoFaceCentreAndArea();
|
||||
|
||||
void calcIsoFacePointsFromEdges();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from fvMesh and a scalarField
|
||||
// Length of scalarField should equal number of mesh points
|
||||
isoCutCell(const fvMesh&, scalarField&);
|
||||
|
||||
// Static data
|
||||
|
||||
static int debug;
|
||||
|
||||
|
||||
// Member functions
|
||||
|
||||
label calcSubCell(const label celli, const scalar isoValue);
|
||||
|
||||
const point& subCellCentre();
|
||||
|
||||
scalar subCellVolume();
|
||||
|
||||
const DynamicList<point>& isoFacePoints();
|
||||
|
||||
const point& isoFaceCentre();
|
||||
|
||||
const vector& isoFaceArea();
|
||||
|
||||
scalar volumeOfFluid();
|
||||
|
||||
scalar isoValue() const;
|
||||
|
||||
void clearStorage();
|
||||
|
||||
label vofCutCell
|
||||
(
|
||||
const label celli,
|
||||
const scalar alpha1,
|
||||
const scalar tol,
|
||||
const label maxIter
|
||||
);
|
||||
|
||||
void volumeOfFluid(volScalarField& alpha1, const scalar f0);
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,629 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "isoCutFace.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
int Foam::isoCutFace::debug = 0;
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::isoCutFace::isoCutFace
|
||||
(
|
||||
const fvMesh& mesh,
|
||||
scalarField& f
|
||||
)
|
||||
:
|
||||
mesh_(mesh),
|
||||
f_(f),
|
||||
firstEdgeCut_(-1),
|
||||
lastEdgeCut_(-1),
|
||||
firstFullySubmergedPoint_(-1),
|
||||
nFullySubmergedPoints_(0),
|
||||
subFaceCentre_(vector::zero),
|
||||
subFaceArea_(vector::zero),
|
||||
subFacePoints_(10),
|
||||
surfacePoints_(4),
|
||||
subFaceCentreAndAreaIsCalculated_(false)
|
||||
{
|
||||
clearStorage();
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
void Foam::isoCutFace::calcSubFaceCentreAndArea()
|
||||
{
|
||||
const label nPoints = subFacePoints_.size();
|
||||
|
||||
// If the face is a triangle, do a direct calculation for efficiency
|
||||
// and to avoid round-off error-related problems
|
||||
if (nPoints == 3)
|
||||
{
|
||||
subFaceCentre_ = sum(subFacePoints_)/scalar(3);
|
||||
subFaceArea_ =
|
||||
0.5
|
||||
*(
|
||||
(subFacePoints_[1] - subFacePoints_[0])
|
||||
^(subFacePoints_[2] - subFacePoints_[0])
|
||||
);
|
||||
}
|
||||
else if (nPoints > 0)
|
||||
{
|
||||
vector sumN = vector::zero;
|
||||
scalar sumA = 0.0;
|
||||
vector sumAc = vector::zero;
|
||||
const point fCentre = sum(subFacePoints_)/scalar(nPoints);
|
||||
|
||||
for (label pi = 0; pi < nPoints; pi++)
|
||||
{
|
||||
const point& nextPoint = subFacePoints_[subFacePoints_.fcIndex(pi)];
|
||||
|
||||
vector c = subFacePoints_[pi] + nextPoint + fCentre;
|
||||
vector n =
|
||||
(nextPoint - subFacePoints_[pi])^(fCentre - subFacePoints_[pi]);
|
||||
scalar a = magSqr(n);
|
||||
|
||||
sumN += n;
|
||||
sumA += a;
|
||||
sumAc += a*c;
|
||||
}
|
||||
|
||||
// This is to deal with zero-area faces. Mark very small faces
|
||||
// to be detected in e.g., processorPolyPatch.
|
||||
if (sumA < ROOTVSMALL)
|
||||
{
|
||||
subFaceCentre_ = fCentre;
|
||||
subFaceArea_ = vector::zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
subFaceCentre_ = (1.0/3.0)*sumAc/sumA;
|
||||
subFaceArea_ = 0.5*sumN;
|
||||
}
|
||||
}
|
||||
|
||||
subFaceCentreAndAreaIsCalculated_ = true;
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::isoCutFace::calcSubFace
|
||||
(
|
||||
const scalar isoValue,
|
||||
const pointField& points,
|
||||
const scalarField& f,
|
||||
const labelList& pLabels
|
||||
)
|
||||
{
|
||||
// Face status set to one of the values:
|
||||
// -1: face is fully below the isosurface
|
||||
// 0: face is cut, i.e. has values larger and smaller than isoValue
|
||||
// +1: face is fully above the isosurface
|
||||
label faceStatus;
|
||||
|
||||
label pl1 = pLabels[0];
|
||||
scalar f1 = f[pl1];
|
||||
|
||||
// If vertex values are very close to isoValue lift them slightly to avoid
|
||||
// dealing with the many special cases of a face being touched either at a
|
||||
// single point, along an edge, or the entire face being on the surface.
|
||||
if (mag(f1 - isoValue) < 10*SMALL)
|
||||
{
|
||||
f1 += sign(f1 - isoValue)*10*SMALL;
|
||||
}
|
||||
|
||||
// Finding cut edges, the point along them where they are cut, and all fully
|
||||
// submerged face points.
|
||||
forAll(pLabels, pi)
|
||||
{
|
||||
label pl2 = pLabels[pLabels.fcIndex(pi)];
|
||||
scalar f2 = f[pl2];
|
||||
if (mag(f2 - isoValue) < 10*SMALL)
|
||||
{
|
||||
f2 += sign(f2 - isoValue)*10*SMALL;
|
||||
}
|
||||
|
||||
if (f1 > isoValue)
|
||||
{
|
||||
nFullySubmergedPoints_ += 1;
|
||||
|
||||
if (f2 < isoValue)
|
||||
{
|
||||
lastEdgeCut_ = (isoValue - f1)/(f2 - f1);
|
||||
}
|
||||
}
|
||||
else if (f1 < isoValue && f2 > isoValue)
|
||||
{
|
||||
if (firstFullySubmergedPoint_ == -1)
|
||||
{
|
||||
firstFullySubmergedPoint_ = pLabels.fcIndex(pi);
|
||||
|
||||
firstEdgeCut_ = (isoValue - f1)/(f2 - f1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
const face fl(pLabels);
|
||||
|
||||
WarningInFunction
|
||||
<< "More than two face cuts for face " << fl
|
||||
<< endl;
|
||||
|
||||
Pout<< "Face values: f-isoValue = " << endl;
|
||||
forAll(fl, fpi)
|
||||
{
|
||||
Pout<< f[fl[fpi]] - isoValue << " ";
|
||||
}
|
||||
Pout<< " " << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
pl1 = pl2;
|
||||
f1 = f2;
|
||||
}
|
||||
|
||||
if (firstFullySubmergedPoint_ != -1)
|
||||
{
|
||||
// Face is cut
|
||||
faceStatus = 0;
|
||||
subFacePoints(points, pLabels);
|
||||
}
|
||||
else if (f1 < isoValue)
|
||||
{
|
||||
// Face entirely above isosurface
|
||||
faceStatus = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Face entirely below isosurface
|
||||
faceStatus = -1;
|
||||
}
|
||||
|
||||
return faceStatus;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutFace::subFacePoints
|
||||
(
|
||||
const pointField& points,
|
||||
const labelList& pLabels
|
||||
)
|
||||
{
|
||||
const label nPoints = pLabels.size();
|
||||
|
||||
surfacePoints(points, pLabels);
|
||||
|
||||
forAll(surfacePoints_, pi)
|
||||
{
|
||||
subFacePoints_.append(surfacePoints_[pi]);
|
||||
}
|
||||
|
||||
for (label pi = 0; pi < nFullySubmergedPoints_; pi++)
|
||||
{
|
||||
subFacePoints_.append
|
||||
(
|
||||
points[pLabels[(firstFullySubmergedPoint_ + pi) % nPoints]]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutFace::surfacePoints
|
||||
(
|
||||
const pointField& points,
|
||||
const labelList& pLabels
|
||||
)
|
||||
{
|
||||
const label nPoints = pLabels.size();
|
||||
|
||||
const label n = firstFullySubmergedPoint_ + nFullySubmergedPoints_;
|
||||
|
||||
label pl1 = pLabels[(n - 1) % nPoints];
|
||||
|
||||
label pl2 = pLabels[n % nPoints];
|
||||
|
||||
surfacePoints_.append
|
||||
(
|
||||
points[pl1] + lastEdgeCut_*(points[pl2] - points[pl1])
|
||||
);
|
||||
|
||||
pl1 = pLabels[(firstFullySubmergedPoint_ - 1 + nPoints) % nPoints];
|
||||
pl2 = pLabels[firstFullySubmergedPoint_];
|
||||
|
||||
surfacePoints_.append
|
||||
(
|
||||
points[pl1] + firstEdgeCut_*(points[pl2] - points[pl1])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * Public Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
Foam::label Foam::isoCutFace::calcSubFace
|
||||
(
|
||||
const label faceI,
|
||||
const scalar isoValue
|
||||
)
|
||||
{
|
||||
clearStorage();
|
||||
const labelList& pLabels = mesh_.faces()[faceI];
|
||||
const pointField& points = mesh_.points();
|
||||
return calcSubFace(isoValue, points, f_, pLabels);
|
||||
}
|
||||
|
||||
|
||||
Foam::label Foam::isoCutFace::calcSubFace
|
||||
(
|
||||
const pointField& points,
|
||||
const scalarField& f,
|
||||
const scalar isoValue
|
||||
)
|
||||
{
|
||||
clearStorage();
|
||||
const labelList pLabels(identity(f.size()));
|
||||
return calcSubFace(isoValue, points, f, pLabels);
|
||||
}
|
||||
|
||||
|
||||
const Foam::point& Foam::isoCutFace::subFaceCentre()
|
||||
{
|
||||
if (!subFaceCentreAndAreaIsCalculated_)
|
||||
{
|
||||
calcSubFaceCentreAndArea();
|
||||
}
|
||||
return subFaceCentre_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::vector& Foam::isoCutFace::subFaceArea()
|
||||
{
|
||||
if (!subFaceCentreAndAreaIsCalculated_)
|
||||
{
|
||||
calcSubFaceCentreAndArea();
|
||||
}
|
||||
return subFaceArea_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::DynamicList<Foam::point>& Foam::isoCutFace::subFacePoints() const
|
||||
{
|
||||
return subFacePoints_;
|
||||
}
|
||||
|
||||
|
||||
const Foam::DynamicList<Foam::point>& Foam::isoCutFace::surfacePoints() const
|
||||
{
|
||||
return surfacePoints_;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutFace::clearStorage()
|
||||
{
|
||||
firstEdgeCut_ = -1;
|
||||
lastEdgeCut_ = -1;
|
||||
firstFullySubmergedPoint_ = -1;
|
||||
nFullySubmergedPoints_ = 0;
|
||||
subFaceCentre_ = vector::zero;
|
||||
subFaceArea_ = vector::zero;
|
||||
subFacePoints_.clear();
|
||||
surfacePoints_.clear();
|
||||
subFaceCentreAndAreaIsCalculated_ = false;
|
||||
}
|
||||
|
||||
|
||||
Foam::scalar Foam::isoCutFace::timeIntegratedArea
|
||||
(
|
||||
const pointField& fPts,
|
||||
const scalarField& pTimes,
|
||||
const scalar dt,
|
||||
const scalar magSf,
|
||||
const scalar Un0
|
||||
)
|
||||
{
|
||||
// Initialise time integrated area returned by this function
|
||||
scalar tIntArea = 0.0;
|
||||
|
||||
// Finding ordering of vertex points
|
||||
labelList order(pTimes.size());
|
||||
sortedOrder(pTimes, order);
|
||||
const scalar firstTime = pTimes[order.first()];
|
||||
const scalar lastTime = pTimes[order.last()];
|
||||
|
||||
// Dealing with case where face is not cut by surface during time interval
|
||||
// [0,dt] because face was already passed by surface
|
||||
if (lastTime <= 0)
|
||||
{
|
||||
// If all face cuttings were in the past and cell is filling up (Un0>0)
|
||||
// then face must be full during whole time interval
|
||||
tIntArea = magSf*dt*pos(Un0);
|
||||
return tIntArea;
|
||||
}
|
||||
|
||||
// Dealing with case where face is not cut by surface during time interval
|
||||
// [0, dt] because dt is too small for surface to reach closest face point
|
||||
if (firstTime >= dt)
|
||||
{
|
||||
// If all cuttings are in the future but non of them within [0,dt] then
|
||||
// if cell is filling up (Un0 > 0) face must be empty during whole time
|
||||
// interval
|
||||
tIntArea = magSf*dt*(1 - pos(Un0));
|
||||
return tIntArea;
|
||||
}
|
||||
|
||||
// If we reach this point in the code some part of the face will be swept
|
||||
// during [tSmall, dt-tSmall]. However, it may be the case that there are no
|
||||
// vertex times within the interval. This will happen sometimes for small
|
||||
// time steps where both the initial and the final face-interface
|
||||
// intersection line (FIIL) will be along the same two edges.
|
||||
|
||||
// Face-interface intersection line (FIIL) to be swept across face
|
||||
DynamicList<point> FIIL(3);
|
||||
// Submerged area at beginning of each sub time interval time
|
||||
scalar initialArea = 0.0;
|
||||
//Running time keeper variable for the integration process
|
||||
scalar time = 0.0;
|
||||
|
||||
// Special treatment of first sub time interval
|
||||
if (firstTime > 0)
|
||||
{
|
||||
// If firstTime > 0 the face is uncut in the time interval
|
||||
// [0, firstTime] and hence fully submerged in fluid A or B.
|
||||
// If Un0 > 0 cell is filling up and it must initially be empty.
|
||||
// If Un0 < 0 cell must initially be full(y immersed in fluid A).
|
||||
time = firstTime;
|
||||
initialArea = magSf*(1.0 - pos(Un0));
|
||||
tIntArea = initialArea*time;
|
||||
cutPoints(fPts, pTimes, time, FIIL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If firstTime <= 0 then face is initially cut and we must
|
||||
// calculate the initial submerged area and FIIL:
|
||||
time = 0.0;
|
||||
// Note: calcSubFace assumes well-defined 2-point FIIL!!!!
|
||||
calcSubFace(fPts, -sign(Un0)*pTimes, time);
|
||||
initialArea = mag(subFaceArea());
|
||||
cutPoints(fPts, pTimes, time, FIIL);
|
||||
}
|
||||
|
||||
// Making sorted array of all vertex times that are between max(0,firstTime)
|
||||
// and dt and further than tSmall from the previous time.
|
||||
DynamicList<scalar> sortedTimes(pTimes.size());
|
||||
{
|
||||
scalar prevTime = time;
|
||||
const scalar tSmall = max(1e-6*dt, 10*SMALL);
|
||||
forAll(order, ti)
|
||||
{
|
||||
const scalar timeI = pTimes[order[ti]];
|
||||
if ( timeI > prevTime + tSmall && timeI <= dt)
|
||||
{
|
||||
sortedTimes.append(timeI);
|
||||
prevTime = timeI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sweeping all quadrilaterals corresponding to the intervals defined above
|
||||
forAll(sortedTimes, ti)
|
||||
{
|
||||
const scalar newTime = sortedTimes[ti];
|
||||
// New face-interface intersection line
|
||||
DynamicList<point> newFIIL(3);
|
||||
cutPoints(fPts, pTimes, newTime, newFIIL);
|
||||
|
||||
// quadrilateral area coefficients
|
||||
scalar alpha = 0, beta = 0;
|
||||
quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
|
||||
// Integration of area(t) = A*t^2+B*t from t = 0 to 1
|
||||
tIntArea += (newTime - time)*
|
||||
(initialArea + sign(Un0)*(alpha/3.0 + 0.5*beta));
|
||||
// Adding quad area to submerged area
|
||||
initialArea += sign(Un0)*(alpha + beta);
|
||||
|
||||
FIIL = newFIIL;
|
||||
time = newTime;
|
||||
}
|
||||
|
||||
// Special treatment of last time interval
|
||||
if (lastTime > dt)
|
||||
{
|
||||
// FIIL will end up cutting the face at dt
|
||||
// New face-interface intersection line
|
||||
DynamicList<point> newFIIL(3);
|
||||
cutPoints(fPts, pTimes, dt, newFIIL);
|
||||
|
||||
// quadrilateral area coefficients
|
||||
scalar alpha = 0, beta = 0;
|
||||
quadAreaCoeffs(FIIL, newFIIL, alpha, beta);
|
||||
// Integration of area(t) = A*t^2+B*t from t = 0 to 1
|
||||
tIntArea += (dt - time)*
|
||||
(initialArea + sign(Un0)*(alpha/3.0 + 0.5*beta));
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIIL will leave the face at lastTime and face will be fully in fluid
|
||||
// A or fluid B in the time interval from lastTime to dt.
|
||||
tIntArea += magSf*(dt - lastTime)*pos(Un0);
|
||||
}
|
||||
|
||||
return tIntArea;
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutFace::cutPoints
|
||||
(
|
||||
const pointField& pts,
|
||||
const scalarField& f,
|
||||
const scalar f0,
|
||||
DynamicList<point>& cutPoints
|
||||
)
|
||||
{
|
||||
const label nPoints = pts.size();
|
||||
scalar f1(f[0]);
|
||||
|
||||
// Snapping vertex value to f0 if very close (needed for 2D cases)
|
||||
if (mag(f1 - f0) < 10*SMALL)
|
||||
{
|
||||
f1 = f0;
|
||||
}
|
||||
|
||||
forAll(pts, pi)
|
||||
{
|
||||
label pi2 = (pi + 1) % nPoints;
|
||||
scalar f2 = f[pi2];
|
||||
|
||||
// Snapping vertex value
|
||||
if (mag(f2 - f0) < 10*SMALL)
|
||||
{
|
||||
f2 = f0;
|
||||
}
|
||||
|
||||
if ((f1 < f0 && f2 > f0) || (f1 > f0 && f2 < f0))
|
||||
{
|
||||
const scalar s = (f0 - f1)/(f2 - f1);
|
||||
cutPoints.append(pts[pi] + s*(pts[pi2] - pts[pi]));
|
||||
}
|
||||
else if (f1 == f0)
|
||||
{
|
||||
cutPoints.append(pts[pi]);
|
||||
}
|
||||
f1 = f2;
|
||||
}
|
||||
|
||||
if (cutPoints.size() > 2)
|
||||
{
|
||||
WarningInFunction << "cutPoints = " << cutPoints << " for pts = " << pts
|
||||
<< ", f - f0 = " << f - f0 << " and f0 = " << f0 << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Foam::isoCutFace::quadAreaCoeffs
|
||||
(
|
||||
const DynamicList<point>& pf0,
|
||||
const DynamicList<point>& pf1,
|
||||
scalar& alpha,
|
||||
scalar& beta
|
||||
) const
|
||||
{
|
||||
// Number of points in provided face-interface intersection lines
|
||||
const label np0 = pf0.size();
|
||||
const label np1 = pf1.size();
|
||||
|
||||
// quad area coeffs such that area(t) = alpha*t^2 + beta*t.
|
||||
// With time interval normalised, we have full quadArea = alpha + beta
|
||||
// and time integrated quad area = alpha/3 + beta/2;
|
||||
alpha = 0.0;
|
||||
beta = 0.0;
|
||||
|
||||
if (np0 && np1)
|
||||
{
|
||||
// Initialising quadrilateral vertices A, B, C and D
|
||||
vector A(pf0[0]);
|
||||
vector C(pf1[0]);
|
||||
vector B(pf0[0]);
|
||||
vector D(pf1[0]);
|
||||
|
||||
if (np0 == 2)
|
||||
{
|
||||
B = pf0[1];
|
||||
}
|
||||
else if (np0 > 2)
|
||||
{
|
||||
WarningInFunction << "Vertex face was cut at pf0 = " << pf0 << endl;
|
||||
}
|
||||
|
||||
if (np1 == 2)
|
||||
{
|
||||
D = pf1[1];
|
||||
}
|
||||
else if (np1 > 2)
|
||||
{
|
||||
WarningInFunction << "Vertex face was cut at pf1 = " << pf1 << endl;
|
||||
}
|
||||
|
||||
// Swapping pf1 points if pf0 and pf1 point in same general direction
|
||||
// (because we want a quadrilateral ABCD where pf0 = AB and pf1 = CD)
|
||||
if (((B - A) & (D - C)) > 0)
|
||||
{
|
||||
vector tmp = D;
|
||||
D = C;
|
||||
C = tmp;
|
||||
}
|
||||
|
||||
// Defining local coordinates (xhat, yhat) for area integration of swept
|
||||
// quadrilateral ABCD such that A = (0,0), B = (Bx,0), C = (Cx,Cy) and
|
||||
// D = (Dx,Dy) with Cy = 0 and Dy > 0.
|
||||
|
||||
const scalar Bx = mag(B - A);
|
||||
|
||||
vector xhat(vector::zero);
|
||||
if (Bx > 10*SMALL)
|
||||
{
|
||||
// If |AB| > 0 ABCD we use AB to define xhat
|
||||
xhat = (B - A)/mag(B - A);
|
||||
}
|
||||
else if (mag(C - D) > 10*SMALL)
|
||||
{
|
||||
// If |AB| ~ 0 ABCD is a triangle ACD and we use CD for xhat
|
||||
xhat = (C - D)/mag(C - D);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Defining vertical axis in local coordinates
|
||||
vector yhat = D - A;
|
||||
yhat -= ((yhat & xhat)*xhat);
|
||||
|
||||
if (mag(yhat) > 10*SMALL)
|
||||
{
|
||||
yhat /= mag(yhat);
|
||||
|
||||
const scalar Cx = (C - A) & xhat;
|
||||
const scalar Cy = mag((C - A) & yhat);
|
||||
const scalar Dx = (D - A) & xhat;
|
||||
const scalar Dy = mag((D - A) & yhat);
|
||||
|
||||
// area = ((Cx - Bx)*Dy - Dx*Cy)/6.0 + 0.25*Bx*(Dy + Cy);
|
||||
alpha = 0.5*((Cx - Bx)*Dy - Dx*Cy);
|
||||
beta = 0.5*Bx*(Dy + Cy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WarningInFunction
|
||||
<< "Vertex face was cut at " << pf0 << " and at " << pf1 << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -0,0 +1,200 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2016 OpenCFD Ltd.
|
||||
\\/ M anipulation |
|
||||
-------------------------------------------------------------------------------
|
||||
isoAdvector | Copyright (C) 2016 DHI
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::isoCutFace
|
||||
|
||||
Description
|
||||
Class for cutting a face, faceI, of an fvMesh, mesh_, at its intersection
|
||||
with an isosurface defined by the mesh point values f_ and the isovalue,
|
||||
isoValue_.
|
||||
|
||||
Reference:
|
||||
\verbatim
|
||||
Roenby, J., Bredmose, H. and Jasak, H. (2016).
|
||||
A computational method for sharp interface advection
|
||||
Royal Society Open Science, 3
|
||||
doi 10.1098/rsos.160405
|
||||
\endverbatim
|
||||
|
||||
Original code supplied by Johan Roenby, DHI (2016)
|
||||
|
||||
SourceFiles
|
||||
isoCutFace.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef isoCutFace_H
|
||||
#define isoCutFace_H
|
||||
|
||||
#include "fvMesh.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class isoCutFaces Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class isoCutFace
|
||||
{
|
||||
// Private data
|
||||
|
||||
//- Mesh whose cells and faces to cut at their intersection with an
|
||||
// isoface
|
||||
const fvMesh& mesh_;
|
||||
|
||||
//- Isofunction values at mesh points. f_size() = mesh_.nPoints()
|
||||
scalarField& f_;
|
||||
|
||||
//- Point along first cut edge where isosurface cuts edge
|
||||
scalar firstEdgeCut_;
|
||||
|
||||
//- Point along last cut edge where isosurface cuts edge
|
||||
scalar lastEdgeCut_;
|
||||
|
||||
//- Index in mesh_.faces()[faceI_] of first fully submerged (f > f0)
|
||||
// face point
|
||||
label firstFullySubmergedPoint_;
|
||||
|
||||
//- Index in mesh_.faces()[faceI_] of last fully submerged (f > f0)
|
||||
// face point
|
||||
label nFullySubmergedPoints_;
|
||||
|
||||
//- Storage for centre of subface
|
||||
point subFaceCentre_;
|
||||
|
||||
//- Storage for area vector of subface
|
||||
vector subFaceArea_;
|
||||
|
||||
//- Storage for subFacePoints
|
||||
DynamicList<point> subFacePoints_;
|
||||
|
||||
//- Storage for subFacePoints
|
||||
DynamicList<point> surfacePoints_;
|
||||
|
||||
//- Boolean telling if subface centre and area have been calculated
|
||||
bool subFaceCentreAndAreaIsCalculated_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
void calcSubFaceCentreAndArea();
|
||||
|
||||
//- Calculate cut points along edges of face with values f[pLabels]
|
||||
// Returns the face status, where:
|
||||
// -1: face is fully below the isosurface
|
||||
// 0: face is cut, i.e. has values larger and smaller than isoValue
|
||||
// +1: face is fully above the isosurface
|
||||
label calcSubFace
|
||||
(
|
||||
const scalar isoValue,
|
||||
const pointField& points,
|
||||
const scalarField& f,
|
||||
const labelList& pLabels
|
||||
);
|
||||
|
||||
void subFacePoints(const pointField& points, const labelList& pLabels);
|
||||
|
||||
void surfacePoints(const pointField& points, const labelList& pLabels);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from fvMesh and a scalarField
|
||||
// Length of scalarField should equal number of mesh points
|
||||
isoCutFace(const fvMesh& mesh, scalarField& f);
|
||||
|
||||
// Static data
|
||||
|
||||
static int debug;
|
||||
|
||||
|
||||
// Member functions
|
||||
|
||||
//- Calculate cut points along edges of faceI
|
||||
label calcSubFace(const label faceI, const scalar isoValue);
|
||||
|
||||
//- Calculate cut points along edges of face with values f
|
||||
label calcSubFace
|
||||
(
|
||||
const pointField& points,
|
||||
const scalarField& f,
|
||||
const scalar isoValue
|
||||
);
|
||||
|
||||
const point& subFaceCentre();
|
||||
|
||||
const vector& subFaceArea();
|
||||
|
||||
const DynamicList<point>& subFacePoints() const;
|
||||
|
||||
const DynamicList<point>& surfacePoints() const;
|
||||
|
||||
void clearStorage();
|
||||
|
||||
//- Calculate time integrated area for a face
|
||||
scalar timeIntegratedArea
|
||||
(
|
||||
const pointField& fPts,
|
||||
const scalarField& pTimes,
|
||||
const scalar dt,
|
||||
const scalar magSf,
|
||||
const scalar Un0
|
||||
);
|
||||
|
||||
void cutPoints
|
||||
(
|
||||
const pointField& pts,
|
||||
const scalarField& f,
|
||||
const scalar f0,
|
||||
DynamicList<point>& cutPoints
|
||||
);
|
||||
|
||||
|
||||
void quadAreaCoeffs
|
||||
(
|
||||
const DynamicList<point>& pf0,
|
||||
const DynamicList<point>& pf1,
|
||||
scalar& alpha,
|
||||
scalar& beta
|
||||
) const;
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
@ -18,6 +18,8 @@ nearWallFields/findCellParticleCloud.C
|
||||
processorField/processorField.C
|
||||
readFields/readFields.C
|
||||
|
||||
setFlow/setFlow.C
|
||||
|
||||
streamLine/streamLine.C
|
||||
streamLine/streamLineBase.C
|
||||
streamLine/streamLineParticle.C
|
||||
|
||||
@ -906,7 +906,7 @@ bool Foam::functionObjects::externalCoupled::read(const dictionary& dict)
|
||||
|
||||
timeOut_ = dict.lookupOrDefault("timeOut", 100*waitInterval_);
|
||||
stateEnd_ =
|
||||
stateEndNames_.lookupOrDefault("stateEnd", dict, stateEnd::REMOVE);
|
||||
stateEndNames_.lookupOrDefault("stateEnd", dict, stateEnd::DONE);
|
||||
|
||||
|
||||
// Get names of all fvMeshes (and derived types)
|
||||
|
||||
@ -32,12 +32,13 @@ Description
|
||||
an external application. The coupling is through plain text files
|
||||
where OpenFOAM boundary data is read/written as one line per face
|
||||
(data from all processors collated):
|
||||
|
||||
\verbatim
|
||||
# Patch: <patch name>
|
||||
<fld1> <fld2> .. <fldn> //face0
|
||||
<fld1> <fld2> .. <fldn> //face1
|
||||
..
|
||||
<fld1> <fld2> .. <fldn> //faceN
|
||||
\endverbatim
|
||||
|
||||
where the actual entries depend on the bc type:
|
||||
- mixed: value, snGrad, refValue, refGrad, valueFraction
|
||||
@ -47,29 +48,33 @@ Description
|
||||
These text files are located in a user specified communications directory
|
||||
which gets read/written on the master processor only. In the
|
||||
communications directory the structure will be
|
||||
|
||||
\verbatim
|
||||
<regionsName>/<patchGroup>/<fieldName>.[in|out]
|
||||
\endverbatim
|
||||
|
||||
(where regionsName is either the name of a single region or a composite
|
||||
of multiple region names)
|
||||
|
||||
At start-up, the boundary creates a lock file, i.e..
|
||||
|
||||
\verbatim
|
||||
OpenFOAM.lock
|
||||
\endverbatim
|
||||
|
||||
... to signal the external source to wait. During the functionObject
|
||||
execution the boundary values are written to files (one per region,
|
||||
per patch(group), per field), e.g.
|
||||
|
||||
\verbatim
|
||||
<regionsName>/<patchGroup>/<fieldName>.out
|
||||
\endverbatim
|
||||
|
||||
The lock file is then removed, instructing the external source to take
|
||||
control of the program execution. When ready, the external program
|
||||
should create the return values, e.g. to files
|
||||
|
||||
\verbatim
|
||||
<regionsName>/<patchGroup>/<fieldName>.in
|
||||
\endverbatim
|
||||
|
||||
... and then re-instate the lock file. The functionObject will then
|
||||
... and then reinstate the lock file. The functionObject will then
|
||||
read these values, apply them to the boundary conditions and pass
|
||||
program execution back to OpenFOAM.
|
||||
|
||||
@ -98,21 +103,39 @@ Usage
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
This reads/writes (on the master processor) the directory:
|
||||
|
||||
This reads/writes (on the master processor) the directory:
|
||||
\verbatim
|
||||
comms/region0_region1/TPatchGroup/
|
||||
\endverbatim
|
||||
|
||||
with contents:
|
||||
\verbatim
|
||||
patchPoints (collected points)
|
||||
patchFaces (collected faces)
|
||||
p.in (input file of p, written by external application)
|
||||
T.out (output file of T, written by OpenFOAM)
|
||||
\endverbatim
|
||||
|
||||
The patchPoints/patchFaces files denote the (collated) geometry
|
||||
which will be written if it does not exist yet or can be written as
|
||||
a preprocessing step using the createExternalCoupledPatchGeometry
|
||||
application.
|
||||
|
||||
The entries comprise:
|
||||
\table
|
||||
Property | Description | Required | Default value
|
||||
type | type name: externalCoupled | yes |
|
||||
commsDir | communication directory | yes |
|
||||
waitInterval | wait interval in (s) | no | 1
|
||||
timeOut | timeout in (s) | no | 100*waitInterval
|
||||
stateEnd | Lockfile treatment on termination | no | done
|
||||
initByExternal | initialization values supplied by external app | yes
|
||||
calcFrequency | calculation frequency | no | 1
|
||||
regions | the regions to couple | yes
|
||||
\endtable
|
||||
|
||||
|
||||
SourceFiles
|
||||
externalCoupled.C
|
||||
externalCoupledTemplates.C
|
||||
|
||||
@ -38,10 +38,10 @@ Usage
|
||||
...
|
||||
faceZone f0;
|
||||
nLocations 10;
|
||||
alphaName alpha.water;
|
||||
UName U;
|
||||
rhoName rho;
|
||||
phiName phi;
|
||||
alpha alpha.water;
|
||||
U U;
|
||||
rho rho;
|
||||
phi phi;
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
@ -50,10 +50,14 @@ Usage
|
||||
Property | Description | Required | Default value
|
||||
type | type name: extractEulerianParticles | yes |
|
||||
faceZone | Name of faceZone used as collection surface | yes |
|
||||
nLocations | Number of injection bins to generate | yes |
|
||||
aplhaName | Name of phase indicator field | yes |
|
||||
rhoName | Name of density field | yes |
|
||||
phiNane | Name of flux field | yes |
|
||||
alpha | Name of phase indicator field | yes |
|
||||
alphaThreshold | Threshold for alpha field | no | 0.1 |
|
||||
nLocations | Number of injection bins to generate | no | 0 |
|
||||
U | Name of velocity field | no | U |
|
||||
rho | Name of density field | no | rho |
|
||||
phi | Name of flux field | no | phi |
|
||||
minDiameter | min diameter | no | small |
|
||||
maxDiameter | max diameter | no | great |
|
||||
\endtable
|
||||
|
||||
SourceFiles
|
||||
|
||||
448
src/functionObjects/field/setFlow/setFlow.C
Normal file
448
src/functionObjects/field/setFlow/setFlow.C
Normal file
@ -0,0 +1,448 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
|
||||
\\/ 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "setFlow.H"
|
||||
#include "volFields.H"
|
||||
#include "surfaceFields.H"
|
||||
#include "fvcFlux.H"
|
||||
#include "addToRunTimeSelectionTable.H"
|
||||
#include "fvcSurfaceIntegrate.H"
|
||||
|
||||
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace functionObjects
|
||||
{
|
||||
defineTypeNameAndDebug(setFlow, 0);
|
||||
|
||||
addToRunTimeSelectionTable
|
||||
(
|
||||
functionObject,
|
||||
setFlow,
|
||||
dictionary
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Foam::Enum<Foam::functionObjects::setFlow::modeType>
|
||||
Foam::functionObjects::setFlow::modeTypeNames
|
||||
{
|
||||
{ functionObjects::setFlow::modeType::FUNCTION, "function" },
|
||||
{ functionObjects::setFlow::modeType::ROTATION, "rotation" },
|
||||
{ functionObjects::setFlow::modeType::VORTEX2D, "vortex2D" },
|
||||
{ functionObjects::setFlow::modeType::VORTEX3D, "vortex3D" }
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||
|
||||
void Foam::functionObjects::setFlow::setPhi(const volVectorField& U)
|
||||
{
|
||||
surfaceScalarField* phiptr =
|
||||
mesh_.lookupObjectRefPtr<surfaceScalarField>(phiName_);
|
||||
|
||||
if (!phiptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (rhoName_ != "none")
|
||||
{
|
||||
const volScalarField* rhoptr =
|
||||
mesh_.lookupObjectPtr<volScalarField>(rhoName_);
|
||||
|
||||
if (rhoptr)
|
||||
{
|
||||
const volScalarField& rho = *rhoptr;
|
||||
*phiptr = fvc::flux(rho*U);
|
||||
}
|
||||
else
|
||||
{
|
||||
FatalErrorInFunction
|
||||
<< "Unable to find rho field'" << rhoName_
|
||||
<< "' in the mesh database. Available fields are:"
|
||||
<< mesh_.names<volScalarField>()
|
||||
<< exit(FatalError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*phiptr = fvc::flux(U);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::functionObjects::setFlow::setFlow
|
||||
(
|
||||
const word& name,
|
||||
const Time& runTime,
|
||||
const dictionary& dict
|
||||
)
|
||||
:
|
||||
fvMeshFunctionObject(name, runTime, dict),
|
||||
UName_("U"),
|
||||
rhoName_("none"),
|
||||
phiName_("phi"),
|
||||
mode_(modeType::FUNCTION),
|
||||
reverseTime_(VGREAT),
|
||||
scalePtr_(nullptr),
|
||||
origin_(vector::zero),
|
||||
R_(tensor::I),
|
||||
omegaPtr_(nullptr),
|
||||
velocityPtr_(nullptr)
|
||||
{
|
||||
read(dict);
|
||||
}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
|
||||
|
||||
Foam::functionObjects::setFlow::~setFlow()
|
||||
{}
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||
|
||||
bool Foam::functionObjects::setFlow::read(const dictionary& dict)
|
||||
{
|
||||
if (fvMeshFunctionObject::read(dict))
|
||||
{
|
||||
Info<< name() << ":" << endl;
|
||||
mode_ = modeTypeNames.read(dict.lookup("mode"));
|
||||
|
||||
Info<< " operating mode: " << modeTypeNames[mode_] << endl;
|
||||
|
||||
if (dict.readIfPresent("U", UName_))
|
||||
{
|
||||
Info<< " U field name: " << UName_ << endl;
|
||||
}
|
||||
|
||||
if (dict.readIfPresent("rho", rhoName_))
|
||||
{
|
||||
Info<< " rho field name: " << rhoName_ << endl;
|
||||
}
|
||||
|
||||
if (dict.readIfPresent("phi", phiName_))
|
||||
{
|
||||
Info<< " phi field name: " << phiName_ << endl;
|
||||
}
|
||||
|
||||
if (dict.readIfPresent("reverseTime", reverseTime_))
|
||||
{
|
||||
Info<< " reverse flow direction at time: " << reverseTime_
|
||||
<< endl;
|
||||
reverseTime_ = mesh_.time().userTimeToTime(reverseTime_);
|
||||
}
|
||||
|
||||
// Scaling is applied across all modes
|
||||
scalePtr_ = Function1<scalar>::New("scale", dict);
|
||||
|
||||
switch (mode_)
|
||||
{
|
||||
case modeType::FUNCTION:
|
||||
{
|
||||
velocityPtr_ = Function1<vector>::New("velocity", dict);
|
||||
break;
|
||||
}
|
||||
case modeType::ROTATION:
|
||||
{
|
||||
omegaPtr_ = Function1<scalar>::New("omega", dict);
|
||||
dict.lookup("origin") >> origin_;
|
||||
vector refDir(dict.lookup("refDir"));
|
||||
refDir /= mag(refDir) + ROOTVSMALL;
|
||||
vector axis(dict.lookup("axis"));
|
||||
axis /= mag(axis) + ROOTVSMALL;
|
||||
R_ = tensor(refDir, axis, refDir^axis);
|
||||
break;
|
||||
}
|
||||
case modeType::VORTEX2D:
|
||||
case modeType::VORTEX3D:
|
||||
{
|
||||
dict.lookup("origin") >> origin_;
|
||||
vector refDir(dict.lookup("refDir"));
|
||||
refDir /= mag(refDir) + ROOTVSMALL;
|
||||
vector axis(dict.lookup("axis"));
|
||||
axis /= mag(axis) + ROOTVSMALL;
|
||||
R_ = tensor(refDir, axis, refDir^axis);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Info<< endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::setFlow::execute()
|
||||
{
|
||||
volVectorField* Uptr = mesh_.lookupObjectRefPtr<volVectorField>(UName_);
|
||||
|
||||
surfaceScalarField* phiptr =
|
||||
mesh_.lookupObjectRefPtr<surfaceScalarField>(phiName_);
|
||||
|
||||
Log << nl << name() << ":" << nl;
|
||||
|
||||
if (!Uptr || !phiptr)
|
||||
{
|
||||
Info<< " Either field " << UName_ << " or " << phiName_
|
||||
<< " not found in the mesh database" << nl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const scalar t = mesh_.time().timeOutputValue();
|
||||
|
||||
Log << " setting " << UName_ << " and " << phiName_ << nl;
|
||||
|
||||
volVectorField& U = *Uptr;
|
||||
surfaceScalarField& phi = *phiptr;
|
||||
|
||||
switch (mode_)
|
||||
{
|
||||
case modeType::FUNCTION:
|
||||
{
|
||||
const vector Uc = velocityPtr_->value(t);
|
||||
U == dimensionedVector("Uc", dimVelocity, Uc);
|
||||
U.correctBoundaryConditions();
|
||||
setPhi(U);
|
||||
|
||||
break;
|
||||
}
|
||||
case modeType::ROTATION:
|
||||
{
|
||||
const volVectorField& C = mesh_.C();
|
||||
const volVectorField d
|
||||
(
|
||||
typeName + ":d",
|
||||
C - dimensionedVector("origin", dimLength, origin_)
|
||||
);
|
||||
const scalarField x(d.component(vector::X));
|
||||
const scalarField z(d.component(vector::Z));
|
||||
|
||||
const scalar omega = omegaPtr_->value(t);
|
||||
vectorField& Uc = U.primitiveFieldRef();
|
||||
Uc.replace(vector::X, -omega*z);
|
||||
Uc.replace(vector::Y, scalar(0));
|
||||
Uc.replace(vector::Z, omega*x);
|
||||
|
||||
volVectorField::Boundary& Ubf = U.boundaryFieldRef();
|
||||
forAll(Ubf, patchi)
|
||||
{
|
||||
vectorField& Uf = Ubf[patchi];
|
||||
if (Uf.size())
|
||||
{
|
||||
const vectorField& Cp = C.boundaryField()[patchi];
|
||||
const vectorField dp(Cp - origin_);
|
||||
const scalarField xp(dp.component(vector::X));
|
||||
const scalarField zp(dp.component(vector::Z));
|
||||
Uf.replace(vector::X, -omega*zp);
|
||||
Uf.replace(vector::Y, scalar(0));
|
||||
Uf.replace(vector::Z, omega*xp);
|
||||
}
|
||||
}
|
||||
|
||||
U = U & R_;
|
||||
U.correctBoundaryConditions();
|
||||
setPhi(U);
|
||||
|
||||
break;
|
||||
}
|
||||
case modeType::VORTEX2D:
|
||||
{
|
||||
const scalar pi = Foam::constant::mathematical::pi;
|
||||
|
||||
const volVectorField& C = mesh_.C();
|
||||
|
||||
const volVectorField d
|
||||
(
|
||||
typeName + ":d",
|
||||
C - dimensionedVector("origin", dimLength, origin_)
|
||||
);
|
||||
const scalarField x(d.component(vector::X));
|
||||
const scalarField z(d.component(vector::Z));
|
||||
|
||||
vectorField& Uc = U.primitiveFieldRef();
|
||||
Uc.replace(vector::X, -sin(2*pi*z)*sqr(sin(pi*x)));
|
||||
Uc.replace(vector::Y, scalar(0));
|
||||
Uc.replace(vector::Z, sin(2*pi*x)*sqr(sin(pi*z)));
|
||||
|
||||
U = U & R_;
|
||||
|
||||
// Calculating incompressible flux based on stream function
|
||||
// Note: R_ rotation not implemented in phi calculation
|
||||
const scalarField xp(mesh_.points().component(0) - origin_[0]);
|
||||
const scalarField yp(mesh_.points().component(1) - origin_[1]);
|
||||
const scalarField zp(mesh_.points().component(2) - origin_[2]);
|
||||
const scalarField psi((1.0/pi)*sqr(sin(pi*xp))*sqr(sin(pi*zp)));
|
||||
|
||||
scalarField& phic = phi.primitiveFieldRef();
|
||||
forAll(phic, fi)
|
||||
{
|
||||
phic[fi] = 0;
|
||||
const face& f = mesh_.faces()[fi];
|
||||
const label nPoints = f.size();
|
||||
|
||||
forAll(f, fpi)
|
||||
{
|
||||
const label p1 = f[fpi];
|
||||
const label p2 = f[(fpi + 1) % nPoints];
|
||||
phic[fi] += 0.5*(psi[p1] + psi[p2])*(yp[p2] - yp[p1]);
|
||||
}
|
||||
}
|
||||
|
||||
surfaceScalarField::Boundary& phibf = phi.boundaryFieldRef();
|
||||
forAll(phibf, patchi)
|
||||
{
|
||||
scalarField& phif = phibf[patchi];
|
||||
const label start = mesh_.boundaryMesh()[patchi].start();
|
||||
|
||||
forAll(phif, fi)
|
||||
{
|
||||
phif[fi] = 0;
|
||||
const face& f = mesh_.faces()[start + fi];
|
||||
const label nPoints = f.size();
|
||||
|
||||
forAll(f, fpi)
|
||||
{
|
||||
const label p1 = f[fpi];
|
||||
const label p2 = f[(fpi + 1) % nPoints];
|
||||
phif[fi] += 0.5*(psi[p1] + psi[p2])*(yp[p2] - yp[p1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case modeType::VORTEX3D:
|
||||
{
|
||||
const scalar pi = Foam::constant::mathematical::pi;
|
||||
const volVectorField& C = mesh_.C();
|
||||
|
||||
const volVectorField d
|
||||
(
|
||||
typeName + ":d",
|
||||
C - dimensionedVector("origin", dimLength, origin_)
|
||||
);
|
||||
const scalarField x(d.component(vector::X));
|
||||
const scalarField y(d.component(vector::Y));
|
||||
const scalarField z(d.component(vector::Z));
|
||||
|
||||
vectorField& Uc = U.primitiveFieldRef();
|
||||
Uc.replace(vector::X, 2*sqr(sin(pi*x))*sin(2*pi*y)*sin(2*pi*z));
|
||||
Uc.replace(vector::Y, -sin(2*pi*x)*sqr(sin(pi*y))*sin(2*pi*z));
|
||||
Uc.replace(vector::Z, -sin(2*pi*x)*sin(2*pi*y)*sqr(sin(pi*z)));
|
||||
|
||||
U = U & R_;
|
||||
U.correctBoundaryConditions();
|
||||
|
||||
// Calculating phi
|
||||
// Note: R_ rotation not implemented in phi calculation
|
||||
const vectorField Cf(mesh_.Cf().primitiveField() - origin_);
|
||||
const scalarField Xf(Cf.component(vector::X));
|
||||
const scalarField Yf(Cf.component(vector::Y));
|
||||
const scalarField Zf(Cf.component(vector::Z));
|
||||
vectorField Uf(Xf.size());
|
||||
Uf.replace(0, 2*sqr(sin(pi*Xf))*sin(2*pi*Yf)*sin(2*pi*Zf));
|
||||
Uf.replace(1, -sin(2*pi*Xf)*sqr(sin(pi*Yf))*sin(2*pi*Zf));
|
||||
Uf.replace(2, -sin(2*pi*Xf)*sin(2*pi*Yf)*sqr(sin(pi*Zf)));
|
||||
|
||||
scalarField& phic = phi.primitiveFieldRef();
|
||||
const vectorField& Sfc = mesh_.Sf().primitiveField();
|
||||
phic = Uf & Sfc;
|
||||
|
||||
surfaceScalarField::Boundary& phibf = phi.boundaryFieldRef();
|
||||
const surfaceVectorField::Boundary& Sfbf =
|
||||
mesh_.Sf().boundaryField();
|
||||
const surfaceVectorField::Boundary& Cfbf =
|
||||
mesh_.Cf().boundaryField();
|
||||
|
||||
forAll(phibf, patchi)
|
||||
{
|
||||
scalarField& phif = phibf[patchi];
|
||||
const vectorField& Sff = Sfbf[patchi];
|
||||
const vectorField& Cff = Cfbf[patchi];
|
||||
const scalarField xf(Cff.component(vector::X));
|
||||
const scalarField yf(Cff.component(vector::Y));
|
||||
const scalarField zf(Cff.component(vector::Z));
|
||||
vectorField Uf(xf.size());
|
||||
Uf.replace(0, 2*sqr(sin(pi*xf))*sin(2*pi*yf)*sin(2*pi*zf));
|
||||
Uf.replace(1, -sin(2*pi*xf)*sqr(sin(pi*yf))*sin(2*pi*zf));
|
||||
Uf.replace(2, -sin(2*pi*xf)*sin(2*pi*yf)*sqr(sin(pi*zf)));
|
||||
phif = Uf & Sff;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (t > reverseTime_)
|
||||
{
|
||||
Log << " flow direction: reverse" << nl;
|
||||
U.negate();
|
||||
phi.negate();
|
||||
}
|
||||
|
||||
// Apply scaling
|
||||
const scalar s = scalePtr_->value(t);
|
||||
U *= s;
|
||||
phi *= s;
|
||||
|
||||
U.correctBoundaryConditions();
|
||||
|
||||
const scalarField sumPhi(fvc::surfaceIntegrate(phi));
|
||||
Log << " Continuity error: max(mag(sum(phi))) = "
|
||||
<< gMax(mag(sumPhi)) << nl << endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Foam::functionObjects::setFlow::write()
|
||||
{
|
||||
const auto& Uptr = mesh_.lookupObjectRefPtr<volVectorField>(UName_);
|
||||
if (Uptr)
|
||||
{
|
||||
Uptr->write();
|
||||
}
|
||||
|
||||
const auto& phiptr = mesh_.lookupObjectRefPtr<surfaceScalarField>(phiName_);
|
||||
if (phiptr)
|
||||
{
|
||||
phiptr->write();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ************************************************************************* //
|
||||
199
src/functionObjects/field/setFlow/setFlow.H
Normal file
199
src/functionObjects/field/setFlow/setFlow.H
Normal file
@ -0,0 +1,199 @@
|
||||
/*---------------------------------------------------------------------------*\
|
||||
========= |
|
||||
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
||||
\\ / O peration |
|
||||
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
|
||||
\\/ 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
Class
|
||||
Foam::functionObjects::setFlow
|
||||
|
||||
Group
|
||||
grpFieldFunctionObjects
|
||||
|
||||
Description
|
||||
Provides options to set the velocity and flux fields as a function of time
|
||||
|
||||
Useful for testing advection behaviour of numerical schemes by e.g.
|
||||
imposing solid body rotation, vortex flows. All types include a scaling
|
||||
Foam::Function1 type enabling the strength of the transformation to vary as
|
||||
a function of time.
|
||||
|
||||
Usage
|
||||
Example of function object specification:
|
||||
\verbatim
|
||||
setFlow1
|
||||
{
|
||||
type setFlow;
|
||||
libs ("libfieldFunctionObjects.so");
|
||||
...
|
||||
mode rotation;
|
||||
scale 1;
|
||||
reverseTime 1;
|
||||
omega 6.28318530718;
|
||||
origin (0.5 0 0.5);
|
||||
refDir (1 0 0);
|
||||
axis (0 1 0);
|
||||
}
|
||||
\endverbatim
|
||||
|
||||
Where the entries comprise:
|
||||
\table
|
||||
Property | Description | Required | Default value
|
||||
type | Type name: setFlow | yes |
|
||||
U | Name of velocity field | no | U
|
||||
rho | Name of density field | no | none
|
||||
phi | Name of flux field | no | phi
|
||||
mode | operating mode - see below | yes |
|
||||
\endtable
|
||||
|
||||
Available \c mode types include:
|
||||
- function
|
||||
- rortation
|
||||
- vortex2D
|
||||
- vortex3D
|
||||
|
||||
|
||||
See also
|
||||
Foam::functionObjects::fvMeshFunctionObject
|
||||
|
||||
SourceFiles
|
||||
setFlow.C
|
||||
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef functionObjects_setFlow_H
|
||||
#define functionObjects_setFlow_H
|
||||
|
||||
#include "fvMeshFunctionObject.H"
|
||||
#include "Function1.H"
|
||||
#include "Enum.H"
|
||||
#include "point.H"
|
||||
#include "volFieldsFwd.H"
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
namespace Foam
|
||||
{
|
||||
namespace functionObjects
|
||||
{
|
||||
|
||||
/*---------------------------------------------------------------------------*\
|
||||
Class setFlow Declaration
|
||||
\*---------------------------------------------------------------------------*/
|
||||
|
||||
class setFlow
|
||||
:
|
||||
public fvMeshFunctionObject
|
||||
{
|
||||
enum class modeType
|
||||
{
|
||||
FUNCTION,
|
||||
ROTATION,
|
||||
VORTEX2D,
|
||||
VORTEX3D
|
||||
};
|
||||
|
||||
static const Foam::Enum<modeType> modeTypeNames;
|
||||
|
||||
|
||||
// Private Data
|
||||
|
||||
//- Name of vecloity field, default = "U"
|
||||
word UName_;
|
||||
|
||||
//- Name of density field, default = "none"
|
||||
word rhoName_;
|
||||
|
||||
//- Name of flux field, default = "phi"
|
||||
word phiName_;
|
||||
|
||||
//- Operating mode
|
||||
modeType mode_;
|
||||
|
||||
//- Reverse time
|
||||
scalar reverseTime_;
|
||||
|
||||
//- Scaling function
|
||||
autoPtr<Function1<scalar>> scalePtr_;
|
||||
|
||||
|
||||
// Rotation
|
||||
|
||||
//- Origin
|
||||
point origin_;
|
||||
|
||||
//- Rotation tensor for rotational mode
|
||||
tensor R_;
|
||||
|
||||
//- Rotational speed function
|
||||
autoPtr<Function1<scalar>> omegaPtr_;
|
||||
|
||||
|
||||
// Function
|
||||
|
||||
//- Velocity function
|
||||
autoPtr<Function1<vector>> velocityPtr_;
|
||||
|
||||
|
||||
// Private Member Functions
|
||||
|
||||
//- Set the flux field
|
||||
void setPhi(const volVectorField& U);
|
||||
|
||||
|
||||
public:
|
||||
|
||||
//- Runtime type information
|
||||
TypeName("setFlow");
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
//- Construct from Time and dictionary
|
||||
setFlow
|
||||
(
|
||||
const word& name,
|
||||
const Time& runTime,
|
||||
const dictionary& dict
|
||||
);
|
||||
|
||||
|
||||
//- Destructor
|
||||
virtual ~setFlow();
|
||||
|
||||
|
||||
virtual bool read(const dictionary& dict);
|
||||
|
||||
virtual bool execute();
|
||||
|
||||
virtual bool write();
|
||||
};
|
||||
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
} // End namespace functionObjects
|
||||
} // End namespace Foam
|
||||
|
||||
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||
|
||||
#endif
|
||||
|
||||
// ************************************************************************* //
|
||||
Reference in New Issue
Block a user