OpenFOAM中的fvOptions是一个功能灵活的框架,可以在不重写原始源代码的情况下,在指定区域内向控制方程添加各种源项或者其他约束(比如固定温度、多孔介质、磁流变流体等)。
本质上,fvOptions类有一个公共函数AddSup
。在这个函数中, eqn.source()
用于设置源项,其中,当求解器创建IOOptionList
时,eqn
将显示为引用UEqn
。使用eqn.psi()
,fvOption类也可以访问当前速度,mesh_
是基类选项提供的mesh数据库的引用。所以很容易得到动量源项,它取决于当前的速度场。
这里,我们以pisoFoam求解器为例,分析求解器中是怎么调用 fvOptions的。
#include "createFvOptions.H"//添加头文件createFvOptions.H
fvVectorMatrix UEqn
(
fvm::ddt(U) + fvm::div(phi, U)
+ MRF.DDt(U)
+ turbulence->divDevReff(U)
==
fvOptions(U)//UEqn在 RHS(right-hand side)上用==fvOptions(U)定义
);
fvOptions.correct(U);//对速度场进行修正
接下来,我们对以上添加部分做逐一分析。
fv::options& fvOptions(fv::options::New(mesh));
fvOptions
是一个fv::options
类对象的引用,New(mesh)
是fv::options
类中的一个静态static
方法,其返回fv::options
类对象。
以下为fvOptions.H
代码:
Class
Foam::fv::options
Description
Finite-volume options
SourceFiles
options.C
\*---------------------------------------------------------------------------*/
#ifndef options_H
#define options_H
#include "fvOptionList.H"
#include "IOdictionary.H"
#include "autoPtr.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
/*---------------------------------------------------------------------------*\
Class options Declaration
\*---------------------------------------------------------------------------*/
class options
:
public IOdictionary,
public optionList
{
// Private Member Functions
//- Create IO object if dictionary is present
IOobject createIOobject(const fvMesh& mesh) const;
//- Disallow default bitwise copy construct
options(const options&);
//- Disallow default bitwise assignment
void operator=(const options&);
public:
// Declare name of the class and its debug switch
ClassName("fvOptions");
// Constructors
//- Construct from components with list of field names
options(const fvMesh& mesh);
//- Construct fvOptions and register to datbase if not present
// otherwise lookup and return
static options& New(const fvMesh& mesh);
//- Destructor
virtual ~options()
{}
// Member Functions
//- Inherit read from optionList
using optionList::read;
//- Read dictionary
virtual bool read();
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace fv
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
fv::options
类继承于optionList
与IOdictionary
类,并调用了私有成员函数createIOobject
IOdictionary
类用于处理 fvOptions
相关的字典文件,optionList
中定义 fvOptions
源项具体内容。createIOobject
:该私有成员函数会接连访问constant
文件夹和system
文件夹,如果存在fvOptions
文件,则进行数据读取并初始化,否则放弃读取。\*---------------------------------------------------------------------------*/
#include "fvOptions.H"
#include "fvMesh.H"
#include "Time.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
defineTypeNameAndDebug(options, 0);
}
}
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
Foam::IOobject Foam::fv::options::createIOobject
(
const fvMesh& mesh
) const
{
IOobject io
(
typeName,
mesh.time().constant(),
mesh,
IOobject::MUST_READ,
IOobject::NO_WRITE
);
if (io.headerOk())
{
Info<< "Creating finite volume options from "
<< io.instance()/io.name() << nl
<< endl;
io.readOpt() = IOobject::MUST_READ_IF_MODIFIED;
return io;
}
else
{
// Check if the fvOptions file is in system
io.instance() = mesh.time().system();
if (io.headerOk())
{
Info<< "Creating finite volume options from "
<< io.instance()/io.name() << nl
<< endl;
io.readOpt() = IOobject::MUST_READ_IF_MODIFIED;
return io;
}
else
{
Info<< "No finite volume options present" << nl << endl;
io.readOpt() = IOobject::NO_READ;
return io;
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fv::options::options
(
const fvMesh& mesh
)
:
IOdictionary(createIOobject(mesh)), //构造函数中调用 createIOobject 函数来对父类 IOdictionary 进行初始化
optionList(mesh, *this)//通过(*this) 作为参数传递给父类 optionList
{}
//存在,读取;否则创建并引用
Foam::fv::options& Foam::fv::options::New(const fvMesh& mesh)
{
if (mesh.thisDb().foundObject<options>(typeName))
{
return const_cast<options&>
(
mesh.lookupObject<options>(typeName)
);
}
else
{
if (debug)
{
InfoInFunction
<< "Constructing " << typeName
<< " for region " << mesh.name() << endl;
}
options* objectPtr = new options(mesh);
regIOobject::store(objectPtr);
return *objectPtr;
}
}
bool Foam::fv::options::read()
{
if (IOdictionary::regIOobject::read())
{
optionList::read(*this);
return true;
}
else
{
return false;
}
}
// ************************************************************************* //
optionList(mesh,*this)
参数一个是fvmesh
类,一个是options
类,在fvOptionList.H
文件中,对应于optionList(const fvMesh& mesh, const dictionary& dict)
,可见options
类对象是对dictionary
类对象的引用。
Class
Foam::fv::optionList
Description
List of finite volume options
SourceFile
optionList.C
\*---------------------------------------------------------------------------*/
#ifndef fvOptionList_H
#define fvOptionList_H
#include "fvOption.H"
#include "PtrList.H"
#include "GeometricField.H"
#include "geometricOneField.H"
#include "fvPatchField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declaration of friend functions and operators
namespace fv
{
class optionList;
}
Ostream& operator<<(Ostream& os, const fv::optionList& options);
namespace fv
{
/*---------------------------------------------------------------------------*\
Class optionList Declaration
\*---------------------------------------------------------------------------*/
class optionList
:
public PtrList<option>
{
protected:
// Protected data
//- Reference to the mesh database
const fvMesh& mesh_;
//- Time index to check that all defined sources have been applied
label checkTimeIndex_;
// Protected Member Functions
//- Return the "options" sub-dictionary if present otherwise return dict
const dictionary& optionsDict(const dictionary& dict) const;
//- Read options dictionary
bool readOptions(const dictionary& dict);
//- Check that all sources have been applied
void checkApplied() const;
//- Disallow default bitwise copy construct
optionList(const optionList&);
//- Disallow default bitwise assignment
void operator=(const optionList&);
public:
//- Runtime type information
TypeName("optionList");
// Constructors
//- Construct null
optionList(const fvMesh& mesh);
//- Construct from mesh and dictionary
optionList(const fvMesh& mesh, const dictionary& dict);
//- Destructor
virtual ~optionList()
{}
// Member Functions
//- Reset the source list
void reset(const dictionary& dict);
// Sources
//- Return source for equation
// 重载的括号操作符,在求解器里,方程的构造过程中调用的就是这些括号操作符
template<class Type>
tmp<fvMatrix<Type>> operator()
(
GeometricField<Type, fvPatchField, volMesh>& field
);
//- Return source for equation with specified name
template<class Type>
tmp<fvMatrix<Type>> operator()
(
GeometricField<Type, fvPatchField, volMesh>& field,
const word& fieldName
);
//- Return source for equation
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const volScalarField& rho,
GeometricField<Type, fvPatchField, volMesh>& field
);
//- Return source for equation with specified name
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const volScalarField& rho,
GeometricField<Type, fvPatchField, volMesh>& field,
const word& fieldName
);
//- Return source for equation
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const volScalarField& alpha,
const volScalarField& rho,
GeometricField<Type, fvPatchField, volMesh>& field
);
//- Return source for equation with specified name
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const volScalarField& alpha,
const volScalarField& rho,
GeometricField<Type, fvPatchField, volMesh>& field,
const word& fieldName
);
//- Return source for equation
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const volScalarField& alpha,
const geometricOneField& rho,
GeometricField<Type, fvPatchField, volMesh>& field
);
//- Return source for equation
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const geometricOneField& alpha,
const volScalarField& rho,
GeometricField<Type, fvPatchField, volMesh>& field
);
//- Return source for equation
template<class Type>
tmp<fvMatrix<Type>> operator()
(
const geometricOneField& alpha,
const geometricOneField& rho,
GeometricField<Type, fvPatchField, volMesh>& field
);
// Constraints
//- Apply constraints to equation
template<class Type>
void constrain(fvMatrix<Type>& eqn);
// Correction
//- Apply correction to field
template<class Type>
void correct(GeometricField<Type, fvPatchField, volMesh>& field);
// IO
//- Read dictionary
virtual bool read(const dictionary& dict);
//- Write data to Ostream
virtual bool writeData(Ostream& os) const;
//- Ostream operator
friend Ostream& operator<<
(
Ostream& os,
const optionList& options
);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace fv
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "fvOptionListTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
这里注意以下几点:
optionList
类继承自 PtrList
,这 意味着 fvOptions
类是支持同时定义多个源项;()
;void reset(const dictionary& dict);
的定义(这里涉及到reset
和optionsDict
函数的定义)查看fvOptionList.C
。\*---------------------------------------------------------------------------*/
#include "fvOptionList.H"
#include "surfaceFields.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace fv
{
defineTypeNameAndDebug(optionList, 0);
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
const Foam::dictionary& Foam::fv::optionList::optionsDict
(
const dictionary& dict
) const
{
if (dict.found("options"))
{
return dict.subDict("options");
}
else
{
return dict;
}
}
bool Foam::fv::optionList::readOptions(const dictionary& dict)
{
checkTimeIndex_ = mesh_.time().timeIndex() + 2;
bool allOk = true;
forAll(*this, i)
{
option& bs = this->operator[](i);
bool ok = bs.read(dict.subDict(bs.name()));
allOk = (allOk && ok);
}
return allOk;
}
void Foam::fv::optionList::checkApplied() const
{
if (mesh_.time().timeIndex() == checkTimeIndex_)
{
forAll(*this, i)
{
const option& bs = this->operator[](i);
bs.checkApplied();
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fv::optionList::optionList(const fvMesh& mesh, const dictionary& dict)
:
PtrList<option>(),
mesh_(mesh),
checkTimeIndex_(mesh_.time().startTimeIndex() + 2)
{
reset(optionsDict(dict));
}
Foam::fv::optionList::optionList(const fvMesh& mesh)
:
PtrList<option>(),
mesh_(mesh),
checkTimeIndex_(mesh_.time().startTimeIndex() + 2)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::fv::optionList::reset(const dictionary& dict)
{
// Count number of active fvOptions
label count = 0;
forAllConstIter(dictionary, dict, iter)
{
if (iter().isDict())
{
count++;
}
}
this->setSize(count);
label i = 0;
forAllConstIter(dictionary, dict, iter)
{
if (iter().isDict())
{
const word& name = iter().keyword();
const dictionary& sourceDict = iter().dict();
this->set
(
i++,
option::New(name, sourceDict, mesh_)
);
}
}
}
bool Foam::fv::optionList::read(const dictionary& dict)
{
return readOptions(optionsDict(dict));
}
bool Foam::fv::optionList::writeData(Ostream& os) const
{
// Write list contents
forAll(*this, i)
{
os << nl;
this->operator[](i).writeHeader(os);
this->operator[](i).writeData(os);
this->operator[](i).writeFooter(os);
}
// Check state of IOstream
return os.good();
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<(Ostream& os, const fv::optionList& options)
{
options.writeData(os);
return os;
}
// ************************************************************************* //
这一部分可参考SemiImplicitSource.C
文件
(文件位置:$FOAM_SRC/fvOptions/sources/general/
)
#include "SemiImplicitSource.H"
#include "fvMesh.H"
#include "fvMatrices.H"
#include "DimensionedField.H"
#include "fvmSup.H"
// * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * * //
template<class Type>
const Foam::wordList Foam::fv::SemiImplicitSource<Type>::volumeModeTypeNames_
(
IStringStream("(absolute specific)")()
);
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
template<class Type>
typename Foam::fv::SemiImplicitSource<Type>::volumeModeType
Foam::fv::SemiImplicitSource<Type>::wordToVolumeModeType
(
const word& vmtName
) const
{
forAll(volumeModeTypeNames_, i)
{
if (vmtName == volumeModeTypeNames_[i])
{
return volumeModeType(i);
}
}
FatalErrorInFunction
<< "Unknown volumeMode type " << vmtName
<< ". Valid volumeMode types are:" << nl << volumeModeTypeNames_
<< exit(FatalError);
return volumeModeType(0);
}
template<class Type>
Foam::word Foam::fv::SemiImplicitSource<Type>::volumeModeTypeToWord
(
const volumeModeType& vmtType
) const
{
if (vmtType > volumeModeTypeNames_.size())
{
return "UNKNOWN";
}
else
{
return volumeModeTypeNames_[vmtType];
}
}
template<class Type>
void Foam::fv::SemiImplicitSource<Type>::setFieldData(const dictionary& dict)
{
fieldNames_.setSize(dict.toc().size());
injectionRate_.setSize(fieldNames_.size());
applied_.setSize(fieldNames_.size(), false);
label i = 0;
forAllConstIter(dictionary, dict, iter)
{
fieldNames_[i] = iter().keyword();
dict.lookup(iter().keyword()) >> injectionRate_[i];
i++;
}
// Set volume normalisation
if (volumeMode_ == vmAbsolute)
{
VDash_ = V_;
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class Type>
Foam::fv::SemiImplicitSource<Type>::SemiImplicitSource
(
const word& name,
const word& modelType,
const dictionary& dict,
const fvMesh& mesh
)
:
cellSetOption(name, modelType, dict, mesh),
volumeMode_(vmAbsolute),
VDash_(1.0),
injectionRate_()
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
template<class Type>
void Foam::fv::SemiImplicitSource<Type>::addSup
(
fvMatrix<Type>& eqn,
const label fieldi
)
{
if (debug)
{
Info<< "SemiImplicitSource<" << pTraits<Type>::typeName
<< ">::addSup for source " << name_ << endl;
}
const GeometricField<Type, fvPatchField, volMesh>& psi = eqn.psi();
DimensionedField<Type, volMesh> Su
(
IOobject
(
name_ + fieldNames_[fieldi] + "Su",
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensioned<Type>
(
"zero",
eqn.dimensions()/dimVolume,
Zero
),
false
);
UIndirectList<Type>(Su, cells_) = injectionRate_[fieldi].first()/VDash_;
DimensionedField<scalar, volMesh> Sp
(
IOobject
(
name_ + fieldNames_[fieldi] + "Sp",
mesh_.time().timeName(),
mesh_,
IOobject::NO_READ,
IOobject::NO_WRITE
),
mesh_,
dimensioned<scalar>
(
"zero",
Su.dimensions()/psi.dimensions(),
0.0
),
false
);
UIndirectList<scalar>(Sp, cells_) = injectionRate_[fieldi].second()/VDash_;
eqn += Su + fvm::SuSp(Sp, psi);
}
template<class Type>
void Foam::fv::SemiImplicitSource<Type>::addSup
(
const volScalarField& rho,
fvMatrix<Type>& eqn,
const label fieldi
)
{
if (debug)
{
Info<< "SemiImplicitSource<" << pTraits<Type>::typeName
<< ">::addSup for source " << name_ << endl;
}
return this->addSup(eqn, fieldi);
}
在 optionList 类里,所有可能出现在求解器代码里函数都有了,包括
correct
,constrain
,
makeRelative
,makeAbsolute
,relative
以及()
运算符的重载。但是,注意这里并没有具体的代码实现,而是通过类似
forAll(*this, i)
{
this->operator[](i).makeAbsolute(phi);
}
调用其他地方的函数。
optionList
的一个重要使命是,统计fvOptions
文件里定义了多少个源项,并将每一个源项都作为一个储存起来,然后再根据词典的内容创建特定的源项。核心在于reset
函数。
reset(optionsDict(dict));
可见构造函数里调用了
reset
函数,并且用optionsDict
函数的返回值作为reset
函数的参数。 ------以上部分引自参考2
参考: