• SUNTANS 及 FVCOM 对流扩散方程求解简介[TBC]


    最近接到一个任务,就是解决FVCOM中对流扩散计算不守衡问题。导师认为是其求解时候水平和垂向计算分开求解所导致的,目前我也没搞清到底有什么问题,反正就是让把SUNTANS的对流扩散计算挪到FVCOM中,下面就把 SUNTANS 和 FVCOM 数值求解的过程贴出来,备忘

    SUNTANS模型求解过程

    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 tridiagonal
    117     // 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 schemes
    124       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 advection
    134     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 advection
    139     b[(grid->Nk[i]-1)-ktop]+=c[(grid->Nk[i]-1)-ktop];
    140 
    141     // Implicit vertical diffusion terms
    142     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 layer
    156     if(ktop<grid->Nk[i]-1) {
    157       // Top cell diffusion
    158       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 diffusion
    162       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 tridiagonal
    187     // that use the new velocity
    188     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 schemes
    194       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 diffusion
    198     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 cell
    208       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 cell
    216       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     else
    244       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 step
    248     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         else
    264           sp=phys->stmp[nc1];
    265       } else 
    266         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           else
    278             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 < -d
    297     /*
    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       else 
    317         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.h
    328   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
    ",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
    ",
    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
    ",
    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
    ",
    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
    ",
    444           allmincount,allmaxcount);
    445   }
    446   }
    View Code

    下面介绍解读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. 将程序数值离散过程和理论结合起来,了解程序细节

    FVCOM 模型求解过程

    FVCOM 也是使用有限体积方法,但是求解和 SUNTANS 有很大不同。由于FVCOM并没有介绍对流扩散方程求解具体过程的文献,这时程序看起来就比较头疼,只能全部通过读程序来一点点理解。

    FVCOM 扩散方程计算主要在程序 mod_scal.F 中,模块内全部程序如下

      1 !/===========================================================================/
      2 ! Copyright (c) 2007, The University of Massachusetts Dartmouth 
      3 ! Produced at the School of Marine Science & Technology 
      4 ! Marine Ecosystem Dynamics Modeling group
      5 ! All rights reserved.
      6 !
      7 ! FVCOM has been developed by the joint UMASSD-WHOI research team. For 
      8 ! details of authorship and attribution of credit please see the FVCOM
      9 ! technical manual or contact the MEDM group.
     10 !
     11 ! 
     12 ! This file is part of FVCOM. For details, see http://fvcom.smast.umassd.edu 
     13 ! The full copyright notice is contained in the file COPYRIGHT located in the 
     14 ! root directory of the FVCOM code. This original header must be maintained
     15 ! in all distributed versions.
     16 !
     17 ! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
     18 ! AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO,
     19 ! THE IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR
     20 ! PURPOSE ARE DISCLAIMED.  
     21 !
     22 !/---------------------------------------------------------------------------/
     23 ! CVS VERSION INFORMATION
     24 ! $Id$
     25 ! $Name$
     26 ! $Revision$
     27 !/===========================================================================/
     28 
     29 !=======================================================================
     30 ! FVCOM Scalar Module  
     31 !
     32 !    contains methods:
     33 !        Adv_Scal            => Advect a Scalar Quantity 
     34 !        Vdif_Scal           => Vertical Diffusion of Scalar Quantity
     35 !        Bcond_Scal_OBC      => Open Boundary Condition for Scalar
     36 !        Bcond_Scal_PTsource => Point Sources of Scalar
     37 !=======================================================================
     38 Module Scalar
     39 
     40   logical, parameter :: debug = .true. 
     41 
     42   contains
     43 !==============================================================================|
     44 ! Calculate Horizontal Advection and Diffusion For Scalar (f)                  |
     45 !==============================================================================|
     46   Subroutine Adv_Scal(f,fn,d_fdis,fdis,d_fflux,fflux_obc,deltat,source)
     47 !------------------------------------------------------------------------------|
     48 
     49   use all_vars
     50   use lims, only: m,mt,n,nt,kbm1,kb
     51   use bcs
     52   use mod_obcs
     53 # if defined (MULTIPROCESSOR)
     54   use mod_par
     55 # endif
     56 # if defined (WET_DRY)
     57   use mod_wd
     58 # endif
     59 
     60 # if defined (THIN_DAM)
     61   use mod_dam, only : kdam,N_DAM_MATCH,IS_DAM
     62 # endif
     63 
     64   implicit none
     65   real(sp), intent(in ), dimension(0:mt,kb)      :: f 
     66   real(sp), intent(out), dimension(0:mt,kb)      :: fn
     67   integer , intent(in )                          :: d_fdis
     68   real(sp), intent(in ), dimension(d_fdis)       :: fdis
     69   integer , intent(in )                          :: d_fflux
     70   real(sp), intent(out), dimension(d_fflux,kbm1) :: fflux_obc 
     71   real(sp), intent(in )                          :: deltat
     72   logical , intent(in )                          :: source
     73 
     74   !----------------local--------------------------------------
     75   real(sp), dimension(0:mt,kb)   :: xflux,xflux_adv
     76   real(sp), dimension(m)         :: pupx,pupy,pvpx,pvpy  
     77   real(sp), dimension(m)         :: pfpx,pfpy,pfpxd,pfpyd,viscoff
     78   real(sp), dimension(3*nt)      :: dtij 
     79   real(sp), dimension(3*nt,kbm1) :: uvn
     80   real(sp), dimension(kb)        :: vflux
     81   real(sp) :: utmp,vtmp,sitai,ffd,ff1,x11,y11,x22,y22,x33,y33
     82   real(sp) :: tmp1,tmp2,xi,yi
     83   real(sp) :: dxa,dya,dxb,dyb,fij1,fij2,un
     84   real(sp) :: txx,tyy,fxx,fyy,viscof,exflux,temp,fpoint
     85   real(sp) :: fact,fm1,fmean
     86   integer  :: i,i1,i2,ia,ib,j,j1,j2,k,jtmp,jj
     87 # if defined (SPHERICAL)
     88   real(sp) :: ty,txpi,typi
     89 # endif
     90 
     91 # if defined (THIN_DAM)
     92   INTEGER  :: NX
     93   real(sp) :: tmpflx
     94   real(sp),dimension(kb) :: wvel
     95 # endif
     96 
     97 
     98 !------------------------------------------------------------------------------!
     99 
    100 !-------------------------------------------------------
    101 !Calculate Mean Values
    102 !-------------------------------------------------------
    103 
    104   fmean = sum(f(1:m,1:kbm1))/float(m*kbm1)
    105 
    106 !-------------------------------------------------------
    107 !Initialize Multipliers to Control Horizontal Diff
    108 !-------------------------------------------------------
    109 
    110   fact = 0.0_sp
    111   fm1  = 1.0_sp
    112   if(HORIZONTAL_MIXING_TYPE == 'closure') then
    113     fact = 1.0_sp
    114     fm1  = 0.0_sp
    115   end if
    116      
    117 !-------------------------------------------------------
    118 !Initialize Fluxes
    119 !-------------------------------------------------------
    120   xflux     = 0.0_sp
    121   xflux_adv = 0.0_sp
    122 
    123 !-------------------------------------------------------
    124 !Calculate Normal Velocity on Control Volume Edges
    125 !-------------------------------------------------------
    126 !!# if !defined (WET_DRY)
    127   do i=1,ncv
    128     i1=ntrg(i)
    129     dtij(i)=dt1(i1)
    130     do k=1,kbm1
    131       uvn(i,k) = v(i1,k)*dltxe(i) - u(i1,k)*dltye(i)
    132 
    133 #  if defined(PLBC)
    134       uvn(i,k) =  - u(i1,k)*dltye(i)
    135 #  endif
    136 
    137     end do
    138   end do
    139 !!# else
    140 !!  do i=1,ncv
    141 !!    i1=ntrg(i)
    142 !!    dtij(i)=dt1(i1)
    143 !!    do k=1,kbm1
    144 !!      uvn(i,k) = vs(i1,k)*dltxe(i) - us(i1,k)*dltye(i)
    145 !!    end do
    146 !!  end do
    147 !!# endif
    148 
    149 !
    150 !--Calculate the Advection and Horizontal Diffusion Terms----------------------!
    151 !
    152 
    153    do k=1,kbm1
    154       pfpx  = 0.0_sp 
    155       pfpy  = 0.0_sp 
    156       pfpxd = 0.0_sp 
    157       pfpyd = 0.0_sp
    158      do i=1,m
    159        do j=1,ntsn(i)-1
    160          i1=nbsn(i,j)
    161          i2=nbsn(i,j+1)
    162 
    163 #    if defined (WET_DRY)
    164          IF(ISWETN(I1) == 0 .AND. ISWETN(I2) == 1)THEN
    165           FFD=0.5_SP*(f(I,K)+f(I2,K))
    166           FF1=0.5_SP*(f(I,K)+f(I2,K))
    167      ELSE IF(ISWETN(I1) == 1 .AND. ISWETN(I2) == 0)THEN
    168           FFD=0.5_SP*(f(I1,K)+f(I,K))
    169           FF1=0.5_SP*(f(I1,K)+f(I,K))
    170      ELSE IF(ISWETN(I1) == 0 .AND. ISWETN(I2) == 0)THEN
    171           FFD=0.5_SP*(f(I,K)+f(I,K))
    172           FF1=0.5_SP*(f(I,K)+f(I,K))
    173      ELSE
    174           FFD=0.5_SP*(f(I1,K)+f(I2,K))
    175           FF1=0.5_SP*(f(I1,K)+f(I2,K))
    176      END IF 
    177 #    else     
    178          ffd=0.5_sp*(f(i1,k)+f(i2,k)) !-fmean1(i1,k)-fmean1(i2,k))
    179          ff1=0.5_sp*(f(i1,k)+f(i2,k))
    180 #    endif     
    181      
    182 #        if defined (SPHERICAL)
    183          ty=0.5_sp*(vy(i1)+vy(i2))
    184          txpi=(vx(i2)-vx(i1))*tpi*cos(deg2rad*ty)
    185          typi=(vy(i1)-vy(i2))*tpi
    186          pfpx(i)=pfpx(i)+ff1*typi
    187          pfpy(i)=pfpy(i)+ff1*txpi
    188          pfpxd(i)=pfpxd(i)+ffd*typi
    189          pfpyd(i)=pfpyd(i)+ffd*txpi
    190 #        else
    191          pfpx(i) = pfpx(i) +ff1*(vy(i1)-vy(i2))
    192          pfpy(i) = pfpy(i) +ff1*(vx(i2)-vx(i1))
    193          pfpxd(i)= pfpxd(i)+ffd*(vy(i1)-vy(i2))
    194          pfpyd(i)= pfpyd(i)+ffd*(vx(i2)-vx(i1))
    195 #        endif
    196        end do
    197 
    198 ! gather all neighboring control volumes connecting at dam node 
    199 # if defined (THIN_DAM)
    200        IF(IS_DAM(I)==1.AND.K<=KDAM(I))THEN
    201          DO NX=1,N_DAM_MATCH(I,1)
    202            DO J=1,NTSN(N_DAM_MATCH(I,NX+1))-1
    203              I1=NBSN(N_DAM_MATCH(I,NX+1),J)
    204              I2=NBSN(N_DAM_MATCH(I,NX+1),J+1)
    205              FFD=0.5_SP*(f(I1,K)+f(I2,K)) !-SMEAN1(I1,K)-SMEAN1(I2,K))
    206              FF1=0.5_SP*(f(I1,K)+f(I2,K))
    207 #        if defined (SPHERICAL)
    208              ty=0.5_sp*(vy(i1)+vy(i2))
    209              txpi=(vx(i2)-vx(i1))*tpi*cos(deg2rad*ty)
    210              typi=(vy(i1)-vy(i2))*tpi
    211              pfpx(i)=pfpx(i)+ff1*typi
    212              pfpy(i)=pfpy(i)+ff1*txpi
    213              pfpxd(i)=pfpxd(i)+ffd*typi
    214              pfpyd(i)=pfpyd(i)+ffd*txpi
    215 #        else
    216              pfpx(i) = pfpx(i) +ff1*(vy(i1)-vy(i2))
    217              pfpy(i) = pfpy(i) +ff1*(vx(i2)-vx(i1))
    218              pfpxd(i)= pfpxd(i)+ffd*(vy(i1)-vy(i2))
    219              pfpyd(i)= pfpyd(i)+ffd*(vx(i2)-vx(i1))
    220 #        endif
    221            END DO
    222          END DO
    223        END IF
    224 # endif
    225 
    226 # if !defined (THIN_DAM)
    227        pfpx(i)  =pfpx(i )/art2(i)
    228        pfpy(i)  =pfpy(i )/art2(i)
    229        pfpxd(i) =pfpxd(i)/art2(i)
    230        pfpyd(i) =pfpyd(i)/art2(i)
    231 # else
    232        IF(IS_DAM(I)==1.AND.K<=KDAM(I))THEN
    233          PFPX(I)=PFPX(I)/(ART2(I)+SUM(ART2(N_DAM_MATCH(I,2:1+N_DAM_MATCH(I,1)))))
    234          PFPY(I)=PFPY(I)/(ART2(I)+SUM(ART2(N_DAM_MATCH(I,2:1+N_DAM_MATCH(I,1)))))
    235          PFPXD(I)=PFPXD(I)/(ART2(I)+SUM(ART2(N_DAM_MATCH(I,2:1+N_DAM_MATCH(I,1)))))
    236          PFPYD(I)=PFPYD(I)/(ART2(I)+SUM(ART2(N_DAM_MATCH(I,2:1+N_DAM_MATCH(I,1)))))
    237        ELSE
    238          PFPX(I)=PFPX(I)/ART2(I)
    239          PFPY(I)=PFPY(I)/ART2(I)
    240          PFPXD(I)=PFPXD(I)/ART2(I)
    241          PFPYD(I)=PFPYD(I)/ART2(I)
    242        END IF
    243 # endif
    244 
    245      end do
    246           
    247      if(k == kbm1)then
    248        do i=1,m
    249          pfpxb(i) = pfpx(i)
    250          pfpyb(i) = pfpy(i)
    251        end do
    252      end if
    253 
    254      do i=1,m
    255        pupx(i)=0.0_sp
    256        pupy(i)=0.0_sp
    257        pvpx(i)=0.0_sp
    258        pvpy(i)=0.0_sp
    259        j=1
    260        i1=nbve(i,j)
    261        jtmp=nbvt(i,j)
    262        j1=jtmp+1-(jtmp+1)/4*3
    263        j2=jtmp+2-(jtmp+2)/4*3
    264        x11=0.5_sp*(vx(i)+vx(nv(i1,j1)))
    265        y11=0.5_sp*(vy(i)+vy(nv(i1,j1)))
    266        x22=xc(i1)
    267        y22=yc(i1)
    268        x33=0.5_sp*(vx(i)+vx(nv(i1,j2)))
    269        y33=0.5_sp*(vy(i)+vy(nv(i1,j2)))
    270 
    271 #      if defined (SPHERICAL)
    272        ty  =0.5_sp*(y11+y33)
    273        txpi=(x33-x11)*tpi*cos(deg2rad*ty)
    274        typi=(y11-y33)*tpi
    275        pupx(i)=pupx(i)+u(i1,k)*typi 
    276        pupy(i)=pupy(i)+u(i1,k)*txpi
    277        pvpx(i)=pvpx(i)+v(i1,k)*typi
    278        pvpy(i)=pvpy(i)+v(i1,k)*txpi
    279 #      else
    280        pupx(i)=pupx(i)+u(i1,k)*(y11-y33)
    281        pupy(i)=pupy(i)+u(i1,k)*(x33-x11)
    282        pvpx(i)=pvpx(i)+v(i1,k)*(y11-y33)
    283        pvpy(i)=pvpy(i)+v(i1,k)*(x33-x11)
    284 #      endif
    285 
    286        if(isonb(i) /= 0) then
    287 #        if defined (SPHERICAL)
    288          ty=0.5_sp*(vy(i)+y11)
    289          txpi=(x11-vx(i))*tpi*cos(deg2rad*ty)
    290          typi=(vy(i)-y11)*tpi
    291          pupx(i)=pupx(i)+u(i1,k)*typi
    292          pupy(i)=pupy(i)+u(i1,k)*txpi
    293          pvpx(i)=pvpx(i)+v(i1,k)*typi
    294          pvpy(i)=pvpy(i)+v(i1,k)*txpi
    295 #        else
    296          pupx(i)=pupx(i)+u(i1,k)*(vy(i)-y11)
    297          pupy(i)=pupy(i)+u(i1,k)*(x11-vx(i))
    298          pvpx(i)=pvpx(i)+v(i1,k)*(vy(i)-y11)
    299          pvpy(i)=pvpy(i)+v(i1,k)*(x11-vx(i))
    300 #        endif
    301        end if
    302 
    303        do j=2,ntve(i)-1
    304          i1=nbve(i,j)
    305          jtmp=nbvt(i,j)
    306          j1=jtmp+1-(jtmp+1)/4*3
    307          j2=jtmp+2-(jtmp+2)/4*3
    308          x11=0.5_sp*(vx(i)+vx(nv(i1,j1)))
    309          y11=0.5_sp*(vy(i)+vy(nv(i1,j1)))
    310          x22=xc(i1)
    311          y22=yc(i1)
    312          x33=0.5_sp*(vx(i)+vx(nv(i1,j2)))
    313          y33=0.5_sp*(vy(i)+vy(nv(i1,j2)))
    314 
    315 #        if defined (SPHERICAL)
    316          ty=0.5_sp*(y11+y33)
    317          txpi=(x33-x11)*tpi*COS(deg2rad*TY)
    318          typi=(y11-y33)*tpi
    319          pupx(i)=pupx(i)+u(i1,k)*typi
    320          pupy(i)=pupy(i)+u(i1,k)*txpi
    321          pvpx(i)=pvpx(i)+v(i1,k)*typi
    322          pvpy(i)=pvpy(i)+v(i1,k)*txpi
    323 #        else
    324          pupx(i)=pupx(i)+u(i1,k)*(y11-y33)
    325          pupy(i)=pupy(i)+u(i1,k)*(x33-x11)
    326          pvpx(i)=pvpx(i)+v(i1,k)*(y11-y33)
    327          pvpy(i)=pvpy(i)+v(i1,k)*(x33-x11)
    328 #        endif
    329        end do
    330        j=ntve(i)
    331        i1=nbve(i,j)
    332        jtmp=nbvt(i,j)
    333        j1=jtmp+1-(jtmp+1)/4*3
    334        j2=jtmp+2-(jtmp+2)/4*3
    335        x11=0.5_sp*(vx(i)+vx(nv(i1,j1)))
    336        y11=0.5_sp*(vy(i)+vy(nv(i1,j1)))
    337        x22=xc(i1)
    338        y22=yc(i1)
    339        x33=0.5_sp*(vx(i)+vx(nv(i1,j2)))
    340        y33=0.5_sp*(vy(i)+vy(nv(i1,j2)))
    341 
    342 #      if defined (SPHERICAL)
    343        ty=0.5*(Y11+Y33)
    344        txpi=(x33-x11)*tpi*cos(deg2rad*TY)
    345        typi=(y11-y33)*tpi
    346        pupx(i)=pupx(i)+u(i1,k)*typi
    347        pupy(i)=pupy(i)+u(i1,k)*txpi
    348        pvpx(i)=pvpx(i)+v(i1,k)*typi
    349        pvpy(i)=pvpy(i)+v(i1,k)*txpi
    350 #      else
    351        pupx(i)=pupx(i)+u(i1,k)*(y11-y33)
    352        pupy(i)=pupy(i)+u(i1,k)*(x33-x11)
    353        pvpx(i)=pvpx(i)+v(i1,k)*(y11-y33)
    354        pvpy(i)=pvpy(i)+v(i1,k)*(x33-x11)
    355 #      endif
    356 
    357        if(isonb(i) /= 0) then
    358 #      if defined (SPHERICAL)
    359          ty=0.5*(Y11+VY(I))
    360          txpi=(VX(I)-X11)*tpi*COS(deg2rad*ty)
    361          typi=(Y11-VY(I))*tpi
    362          pupx(i)=pupx(i)+u(i1,k)*typi
    363          pupy(i)=pupy(i)+u(i1,k)*txpi
    364          pvpx(i)=pvpx(i)+v(i1,k)*typi
    365          pvpy(i)=pvpy(i)+v(i1,k)*txpi
    366 #        else
    367          pupx(i)=pupx(i)+u(i1,k)*(y11-vy(i))
    368          pupy(i)=pupy(i)+u(i1,k)*(vx(i)-x11)
    369          pvpx(i)=pvpx(i)+v(i1,k)*(y11-vy(i))
    370          pvpy(i)=pvpy(i)+v(i1,k)*(vx(i)-x11)
    371 #        endif
    372        end if
    373        pupx(i)=pupx(i)/art1(i)
    374        pupy(i)=pupy(i)/art1(i)
    375        pvpx(i)=pvpx(i)/art1(i)
    376        pvpy(i)=pvpy(i)/art1(i)
    377        tmp1=pupx(i)**2+pvpy(i)**2
    378        tmp2=0.5_sp*(pupy(i)+pvpx(i))**2
    379        viscoff(i)=sqrt(tmp1+tmp2)*art1(i)
    380      end do
    381 !     if(k == kbm1) then
    382 !       ah_bottom(1:m) = horcon*(fact*viscoff(1:m) + fm1)
    383 !     end if
    384 
    385 
    386      do i=1,ncv_i
    387        ia=niec(i,1)
    388        ib=niec(i,2)
    389        xi=0.5_sp*(xije(i,1)+xije(i,2))
    390        yi=0.5_sp*(yije(i,1)+yije(i,2))
    391 #      if defined (SPHERICAL)
    392        ty=0.5_sp*(yi+vy(ia))
    393        dxa=(xi-vx(ia))*tpi*cos(deg2rad*ty)
    394        dya=(yi-vy(ia))*tpi
    395        ty=0.5*(YI+VY(IB))
    396        DXB=(XI-VX(IB))*tpi*COS(deg2rad*ty)
    397        DYB=(YI-VY(IB))*tpi
    398 #      else
    399        dxa=xi-vx(ia)
    400        dya=yi-vy(ia)
    401        dxb=xi-vx(ib)
    402        dyb=yi-vy(ib)
    403 #      endif
    404        fij1=f(ia,k)+dxa*pfpx(ia)+dya*pfpy(ia)
    405        fij2=f(ib,k)+dxb*pfpx(ib)+dyb*pfpy(ib)
    406        un=uvn(i,k)
    407 
    408 !       viscof=horcon*(fact*(viscoff(ia)+viscoff(ib))*0.5_sp + fm1)
    409         VISCOF=(FACT*0.5_SP*(VISCOFF(IA)*NN_HVC(IA)+VISCOFF(IB)*NN_HVC(IB)) + FM1*0.5_SP*(NN_HVC(IA)+NN_HVC(IB)))
    410 
    411        txx=0.5_sp*(pfpxd(ia)+pfpxd(ib))*viscof
    412        tyy=0.5_sp*(pfpyd(ia)+pfpyd(ib))*viscof
    413 
    414        fxx=-dtij(i)*txx*dltye(i)
    415        fyy= dtij(i)*tyy*dltxe(i)
    416 
    417 # if defined (PLBC)
    418        fyy=0.0_SP
    419 # endif
    420 
    421        exflux=-un*dtij(i)* &
    422           ((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp+fxx+fyy
    423 
    424        xflux(ia,k)=xflux(ia,k)+exflux
    425        xflux(ib,k)=xflux(ib,k)-exflux
    426 
    427        xflux_adv(ia,k)=xflux_adv(ia,k)+(exflux-fxx-fyy)
    428        xflux_adv(ib,k)=xflux_adv(ib,k)-(exflux-fxx-fyy)
    429 
    430 #      if defined (THIN_DAM)
    431        IF(K<=KDAM(IA).AND.IS_DAM(IA)==1)THEN
    432          IF(N_DAM_MATCH(IA,1)==1)THEN
    433            XFLUX(N_DAM_MATCH(IA,2),K) = XFLUX(N_DAM_MATCH(IA,2),K) + EXFLUX
    434            XFLUX_ADV(N_DAM_MATCH(IA,2),K) = XFLUX_ADV(N_DAM_MATCH(IA,2),K) +(EXFLUX-FXX-FYY)
    435          END IF
    436          IF(N_DAM_MATCH(IA,1)==2)THEN
    437            XFLUX(N_DAM_MATCH(IA,2),K) = XFLUX(N_DAM_MATCH(IA,2),K) + EXFLUX
    438            XFLUX(N_DAM_MATCH(IA,3),K) = XFLUX(N_DAM_MATCH(IA,3),K) + EXFLUX
    439            XFLUX_ADV(N_DAM_MATCH(IA,2),K) = XFLUX_ADV(N_DAM_MATCH(IA,2),K) +(EXFLUX-FXX-FYY)
    440            XFLUX_ADV(N_DAM_MATCH(IA,3),K) = XFLUX_ADV(N_DAM_MATCH(IA,3),K) +(EXFLUX-FXX-FYY)
    441          END IF
    442          IF(N_DAM_MATCH(IA,1)==3)THEN
    443            XFLUX(N_DAM_MATCH(IA,2),K) = XFLUX(N_DAM_MATCH(IA,2),K) + EXFLUX
    444            XFLUX(N_DAM_MATCH(IA,3),K) = XFLUX(N_DAM_MATCH(IA,3),K) + EXFLUX
    445            XFLUX(N_DAM_MATCH(IA,4),K) = XFLUX(N_DAM_MATCH(IA,4),K) + EXFLUX
    446            XFLUX_ADV(N_DAM_MATCH(IA,2),K) = XFLUX_ADV(N_DAM_MATCH(IA,2),K) +(EXFLUX-FXX-FYY)
    447            XFLUX_ADV(N_DAM_MATCH(IA,3),K) = XFLUX_ADV(N_DAM_MATCH(IA,3),K) +(EXFLUX-FXX-FYY)
    448            XFLUX_ADV(N_DAM_MATCH(IA,4),K) = XFLUX_ADV(N_DAM_MATCH(IA,4),K) +(EXFLUX-FXX-FYY)
    449          END IF
    450        END IF
    451        IF(K<=KDAM(IB).AND.IS_DAM(IB)==1)THEN
    452          IF(N_DAM_MATCH(IB,1)==1)THEN
    453            XFLUX(N_DAM_MATCH(IB,2),K) = XFLUX(N_DAM_MATCH(IB,2),K) - EXFLUX
    454            XFLUX_ADV(N_DAM_MATCH(IB,2),K) = XFLUX_ADV(N_DAM_MATCH(IB,2),K) - (EXFLUX-FXX-FYY)
    455          END IF
    456          IF(N_DAM_MATCH(IB,1)==2)THEN
    457            XFLUX(N_DAM_MATCH(IB,2),K) = XFLUX(N_DAM_MATCH(IB,2),K) - EXFLUX
    458            XFLUX(N_DAM_MATCH(IB,3),K) = XFLUX(N_DAM_MATCH(IB,3),K) - EXFLUX
    459            XFLUX_ADV(N_DAM_MATCH(IB,2),K) = XFLUX_ADV(N_DAM_MATCH(IB,2),K) - (EXFLUX-FXX-FYY)
    460            XFLUX_ADV(N_DAM_MATCH(IB,3),K) = XFLUX_ADV(N_DAM_MATCH(IB,3),K) - (EXFLUX-FXX-FYY)
    461          END IF
    462          IF(N_DAM_MATCH(IB,1)==3)THEN
    463            XFLUX(N_DAM_MATCH(IB,2),K) = XFLUX(N_DAM_MATCH(IB,2),K) - EXFLUX
    464            XFLUX(N_DAM_MATCH(IB,3),K) = XFLUX(N_DAM_MATCH(IB,3),K) - EXFLUX
    465            XFLUX(N_DAM_MATCH(IB,4),K) = XFLUX(N_DAM_MATCH(IB,4),K) - EXFLUX
    466            XFLUX_ADV(N_DAM_MATCH(IB,2),K) = XFLUX_ADV(N_DAM_MATCH(IB,2),K) - (EXFLUX-FXX-FYY)
    467            XFLUX_ADV(N_DAM_MATCH(IB,3),K) = XFLUX_ADV(N_DAM_MATCH(IB,3),K) - (EXFLUX-FXX-FYY)
    468            XFLUX_ADV(N_DAM_MATCH(IB,4),K) = XFLUX_ADV(N_DAM_MATCH(IB,4),K) - (EXFLUX-FXX-FYY)
    469          END IF
    470        END IF
    471 #      endif
    472      end do
    473   end do !!sigma loop
    474 
    475 !---------------------------------------------------------------------------------
    476 ! Accumulate Fluxes at Boundary Nodes
    477 !---------------------------------------------------------------------------------
    478  
    479 # if defined (MULTIPROCESSOR)
    480   if(par)call node_match(0,nbn,bn_mlt,bn_loc,bnc,mt,kb,myid,nprocs,xflux,xflux_adv)
    481 # endif
    482 
    483 !---------------------------------------------------------------------------------
    484 ! Store Advective Fluxes at the Boundary
    485 !---------------------------------------------------------------------------------
    486   do k=1,kbm1
    487      if(iobcn > 0) then
    488        do i=1,iobcn
    489          i1=i_obc_n(i)
    490          fflux_obc(i,k)=xflux_adv(i1,k)
    491        end do
    492      end if
    493   end do
    494 
    495 !---------------------------------------------------------------------------------
    496 ! Calculate Vertical Advection Terms 
    497 !---------------------------------------------------------------------------------
    498 
    499    do i=1,m 
    500 #    if defined (WET_DRY)
    501      if(iswetn(i)*iswetnt(i) == 1) then
    502 #    endif
    503 #    if defined (THIN_DAM)
    504      if(IS_DAM(I)==1)then
    505        wvel(1:kb)=0.0_sp
    506        call calc_vflux(kbm1,f(i,1:kbm1),wvel(1:kb),vflux)
    507      else
    508        call calc_vflux(kbm1,f(i,1:kbm1),wts(i,1:kb),vflux)
    509      end if
    510 #    else
    511      call calc_vflux(kbm1,f(i,1:kbm1),wts(i,1:kb),vflux)
    512 #    endif
    513 
    514      do k=1,kbm1
    515        if(isonb(i) == 2) then
    516          xflux(i,k)= (vflux(k)-vflux(k+1))*art1(i)/dz(i,k)
    517        else
    518          xflux(i,k)=xflux(i,k)+ (vflux(k)-vflux(k+1))*art1(i)/dz(i,k)
    519        end if
    520 #    if defined (THIN_DAM)
    521        IF(IS_DAM(I)==1.AND.K<=KDAM(I))THEN
    522          tmpflx = (vflux(k)-vflux(k+1))*art1(i)/dz(i,k)
    523          IF(N_DAM_MATCH(I,1)==1)THEN
    524             XFLUX(N_DAM_MATCH(I,2),K) = XFLUX(N_DAM_MATCH(I,2),K)+tmpflx
    525          END IF
    526          IF(N_DAM_MATCH(I,1)==2)THEN
    527             XFLUX(N_DAM_MATCH(I,2),K) = XFLUX(N_DAM_MATCH(I,2),K)+tmpflx
    528             XFLUX(N_DAM_MATCH(I,3),K) = XFLUX(N_DAM_MATCH(I,3),K)+tmpflx
    529          END IF
    530          IF(N_DAM_MATCH(I,1)==3)THEN
    531             XFLUX(N_DAM_MATCH(I,2),K) = XFLUX(N_DAM_MATCH(I,2),K)+tmpflx
    532             XFLUX(N_DAM_MATCH(I,3),K) = XFLUX(N_DAM_MATCH(I,3),K)+tmpflx
    533             XFLUX(N_DAM_MATCH(I,4),K) = XFLUX(N_DAM_MATCH(I,4),K)+tmpflx
    534          END IF                
    535        END IF
    536 #    endif
    537      end do
    538 #    if defined (WET_DRY)
    539      end if
    540 #    endif
    541    end do
    542 
    543 !-------------------------------------------------------
    544 !Point Source                                      
    545 !-------------------------------------------------------
    546   if(source)then  !!user specified
    547 
    548   if(RIVER_TS_SETTING == 'calculated') then
    549     if(RIVER_INFLOW_LOCATION == 'node') then
    550         do j=1,numqbc
    551           jj=inodeq(j)
    552           fpoint=fdis(j)
    553           do k=1,kbm1
    554             xflux(jj,k)=xflux(jj,k) - qdis(j)*vqdist(j,k)*fpoint !/dz(jj,k)
    555           end do
    556         end do
    557     else if(RIVER_INFLOW_LOCATION == 'edge') then
    558       write(*,*)'scalar advection not setup for "edge" point source'
    559       stop
    560     end if
    561   end if
    562 
    563   else
    564 
    565   if(RIVER_TS_SETTING == 'calculated') then
    566     if(RIVER_INFLOW_LOCATION == 'node') then
    567         do j=1,numqbc
    568           jj=inodeq(j)
    569           do k=1,kbm1
    570             fpoint = f(jj,k)
    571             xflux(jj,k)=xflux(jj,k) - qdis(j)*vqdist(j,k)*fpoint !/dz(jj,k)
    572           end do
    573         end do
    574     else if(RIVER_INFLOW_LOCATION == 'edge') then
    575       write(*,*)'scalar advection not setup for "edge" point source'
    576       stop
    577     end if
    578   end if
    579 
    580   endif
    581 !------------------------------------------------------------------------
    582 !Update Scalar Quantity
    583 !------------------------------------------------------------------------
    584 
    585   do i=1,m
    586 #   if defined (WET_DRY)
    587     if(iswetn(i)*iswetnt(i) == 1 )then
    588 #   endif
    589     do k=1,kbm1
    590 #   if !defined (THIN_DAM)
    591       fn(i,k)=(f(i,k)-xflux(i,k)/art1(i)*(deltat/dt(i)))*(dt(i)/dtfa(i))
    592 #   else
    593       IF(IS_DAM(I)==1.AND.K<=KDAM(I))THEN
    594         fn(i,k)=(f(i,k)-xflux(i,k)/(ART1(I)&
    595         &+SUM(ART1(N_DAM_MATCH(I,2:1+N_DAM_MATCH(I,1)))))*(deltat/dt(i)))*(dt(i)/dtfa(i))
    596       ELSE
    597         fn(i,k)=(f(i,k)-xflux(i,k)/art1(i)*(deltat/dt(i)))*(dt(i)/dtfa(i))
    598       END IF
    599 #   endif
    600     end do
    601 #   if defined (WET_DRY)
    602     else
    603     do k=1,kbm1
    604       fn(i,k)=f(i,k)
    605     end do
    606     end if
    607 #   endif
    608   end do
    609 
    610   return
    611   End Subroutine Adv_Scal
    612 !==============================================================================|
    613 
    614 !==============================================================================|
    615 ! Vertical Diffusion of Scalar                                                 |
    616 !==============================================================================|
    617   Subroutine Vdif_Scal(f,deltat)
    618 
    619   use mTridiagonal
    620   use all_vars 
    621 # if defined (THIN_DAM)
    622   use mod_dam,only : NODE_DAM1_N,NODE_DAM2_N,NODE_DAM3_N, &
    623                    &I_NODE_DAM1_N,I_NODE_DAM2_N,I_NODE_DAM3_N, &
    624                    &kdam
    625 # endif
    626 
    627   Implicit None 
    628   Real(sp), intent(inout) :: f(0:mt,kb)
    629   Real(sp), intent(in   ) :: deltat
    630   !--local--------------------
    631   integer  :: i,k,ll
    632   real(sp) :: dsqrd,dfdz,visb
    633   real(sp) :: fsol(0:kb)
    634 
    635 # if defined (THIN_DAM)
    636   real(sp) :: ftmp,stmp
    637 # endif
    638 
    639   call init_tridiagonal(kb)
    640 
    641   Do i=1,m
    642      dsqrd = d(i)*d(i)
    643 
    644     !----------------------------------------------------------------
    645     !  Set up Diagonals of Matrix (lower=au,diag=bu,upper=cu)
    646     !----------------------------------------------------------------
    647     
    648 
    649     !Surface
    650     au(1) = 0.0
    651     cu(1)=      - deltat*(kh(i,2)+umol)/(dzz(i,1)*dz(i,1)*dsqrd)
    652     bu(1)=  1.0 - cu(1) 
    653 
    654     !Interior
    655     do k=2,kbm1-1
    656       au(k) =     - deltat*(kh(i,k  )+umol)/(dzz(i,k-1)*dz(i,k)*dsqrd)
    657       cu(k) =     - deltat*(kh(i,k+1)+umol)/(dzz(i,k  )*dz(i,k)*dsqrd)
    658       bu(k) = 1.0 - cu(k) - au(k) 
    659     end do
    660 
    661     !Bottom
    662      au(kbm1) =     - deltat*(kh(i,kbm1)+umol)/(dzz(i,kbm1-1)*dz(i,kbm1)*dsqrd)
    663      cu(kbm1) = 0.0
    664      bu(kbm1) = 1.0 - au(kbm1) 
    665 
    666     !----------------------------------------------------------------
    667     ! Set up RHS forcing vector and boundary conditions 
    668     !----------------------------------------------------------------
    669     do k=1,kbm1
    670       du(k) = f(i,k)
    671     end do
    672 
    673     !Free Surface: No flux
    674 
    675     !Bottom: No flux
    676       
    677 
    678     !----------------------------------------------------------------
    679     ! Solve 
    680     !----------------------------------------------------------------
    681 
    682      call tridiagonal(kb,1,kbm1,fsol)
    683     
    684      !Transfer
    685      f(i,1:kbm1) = fsol(1:kbm1)
    686 
    687   End Do
    688 
    689 #  if defined (THIN_DAM)
    690    DO K=1,KBM1
    691      DO I=1,NODE_DAM1_N
    692        IF(K<=KDAM(I_NODE_DAM1_N(I,1)).AND.K<=KDAM(I_NODE_DAM1_N(I,2)) )THEN
    693           FTMP=F(I_NODE_DAM1_N(I,1),K)*ART1(I_NODE_DAM1_N(I,1)) &
    694             & +F(I_NODE_DAM1_N(I,2),K)*ART1(I_NODE_DAM1_N(I,2))
    695           STMP=ART1(I_NODE_DAM1_N(I,1))+ART1(I_NODE_DAM1_N(I,2))
    696           F(I_NODE_DAM1_N(I,1),K)=FTMP/STMP
    697           F(I_NODE_DAM1_N(I,2),K)=FTMP/STMP
    698        END IF
    699      END DO
    700 
    701      DO I=1,NODE_DAM2_N
    702        IF(K<=KDAM(I_NODE_DAM2_N(I,1)).AND.K<=KDAM(I_NODE_DAM2_N(I,2)) &
    703           & .AND.K<=KDAM(I_NODE_DAM2_N(I,2)) )THEN
    704           FTMP= F(I_NODE_DAM2_N(I,1),K)*ART1(I_NODE_DAM2_N(I,1)) &
    705            &   +F(I_NODE_DAM2_N(I,2),K)*ART1(I_NODE_DAM2_N(I,2)) &
    706            &   +F(I_NODE_DAM2_N(I,3),K)*ART1(I_NODE_DAM2_N(I,3)) 
    707           STMP=ART1(I_NODE_DAM2_N(I,1))+ART1(I_NODE_DAM2_N(I,2)) &
    708            &   +ART1(I_NODE_DAM2_N(I,3))
    709           F(I_NODE_DAM2_N(I,1),K)=FTMP/STMP
    710           F(I_NODE_DAM2_N(I,2),K)=FTMP/STMP
    711           F(I_NODE_DAM2_N(I,3),K)=FTMP/STMP
    712        END IF
    713      END DO
    714 
    715      DO I=1,NODE_DAM3_N
    716        IF(K<=KDAM(I_NODE_DAM3_N(I,1)).AND.K<=KDAM(I_NODE_DAM3_N(I,2)) &
    717    & .AND.K<=KDAM(I_NODE_DAM3_N(I,3)).AND.K<=KDAM(I_NODE_DAM3_N(I,4)) )THEN
    718           FTMP =F(I_NODE_DAM3_N(I,1),K)*ART1(I_NODE_DAM3_N(I,1)) &
    719            &   +F(I_NODE_DAM3_N(I,2),K)*ART1(I_NODE_DAM3_N(I,2)) &
    720            &   +F(I_NODE_DAM3_N(I,3),K)*ART1(I_NODE_DAM3_N(I,3)) &
    721            &   +F(I_NODE_DAM3_N(I,4),K)*ART1(I_NODE_DAM3_N(I,4))  
    722           STMP =ART1(I_NODE_DAM3_N(I,1)) + ART1(I_NODE_DAM3_N(I,2)) &
    723            &  + ART1(I_NODE_DAM3_N(I,3)) + ART1(I_NODE_DAM3_N(I,4))
    724           F(I_NODE_DAM3_N(I,1),K)=FTMP/STMP
    725           F(I_NODE_DAM3_N(I,2),K)=FTMP/STMP
    726           F(I_NODE_DAM3_N(I,3),K)=FTMP/STMP
    727           F(I_NODE_DAM3_N(I,4),K)=FTMP/STMP
    728        END IF
    729      END DO
    730    END DO
    731 #  endif
    732 
    733 
    734   End Subroutine Vdif_Scal
    735 
    736 
    737 !==============================================================================|
    738 ! Set Point Source Conditions for Scalar Function                              |
    739 !==============================================================================|
    740 
    741   Subroutine Bcond_Scal_PTsource(f,fn,fdis)
    742 
    743 !------------------------------------------------------------------------------|
    744   use all_vars
    745   use bcs
    746   use mod_obcs
    747   implicit none
    748   real(sp), intent(in ), dimension(0:mt,kb)      :: f 
    749   real(sp), intent(out), dimension(0:mt,kb)      :: fn
    750   real(sp), intent(in ), dimension(numqbc )      :: fdis
    751 !--local-------------------------------------------
    752   integer  :: i,j,k,j1,j11,j22
    753 !------------------------------------------------------------------------------|
    754 
    755 
    756 !--------------------------------------------
    757 ! Set Source Terms
    758 !--------------------------------------------
    759   if(RIVER_TS_SETTING == 'specified') then
    760     if(numqbc > 0) then
    761       if(RIVER_INFLOW_LOCATION == 'node') then
    762         do i=1,numqbc
    763           j11=inodeq(i)
    764           do k=1,kbm1
    765             fn(j11,k)=fdis(i)
    766           end do
    767         end do
    768       else if(RIVER_INFLOW_LOCATION == 'edge') then
    769         do i=1,numqbc
    770           j11=n_icellq(i,1)
    771           j22=n_icellq(i,2)
    772           do k=1,kbm1
    773             fn(j11,k)=fdis(i)
    774             fn(j22,k)=fdis(i)
    775           end do
    776         end do
    777       end if
    778     end if
    779   end if
    780 
    781   return
    782   End Subroutine Bcond_Scal_PTSource 
    783 !==============================================================================|
    784 !==============================================================================|
    785 
    786 !==============================================================================|
    787 !   Set Boundary Conditions for Scalar Function on Open Boundary               |
    788 !==============================================================================|
    789 
    790   Subroutine Bcond_Scal_OBC(f,fn,fflux_obc,f_obc,deltat,alpha_nudge)
    791 
    792 !------------------------------------------------------------------------------|
    793   use all_vars
    794   use bcs
    795   use mod_obcs
    796   implicit none
    797   real(sp), intent(in   ), dimension(0:mt,kb)      :: f 
    798   real(sp), intent(inout), dimension(0:mt,kb)      :: fn
    799   real(sp), intent(in   ), dimension(iobcn+1,kbm1) :: fflux_obc 
    800   real(sp), intent(in   ), dimension(iobcn       ) :: f_obc 
    801   real(sp), intent(in   )                          :: deltat
    802   real(sp), intent(in   )                          :: alpha_nudge 
    803 !--local-------------------------------------------
    804   real(sp) :: f2d,f2d_next,f2d_obc,xflux2d,tmp
    805   integer  :: i,j,k,j1,j11,j22
    806 !------------------------------------------------------------------------------|
    807        
    808 !--------------------------------------------
    809 ! Set Scalar Value on Open Boundary
    810 !--------------------------------------------
    811   if(iobcn > 0) then
    812     do i=1,iobcn
    813       j=i_obc_n(i)
    814       j1=next_obc(i)
    815       f2d=0.0_sp
    816       f2d_next=0.0_sp
    817       xflux2d=0.0_sp
    818       do k=1,kbm1
    819         f2d=f2d+f(j,k)*dz(j,k)
    820         f2d_next=f2d_next+fn(j1,k)*dz(j1,k)
    821         xflux2d=xflux2d+fflux_obc(i,k)*dz(j,k)
    822       end do
    823   
    824       if(uard_obcn(i) > 0.0_sp) then
    825         tmp=xflux2d+f2d*uard_obcn(i)
    826         f2d_obc=(f2d*dt(j)-tmp*deltat/art1(j))/d(j)
    827         do k=1,kbm1
    828           fn(j,k)=fn(j1,k) !f2d_obc+(fn(j1,k)-f2d_next)
    829         end do
    830       else
    831         do k=1,kbm1
    832           fn(j,k) = f(j,k)-alpha_nudge*(f(j,k)-f_obc(i))
    833         end do
    834       end if
    835     end do
    836   endif
    837 
    838   return
    839   End Subroutine Bcond_Scal_OBC 
    840 !==============================================================================|
    841 !==============================================================================|
    842 
    843   Subroutine fct_sed(f,fn)
    844   !==============================================================================|
    845   USE ALL_VARS
    846   USE MOD_UTILS
    847   USE BCS
    848   USE MOD_OBCS
    849   IMPLICIT NONE
    850   real(sp), intent(inout), dimension(0:mt,kb)      :: fn
    851   real(sp), intent(in), dimension(0:mt,kb)      :: f
    852   REAL(SP):: SMAX,SMIN
    853   INTEGER :: I,J,K,K1
    854   !==============================================================================|
    855   IF(DBG_SET(DBG_SBR)) WRITE(IPT,*)"Start: fct_sed"
    856 
    857   nodes: DO I=1,M
    858 
    859      ! SKIP OPEN BOUNDARY NODES
    860      IF(IOBCN > 0)THEN
    861         DO J=1,IOBCN
    862            IF(I == I_OBC_N(J)) CYCLE nodes
    863         END DO
    864      END IF
    865 
    866      ! SKIP RIVER INFLOW POINTS
    867      IF(NUMQBC > 0)THEN
    868         DO J=1,NUMQBC
    869            IF(RIVER_INFLOW_LOCATION == 'node')THEN
    870               IF(I == INODEQ(J)) CYCLE nodes
    871            END IF
    872            IF(RIVER_INFLOW_LOCATION == 'edge')THEN
    873               IF(I == N_ICELLQ(J,1) .OR. I == N_ICELLQ(J,2)) CYCLE nodes
    874            END IF
    875         END DO
    876      END IF
    877 
    878      ! SKIP GROUND WATER INFLOW POINTS
    879      IF(BFWDIS(I) .GT. 0.0_SP .and. GROUNDWATER_SALT_ON) CYCLE nodes
    880 
    881      K1 = 1
    882      IF(PRECIPITATION_ON) K1 = 2
    883 !     DO K=1,KBM1
    884      DO K=K1,KBM1
    885         SMAX = MAXVAL(f(NBSN(I,1:NTSN(I)),K))
    886         SMIN = MINVAL(f(NBSN(I,1:NTSN(I)),K))
    887 
    888         IF(K == 1)THEN
    889            SMAX = MAX(SMAX,(f(I,K)*DZ(I,K+1)+f(I,K+1)*DZ(I,K))/  &
    890                 (DZ(I,K)+DZ(I,K+1)))
    891            SMIN = MIN(SMIN,(f(I,K)*DZ(I,K+1)+f(I,K+1)*DZ(I,K))/  &
    892                 (DZ(I,K)+DZ(I,K+1)))
    893         ELSE IF(K == KBM1)THEN
    894            SMAX = MAX(SMAX,(f(I,K)*DZ(I,K-1)+f(I,K-1)*DZ(I,K))/  &
    895                 (DZ(I,K)+DZ(I,K-1)))
    896            SMIN = MIN(SMIN,(f(I,K)*DZ(I,K-1)+f(I,K-1)*DZ(I,K))/  &
    897                 (DZ(I,K)+DZ(I,K-1)))
    898         ELSE
    899            SMAX = MAX(SMAX,(f(I,K)*DZ(I,K-1)+f(I,K-1)*DZ(I,K))/  &
    900                 (DZ(I,K)+DZ(I,K-1)),                             &
    901                 (f(I,K)*DZ(I,K+1)+f(I,K+1)*DZ(I,K))/           &
    902                 (DZ(I,K)+DZ(I,K+1)))
    903            SMIN = MIN(SMIN,(f(I,K)*DZ(I,K-1)+f(I,K-1)*DZ(I,K))/  &
    904                 (DZ(I,K)+DZ(I,K-1)),                             &
    905                 (f(I,K)*DZ(I,K+1)+f(I,K+1)*DZ(I,K))/           &
    906                 (DZ(I,K)+DZ(I,K+1)))
    907         END IF
    908 
    909         IF(SMIN-fn(I,K) > 0.0_SP)fn(I,K) = SMIN
    910         IF(fn(I,K)-SMAX > 0.0_SP)fn(I,K) = SMAX
    911 
    912      END DO
    913   END DO nodes
    914 
    915   WHERE(fn < 0.0_SP)fn=0.0_SP
    916   
    917   IF(DBG_SET(DBG_SBR)) WRITE(IPT,*)"End: fct_sed"
    918   End Subroutine fct_sed
    919 
    920 !==========================================================================
    921 ! Calculate Fluxes for Vertical Advection Equation                            
    922 ! n: number of cells
    923 ! c: scalar variable (1:n)
    924 ! w: velocity field at cell interfaces (1:n+1)
    925 ! note:  we dont use face normals to construct inflow/outflow
    926 !        thus we add dissipation term instead of subtracting because 
    927 !        positive velocity is up while computational coordinates increase
    928 !        down towards bottom.  
    929 !==========================================================================
    930   Subroutine Calc_VFlux(n,c,w,flux) 
    931   use mod_prec
    932   implicit none
    933   integer , intent(in ) :: n
    934   real(sp), intent(in ) ::  c(n)
    935   real(sp), intent(in ) ::  w(n+1) 
    936   real(sp), intent(out) ::  flux(n+1)
    937   real(sp) :: conv(n+1),diss(n+1)
    938   real(sp) :: cin(-1:n+2)
    939   real(sp) :: dis4
    940   integer  :: i
    941 
    942   !transfer to working array
    943   cin(1:n) = c(1:n)
    944 
    945   !surface bcs (no flux)
    946   cin(0)  =  -cin(1) 
    947   cin(-1) =  -cin(2)
    948   
    949   !bottom bcs (no flux)
    950   cin(n+1) = -cin(n) 
    951   cin(n+2) = -cin(n-1)
    952 
    953   !flux computation
    954   do i=1,n+1
    955     dis4    = .5*abs(w(i))
    956     conv(i) = w(i)*(cin(i)+cin(i-1))/2. 
    957     diss(i) = dis4*(cin(i)-cin(i-1)-lim(cin(i+1)-cin(i),cin(i-1)-cin(i-2))) 
    958     flux(i) = conv(i)+diss(i)
    959   end do
    960 
    961   End Subroutine Calc_VFlux
    962   
    963 !==========================================================================
    964 ! Calculate LED Limiter L(u,v)  
    965 !==========================================================================
    966   Function Lim(a,b)
    967   use mod_prec
    968   real(sp) lim,a,b
    969   real(sp) q,R
    970   real(sp) eps
    971   eps = epsilon(eps)
    972   
    973   q = 0. !1st order
    974   q = 1. !minmod
    975   q = 2. !van leer
    976 
    977   R = abs(   (a-b)/(abs(a)+abs(b)+eps) )**q
    978   lim = .5*(1-R)*(a+b)
    979 
    980   End Function Lim
    981 
    982 
    983 End Module Scalar
    View Code

    为了更快速的了解计算过程,自己设置了一个只有7个节点、6个单元的简单地形,如下图所示,然后通过 printf 的方法快速了解每个变量含义。

    有限体积法离散控制方程(理论)

    首先介绍 FVCOM 控制方程离散,虽然其也是按照有限体积方法思想将积分降维,但是只是在平面二维方向上使用有限体积法,垂向计算使用的是有限差分法。

    控制方程

    FVCOM对流扩散控制方程

     控制方程离散过程

    FVCOM对流扩散方程离散过程

    最终更新标量值Ci的程序为: fn(i,k)=(f(i,k)-xflux(i,k)/art1(i)*(deltat/dt(i)))*(dt(i)/dtfa(i))

    这里deltat表示时间步长,dt为上个时间步水深,dtfa为新计算水深,这里之所以需要除以水深是因为垂向梯度项计算需要。

    数值求解(结合程序分析)

    首先说一下 FVCOM 选取控制体的方法,如下图所示。例如节点1所在控制体,就是由下边4条红线和上边2条黑线所组成的6面体,而节点6则是由周围12条红线组成。

    FVCOM计算时候也不是按照控制体进行循环,而是按照控制边的个数循环,也就是说,像节点1和节点6这种相邻控制体,两条红色邻边各只计算一次,控制边的通量在节点1和6控制体内是大小相同、符号相反的。

    1. 水平对流项计算

    水平对流项 

    i1=ntrg(i) dtij(i)=dt1(i1) do k=1,kbm1 uvn(i,k) = v(i1,k)*dltxe(i) - u(i1,k)*dltye(i) end do exflux=-un*dtij(i)* ((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp+fxx+fyy

     这里前面一项 -un*dtij(i)*((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp 毫无疑问就是水平对流项,((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp 表示其采用的是迎风格式,控制边上 fij1 及 fij2 计算采用泰勒公式达到2阶精度,泰勒公式中的一阶偏导计算在后面统一介绍

    2. 水平扩散项计算

    FVCOM水平扩散项计算

           txx=0.5_sp*(pfpxd(ia)+pfpxd(ib))*viscof
           tyy=0.5_sp*(pfpyd(ia)+pfpyd(ib))*viscof
    
           fxx=-dtij(i)*txx*dltye(i)
           fyy= dtij(i)*tyy*dltxe(i)
    
           exflux=-un*dtij(i)* &
              ((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp+fxx+fyy
    

    其中 viscof 为水平扩散系数,一阶偏导采用控制边相邻两个节点平均值。 fxx 和 fyy 中还包含了水深dtij,后面会在计算流量时除去。

           exflux=-un*dtij(i)* &
              ((1.0_sp+sign(1.0_sp,un))*fij2+(1.0_sp-sign(1.0_sp,un))*fij1)*0.5_sp+fxx+fyy
    
           xflux(ia,k)=xflux(ia,k)+exflux
           xflux(ib,k)=xflux(ib,k)-exflux
    
    
           fn(i,k)=(f(i,k)-xflux(i,k)/art1(i)*(deltat/dt(i)))*(dt(i)/dtfa(i))
    

    3. 一阶偏导项计算

    一阶偏导计算也采用格林公式将面积分降维化为线积分计算,但是平面积分所采用的控制体和上面不同,这里采用和节点相邻的所有三角形单元计算,对于边界点来说,只取相邻的两个单元。

    对应的程序如下

             pfpx(i) = pfpx(i) +ff1*(vy(i1)-vy(i2))
             pfpy(i) = pfpy(i) +ff1*(vx(i2)-vx(i1))
             pfpxd(i)= pfpxd(i)+ffd*(vy(i1)-vy(i2))
             pfpyd(i)= pfpyd(i)+ffd*(vx(i2)-vx(i1))
    
            ……
    
    
             PFPX(I)=PFPX(I)/ART2(I)
             PFPY(I)=PFPY(I)/ART2(I)
             PFPXD(I)=PFPXD(I)/ART2(I)
             PFPYD(I)=PFPYD(I)/ART2(I)
    

     对于水平对流项中控制边上Ci的二阶精度计算,只需按照泰勒公式计算即可

           fij1=f(ia,k)+dxa*pfpx(ia)+dya*pfpy(ia)
           fij2=f(ib,k)+dxb*pfpx(ib)+dyb*pfpy(ib)
    

     

  • 相关阅读:
    使用proguard导出项目时 报错
    一个有关canvas的Bug
    一点小想法
    C#调用非托管代码(转)
    对3DES加密的运用的一个简单示例(转)
    使用X.509数字证书加密解密实务(二) 使用RSA证书加密敏感数据(转)
    dtree用法(转)
    使用X.509数字证书加密解密实务(一) 证书的获得和管理(转)
    Oracle SQL Loader的详细语法(转)
    使用X.509数字证书加密解密实务(三) 使用RSA证书结合对称加密技术加密长数据(转)
  • 原文地址:https://www.cnblogs.com/li12242/p/4003350.html
Copyright © 2020-2023  润新知