自定义viscosityModel的实现是基于粘度取决于剪切速率的示例构建的。
以血液作为流体示例,粘度与剪切速率相关的实验数据见[1,图2]。使用engauge数字化仪将[1]中的实验数据数字化为CSV文件,第一列为应变率,第二列为相应的有效动力粘度。
注:该示例侧重于在OpenFOAM中实现新的粘度模型,而不是模型的物理正确性。
运动粘度通过除以室温下血液的近似密度得到,如图11.4所示。
本示例介绍如何实现一个新的粘度模型类,该模型类读取运动粘度和局部应变率,并返回有效粘度。这种基于数据表的方法与其他更常见的粘度模型形成对比,后者使用应变率与粘度关系的解析表达式。图11.4显示了剪切流变仪的一组测量值示例(有效粘度与应变率)。由于流变仪表中只有离散的数据点,因此必须使用插值方案在它们之间进行插值,并将粘度分配给任何给定的应变率。OpenFOAM提供了可用于此目的基于二维样条的插值:插值XY样条线。此库的源代码可在示例存储库中找到,位于:
ofbook/ofprimer/src/ofBookTransportModels
这里,仅涉及负责粘度计算的虚拟成员函数的实现。源代码中提供了有关实现的更多信息。
新的粘度模型继承了viscosityModel,因此新的粘度模型必须满足清单69中所示的viscosityModel的接口。
//清单69
//- Return the laminar viscosity
virtual tmp<volScalarField> nu() const = 0;
//- Return the laminar viscosity for patch
virtual tmp<scalarField> nu(const label patchi) const = 0;
//- Correct the laminar viscosity
virtual void correct() = 0;
//- Read transportProperties dictionary
virtual bool read(const dictionary& viscosityProperties) = 0;
correct成员函数执行运动粘度的计算(更新),而nu成员函数提供访问。
注:在OpenFOAM中,就像在其他大规模面向对象软件中一样,虚函数的替代实现扩展了库的功能。换句话说,在OpenFOAM中扩展库时,要了解虚函数在其他派生类中的作用,因为这些成员函数也很可能在新类中被修改。
粘度模型扩展从viscosityModel的公共继承和私有数据成员的定义开始,这是粘度计算所必需的,如清单70所示。清单70中的私有成员函数loadViscosityTable负责从CSV文件中阅读实验数据,该数据被转换为标量场rheologyTableX和rheologyTableY,用作OpenFOAM中基于样条的插值的输入。
//清单70
class interpolatedSplineViscosityModel
:
public viscosityModel
{
// Private data
//Dictionary for the viscosity model
dictionary modelDict_;
//x and y entries of the rheology data for
// use with the spline interpolator
fileName dataFileName_;
scalarField rheologyTableX_;
scalarField rheologyTableY_;
tmp<volScalarField> nuPtr_;
// Private Member Functions
// Load the viscosity data table
void loadViscosityTable();
从viscosityModel继承的纯虚成员函数的声明如清单71所示。运动粘度字段是私有数据成员(包装在tmp智能指针中),因此粘度字段和补丁粘度字段可以轻松返回。因此,模型实现的主要部分在于loadViscosityTable并校正。
// 清单71
//- Return the laminar viscosity
tmp<volScalarField> nu() const
{
return nuPtr_;
}
//- Return the laminar viscosity for patch
virtual tmp<scalarField> nu(const label patchi) const
{
return nuPtr_-> boundaryField()[patchi];
}
//- Correct the laminar viscosity
virtual void correct();
//- Read transportProperties dictionary
bool read(const dictionary& viscosityProperties);
csvTableReader类用于读取包含图11.4中实验数据的CSV文件。loadViscosityTable在构造函数体中调用一次,在读取成员函数中调用一次。正确的成员函数从每个单元中的viscosityModel获取应变率,然后使用基于样条的插值法从loadViscosityTable读取的实验数据中插值表观运动粘度。清单72概述了实现。
void interpolatedSplineViscosityModel::loadViscosityTable()
{
csvTableReader<scalar> reader(modelDict_);
List<Tuple2<scalar, scalar> > data;
reader(dataFileName_, data);
// Resize to experimental data
rheologyTableX_.resize(data.size());
rheologyTableY_.resize(data.size());
forAll(data, lineI)
{
const auto& dataTuple = data[lineI];
rheologyTableX_[lineI] = dataTuple.first();
rheologyTableY_[lineI] = dataTuple.second();
}
新粘度模型的定义位于constant/transportProperties字典文件中:
transportModel interpolatedSplineViscosityModel;
interpolatedSplineViscosityModelCoeffs
{
dataFileName "constant/viscosityData/anand2004kinematic.csv";
hasHeaderLine false;
refColumn 0;
componentColumns (1);
}
rho 1060;
新粘度模型的输入包含实验数据文件的路径,以及定义CSV文件结构的信息。CSV文件不包含标题,参考列为0,只有单个组分列1,表观运动粘度来自[1]。
成员函数correct代码如下所示:
void interpolatedSplineViscosityModel::correct()
{
// Interpolate kinematic viscosity in each cell from tabular data.
volScalarField& nu = nuPtr_.ref();
const volScalarField cellStrainRate = strainRate();
forAll(cellStrainRate, cellI)
{
nu[cellI] = interpolateSplineXY
(
cellStrainRate[cellI],
rheologyTableX_,
rheologyTableY_
);
}
nu.correctBoundaryConditions();
}
11.3.1 示例案例
一个下落的“血液”液滴撞击一个无法穿透的壁面是样条粘性模型的例子。 案例本身包含在存储库中,位于
ofbook/ofprimer/cases/chapter11/falling-droplet-2D
两相VoF解算器interIsoFoam [2]用于模拟非牛顿液滴通过空气下落并撞击固体表面。注意,该模拟是二维的并且在空间上是欠分辨的;但它仍然很好地用作示例模拟。正方形区域有三个不可穿透的壁和一个开放大气边界条件,如图11.5所示。可使用需要4个CPU内核的准备好的Allrun脚本运行案例直至完成。
直径为1毫米的液滴从零速度开始,在畴中居中。 重力加速液滴向下,最终冲击无滑移壁面。 当液滴冲击壁面时,高的局部应变率改变了液体中的有效粘度。 非牛顿效应可视化在图11.6。