首页 > 代码库 > SUNTANS 对流扩散求解[Tbc]
SUNTANS 对流扩散求解[Tbc]
最近接到一个任务,就是解决FVCOM中对流扩散计算不守衡问题。导师认为是其求解时候水平和垂向计算分开求解所导致的,目前我也没搞清到底有什么问题,反正就是让把SUNTANS的对流扩散计算挪到FVCOM中。
SUNTANS模型手册:http://web.stanford.edu/group/suntans/cgi-bin/documentation/user_guide/user_guide.html
介绍文献:《An unstructured-grid, finite-volume, nonhydrostatic, parallel coastal ocean simulator》
代码所谓研究讨论之用这里只公布部分:
1 /* 2 * File: scalars.c 3 * Author: Oliver B. Fringer 4 * Institution: Stanford University 5 * ---------------------------------------- 6 * This file contains the scalar transport function. 7 * 8 * Copyright (C) 2005-2006 The Board of Trustees of the Leland Stanford Junior 9 * University. All Rights Reserved. 10 * 11 */ 12 #include "scalars.h" 13 #include "util.h" 14 #include "tvd.h" 15 #include "initialization.h" 16 17 #define SMALL_CONSISTENCY 1e-5 18 19 REAL smin_value, smax_value; 20 21 /* 22 * Function: UpdateScalars 23 * Usage: UpdateScalars(grid,phys,prop,wnew,scalar,Cn,kappa,kappaH,kappa_tv,theta); 24 * --------------------------------------------------------------------------- 25 * Update the scalar quantity stored in the array denoted by scal using the 26 * theta method for vertical advection and vertical diffusion and Adams-Bashforth 27 * for horizontal advection and diffusion. 28 * 29 * Cn must store the AB terms from time step n-1 for this scalar 30 * kappa denotes the vertical scalar diffusivity 31 * kappaH denotes the horizontal scalar diffusivity 32 * kappa_tv denotes the vertical turbulent scalar diffusivity 33 * 34 */ 35 void UpdateScalars(gridT *grid, physT *phys, propT *prop, REAL **wnew, REAL **scal, REAL **boundary_scal, REAL **Cn, 36 REAL kappa, REAL kappaH, REAL **kappa_tv, REAL theta, 37 REAL **src1, REAL **src2, REAL *Ftop, REAL *Fbot, int alpha_top, int alpha_bot, 38 MPI_Comm comm, int myproc, int checkflag, int TVDscheme) 39 { 40 int i, iptr, j, jptr, ib, k, nf, ktop; 41 int Nc=grid->Nc, normal, nc1, nc2, ne; 42 REAL df, dg, Ac, dt=prop->dt, fab, *a, *b, *c, *d, *ap, *am, *bd, *uflux, dznew, mass, *sp, *temp; 43 REAL smin, smax, div_local, div_da; 44 int k1, k2, kmin, imin, kmax, imax, mincount, maxcount, allmincount, allmaxcount, flag; 45 46 prop->TVD = TVDscheme; 47 // These are used mostly debugging to turn on/off vertical and horizontal TVD. 48 prop->horiTVD = 1; 49 prop->vertTVD = 1; 50 51 ap = phys->ap; 52 am = phys->am; 53 bd = phys->bp; 54 temp = phys->bm; 55 a = phys->a; 56 b = phys->b; 57 c = phys->c; 58 d = phys->d; 59 60 // Never use AB2 61 if(1) { 62 fab=1; 63 for(i=0;i<grid->Nc;i++) 64 for(k=0;k<grid->Nk[i];k++) 65 Cn[i][k]=0; 66 } else 67 fab=1.5; 68 69 for(i=0;i<Nc;i++) 70 for(k=0;k<grid->Nk[i];k++) 71 phys->stmp[i][k]=scal[i][k]; 72 73 // Add on boundary fluxes, using stmp2 as the temporary storage 74 // variable 75 //for(iptr=grid->celldist[0];iptr<grid->celldist[1];iptr++) { 76 for(iptr=grid->celldist[0];iptr<grid->celldist[2];iptr++) { 77 i = grid->cellp[iptr]; 78 79 for(k=grid->ctop[i];k<grid->Nk[i];k++) 80 phys->stmp2[i][k]=0; 81 } 82 83 if(boundary_scal) { 84 for(jptr=grid->edgedist[2];jptr<grid->edgedist[5];jptr++) { 85 j = grid->edgep[jptr]; 86 87 ib = grid->grad[2*j]; 88 89 // Set the value of stmp2 adjacent to the boundary to the value of the boundary. 90 // This will be used to add the boundary flux when stmp2 is used again below. 91 for(k=grid->ctop[ib];k<grid->Nk[ib];k++) 92 phys->stmp2[ib][k]=boundary_scal[jptr-grid->edgedist[2]][k]; 93 } 94 } 95 96 // Compute the scalar on the vertical faces (for horiz. advection) 97 98 if(prop->TVD && prop->horiTVD) 99 HorizontalFaceScalars(grid,phys,prop,scal,boundary_scal,prop->TVD,comm,myproc); 100 101 //for(iptr=grid->celldist[0];iptr<grid->celldist[1];iptr++) {102 for(iptr=grid->celldist[0];iptr<grid->celldist[2];iptr++) {103 i = grid->cellp[iptr];104 Ac = grid->Ac[i];105 106 if(grid->ctop[i]>=grid->ctopold[i]) {107 ktop=grid->ctop[i];108 dznew=grid->dzz[i][ktop];109 } else {110 ktop=grid->ctopold[i];111 dznew=0;112 for(k=grid->ctop[i];k<=grid->ctopold[i];k++)113 dznew+=grid->dzz[i][k];114 }115 116 // These are the advective components of the tridiagonal117 // at the new time step.118 if(!(prop->TVD && prop->vertTVD))119 for(k=0;k<grid->Nk[i]+1;k++) {120 ap[k] = 0.5*(wnew[i][k]+fabs(wnew[i][k]));121 am[k] = 0.5*(wnew[i][k]-fabs(wnew[i][k]));122 }123 else // Compute the ap/am for TVD schemes124 GetApAm(ap,am,phys->wp,phys->wm,phys->Cp,phys->Cm,phys->rp,phys->rm,125 wnew,grid->dzz,scal,i,grid->Nk[i],ktop,prop->dt,prop->TVD);126 127 for(k=ktop+1;k<grid->Nk[i];k++){128 a[k-ktop]=theta*dt*am[k];129 b[k-ktop]=grid->dzz[i][k]+theta*dt*(ap[k]-am[k+1]);130 c[k-ktop]=-theta*dt*ap[k+1];131 }132 133 // Top cell advection134 a[0]=0;135 b[0]=dznew-theta*dt*am[ktop+1];136 c[0]=-theta*dt*ap[ktop+1];137 138 // Bottom cell no-flux boundary condition for advection139 b[(grid->Nk[i]-1)-ktop]+=c[(grid->Nk[i]-1)-ktop];140 141 // Implicit vertical diffusion terms142 for(k=ktop+1;k<grid->Nk[i];k++)143 bd[k]=(2.0*kappa+kappa_tv[i][k-1]+kappa_tv[i][k])/144 (grid->dzz[i][k-1]+grid->dzz[i][k]);145 146 for(k=ktop+1;k<grid->Nk[i]-1;k++) {147 a[k-ktop]-=theta*dt*bd[k];148 b[k-ktop]+=theta*dt*(bd[k]+bd[k+1]);149 c[k-ktop]-=theta*dt*bd[k+1];150 }151 if(src1)152 for(k=ktop;k<grid->Nk[i];k++)153 b[k-ktop]+=grid->dzz[i][k]*src1[i][k]*theta*dt;154 155 // Diffusive fluxes only when more than 1 layer156 if(ktop<grid->Nk[i]-1) {157 // Top cell diffusion158 b[0]+=theta*dt*(bd[ktop+1]+2*alpha_top*bd[ktop+1]);159 c[0]-=theta*dt*bd[ktop+1];160 161 // Bottom cell diffusion162 a[(grid->Nk[i]-1)-ktop]-=theta*dt*bd[grid->Nk[i]-1];163 b[(grid->Nk[i]-1)-ktop]+=theta*dt*(bd[grid->Nk[i]-1]+2*alpha_bot*bd[grid->Nk[i]-1]);164 }165 166 // Explicit part into source term d[] 167 for(k=ktop+1;k<grid->Nk[i];k++)168 d[k-ktop]=grid->dzzold[i][k]*phys->stmp[i][k];169 if(src1)170 for(k=ktop+1;k<grid->Nk[i];k++)171 d[k-ktop]-=src1[i][k]*(1-theta)*dt*grid->dzzold[i][k]*phys->stmp[i][k];172 173 d[0]=0;174 if(grid->ctopold[i]<=grid->ctop[i]) {175 for(k=grid->ctopold[i];k<=grid->ctop[i];k++)176 d[0]+=grid->dzzold[i][k]*phys->stmp[i][k];177 if(src1)178 for(k=grid->ctopold[i];k<=grid->ctop[i];k++)179 d[0]-=src1[i][k]*(1-theta)*dt*grid->dzzold[i][k]*phys->stmp[i][k];180 } else {181 d[0]=grid->dzzold[i][ktop]*phys->stmp[i][ktop];182 if(src1)183 d[0]-=src1[i][ktop]*(1-theta)*dt*grid->dzzold[i][ktop]*phys->stmp[i][k];184 }185 186 // These are the advective components of the tridiagonal187 // that use the new velocity188 if(!(prop->TVD && prop->vertTVD))189 for(k=0;k<grid->Nk[i]+1;k++) {190 ap[k] = 0.5*(phys->wtmp2[i][k]+fabs(phys->wtmp2[i][k]));191 am[k] = 0.5*(phys->wtmp2[i][k]-fabs(phys->wtmp2[i][k]));192 }193 else // Compute the ap/am for TVD schemes194 GetApAm(ap,am,phys->wp,phys->wm,phys->Cp,phys->Cm,phys->rp,phys->rm,195 phys->wtmp2,grid->dzzold,phys->stmp,i,grid->Nk[i],ktop,prop->dt,prop->TVD);196 197 // Explicit advection and diffusion198 for(k=ktop+1;k<grid->Nk[i]-1;k++)199 d[k-ktop]-=(1-theta)*dt*(am[k]*phys->stmp[i][k-1]+200 (ap[k]-am[k+1])*phys->stmp[i][k]-201 ap[k+1]*phys->stmp[i][k+1])-202 (1-theta)*dt*(bd[k]*phys->stmp[i][k-1]203 -(bd[k]+bd[k+1])*phys->stmp[i][k]204 +bd[k+1]*phys->stmp[i][k+1]);205 206 if(ktop<grid->Nk[i]-1) {207 //Flux through bottom of top cell208 k=ktop;209 d[0]=d[0]-(1-theta)*dt*(-am[k+1]*phys->stmp[i][k]-210 ap[k+1]*phys->stmp[i][k+1])+211 (1-theta)*dt*(-(2*alpha_top*bd[k+1]+bd[k+1])*phys->stmp[i][k]+212 bd[k+1]*phys->stmp[i][k+1]);213 if(Ftop) d[0]+=dt*(1-alpha_top+2*alpha_top*bd[k+1])*Ftop[i];214 215 // Through top of bottom cell216 k=grid->Nk[i]-1;217 d[k-ktop]-=(1-theta)*dt*(am[k]*phys->stmp[i][k-1]+218 ap[k]*phys->stmp[i][k])-219 (1-theta)*dt*(bd[k]*phys->stmp[i][k-1]-220 (bd[k]+2*alpha_bot*bd[k])*phys->stmp[i][k]);221 if(Fbot) d[k-ktop]+=dt*(-1+alpha_bot+2*alpha_bot*bd[k])*Fbot[i];222 }223 224 // First add on the source term from the previous time step.225 if(grid->ctop[i]<=grid->ctopold[i]) {226 for(k=grid->ctop[i];k<=grid->ctopold[i];k++)227 d[0]+=(1-fab)*Cn[i][grid->ctopold[i]]/(1+fabs(grid->ctop[i]-grid->ctopold[i]));228 for(k=grid->ctopold[i]+1;k<grid->Nk[i];k++)229 d[k-grid->ctopold[i]]+=(1-fab)*Cn[i][k];230 } else {231 for(k=grid->ctopold[i];k<=grid->ctop[i];k++)232 d[0]+=(1-fab)*Cn[i][k];233 for(k=grid->ctop[i]+1;k<grid->Nk[i];k++)234 d[k-grid->ctop[i]]+=(1-fab)*Cn[i][k];235 }236 237 for(k=0;k<grid->ctop[i];k++)238 Cn[i][k]=0;239 240 if(src2)241 for(k=grid->ctop[i];k<grid->Nk[i];k++)242 Cn[i][k-ktop]=dt*src2[i][k]*grid->dzzold[i][k];243 else244 for(k=grid->ctop[i];k<grid->Nk[i];k++)245 Cn[i][k]=0;246 247 // Now create the source term for the current time step248 for(k=0;k<grid->Nk[i];k++)249 ap[k]=0;250 251 for(nf=0;nf<grid->nfaces[i];nf++) {252 ne = grid->face[i*grid->maxfaces+nf];253 normal = grid->normal[i*grid->maxfaces+nf];254 df = grid->df[ne];255 dg = grid->dg[ne];256 nc1 = grid->grad[2*ne];257 nc2 = grid->grad[2*ne+1];258 if(nc1==-1) nc1=nc2;259 if(nc2==-1) {260 nc2=nc1;261 if(boundary_scal && (grid->mark[ne]==2 || grid->mark[ne]==3))262 sp=phys->stmp2[nc1];263 else264 sp=phys->stmp[nc1];265 } else266 sp=phys->stmp[nc2];267 268 if(!(prop->TVD && prop->horiTVD)) {269 for(k=0;k<grid->Nke[ne];k++)270 temp[k]=UpWind(phys->utmp2[ne][k],271 phys->stmp[nc1][k],272 sp[k]);273 } else {274 for(k=0;k<grid->Nke[ne];k++)275 if(phys->utmp2[ne][k]>0)276 temp[k]=phys->SfHp[ne][k];277 else278 temp[k]=phys->SfHm[ne][k];279 }280 281 for(k=0;k<grid->Nke[ne];k++)282 ap[k] += dt*df*normal/Ac*(theta*phys->u[ne][k]+(1-theta)*phys->utmp2[ne][k])283 *temp[k]*grid->dzf[ne][k];284 }285 286 for(k=ktop+1;k<grid->Nk[i];k++)287 Cn[i][k-ktop]-=ap[k];288 289 for(k=0;k<=ktop;k++)290 Cn[i][0]-=ap[k];291 292 // Add on the source from the current time step to the rhs.293 for(k=0;k<grid->Nk[i]-ktop;k++)294 d[k]+=fab*Cn[i][k];295 296 // Add on the volume correction if h was < -d297 /*298 if(grid->ctop[i]==grid->Nk[i]-1)299 d[grid->Nk[i]-ktop-1]+=phys->hcorr[i]*phys->stmp[i][grid->ctop[i]];300 */301 302 for(k=ktop;k<grid->Nk[i];k++)303 ap[k]=Cn[i][k-ktop];304 for(k=0;k<=ktop;k++)305 Cn[i][k]=0;306 for(k=ktop+1;k<grid->Nk[i];k++)307 Cn[i][k]=ap[k];308 for(k=grid->ctop[i];k<=ktop;k++)309 Cn[i][k]=ap[ktop]/(1+fabs(grid->ctop[i]-ktop));310 311 if(grid->Nk[i]-ktop>1)312 TriSolve(a,b,c,d,&(scal[i][ktop]),grid->Nk[i]-ktop);313 else if(prop->n>1) {314 if(b[0]>0 && phys->active[i])315 scal[i][ktop]=d[0]/b[0];316 else317 scal[i][ktop]=0;318 }319 320 for(k=0;k<grid->ctop[i];k++)321 scal[i][k]=0;322 323 for(k=grid->ctop[i];k<grid->ctopold[i];k++)324 scal[i][k]=scal[i][ktop];325 }326 327 // Code to check divergence change CHECKCONSISTENCY to 1 in suntans.h328 if(CHECKCONSISTENCY && checkflag) {329 330 if(prop->n==1+prop->nstart) {331 smin=INFTY;332 smax=-INFTY;333 for(i=0;i<grid->Nc;i++) {334 for(k=grid->ctop[i];k<grid->Nk[i];k++) {335 if(phys->stmp[i][k]>smax) { 336 smax=phys->stmp[i][k]; 337 imax=i; 338 kmax=k; 339 }340 if(phys->stmp[i][k]<smin) { 341 smin=phys->stmp[i][k]; 342 imin=i; 343 kmin=k; 344 }345 }346 }347 MPI_Reduce(&smin,&smin_value,1,MPI_DOUBLE,MPI_MIN,0,comm);348 MPI_Reduce(&smax,&smax_value,1,MPI_DOUBLE,MPI_MAX,0,comm);349 MPI_Bcast(&smin_value,1,MPI_DOUBLE,0,comm);350 MPI_Bcast(&smax_value,1,MPI_DOUBLE,0,comm);351 352 if(myproc==0)353 printf("Minimum scalar: %.2f, maximum: %.2f\n",smin_value,smax_value);354 } 355 356 //for(iptr=grid->celldist[0];iptr<grid->celldist[1];iptr++) {357 for(iptr=grid->celldist[0];iptr<grid->celldist[2];iptr++) {358 i = grid->cellp[iptr];359 360 flag=0;361 for(nf=0;nf<grid->nfaces[i];nf++) {362 if(grid->mark[grid->face[i*grid->maxfaces+nf]]==2 || 363 grid->mark[grid->face[i*grid->maxfaces+nf]]==3) {364 flag=1;365 break;366 }367 }368 369 if(!flag) {370 div_da=0;371 372 for(k=0;k<grid->Nk[i];k++) {373 div_da+=grid->Ac[i]*(grid->dzz[i][k]-grid->dzzold[i][k])/prop->dt;374 375 div_local=0;376 for(nf=0;nf<grid->nfaces[i];nf++) {377 ne=grid->face[i*grid->maxfaces+nf];378 div_local+=(theta*phys->u[ne][k]+(1-theta)*phys->utmp2[ne][k])379 *grid->dzf[ne][k]*grid->normal[i*grid->maxfaces+nf]*grid->df[ne];380 }381 div_da+=div_local;382 div_local+=grid->Ac[i]*(theta*(wnew[i][k]-wnew[i][k+1])+383 (1-theta)*(phys->wtmp2[i][k]-phys->wtmp2[i][k+1]));384 385 if(k>=grid->ctop[i]) {386 if(fabs(div_local)>SMALL_CONSISTENCY && grid->dzz[imin][0]>DRYCELLHEIGHT) 387 printf("Step: %d, proc: %d, locally-divergent at %d, %d, div=%e\n",388 prop->n,myproc,i,k,div_local);389 }390 }391 if(fabs(div_da)>SMALL_CONSISTENCY && phys->h[i]+grid->dv[i]>DRYCELLHEIGHT)392 printf("Step: %d, proc: %d, Depth-Ave divergent at i=%d, div=%e\n",393 prop->n,myproc,i,div_da);394 }395 }396 397 mincount=0;398 maxcount=0;399 smin=INFTY;400 smax=-INFTY;401 //for(iptr=grid->celldist[0];iptr<grid->celldist[1];iptr++) {402 for(iptr=grid->celldist[0];iptr<grid->celldist[2];iptr++) {403 i = grid->cellp[iptr];404 405 flag=0;406 for(nf=0;nf<grid->nfaces[i];nf++) {407 if(grid->mark[grid->face[i*grid->maxfaces+nf]]==2 || grid->mark[grid->face[i*grid->maxfaces+nf]]==3) {408 flag=1;409 break;410 }411 }412 413 if(!flag) {414 for(k=grid->ctop[i];k<grid->Nk[i];k++) {415 if(scal[i][k]>smax) { 416 smax=scal[i][k]; 417 imax=i; 418 kmax=k; 419 }420 if(scal[i][k]<smin) { 421 smin=scal[i][k]; 422 imin=i; 423 kmin=k; 424 }425 426 if(scal[i][k]>smax_value+SMALL_CONSISTENCY && grid->dzz[i][k]>DRYCELLHEIGHT)427 maxcount++;428 if(scal[i][k]<smin_value-SMALL_CONSISTENCY && grid->dzz[i][k]>DRYCELLHEIGHT)429 mincount++;430 }431 }432 }433 MPI_Reduce(&mincount,&allmincount,1,MPI_INT,MPI_SUM,0,comm);434 MPI_Reduce(&maxcount,&allmaxcount,1,MPI_INT,MPI_SUM,0,comm);435 436 if(mincount!=0 || maxcount!=0) 437 printf("Not CWC, step: %d, proc: %d, smin = %e at i=%d,H=%e, smax = %e at i=%d,H=%e\n",438 prop->n,myproc,439 smin,imin,phys->h[imin]+grid->dv[imin],440 smax,imax,phys->h[imax]+grid->dv[imax]);441 442 if(myproc==0 && (allmincount !=0 || allmaxcount !=0))443 printf("Total number of CWC violations (all procs): s<s_min: %d, s>s_max: %d\n",444 allmincount,allmaxcount);445 }446 }
下面介绍解读UpdateScalars函数过程:
1. 首先作为一个复杂的非静压N-S模型,变量比较多是很正常的,所以不要纠结每个变量是什么意思,能看懂就看,看不懂就猜好了。
2.要从整体入手。根据目前已知信息,这是用有限体积法求解对流扩散方程模块,而所求标量值应该就是就是第5个参数 **scal 所代表的变量。那么从函数最后一次更新 scal 变量的地方,或许能获得些许线索。
第311行:
if(grid->Nk[i]-ktop>1) TriSolve(a,b,c,d,&(scal[i][ktop]),grid->Nk[i]-ktop);
检查 TriSolve 函数的定义,原来是求解三对角方程组的解法,a,b,c 分别是系数矩阵三个对角向量,d是等号右端常向量,未知数为以 scal[i][ktop] 起始的数组。
而准备a,b,c 等系数向量时,循环变量多是按照某个三棱柱各层从上到下进行循环,所以不难看出,这个方程组求解的应该就是某个三棱柱单元体内各层标量大小。
3.
SUNTANS 对流扩散求解[Tbc]