自带的ensightWriter就能整体输出结果
马乔
帖子
-
-
@oitocfd 如果只是交换边界两侧的值,试试syncTools::swapBoundaryCellList. 如果只是查找离边界两层的网格可以用下FaceCellWave这个类(这个类会迭代全场网格,你需要修改类的迭代行为)。如果是固定距离就trisurfaceSearch了。话说为嘛不直接移指下foam-Extend的ib呢?
-
@micro 这个会在每个核里存一个共享的偏移数组,根据当前核编号和核locasize累计。在decompose后,全局编号是当前核的局部编号+偏移,这个一般会跟未分解前不一致(比如blockmesh编号是按定义的x-y-z来的)。一般在需要reconstruct, distribute,聚合的地方用到吧,我也就用过一次。
-
@upc_ngh 试试在这两句lookup之前显式调用下massStick()呢?
-
你可能需要仿照MRFZone类写一个平动的类了,但是相对来说要更加容易些,主要工作是修正面通量,推荐用三角分解计算。还要注意下include的边界面上速度修正。
-
或者你应该在intU[1]=...之前加个if(cellID > -1).
-
@杨英狄 主程序里调用的构造参数不对呗,按匹配的传入就行
-
@oitocfd 如果粒子追踪的话可以尝试下lagrangian库,当前网格周围一圈网格可以直接用primitiveMesh.cellCells()
-
@oitocfd 我错了,meshSearch的查找效率很高,是primitiveMesh不行。你这是在写pic吗?
-
@oitocfd 可能是精度问题?手动写入的有截断误差?刚好这个点位于face上呢?还有不管是primitiveMesh和meshSearch的findcell效率都挺低的,都要去遍历所有的cell,不知道写什么需求要这么写?
-
@zz给我趴下 可以试下columeAveraged
-
@同学博 你这么操作map是对的,我开想的是查找key
-
@李东岳 这么写呢?
List<DynamicList<label>> pL(U_.size()); forAllConstIter(typename MomentumCloud<CloudType>, *this, iter) { const parcelType& p = iter(); pL[p.cell()].append(p.origId()); }
或者
std::Multimap<label,label> Lp; forAllConstIter(typename MomentumCloud<CloudType>, *this, iter) { const parcelType& p = iter(); Lp.insert(std::pair<label,label>(p.cell(), p.origId())); }
都是我云的
-
@同学博 map用迭代器访问效率并不高吧,而且最好不要直接访问value的吧,如果用value匹配,这样复杂度又上来了
-
用个List<dynamicList<label>> parcelsToCell, 然后遍历粒子,得到单元编号,然后将粒子塞到对应的dynamicList中。但是有空的。
或者用个std::Multimap<label>,cell编号就是key,value是粒子id。 -
1,不是,这个边界会构建一个虚拟的2D规则网格,然后利用读入的R和L计算出波动速度,然后加上U并map到真实边界网格上。
2,这些值是模拟计算的值,sample或者其他方式得到的。 -
@oitocfd 直接用MPPICInterFoam就可以了
-
像这样子嘛? -
没得问题,只要在这里不使用具体类对象,而使用指针或引用,就不用预先知道类定义。然后模板类里一般都是用静态函数创建模板参数对象(静态函数属于类,不属于某一对象),有点类似全局函数。
-
@浪迹天大 不,我不相信,你都写了这么久了
-
劣势就是添加新的类或者模型,你需要在这里添加if,RTS只需要新类中定义静态成员往那个容器表添加函数指针(也就是add那一套),不会违背那个什么原则来着?开放-封闭?
-
抽象工厂来着?RTS就是把一堆构造函数指针给存到一个容器,然后让这个容器成为静态的,这样在main函数Load前就初始化了。你用这一堆if应该算简单工厂,不想麻烦的话,效果是一样的
-
操作符重载一下啊,基本操作,很常见的。
PS,我发现你发的帖子很多我早期学习的时候都研究过和实现过代码,难道这就是是做粒子宿?命 -
哪里“黑”了,正常操作而已,看看重要的类Time的构造,有一句
if ( argList::validOptions.found("no-libs") ? !args.found("no-libs") : true ) { libs_.open(controlDict_, "libs"); }
主动去libs字典项查找库名,再参考这个
https://www.cfd-china.com/topic/2837/运行时动态加载库?_=1618541781950
很清晰明了的呀 -
最后再加一句这个write的动作其实是这样被触发的
template<class CloudType> void Foam::CloudFunctionObject<CloudType>::postEvolve() { if (this->owner().time().writeTime()) { this->write(); } }
所以记得子类中记得调用
CloudFunctionObject<CloudType>::postEvolve();
这样整个调用闭环,脉络清晰
-
其实这个问题可以很直接的得到结果,完全不必这么绕的,让我们直接开始吧。
我们注意到,其实平均体积这个量在计算过程中是被计算了的,就在这里:
MPPICParcel<ParcelType>::TrackingData<CloudType>类的构造中,volumeAverage_ ( AveragingMethod<scalar>::New ( IOobject ( cloud.name() + ":volumeAverage", cloud.db().time().timeName(), cloud.mesh() ), cloud.solution().dict(), cloud.mesh() ) ),
但是IO行为被控制了,所以我们接下来要做的是写个类控制它的IO,从头开始朔源,
MPPICCloud<CloudType>::evolve()--->this->solve(*this, td);这个solve是KinematicCloud的{ cloud.preEvolve(); evolveCloud(cloud, td); if (solution_.coupled()) { cloud.scaleSources(); } } cloud.info(); cloud.postEvolve();
可以看到,这个流程里调用两个重要函数preEvolve和postEvolve,
其中这两个函数都调用了functions_.preEvolve();和functions_.postEvolve();来看看functions_的定义
typedef CloudFunctionObjectList<KinematicCloud<CloudType>>
functionType;
以及构造functions_ ( *this, particleProperties_.subOrEmptyDict("cloudFunctions"), solution_.active() ),
是个可以通过字典控制的对象List,好,进入正题,我们建个类
template<class CloudType> class cloudAverageField : public CloudFunctionObject<CloudType> {
并定义我们需要的数据
autoPtr<volScalarField> rhoAvgPtr_; autoPtr<volVectorField> velAvgPtr_; autoPtr<volScalarField> volAvgPtr_;
以及接口preEvolve和postEvolve
preEvolve做的工作是检查和初始化void Foam::cloudAverageField<CloudType>::preEvolve() { const fvMesh& mesh = this->owner().mesh(); if (!rhoAvgPtr_.valid()) { rhoAvgPtr_.reset ( new volScalarField ( IOobject ( this->owner().name() + "RhoAvg", mesh.time().timeName(), mesh, IOobject::NO_READ, IOobject::NO_WRITE ), mesh, dimensionedScalar("zero", dimDensity, 0.0) ) ); } ...
postEvolve则把数据保存下来
Field<scalar> pRhoField(mesh.lookupObject<AveragingMethod<scalar> >(this->owner().name() + ":rhoAverage")[0]); Field<vector> pVelField(mesh.lookupObject<AveragingMethod<vector> >(this->owner().name() + ":uAverage")[0]); Field<scalar> pVolField(mesh.lookupObject<AveragingMethod<scalar> >(this->owner().name() + ":volumeAverage")[0]);
最后在用个write函数在需要的时候把数据写出来
template<class CloudType> void Foam::cloudAverageField<CloudType>::write() { if (rhoAvgPtr_.valid()) { rhoAvgPtr_->write(); velAvgPtr_->write(); volAvgPtr_->write(); } else { FatalErrorIn("void Foam::cloudAverageField<CloudType>::write()") << "thetaPtr not valid" << abort(FatalError); } }
最最后,把函数注册下
namespace Foam { makeCloudFunctionObjectType(cloudAverageField, basicKinematicMPPICCloud); }
这样就可以通过字典在运行时选择了。
回到主题,我们看这个怎么被触发
在前面的evolveCloud(cloud, td);函数会调用cloud.motion(cloud, td);这次回到MPPICCloud<CloudType>::motion
里面会调用td.updateAverages(td.cloud());它的主要工作是// averaging sums forAllConstIter(typename CloudType, cloud, iter) { const typename CloudType::parcelType& p = iter(); const tetIndices tetIs(p.cell(), p.tetFace(), p.tetPt(), cloud.mesh()); const scalar m = p.nParticle()*p.mass(); volumeAverage_->add(p.position(), tetIs, p.nParticle()*p.volume()); rhoAverage_->add(p.position(), tetIs, m*p.rho()); uAverage_->add(p.position(), tetIs, m*p.U()); massAverage_->add(p.position(), tetIs, m); } volumeAverage_->average(); massAverage_->average(); rhoAverage_->average(massAverage_); uAverage_->average(massAverage_);
就是更新计算每个cell的粒子平均信息。
嗯,大概是这样。至于信息的输出控制则具体由CloudFunctionObjectList和字典信息控制,当然这些行为你还可以自己定制。 -
因为这两个continuity erro不是field啊
-
你这是什么版本啊,为嘛那一通运算没有量纲检查。forAll这个只会遍历内场。
-
因为fvOptions只是fv::options的一个对象,这里用了它的操作符()。所以你要定义个fv::option对象。或者直接包含createFvOptions.H吧
-
你可能要自己写个multiRegionReactingFoam这样的求解器来读取多个fvMesh
-
你要精确预测轨迹建议还是用DEM吧。
-
你要的MPPIC大概都在这里了 MPPIC
-
当然是固相应力呀!Harris & Crighton模型
-
const labelList face = mesh.faces()[index]; forAll(face, pointi) { auto point = mesh.points()[face[pointi]]; }
-
修正下
value += this->operator[](i).calcCoupled(p, td, dt, mass, Re, muc); cloud.drag()[this->cell()] += np0*drag; cloudVolDrag.correctBoundaryConditions();
-
这个题我会
第一步,在ParticleForce类中添加虚函数virtual forceSuSp calcDragForce ( const typename CloudType::parcelType& p, const typename CloudType::parcelType::trackingData& td, const scalar dt, const scalar mass, const scalar Re, const scalar muc ) const { return forceSuSp(Zero); }
第二步,在ErgunWenYuDragForce类添加此函数,并返回
... { return this->calcCoupled ( p, td, dt, mass, Re, muc );
第三步,在ParticleForceList类中添加函数
forceSuSp calcDragForce ( const typename CloudType::parcelType& p, const typename CloudType::parcelType::trackingData& td, const scalar dt, const scalar mass, const scalar Re, const scalar muc ) const { forceSuSp value(Zero); if (calcCoupled_) { forAll(*this, i) { value += this->operator[](i).calcDragForce(p, td, dt, mass, Re, muc); } } return value; }
当然也可以这么写
... { forceSuSp value(Zero); if (calcCoupled_) { forAll(*this, i) { if(isA<ErgunWenYuDragForce>(this->operator[](i))) value = this->operator[](i).calcDragForce(p, td, dt, mass, Re, muc); } } return value; }
这么写的话,前面两步就可以不用做了
第四步,在KinematicParcel类的calcVelocity函数中添加template<class ParcelType> template<class TrackCloudType> const Foam::vector Foam::KinematicParcel<ParcelType>::calcVelocity ( TrackCloudType& cloud, trackingData& td, const scalar dt, const scalar Re, const scalar mu, const scalar mass, const vector& Su, vector& dUTrans, vector& drag, //添加返回参数 scalar& Spu ) const { ... const forceSuSp FDrag = forces.calcDragForce(p, ttd, dt, mass, Re, mu); ... drag = -(FDrag.Sp()*td.Uc() + FDrag.Su()); ... }
在calc函数中添加
template<class ParcelType> template<class TrackCloudType> void Foam::KinematicParcel<ParcelType>::calc ( TrackCloudType& cloud, trackingData& td, const scalar dt ) { ... vector drag = Zero; // Calculate new particle velocity this->U_ = calcVelocity(cloud, td, dt, Re, td.muc(), mass0, Su, dUTrans, darg, Spu); if (cloud.solution().coupled()) { // Update momentum transfer cloud.UTrans()[this->cell()] += np0*dUTrans; cloud.dragForce()[this->cell()] += np0*drag; // Update momentum transfer coefficient cloud.UCoeff()[this->cell()] += np0*Spu; }
第五步,在KinematicCloud类中添加变量
autoPtr<volVectorField::Internal> drag_;
构造中添加
drag_ ( new volVectorField::Internal ( IOobject ( this->name() + ":drag", this->db().time().timeName(), this->db(), IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE ), mesh_, dimensionedVector(dimForce, Zero) ) ),
以及函数
template<class CloudType> inline Foam::DimensionedField<Foam::vector, Foam::volMesh>& Foam::KinematicCloud<CloudType>::drag() { return *drag_; } template<class CloudType> inline const Foam::DimensionedField<Foam::vector, Foam::volMesh>& Foam::KinematicCloud<CloudType>::drag() const { return *drag_; }
第六步,主程序添加
auto cloudDrag(kinematicCloud.drag()); volVectorField cloudVolDrag ( IOobject ( "cloudVolDrag", runTime.timeName(), mesh ), mesh, dimensionedVector(cloudDrag.dimensions()/dimVolume, Zero), zeroGradientFvPatchVectorField::typeName ); cloudVolDrag.primitiveFieldRef() = -cloudDrag/mesh.V(); cloudVolSUSu.correctBoundaryConditions();
大概是这样,没编译过,新鲜的。里面的量纲和曳力应该还需要调整。
...
其实大部分情况下,一般都只会用到曳力,所以可以直接用
kinematicCloud.SU(Uc)
就行了,那这样的话,前面所有的都不用做了 -
class base { private: bool ownedByRegistry_; public: template<class Type> static void store(Type* p) { //p->base::ownedByRegistry_ =true; p->ownedByRegistry_ = true; p->printInfo(); } }; class derived :public base { public: derived() {} void printInfo() { Info << "derived class" << endl; } }; int main(int argc, char *argv[]) { ... derived d; base::store(&d); ... }
-
wordHashSet unMatchedkeys(dict.toc()); forAll(names, nameI) { const word& name = names[nameI]; const entry* ePtr = dict.findEntry(name, keyType::REGEX); if(ePtr) unMatchedKeys.erase(ePtr->keyWord()); }
-
valueInternalCoeffs, valueBoundaryCoeffs, gradientInternalCoeffs, gradientBoundaryCoeffs are all key coeffs for boundary correct.
In calculatedFvPatchField, those are all:
tmp<Field<Type> > calculatedFvPatchField<Type>::valueInternalCoeffs ( const tmp<scalarField>& ) const { FatalErrorIn ( "calculatedFvPatchField<Type>::" "valueInternalCoeffs(const tmp<scalarField>&) const" ) << "\n " ...
when construct convection tern
fvm.internalCoeffs()[patchI] = patchFlux*psf.valueInternalCoeffs(pw); fvm.boundaryCoeffs()[patchI] = -patchFlux*psf.valueBoundaryCoeffs(pw);
and laplacian term
fvm.internalCoeffs()[patchI] = patchGamma*psf.gradientInternalCoeffs(); fvm.boundaryCoeffs()[patchI] = -patchGamma*psf.gradientBoundaryCoeffs();
so...
-
PtrList<IOdictionary> transportProperties(fluidRegions.size()); forAll(fluidRegions, i) { Info<< "*** Reading fluid mesh thermophysical properties for region " << fluidRegions[i].name() << nl << endl; Info<< "Reading transportProperties\n" << endl; transportProperties.set ( i, new IOdictionary ( IOobject ( "transportProperties", //runTime.timeName(), runTime.constant(), fluidRegions[i], IOobject::MUST_READ, IOobject::NO_WRITE ) ) ); } forAll(fluidRegions, i) { //const IOdictionary& dict = fluidRegions[i].thisDb() // .lookupObject<IOdictionary>("transportProperties"); const IOdictionary& dict = fluidRegions[i].thisDb() .lookupObject<IOdictionary>("transportPropertie"); }
Log, appear in the List:
Reading transportProperties --> FOAM FATAL ERROR: request for dictionary transportPropertie from objectRegistry bottomAir failed available objects of type dictionary are 3 ( fvSchemes fvSolution transportProperties //get it! )
-
好了,舒服了
-
thormoMPPICFoam的
-
那我再po一遍试试。
-
基于2.3.X写的,貌似有点老了,图片在这里thermoParcel1.zip thermoParcel2.zip thermoParcel3.zip thermoParcel4.zip
-
我本地存的图片发不出去
-
@马乔 呃,图片呢?上传代码吧。。thermoMPPIC.zip
-
发现最近大家对拉格朗日模拟很感兴趣(强行扯话题。。。),最近不小心翻出了几年前写的加热物理的MPPIC粒子类,反正摆那也是吃灰,就拿出来给大家看看吧,话不多说,进入主题。不想打字了,贴图片:
-
modify the matrix, set the value of p and U as 0.
-
#include "fvCFD.H" #include "pisoControl.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // int main(int argc, char *argv[]) { argList::addNote ( "Transient solver for incompressible, laminar flow" " of Newtonian fluids." ); #include "postProcess.H" #include "addCheckCaseOptions.H" #include "setRootCaseLists.H" #include "createTime.H" #include "createMesh.H" pisoControl piso(mesh); #include "createFields.H" #include "initContinuityErrs.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // Info<< "\nStarting time loop\n" << endl; DynamicList<label> lowerFaceCells(mesh.C().size()); DynamicList<label> upperFaceCells(mesh.C().size()); //mesh LDU address const labelUList& lowAddr = mesh.lduAddr().lowerAddr(); const labelUList& upAddr = mesh.lduAddr().upperAddr(); if(mesh.cellZones().size()) { const cellZone& cellBlock = mesh.cellZones()[0]; Info << "cell block size: " << cellBlock.size() << endl; Info << cellBlock << endl; //low efficient forAll(lowAddr, faceI) { for(auto cellI : cellBlock) { if(lowAddr[faceI] == cellI) { lowerFaceCells.append(faceI); } if(upAddr[faceI] == cellI) { upperFaceCells.append(faceI); } } } } lowerFaceCells.shrink(); upperFaceCells.shrink(); Info << "low Cells size: " << lowerFaceCells.size() << ", low Cells size: " << upperFaceCells.size() << endl; while (runTime.loop()) { Info<< "Time = " << runTime.timeName() << nl << endl; #include "CourantNo.H" // Momentum predictor fvVectorMatrix UEqn ( fvm::ddt(U) + fvm::div(phi, U) - fvm::laplacian(nu, U) ); //modify matrix if(mesh.cellZones().size()) { const cellZone& cellBlock = mesh.cellZones()[0]; //should select cellZone accor name scalarField& lower = UEqn.lower(); scalarField& upper = UEqn.upper(); scalarField& diag = UEqn.diag(); for(auto cellI : cellBlock) { diag[cellI] = 1.; UEqn.source()[cellI] = vector::zero; //vector(0.,0.,0.); you can set your deserved value here, or read from dict, ref. fvoptions } for(auto faceI : lowerFaceCells) { lower[faceI] = 0.; } for(auto faceI : upperFaceCells) { upper[faceI] = 0.; } } else { Info << "There is no cell selected" << endl; } if (piso.momentumPredictor()) { solve(UEqn == -fvc::grad(p)); } // --- PISO loop while (piso.correct()) { volScalarField rAU(1.0/UEqn.A()); volVectorField HbyA(constrainHbyA(rAU*UEqn.H(), U, p)); surfaceScalarField phiHbyA ( "phiHbyA", fvc::flux(HbyA) + fvc::interpolate(rAU)*fvc::ddtCorr(U, phi) ); adjustPhi(phiHbyA, U, p); // Update the pressure BCs to ensure flux consistency constrainPressure(p, U, phiHbyA, rAU); // Non-orthogonal pressure corrector loop while (piso.correctNonOrthogonal()) { // Pressure corrector fvScalarMatrix pEqn ( fvm::laplacian(rAU, p) == fvc::div(phiHbyA) ); pEqn.setReference(pRefCell, pRefValue); //modify p matrix if(mesh.cellZones().size()) { const cellZone& cellBlock = mesh.cellZones()[0]; //p matrix symmetry scalarField& offDiag = pEqn.hasUpper() ? pEqn.upper() : pEqn.lower(); scalarField& diag = pEqn.diag(); for(auto cellI : cellBlock) { diag[cellI] = 1.; pEqn.source()[cellI] = 0.; } //for(auto faceI : upperFaceCells) //should find faces again? //{ // offDiag[faceI] = 0.; //} const labelUList& offAddr = pEqn.hasUpper() ? pEqn.lduAddr().upperAddr() : pEqn.lduAddr().lowerAddr(); forAll(offAddr, faceI) { for(auto cellI : cellBlock) { if(offAddr[faceI] == cellI) { offDiag[faceI] = 0.; } } } } pEqn.solve(mesh.solver(p.select(piso.finalInnerIter()))); if (piso.finalNonOrthogonalIter()) { phi = phiHbyA - pEqn.flux(); } } #include "continuityErrs.H" U = HbyA - rAU*fvc::grad(p); U.correctBoundaryConditions(); } runTime.write(); runTime.printExecutionTime(Info); } Info<< "End\n" << endl; return 0; }
toposetDict // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // actions ( // porousBlockage { name porousBlockageCellSet; type cellSet; action new; source boxToCell; box (0.04 0.04 -1) (0.06 0.06 1); } { name porousBlockage; type cellZoneSet; action new; source setToCellZone; set porousBlockageCellSet; } );
-
#include "fvCFD.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // class proxyWriter : public regIOobject { private: scalar value_; proxyWriter(const proxyWriter&) = delete; void operator=(const proxyWriter&) = delete; public: proxyWriter(const IOobject& io, const scalar& value) : regIOobject(io), value_(value) { } virtual bool writeData(Ostream& os) const { os<<value_; return os.good(); } proxyWriter& operator++() { ++value_; return *this; } }; int main(int argc, char *argv[]) { #include "setRootCaseLists.H" #include "createTime.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // proxyWriter writer ( IOobject ( "value", runTime.timeName(), runTime, IOobject::NO_READ, IOobject::AUTO_WRITE ), 0. ); Info<< "\nStarting time loop\n" << endl; while (runTime.loop()) { Info<< "Time = " << runTime.timeName() << nl << endl; ++writer; runTime.write(); runTime.printExecutionTime(Info); } Info<< "End\n" << endl; return 0; } :chigua:
并行计算的结果,不进行reconstructPar,用什么软件能整体处理?
globalNumbering_的作用是怎么发挥的?(OF中的全局索引和局部索引)
globalNumbering_的作用是怎么发挥的?(OF中的全局索引和局部索引)
如何使用lookupObject寻找SRC库内的场变量
OpenFOAM中的MRF方法能否用于模拟物体的直线运动
往cellZone里面添加网格
求解器编译报错
OpenFOAM编程findCell的诡异问题,对同一坐标寻找cell结果不一致
OpenFOAM编程findCell的诡异问题,对同一坐标寻找cell结果不一致
OpenFOAM编程findCell的诡异问题,对同一坐标寻找cell结果不一致
请教OpenFOAM获得某个面域(faceZone)的平均温度的方法
粒子与网格归属问题
粒子与网格归属问题
粒子与网格归属问题
粒子与网格归属问题
请教各位前辈OF中湍流入口DFSEM的设置问题
气液固三项流,耦合interFoam和DPMFoam可以实现吗?
气液固三项流,耦合interFoam和DPMFoam可以实现吗?
一个关于OF中C++模板实例化的问题,请大佬们看下!
运行时选择 RTS 有什么好处?
运行时选择 RTS 有什么好处?
运行时选择 RTS 有什么好处?
MPPIC中,怎么实现的alpha-alphaPacked?
当我们增加自定义库的时候,为什么不需要重新编译上层应用?
如何获得cell里的parcel数量,得到总的体积?
如何获得cell里的parcel数量,得到总的体积?
of输出continuity error残差
关于程序调用问题
添加源项fvOptions编译出错
OpenFoam分区域计算
在控制固体粒子相分数时,当粒子超过"极限",那么粒子怎么被分配呢?
请问,MPPIC这个方法的particle distribution function的运作
在控制固体粒子相分数时,当粒子超过"极限",那么粒子怎么被分配呢?
如何获取一个面上四个角点的坐标?
在输出时间步的同时,输出颗粒所受drag force文件
在输出时间步的同时,输出颗粒所受drag force文件
OpenFOAM小代码
OpenFOAM小代码
有关calculatedFvPatchField
从objectRegistry中无法获得输运字典问题
ThermoMPPICFoam
ThermoMPPICFoam
ThermoMPPICFoam
ThermoMPPICFoam
ThermoMPPICFoam
ThermoMPPICFoam
ThermoMPPICFoam
OpenFOAM有方法能够使一部分网格不参与计算吗?
OpenFOAM有方法能够使一部分网格不参与计算吗?
OF文件读写