补充C++知识:
类的前向声明
友元类与函数
版本:of4.0
sixDoFRigidBodyDisplacementPointPatchVectorField边界条件
void mysixDoFRigidBodyDisplacementPointPatchVectorField::updateCoeffs()
{
...............前面次要部分省略
motion_.update
(
firstIter,
ramp*(f.forceEff() + motion_.mass()*g_),
ramp*(f.momentEff() + motion_.mass()*(motion_.momentArm() ^ g_)),
t.deltaTValue(),
t.deltaT0Value()
);
Field<vector>::operator=
(
motion_.transform(initialPoints_) - initialPoints_
);
fixedValuePointPatchField<vector>::updateCoeffs();
}
首先根据当前时刻的状态计算出力–然后调用 motion_.update
方法根据力计算出下一个时刻的加速度、速度、角动量、旋转中心位置、旋转矩阵。update
方法如下所示:
void Foam::sixDoFRigidBodyMotion::update
(
bool firstIter,
const vector& fGlobal,
const vector& tauGlobal,
scalar deltaT,
scalar deltaT0
)
{
if (Pstream::master())
{
solver_->solve(firstIter, fGlobal, tauGlobal, deltaT, deltaT0);
if (report_)
{
status();
}
}
Pstream::scatter(motionState_);
}
void Foam::sixDoFRigidBodyMotion::status() const
{
Info<< "6-DoF rigid body motion" << nl
<< " Centre of rotation: " << centreOfRotation() << nl
<< " Centre of mass: " << centreOfMass() << nl
<< " Orientation: " << orientation() << nl
<< " Linear velocity: " << v() << nl
<< " Angular velocity: " << omega()
<< endl;
}
在update
方法中调用了solver
函数,RTS
机制来选择solver
所使用的方法,下面是Newmark
方法中的solver
函数,可以看到,其主要作用就是更新状态量。我们要求解欧拉角,并将其储存在文件中,需要拓展的接口是status
。
void Foam::sixDoFSolvers::Newmark::solve
(
bool firstIter,
const vector& fGlobal,
const vector& tauGlobal,
scalar deltaT,
scalar deltaT0
)
{
// Update the linear acceleration and torque
updateAcceleration(fGlobal, tauGlobal);
// Correct linear velocity
v() =
tConstraints()
& (v0() + aDamp()*deltaT*(gamma_*a() + (1 - gamma_)*a0()));
// Correct angular momentum
pi() =
rConstraints()
& (pi0() + aDamp()*deltaT*(gamma_*tau() + (1 - gamma_)*tau0()));
// Correct position
centreOfRotation() =
centreOfRotation0()
+ (
tConstraints()
& (
deltaT*v0()
+ aDamp()*sqr(deltaT)*(beta_*a() + (0.5 - beta_)*a0())
)
);
// Correct orientation
vector piDeltaT =
rConstraints()
& (
deltaT*pi0()
+ aDamp()*sqr(deltaT)*(beta_*tau() + (0.5 - beta_)*tau0())
);
Tuple2<tensor, vector> Qpi = rotate(Q0(), piDeltaT, 1);
Q() = Qpi.first();
}
利用四元素类从旋转矩阵中计算欧拉角:
//- Construct a quaternion from a rotation tensor
inline explicit quaternion(const tensor& rotationTensor);
使用到的成员函数如下:
//- Return a vector of euler angles corresponding to the
// specified rotation sequence
inline vector eulerAngles(const rotationSequence rs) const;
类中的枚举类
enum rotationSequence
{
ZYX, ZYZ, ZXY, ZXZ, YXZ, YXY, YZX, YZY, XYZ, XYX, XZY, XZX
};
inline Foam::vector Foam::quaternion::eulerAngles
(
const rotationSequence rs
) const
{
const scalar w2 = sqr(w());
const scalar x2 = sqr(v().x());
const scalar y2 = sqr(v().y());
const scalar z2 = sqr(v().z());
switch(rs)
{
case ZYX:
return threeAxes
(
2*(v().x()*v().y() + w()*v().z()),
w2 + x2 - y2 - z2,
2*(w()*v().y() - v().x()*v().z()),
2*(v().y()*v().z() + w()*v().x()),
w2 - x2 - y2 + z2
);
break;
case ZYZ:
return twoAxes
(
2*(v().y()*v().z() - w()*v().x()),
2*(v().x()*v().z() + w()*v().y()),
w2 - x2 - y2 + z2,
2*(v().y()*v().z() + w()*v().x()),
2*(w()*v().y() - v().x()*v().z())
);
break;
case ZXY:
return threeAxes
(
2*(w()*v().z() - v().x()*v().y()),
w2 - x2 + y2 - z2,
2*(v().y()*v().z() + w()*v().x()),
2*(w()*v().y() - v().x()*v().z()),
w2 - x2 - y2 + z2
);
break;
case ZXZ:
return twoAxes
(
2*(v().x()*v().z() + w()*v().y()),
2*(w()*v().x() - v().y()*v().z()),
w2 - x2 - y2 + z2,
2*(v().x()*v().z() - w()*v().y()),
2*(v().y()*v().z() + w()*v().x())
);
break;
case YXZ:
return threeAxes
(
2*(v().x()*v().z() + w()*v().y()),
w2 - x2 - y2 + z2,
2*(w()*v().x() - v().y()*v().z()),
2*(v().x()*v().y() + w()*v().z()),
w2 - x2 + y2 - z2
);
break;
case YXY:
return twoAxes
(
2*(v().x()*v().y() - w()*v().z()),
2*(v().y()*v().z() + w()*v().x()),
w2 - x2 + y2 - z2,
2*(v().x()*v().y() + w()*v().z()),
2*(w()*v().x() - v().y()*v().z())
);
break;
case YZX:
return threeAxes
(
2*(w()*v().y() - v().x()*v().z()),
w2 + x2 - y2 - z2,
2*(v().x()*v().y() + w()*v().z()),
2*(w()*v().x() - v().y()*v().z()),
w2 - x2 + y2 - z2
);
break;
case YZY:
return twoAxes
(
2*(v().y()*v().z() + w()*v().x()),
2*(w()*v().z() - v().x()*v().y()),
w2 - x2 + y2 - z2,
2*(v().y()*v().z() - w()*v().x()),
2*(v().x()*v().y() + w()*v().z())
);
break;
case XYZ:
return threeAxes
(
2*(w()*v().x() - v().y()*v().z()),
w2 - x2 - y2 + z2,
2*(v().x()*v().z() + w()*v().y()),
2*(w()*v().z() - v().x()*v().y()),
w2 + x2 - y2 - z2
);
break;
case XYX:
return twoAxes
(
2*(v().x()*v().y() + w()*v().z()),
2*(w()*v().y() - v().x()*v().z()),
w2 + x2 - y2 - z2,
2*(v().x()*v().y() - w()*v().z()),
2*(v().x()*v().z() + w()*v().y())
);
break;
case XZY:
return threeAxes
(
2*(v().y()*v().z() + w()*v().x()),
w2 - x2 + y2 - z2,
2*(w()*v().z() - v().x()*v().y()),
2*(v().x()*v().z() + w()*v().y()),
w2 + x2 - y2 - z2
);
break;
case XZX:
return twoAxes
(
2*(v().x()*v().z() - w()*v().y()),
2*(v().x()*v().y() + w()*v().z()),
w2 + x2 - y2 - z2,
2*(v().x()*v().z() + w()*v().y()),
2*(w()*v().z() - v().x()*v().y())
);
break;
default:
FatalErrorInFunction
<< "Unknown rotation sequence " << rs << abort(FatalError);
return Zero;
break;
}
}
可以看到,要计算欧拉角,首先要指定旋转顺序,固计算欧拉角要添加这三行:函数形参为类quaternion中的枚举类rotationSequence对象
quaternion b = quaternion(orientation());
quaternion::rotationSequence aa=quaternion::rotationSequence (0);
vector ang = b.eulerAngles(aa);
接下来最后一步,定义文件的输出流,将欧拉角保存以追加的形式保存在文件中,openfoam有相关的类库,为了减少学习成本,笔者直接用C++的标准库来实现这个功能:
std::ofstream fout("Euler.txt",ios_base::out | ios_base::app);
fout << ang.x()<< " " << ang.y() << " "<<ang.z() <<'\n';
fout.close();
至此,这一功能便实现了,6Dof库的开发并不困难,首先是自定义边界条件,然后再更改边界条件中使用到的其他类的相应接口便可。