⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 psn_2d.c

📁 Finite Volume Poisson PDE Solver
💻 C
字号:
/*psn_lib/psn_2d.c*/
/***********************************************************************
    Finite Volume Poisson PDE Solver: C-Library & Matlab Toolbox
    Implements numerical solution of Poisson PDE
    in 2D  Cartesian and Cylindrical coordinates

    Copyright (C) 2004 Igor Kaufman
    Copyright (C) 2004 Lancaster University, UK

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


    Author's email: i.kaufman@lancaster.ac.uk

    APPLICATION    : 2D POISSON SOLVER
    VERSION        : 1.0
************************************************************************/
/*17.01.04*/

#include "psn.h"

#define set_boundval(alpha,beta,gamma,h,u) (gamma*h-beta*(u))/(alpha*h-beta)
#define b_bound(alpha,beta,gamma,h)        (alpha*h)/(alpha*h-beta)
#define w_bound(alpha,beta,gamma,h)        (gamma*h)/(alpha*h-beta);
#define over_relax(u_old, u_new, w)        (1-w)*(u_old)+w*(u_new)
#define W_OPT(Nx, Ny)                       2-6/(max(Nx,Ny))

void set_bounds(psn_2d_struct *psd,  double hx,  double hy )
{

    double alpha, beta, gamma, h, tmp;
    size_t col, row, i, row_b, col_b;
    size_t Nx=psd->Nx;
    size_t Ny=psd->Ny;
    PSN_MATRIX u=psd->u;
    PSN_MATRIX v=psd->v;

    h=hy/(psd->grid+1);
    for (col=1;col<=Nx;col++) {
       for (i=LOW;i<=UP;i++){
          if (i==UP || psd->crd==0) {
            alpha=psd->alphaY[i];
            beta=psd->betaY[i];
            switch (i) {
              case LOW:
                row=1;
                row_b=0;
                break;
              case UP:
                row=Ny;
                row_b=Ny+1;
                beta=-beta;
                break;
            }
            gamma=psn_matrix_getval(v, row_b,col);
            tmp=set_boundval(alpha,beta,gamma,h,psn_matrix_getval(u,row,col));
          } else {
            row_b=0;
            tmp=psn_matrix_getval(u,1,col);
          }
          psn_matrix_setval(u,row_b,col,tmp);
       }
    }

    h=hx/(psd->grid+1);
    for (row=1;row<=Ny;row++) {
       for (i=LOW;i<=UP;i++){
          alpha=psd->alphaX[i];
          beta=psd->betaX[i];
          switch (i) {
            case LOW:
              col=1;
              col_b=0;
              break;
            case UP:
              col=Nx;
              col_b=Nx+1;
              beta=-beta;
              break;
          }
          gamma=psn_matrix_getval(v,row,col_b);
          tmp=set_boundval(alpha,beta,gamma,h,psn_matrix_getval(u, row,col));
          psn_matrix_setval(u,row,col_b,tmp);
       }
    }
    tmp=0.5*(psn_matrix_getval(u,0,1)+psn_matrix_getval(u,1,0));
    psn_matrix_setval(u,0,0,tmp);
    tmp=0.5*(psn_matrix_getval(u,0,Nx)+psn_matrix_getval(u,1,Nx+1));
    psn_matrix_setval(u,0,Nx+1,tmp);
    tmp=0.5*(psn_matrix_getval(u,Ny+1,1)+psn_matrix_getval(u,Ny,0));
    psn_matrix_setval(u,Ny+1,0,tmp);
    tmp=0.5*(psn_matrix_getval(u,Ny,Nx+1)+psn_matrix_getval(u,Ny+1,Nx));
    psn_matrix_setval(u,Ny+1,Nx+1,tmp);

}

void set_diff2_coeff(psn_2d_struct *psd,
            double hx,
            double hy,
            size_t row,
            size_t col,

            double a[],
            double b[],
            double c[],
            double w[]
    )
{
   double alpha, beta, gamma, ee;
   PSN_MATRIX e=psd->e;
   PSN_MATRIX v=psd->v;
   size_t dim;


   //Set interior coefficients
   if  (e==NULL)  { //Vacuum
     a[0]= (col>1)? 1.:0;
     c[0]= (col<psd->Nx)? 1.:0;
     a[1]=(row>1)? 1.:0;
     c[1]=(row<psd->Ny)? 1.:0;
   } else {  //Matter
     ee=psn_matrix_getval(e, row,col);
     a[0]=(col>1)? 2.*psn_matrix_getval(e,row,col-1)/(psn_matrix_getval(e,row,col-1)+ee):0;
     c[0]=(col<psd->Nx)? 2.*psn_matrix_getval(e,row,col+1)/(psn_matrix_getval(e,row,col+1)+ee):0;
     a[1]=(row>1) ? 2.*psn_matrix_getval(e,row-1,col)/(psn_matrix_getval(e,row-1,col)+ee):0;
     c[1]=(row<psd->Ny)? 2.*psn_matrix_getval(e,row+1,col)/(psn_matrix_getval(e,row+1,col)+ee):0;
   }
   if (psd->crd==1)  {  //cylindrical coordinates
     double t=1.0/(2*row-1);
     a[1]*=(2.*(row-1))*t;
     c[1]*=(2.*row)*t;
   }
   for (dim=0;dim<2;dim++) {
     b[dim]=-a[dim]-c[dim];
     w[dim]=0;
   }


   if (col==1 || col==psd->Nx) { //Add boundary conditions for X=0 and X=Lx
     double h=hx/(psd->grid+1);
     if (col==1) {
       alpha=psd->alphaX[0];
       beta=psd->betaX[0];
       gamma=psn_matrix_getval(v,row,0);
     } else {
       alpha=psd->alphaX[1];
       beta=-psd->betaX[1];
       gamma=psn_matrix_getval(v,row,psd->Nx+1);
     }
     b[0]-=b_bound(alpha,beta,gamma,h);
     w[0]=w_bound(alpha,beta,gamma,h);
   }

   if (row==1 || row==psd->Ny) {  //Add boundary conditions for Y=0 and Y=Ly
     double h=hy/(psd->grid+1);
     if (row==1) {
       if (psd->crd==0) {   //Don't do for cylindrical where R==0
         alpha=psd->alphaY[0];
         beta=psd->betaY[0];
         gamma=psn_matrix_getval(v,0,col);
       }
     } else {
       alpha=psd->alphaY[1];
       beta=-psd->betaY[1];
       gamma=psn_matrix_getval(v,psd->Ny+1, col);
     }
     if (psd->crd==0 || row==psd->Ny) {
       b[1]-=b_bound(alpha,beta,gamma,h);
       w[1]=w_bound(alpha,beta,gamma,h);
     }
   }

}

int psn_2d_struct_init(psn_2d_struct *psd, const size_t Nx, const size_t Ny)
{
   psd->Nx=Nx;
   psd->Ny=Ny;
   psd->v=psn_matrix_create(Ny,Nx);
   psd->e=psn_matrix_create(Ny,Nx);
   psd->u=psn_matrix_create(Ny,Nx);
   return 0;
}

int psn_2d_struct_free(psn_2d_struct *psd)
{
   psn_matrix_free(psd->v,psd->Ny);
   psn_matrix_free(psd->e,psd->Ny);
   psn_matrix_free(psd->u,psd->Ny);
   psd->Nx=0;
   psd->Ny=0;
   return 0;
}

int psn_2d_solver_LSOR(psn_2d_struct *psd)
{

   int res;
   int stop;
   size_t step, row, col;
   size_t Nx=psd->Nx;
   size_t Ny=psd->Ny;
   PSN_MATRIX u=psd->u, v=psd->v, e=psd->e;

   size_t Nm=(Nx>Ny)?Nx:Ny;
   double hx, hy, W, h2[2];
   double err;

   PSN_VECTOR um=psn_vector_create(Nm+2);
   PSN_VECTOR wm=psn_vector_create(Nm+2);
   PSN_VECTOR a=psn_vector_create(Nm+2);
   PSN_VECTOR b=psn_vector_create(Nm+2);
   PSN_VECTOR c=psn_vector_create(Nm+2);

   //Set grid
   if (psd->grid) {
     hx=psd->Lx/(Nx);
     hy=psd->Ly/(Ny);
   } else {
     hx=psd->Lx/(Nx+1);
     hy=psd->Ly/(Ny+1);
   }
   h2[0]=1/(hx*hx);
   h2[1]=1/(hy*hy);


   W=psd->W;
   if (W==0) W=2.-6./max(Nx,Ny);

   //Main iteration cycle
   for (step=0, stop=0;step<psd->max_step && !stop;step++) {
     err=0;
     for (row=1;row<=Ny;row++) {
        psn_matrix_get_rowcol(Nx,row,smRow,u,um);  //save old row for U
        //Define coeff
        for (col=1;col<=Nx;col++) {
          double aa[2],bb[2], cc[2], ww[2];
          set_diff2_coeff(psd,hx,hy,row,col, aa,bb,cc,ww);
          a[col]=h2[0]*aa[0];
          b[col]=h2[0]*bb[0]+h2[1]*bb[1];
          c[col]=h2[0]*cc[0];
          wm[col]=psn_matrix_getval(v, row,col);
          if (e) wm[col]/=psn_matrix_getval(e, row,col);
          wm[col]-=h2[0]*ww[0];
          wm[col]-=h2[1]*(ww[1]+aa[1]*psn_matrix_getval(u, row-1,col)+cc[1]*psn_matrix_getval(u,row+1,col));
        }
        //and solve for current row
        psn_tridiag_solve_low(Nx,a,b,c,wm);
        //OverRelaxation
        if (W!=1)
          for (col=1;col<=Nx;col++)
             wm[col]=over_relax(um[col],wm[col],W);

        err=max(err,(norm_vector(Nx,wm,um)));
        psn_matrix_set_rowcol(Nx,row,smRow,u,wm);
     }

     if (psd->method==metADI)
     for (col=1;col<=Nx;col++) {
        psn_matrix_get_rowcol(Nx,col,smCol,u,um);  //save old row for U
        //Define coeff
        for (row=1;row<=Ny;row++) {
          double aa[2],bb[2], cc[2], ww[2];
          set_diff2_coeff(psd,hx,hy,row,col,aa,bb,cc,ww);
          a[row]=h2[1]*aa[1];
          b[row]=h2[1]*bb[1]+h2[0]*bb[0];
          c[row]=h2[1]*cc[1];
          wm[row]=psn_matrix_getval(v, row,col);
          if (e) wm[row]/=psn_matrix_getval(e, row,col);
          wm[row]-=h2[1]*ww[1];
          wm[row]-=h2[0]*(ww[0]+aa[0]*psn_matrix_getval(u, row,col-1)+cc[0]*psn_matrix_getval(u,row,col+1));
        }
        //and solve for current row
        psn_tridiag_solve_low(Ny,a,b,c,wm);
        //OverRelaxation
        if (W!=1)
          for (row=1;row<=Ny;row++)
             wm[row]=over_relax(um[row],wm[row],W);

        //err=max(err,(norm_vector(Ny,wm,um)));
        psn_matrix_set_rowcol(Ny,col,smCol,u,wm);
     }

     if (psd->step_err) psd->step_err[step]=err;
     stop=(err<psd->tol);
   }

   set_bounds(psd,hx,hy);
   psd->error=err;
   psd->num_step=step;
   res=(psd->error<psd->tol)? 0:3;
   psd->result=res;

   psn_vector_free(um);
   psn_vector_free(wm);
   psn_vector_free(a);
   psn_vector_free(b);
   psn_vector_free(c);

   return res;
}

int psn_2d_solver_PSOR(psn_2d_struct *psd)
{

   int res;
   int stop;
   size_t step, row, col;
   size_t Nx=psd->Nx;
   size_t Ny=psd->Ny;
   PSN_MATRIX u=psd->u, v=psd->v, e=psd->e;

   double hx, hy, W;
   double kappa, kappa2, hv;
   double err;

   //Set grid
   if (psd->grid) {
     hx=psd->Lx/(Nx);
     hy=psd->Ly/(Ny);
   } else {
     hx=psd->Lx/(Nx+1);
     hy=psd->Ly/(Ny+1);
   }
   kappa=hx/hy;
   kappa2=kappa*kappa;
   hv=hx*hx;


   W=psd->W;
   if (W==0) W=2.-6./max(Nx,Ny);

   //Main iteration cycle
   for (step=0, stop=0;step<psd->max_step && !stop;step++) {
      double aa[2], bb[2], cc[2], ww[2], rhs, b01;
      double r, uval, delta;

      err=0;
      for (row=1;row<=Ny;row++) {
        for (col=1;col<=Nx;col++) {
          set_diff2_coeff(psd,hx,hy,row,col,aa,bb,cc,ww);
          b01=bb[0]+bb[1]*kappa2;
          rhs=psn_matrix_getval(v,row,col)*hv;
          if (e) rhs/=psn_matrix_getval(e, row,col);
          uval=psn_matrix_getval(u,row,col);
          ww[0]+=(aa[0]*psn_matrix_getval(u,row,col-1)+cc[0]*psn_matrix_getval(u,row,col+1));
          ww[1]+=(aa[1]*psn_matrix_getval(u,row-1,col)+cc[1]*psn_matrix_getval(u,row+1,col));
          r=ww[0]+ww[1]*kappa2+b01*uval-rhs;
          delta=-W*r/b01;
          err=max(err, fabs(delta));
          psn_matrix_setval(u,row,col,(uval+delta));
        }
      }

      if (psd->step_err) psd->step_err[step]=err;
      stop=(err<psd->tol);
   }
   set_bounds(psd,hx,hy);
   psd->error=err;
   psd->num_step=step;
   res=(psd->error<psd->tol)? 0:3;
   psd->result=res;

   return res;
}

int psn_2d_solver(psn_2d_struct *psd)
{
   switch (psd->method) {
      case metLSOR:
      case metADI:
        return psn_2d_solver_LSOR(psd);
      case metPSOR:
        return psn_2d_solver_PSOR(psd);
      default:
        return psn_2d_solver_PSOR(psd);
   }
}












⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -