CFD理论过渡到编程的傻瓜入门教程_第1页
CFD理论过渡到编程的傻瓜入门教程_第2页
CFD理论过渡到编程的傻瓜入门教程_第3页
CFD理论过渡到编程的傻瓜入门教程_第4页
CFD理论过渡到编程的傻瓜入门教程_第5页
已阅读5页,还剩30页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

CFD理论过渡到编程的傻瓜入门教程(注:这是一篇不懂得谁写的介绍一维无粘可压缩Euler方程,以及如何编程实现求解该方程的论文。作者从最基本的概念出发,进一步浅出的解说了控制方程,有限体积格式,MSUCL办法,限制器,Roe格式等有关知识。这篇论文我觉得有助于大家学习CFD编程的有关知识,因此推荐给大家。文章的背面附有我写的程序(C语言),用于求解一维激波管问题,大家有爱好能够看看(程序中加了注释阐明)胡偶)借宝地写几个小短文,介绍CFD的某些实际的入门知识。重要是由于这里支持Latex,写起来比较方便。CFD,计算流体力学,是一种挺难的学科,涉及流体力学、数值分析和计算机算法,尚有计算机图形学的某些知识。特别是有关偏微分方程数值分析的东西,不是那么容易入门。大多数图书,片中数学原理而不重实际动手,由于作者都把读者当做已经掌握基础知识的科班学生了。因此数学基础不那么好的读者往往看得很吃力,看了还不懂得怎么实现。本人当年虽说是学航天工程的,但是那时本科教育已经退步,基础的流体力学课被砍得只剩余一维气体动力学了,因此自学CFD的时候也是头晕眼花。不懂得怎么实现,也很难找到教学代码——那时候网络还不发达,只在教研室的故纸堆里搜罗到某些完全没有注释,编程风格也不好的冗长代码,硬着头皮分析。后来网上淘到某些代码研读,结合书籍论文才慢慢入门。能够说中间没有老师教,后来赌博士为了混学分上过CFD专门课程,但是那时候我已经都掌握课堂上那些了。回想自己入门艰辛,不免有一种想法——写点通俗易懂的CFD入门短文给师弟师妹们。本人不打算搞得很系统,而是但愿能结合实际,阐明某些最基本的概念和手段,其中某些复杂的道理只是点到为止。现在也没有具体的计划,想到哪里写到哪里,因此可能会很零碎。但是我争取让初学CFD的人能够理解某些基本的东西,看过之后,会懂得一种CFD代码怎么炼成的(这“炼”字仿佛很流行啊)。欢迎大家提出意见,这样我尽量的能够追加某些修改和解释。言归正传,第一部分,我打算介绍一种最基本的算例,一维激波管问题。说白了就是一根两端封闭的管子,中间有个隔板,隔板左边和右边的气体状态(密度、速度、压力)不同,忽然把隔板抽去,管子内面的气体怎么运动。这是个一维问题,被称作黎曼间断问题,仿佛是黎曼最初研究双曲微分方程的时候提出的一种问题,用一维无粘可压缩Euler方程就能够描述了。这里这个方程就是描述的气体密度、动量和能量随时间的变化()与它们各自的流量(密度流量,动量流量,能量流量)随空间变化()的关系。在CFD中普通把这个方程写成矢量形式这里进一步能够写成散度形式一定要熟悉这种矢量形式以上是控制方程,下面说说求解思路。可压缩流动计算中,有限体积(FVM)是最广泛使用的算法,其它算法多多少少都和FVM有些联系或者共通的思路。理解的FVM,学习其它高级点的算法(例如现在比较热门的间断有限元、谱FVM、谱FDM)就好说点了。针对一种微元控制体,把Euler方程在空间积分用微积分知识能够得到也就是说控制体内气体状态平均值的变化是控制体界面上流通量的成果。因此我们要计算的演化,核心问题是计算控制体界面上的。FVM就是以这个积分关系式出发,把整个流场划分为许多小控制体,每个控制体和周边相邻的某个控制体共享一种界面,通过计算每个界面上的通量来得到相邻控制体之间的影响,一旦每个控制体的变化得到,整个流场的变化也就懂得了。因此,再强调一次,核心问题是计算控制体界面上的。初学者会说,这个不难,把界面上的插值得到,然后就能够计算。有道理!咱们画个图,有三个小控制体i-1到i+1,中间的“|”表达界面,控制体i右边的界面用表达,左边的就是。|i-1|i|i+1|好下个问题:每个小控制体长度都是如何插值计算界面上的?最自然的想法就是:取两边的平均值呗,但是很不幸,这是不行的。那么换个办法?直接平均得到?还是很不行,这样也不行。我靠,这是为什么?这明明是符合微积分里面的知识啊?这个道理有点复杂,说开了去能够讲一本书,能够说从50年代到70年代,CFD科学家就在琢磨这个问题。这里,初学者只需要记住这个结论:对于流动问题,不能够这样简朴取平均值来插值或者差分。如果你非要想懂得这个终究,我现在也不想给你讲清晰,由于我眼下的目的是让你快速上手,并且该不刨根问底的时候就不要刨根问底,这也是初学阶段一种重要的学习办法。好了,既然目的只是为了求,我在这里,只告诉你一种计算办法,也是非常重要、非常流行的一种办法。简朴的说,就是假设流动状态在界面是不持续的,先计算出界面两边的值,和,再由它们用某种办法计算出。上述办法是非常重要的,是由一种苏联人Godunov在50年代首创的,后来被发展成为通用Godunov办法,知名的ENO/WENO就是其中的一种。好了,现在的问题是:1怎么拟定和2怎么计算对于第一种问题,Godunov在他的论文中,是假设每个控制体中是均匀分布的,因此第二个问题,Godunov采用了精确黎曼解来计算。什么是“精确黎曼解”,就是计算这个激波管问题的精确解。既然有精确解,那还费功夫搞这些FVM算法干什么?由于只有这种简朴一维问题有精确解,稍微复杂一点就不行了。精确解也比较麻烦,要分析5种状况,用牛顿法迭代求解(牛顿法是什么?看数值计算的书去,哦,算了,现在临时能够不必看)。这是最初Godunov的办法,后来在这个思想的基础上,多种变体都出来了。也但是是在这两个问题上做文章,怎么拟定,怎么计算。Godunov假设的是每个小控制体内是均匀分布,也就是所谓分段常数(piecewiseconstant),因此后来有分段线性(picewiselinear)或者分段二次分布(picewiseparabolic),到后来ENO/WENO出来,那这个假设的多项式次数就继续往上走了。都是用多项式近似的,这是数值计算中的一种强大工具,你能够在诸多地方看到这种近似。Godunov用的是精确黎曼解,太复杂太慢,也不必要,因此后来就有多种近似黎曼解,最有名的是Roe求解器、HLL求解器和Osher求解器,都是对精确黎曼解做的简化。这个多项式的阶数是和计算精度亲密有关的,阶数越高,误差就越小。但是普通来说,分段线性就能得到不错的成果了,因此工程中都是用这个,Fluent、Fastran以及NASA的CFL3D、OverFlow都是用这个。而黎曼求解器对精度的影响不是那么大,但是对整个算法的物理合用性有影响,也就是说某种近似黎曼求解器可能对某些流动问题不适宜,例如单纯的Roe对于钝头体的脱体激波会算得乱七八糟,后来加了熵修正才算搞定。上次()说到了求解可压缩流动的一种重要算法,通用Godunov办法。其两个重要环节就是1怎么拟定和2怎么计算这里我们给出第一点一种具体的实现办法,就是基于原始变量的MUSCL格式(下列简称MUSCL)。它是一种很简朴的格式,并且含有足够的精度,NASA知名的CFL3D软件就是使用了这个格式,大家能够去它的主页()上看手册,里面空间离散那一章清晰的写着。MUSCL假设控制体内原始变量(就是)的分布是一次或者二次多项式,如果得到了这个多项式,就能够求出控制体左右两个界面的一侧的值和。我们以压力p为例来阐明怎么构造这个多项式。这里我只针对二次多项式来解说,你看完之后必定能自己推导出一次多项式的成果(如果你搞不定,那我对你的智商表达怀疑)。OK,开始假设,这个假设不影响最后结论,由于你总能够把一种区间线性的变换到长度为1的区间。假设压力p在控制体i内部的分布是一种二次多项式,控制体i的中心处在处,左右两个界面就是和。这里先强调一种问题,在FVM中,每个控制体内的求解出来的变量事实上是这个控制体内的平均值。因此,。我们懂得,和,等距网格状况下和处的导数能够近似表达为那么(这里错了,应当是2ax+b)由上述三个有关a,b和c的方程,我们能够得到这样就能够得到f(x)的体现式了,由此能够算出和普通MUSCL格式写成以下形式对应我们的推导成果(二次多项式假设)。但是这不是最后形式。如果直接用这个公式,就会造成流场在激波(间断)附近的振荡。由于直接用二次多项式去逼近一种间断,会造成这样的效果。因此科学家们提出要对间断附近的斜率有所限制,因此引入了一种非常重要的修改——斜率限制器。加入斜率限制器后,上述公式就有点变化。这里是VanAlbada限制器是一种小数(),以避免分母为0。密度和速度通过同样的办法来搞定。密度、速度和压力被称作原始变量,因此上述办法是基于原始变量的MUSCL。另外尚有基于特性变量的MUSCL,要复杂一点,但是被认为适合更高精度的格式。然而普通计算中,基于原始变量的MUSCL由于含有足够的精度、简朴的形式和较低的代价而被广泛使用。OK,搞定了。下面进入第二点,怎么求。有关这一点,我不打算做具体介绍了,直接使用现有的近似黎曼解就能够了,都是通过和计算得到。例如Roe由于形式简朴,而非常流行。在CFL3D软件主页()上看手册,附录C的C.1.3。想了一下,还是把Roe求解器稍微说说吧,力求比较完整。但是不要指望我把Roe求解器解释清晰,由于这个不是很容易三言两语说清的。Roe求解器的数学形式是这样的显然这个公式的第一项是一种中心差分形式,先前说过简朴的中心差分不可行,因素是耗散局限性造成振荡,说得通俗点就像一种弹簧,如果缺少耗散(阻尼)它就会始终振荡。“耗散”这个术语在激波捕获格式中是最常见的。第二项的作用就是提供足够的耗散了。这里和已经用MUSCL求得了,的定义在第一讲中已经介绍了。只有是还没说过的。这个矩阵能够写成特性矩阵和特性向量矩阵的形式而,,和的具体体现式在许多书上都有,并且这里的矩阵体现有问题,因此就不写了。是由、和代入计算得到。而、和采用所谓Roe平均值这才是Roe求解器核心的地方!总结一下,就是用Roe平均计算界面上的气体状态,然后计算得到,这样就能够得到了。如果有时间,我背面会找一种代码逐句分析一下。总之,计算还是很不直接的。构造近似黎曼解是挺有学问的,需要对气体动力学的物理和数学方面有较深的理解。普通,如果不是做基础研究,你只需要懂得它们的特点,会用它们就能够了,而不必深究它们怎么推导出来的。附录程序:(新建一种.c类型文献,将下面的程序复制粘贴到里面,就能够运行了)/****************************************************本程序用于求解一维无粘可压缩欧拉方程(激波管问题)运用DummyCell解决边界条件;通量计算方式:AUSMScheme;重构办法:MUSCL办法限制器:VanAlbada限制器时间离散:四步Runge-Kutta办法****************************************************/#include"stdio.h"#include"conio.h"#include"malloc.h"#include"stdlib.h"#include"math.h"#include"string.h"#defineh(1/400.0)//网格步长#defineNc404//网格总数:与h之间的关系Nc=1/h+4#definePI3.1415927#defineIt1000#definegama1.4//气体比热比doubleKAKA=0.0;//限制器控制参数doubleXS1,XS2;//计算域的两个端点doubledt=2.5e-5;//时间步长doubletimesum;//总的计算时间//函数声明voidoutput();voidSolveWtoU(doubleW[3],doubleU[3]);voidSolveUtoW(doubleW[3],doubleU[3]);//基本变量和守恒变量之间的转换函数//前后各留两个网格单元作为虚拟网格单元计算网格单元从2到NOc-3structcell{ intflag;//网格点的类型doublexc;doubleW[3],Wp[3];//conservationvaraibledoubleU[3];//jibenbianliangdoubleR[3];doubleS;//entropy};structcellcell[Nc];//网格生成及流场初始化voidinitialsolve(){inti;doublex,xi,xe;XS1=-0.0;XS2=1.0;xi=XS1-2*h;xe=XS2+2*h; for(i=0;i<Nc;i++){cell[i].xc=(xi+h/2)+i*h;//网格中心坐标cell[i].flag=0; x=cell[i].xc;if(x<0.5){cell[i].U[0]=0.445;cell[i].U[1]=0.698;cell[i].U[2]=3.528; }else{cell[i].U[0]=0.5;cell[i].U[1]=0.0;cell[i].U[2]=0.571;}SolveUtoW(cell[i].W,cell[i].U);// printf("x=%f,d=%f,u=%f,p=%f\n",cell[i].xc,cell[i].U[0],cell[i].U[1],cell[i].U[2]);getchar();}}//边界条件:DummyCell办法voidboundarycondition(){inti;doubleU[3];//直接赋远场值for(i=0;i<Nc;i++){if(cell[i].flag==1){cell[i].U[0]=0.445;cell[i].U[1]=0.698;cell[i].U[2]=3.528; SolveUtoW(cell[i].W,U);}elseif(cell[i].flag==2){cell[i].U[0]=0.5;cell[i].U[1]=0.0;cell[i].U[2]=0.571;SolveUtoW(cell[i].W,U);}}}//重构:MUSCL办法+VanAlbada限制器voidSolveReconstruction(doubleUm1[],doubleUI[],doubleUp1[],doubleUp2[],doubleUL[],doubleUR[]){inti;doubleepsilon,aR[3],aL[3],bL[3],bR[3],sL[3],sR[3];epsilon=1.0e-5*h; for(i=0;i<3;i++){ aR[i]=Up2[i]-Up1[i];//Dealta+U(I+1) bR[i]=Up1[i]-UI[i];//Dealta_U(I+1) aL[i]=Up1[i]-UI[i];//Dealta+U(I) bL[i]=UI[i]-Um1[i];//Dealta_U(I)}for(i=0;i<3;i++){sR[i]=(2.0*aR[i]*bR[i]+1.0e-6)/(aR[i]*aR[i]+bR[i]*bR[i]+1.0e-6);sL[i]=(2.0*aL[i]*bL[i]+1.0e-6)/(aL[i]*aL[i]+bL[i]*bL[i]+1.0e-6);}for(i=0;i<3;i++){UR[i]=Up1[i]-0.25*sR[i]*((1+KAKA*sR[i])*bR[i]+(1-KAKA*sR[i])*aR[i]);UL[i]=UI[i]+0.25*sL[i]*((1+KAKA*sL[i])*aL[i]+(1-KAKA*sL[i])*bL[i]);}}//守恒变量转化为基本变量voidSolveWtoU(doubleW[3],doubleU[3]){ U[0]=W[0];//d U[1]=W[1]/W[0];//u U[2]=(gama-1.0)*(W[2]-0.5*U[0]*U[1]*U[1]);}//基本变量转化为守恒变量voidSolveUtoW(doubleW[3],doubleU[3]){ W[0]=U[0];//d W[1]=U[1]*U[0];//u W[2]=U[2]/(gama-1.0)+0.5*U[0]*U[1]*U[1];}//格式:AUSM格式voidSolveAUSMFlux(doubleUL[],doubleUR[],doubleFc[])//UL左状态,UR右状态,FC通量{doubleWL[3]={0.0},WR[3]={0.0}; doubledeta,fMn;doubleML,cl,MR,cr,Ml,Mr,Mn;doublepl,pr,pn,PL,PR; deta=0.25;SolveUtoW(WL,UL);SolveUtoW(WR,UR);PL=UL[2];//计算左单元的压力cl=sqrt(gama*PL/UL[0]);PR=UR[2];//计算右单元的压力cr=sqrt(gama*PR/UR[0]);ML=UL[1]/cl;MR=UR[1]/cr;if(ML>=1.0) {Ml=ML;pl=PL;} elseif(fabs(ML)<1.0) {Ml=0.25*(ML+1)*(ML+1);pl=0.25*PL*(ML+1)*(ML+1)*(2.0-ML);}else {Ml=0.0;pl=0.0;}if(MR>=1.0) {Mr=0.0;pr=0.0;} elseif(fabs(MR)<1.0) {Mr=-0.25*(MR-1)*(MR-1);pr=0.25*PR*(MR-1)*(MR-1)*(2.0+MR);} else {Mr=MR;pr=PR;}Mn=Ml+Mr; pn=pl+pr;if(fabs(Mn)>deta)fMn=fabs(Mn); elsefMn=0.5*(fabs(Mn)*fabs(Mn)+deta*deta)/deta;Fc[0]=0.5*Mn*(WL[0]*cl+WR[0]*cr)-0.5*fMn*(WR[0]*cr-WL[0]*cl);Fc[1]=0.5*Mn*(WL[1]*cl+WR[1]*cr)-0.5*fMn*(WR[1]*cr-WL[1]*cl)+pn; Fc[2]=0.5*Mn*((WL[2]+PL)*cl+(WR[2]+PR)*cr)-0.5*fMn*((WR[2]+PR)*cr-(WL[2]+PL)*cl);}//残差计算RvoidSolveResidual(){inti,j;doubleUL[3],UR[3],Fp[3],Fm[3];for(i=0;i<Nc;i++){if(cell[i].flag==0){//i+1/2SolveReconstruction(cell[i-1].U,cell[i].U,cell[i+1].U,cell[i+2].U,UL,UR);//MSUCL重构SolveAUSMFlux(UL,UR,Fp);//二阶格式// SolveAUSMFlux(cell[i].U,cell[i+1].U,Fp);//一阶格式//i-1/2SolveReconstruction(cell[i-2].U,cell[i-1].U,cell[i].U,cell[i+1].U,UL,UR);//MSUCL重构 SolveAUSMFlux(UL,UR,Fm);//二阶格式// SolveAUSMFlux(cell[i-1].U,cell[i].U,Fm);//一阶格式for(j=0;j<3;j++){cell[i].R[j]=-(Fp[j]-Fm[j])/h;}}}}//流场值更新voidSolveNextstep(doublear[],intir){inti,j;for(i=0;i<=Nc;i++){if(cell[i].flag==0){ if(ir==0) { for(j=0;j<3;j++) cell[i].Wp[j]=cell[i].W[j]; } for(j=0;j<3;j++) cell[i].W[j]=cell[i].Wp[j]+ar[ir]*dt*cell[i].R[j];SolveWtoU(cell[i].W,cell[i].U);}}}//Runge-Kutta办法voidSolveRungeKutta(){doublear[4]={1/4.0,1/3.0,0.5,1.0};intit,ir;timesum=0.0;for(it=0;;it++)//迭代步数{ for(ir=0;ir<4;ir++)//四步Rouger-Kutta法{ boundarycondition();SolveResidual(); SolveNextstep(ar,ir); }timesum+=dt;printf("it=%d,timesum=%f\n",it,timesum);if(timesum>=0.16){output();getchar();printf("Pleaseenteranykeytocontinue...");break;}}}//成果输出voidoutput(){inti;FILE*fpd,*fpu,*fpp;if((fpd=fopen("Density.plt","w"))==NULL) {printf("connotopeninfile\n");return; }fprintf(fpd,"TITLE=\"TestCase\"\n");fprintf(fpd,"VARIABLES=\"x\",\"density\"\n");fprintf(fpd,"ZONET=\"OnlyZone\",I=%d,F=POINT\n",Nc);if((fpp=fopen("Pressure.plt","w"))==NULL) {printf("connotopeninfile\n");return; }fprintf(fpp,"TITLE=\"TestCase\"\n");fp

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论