Subversion Repositories pvinversion.ecmwf

Rev

Blame | Last modification | View Log | Download | RSS feed

      PROGRAM hydrostatic

c     Calculate the geopotential and add it to the P file
c     Michael Sprenger / Spring 2006

      implicit none

c     ---------------------------------------------------------------
c     Declaration of variables
c     ---------------------------------------------------------------

c     Variables for input P file : model level
      character*80 ml_pfn
      real         ml_varmin(4),ml_varmax(4),ml_stag(4)
      integer      ml_vardim(4)
      real         ml_mdv
      integer      ml_ndim
      integer      ml_nx,ml_ny,ml_nz
      real         ml_xmin,ml_xmax,ml_ymin,ml_ymax,ml_dx,ml_dy
      integer      ml_ntimes
      real         ml_aklev(500),ml_bklev(500)
      real         ml_aklay(500),ml_bklay(500)
      real         ml_time
      real         ml_pollon,ml_pollat
      integer      ml_nvars
      character*80 ml_vnam(100)
      integer      ml_idate(5)
      real,allocatable, dimension (:,:)   :: ml_ps,ml_zb
      real,allocatable, dimension (:,:,:) :: ml_t3,ml_q3,ml_p3,ml_tv3
      real,allocatable, dimension (:,:,:) :: ml_zlay3

c     Variables for input Z file : pressure level
      character*80 pl_zfn
      real         pl_varmin(4),pl_varmax(4),pl_stag(4)
      integer      pl_vardim(4)
      real         pl_mdv
      integer      pl_ndim
      integer      pl_nx,pl_ny,pl_nz 
      real         pl_xmin,pl_xmax,pl_ymin,pl_ymax,pl_dx,pl_dy
      integer      pl_ntimes
      real         pl_aklev(500),pl_bklev(500)
      real         pl_aklay(500),pl_bklay(500)
      real         pl_time
      real         pl_pollon,pl_pollat
      integer      pl_nvars
      character*80 pl_vnam(100)
      integer      pl_idate(5)
      real,allocatable, dimension (:,:,:) :: pl_z3,pl_p3      

c     Physical and numerical parameters
      real      g
      parameter (g=9.80665)
      real      eps
      parameter (eps=0.01)
      real      tzero
      parameter (tzero=273.15)
      real      kappa
      parameter (kappa=0.6078)
      real      zerodiv
      parameter (zerodiv=0.0000000001)
      real      dpmin
      parameter (dpmin=10.)
      real      rdg
      parameter (rdg=29.271)

c     Auxiliary variables
      integer      ierr
      integer      cdfid,cstid
      character*80 cfn
      integer      stat
      real         time
      real         tv1(1000),z1(1000),p1(1000),f1(1000)
      real         spline_tv1(1000),spline_f1(1000),spline_z1(1000)
      real         pu,po,zu,zo,p,z,dp,p0,tvu,tvo,ff
      integer      i,j,k,l
      integer      lmin,n
      character*80 varname,cdfname
      integer      isok
      integer      mode

c     -----------------------------------------------------------------
c     Read input fields
c     -----------------------------------------------------------------

      print*,'*********************************************************'
      print*,'* hydrostatic                                           *'
      print*,'*********************************************************'

c     Read in the parameter file
      open(10,file='fort.10')
       read(10,*) ml_pfn
       read(10,*) pl_zfn
      close(10)

c     Decide which mode is used (1: reference from Z, 2: reference from ORO/PS)
      if ( trim(ml_pfn).ne.trim(pl_zfn) ) then
         mode=1
         print*,'Taking reference from Z ',trim(pl_zfn)
      else
         mode=2
         print*,'Taking reference from ORO/PS ',trim(pl_zfn)
      endif

      print*,trim(ml_pfn)
      print*,trim(pl_zfn)
      
c     Get grid description for P file : model level
      call cdfopn(ml_pfn,cdfid,ierr)
      if (ierr.ne.0) goto 998
      call getcfn(cdfid,cfn,ierr)
      if (ierr.ne.0) goto 998
      call cdfopn(cfn,cstid,ierr)   
      if (ierr.ne.0) goto 998
      call getvars(cdfid,ml_nvars,ml_vnam,ierr)
      varname='T'
      isok=0
      call check_varok (isok,varname,ml_vnam,ml_nvars)
      if (isok.ne.1) goto 998
      call getdef(cdfid,varname,ml_ndim,ml_mdv,ml_vardim,
     >            ml_varmin,ml_varmax,ml_stag,ierr)
      if (ierr.ne.0) goto 998
      ml_nx  =ml_vardim(1)
      ml_ny  =ml_vardim(2)
      ml_nz  =ml_vardim(3)
      ml_xmin=ml_varmin(1)
      ml_ymin=ml_varmin(2)
      call getlevs(cstid,ml_nz,ml_aklev,ml_bklev,ml_aklay,ml_bklay,ierr)
      call getgrid(cstid,ml_dx,ml_dy,ierr)
      ml_xmax=ml_xmin+real(ml_nx-1)*ml_dx
      ml_ymax=ml_ymin+real(ml_ny-1)*ml_dy
      call gettimes(cdfid,ml_time,ml_ntimes,ierr) 
      call getstart(cstid,ml_idate,ierr)
      call getpole(cstid,ml_pollon,ml_pollat,ierr)
      call clscdf(cstid,ierr)
      call clscdf(cdfid,ierr) 

c     Get grid description reference: either ORO(P) or Z(Z)
      call cdfopn(pl_zfn,cdfid,ierr)
      if (ierr.ne.0) goto 998
      call getcfn(cdfid,cfn,ierr)
      if (ierr.ne.0) goto 998
      call cdfopn(cfn,cstid,ierr)   
      if (ierr.ne.0) goto 998
      call getvars(cdfid,pl_nvars,pl_vnam,ierr)
      if (mode.eq.1) then
         varname='Z'
         isok=0
         call check_varok (isok,varname,pl_vnam,pl_nvars)
         if (isok.ne.1) goto 998
         call getdef(cdfid,varname,pl_ndim,pl_mdv,pl_vardim,
     >               pl_varmin,pl_varmax,pl_stag,ierr)
         if (ierr.ne.0) goto 998
         call getlevs(cstid,pl_nz,pl_aklev,pl_bklev,
     >                pl_aklay,pl_bklay,ierr)
         call getgrid(cstid,pl_dx,pl_dy,ierr)
         call gettimes(cdfid,pl_time,pl_ntimes,ierr)
         call getstart(cstid,pl_idate,ierr)
         call getpole(cstid,pl_pollon,pl_pollat,ierr)

      else if (mode.eq.2) then
         varname='ORO'
         isok=0
         call check_varok (isok,varname,pl_vnam,pl_nvars)
         if (isok.ne.1) goto 998
         call getdef(cdfid,varname,pl_ndim,pl_mdv,pl_vardim,
     >               pl_varmin,pl_varmax,pl_stag,ierr)
         if (ierr.ne.0) goto 998
         call getgrid(cstid,pl_dx,pl_dy,ierr)
         call gettimes(cdfid,pl_time,pl_ntimes,ierr)
         call getstart(cstid,pl_idate,ierr)
         call getpole(cstid,pl_pollon,pl_pollat,ierr)
         
      endif
      pl_nx  =pl_vardim(1)
      pl_ny  =pl_vardim(2)
      pl_nz  =pl_vardim(3)
      pl_xmin=pl_varmin(1)
      pl_ymin=pl_varmin(2)
      pl_xmax=pl_xmin+real(pl_nx-1)*pl_dx
      pl_ymax=pl_ymin+real(pl_ny-1)*pl_dy
      call clscdf(cstid,ierr)
      call clscdf(cdfid,ierr) 

c     Consitency check for the grids
      if ( (ml_nx.ne.pl_nx).or.
     >     (ml_ny.ne.pl_ny).or.
     >     (abs(ml_xmin-pl_xmin    ).gt.eps).or.
     >     (abs(ml_ymin-pl_ymin    ).gt.eps).or.
     >     (abs(ml_xmax-pl_xmax    ).gt.eps).or.
     >     (abs(ml_ymax-pl_ymax    ).gt.eps).or.
     >     (abs(ml_dx  -pl_dx      ).gt.eps).or.
     >     (abs(ml_dy  -pl_dy      ).gt.eps).or.
     >     (abs(ml_time-pl_time    ).gt.eps).or.
     >     (abs(ml_pollon-pl_pollon).gt.eps).or.
     >     (abs(ml_pollat-pl_pollat).gt.eps)) then
         print*,'Input P and Z grids are not consistent... Stop'
         print*,'Xmin   ',ml_xmin    ,pl_xmin
         print*,'Ymin   ',ml_ymin    ,pl_ymin
         print*,'Xmax   ',ml_xmax    ,pl_xmax
         print*,'Ymax   ',ml_ymax    ,pl_ymax
         print*,'Dx     ',ml_dx      ,pl_dx
         print*,'Dy     ',ml_dy      ,pl_dy
         print*,'Pollon ',ml_pollon  ,pl_pollon
         print*,'Pollat ',ml_pollat  ,pl_pollat
         print*,'Time   ',ml_time    ,pl_time
         stop
      endif

c     Allocate memory for all fields
      allocate(ml_ps(ml_nx,ml_ny),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_ps ***'
      allocate(ml_zb(ml_nx,ml_ny),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_zb ***'
      allocate(ml_p3(ml_nx,ml_ny,ml_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_p3 ***'
      allocate(ml_t3(ml_nx,ml_ny,ml_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_t3 ***'
      allocate(ml_q3(ml_nx,ml_ny,ml_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_q3 ***'
      allocate(ml_tv3(ml_nx,ml_ny,ml_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_tv3 ***'
      allocate(ml_zlay3(ml_nx,ml_ny,ml_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array ml_zlay3 ***'

      allocate(pl_z3(pl_nx,pl_ny,pl_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array pl_z3 ***'
      allocate(pl_p3(pl_nx,pl_ny,pl_nz),stat=stat)
      if (stat.ne.0) print*,'*** error allocating array pl_p3 ***'

c     Read T, Q, PS from P file
      call cdfopn(ml_pfn,cdfid,ierr)
      if (ierr.ne.0) goto 998
      isok=0
      varname='T'
      call check_varok (isok,varname, ml_vnam,ml_nvars)
      varname='Q'
      call check_varok (isok,varname, ml_vnam,ml_nvars)
      varname='PS'
      call check_varok (isok,varname,ml_vnam,ml_nvars)
      if (isok.ne.3) goto 998
      print*,'R T  ',trim(ml_pfn)
      call getdat(cdfid,'T',ml_time,0,ml_t3,ierr)
      print*,'R Q  ',trim(ml_pfn)
      call getdat(cdfid,'Q',ml_time,0,ml_q3,ierr)
      print*,'R PS ',trim(ml_pfn)
      call getdat(cdfid,'PS',ml_time,1,ml_ps,ierr)
      call clscdf(cdfid,ierr)

c     Read ORO from P or Z from Z file
      call cdfopn(pl_zfn,cdfid,ierr)
      if (ierr.ne.0) goto 998
      if (mode.eq.1) then
         isok=0
         varname='Z'
         call check_varok (isok,varname,pl_vnam,pl_nvars)
         if (isok.ne.1) goto 998
         print*,'R Z  ',trim(pl_zfn)
         call getdat(cdfid,varname,pl_time,0,pl_z3,ierr)
      else if (mode.eq.2) then
         isok=0
         varname='ORO'
         call check_varok (isok,varname,pl_vnam,pl_nvars)
         if (isok.ne.1) goto 998
         print*,'R ORO  ',trim(pl_zfn)
         call getdat(cdfid,varname,pl_time,1,pl_z3,ierr)
      endif
      call clscdf(cdfid,ierr)

c     Set the values for the pressure on the pressure levels
      do i=1,pl_nx
         do j=1,pl_ny
            do k=1,pl_nz
               if (mode.eq.1) then
                  pl_p3(i,j,k)=pl_aklay(k)
               else if (mode.eq.2) then
                  pl_p3(i,j,k)=ml_ps(i,j)
               endif
            enddo
         enddo
      enddo

c     Calculate 3d pressure field
      print*,'C P'
      do k=1,ml_nz
        do i=1,ml_nx
           do j=1,ml_ny
              ml_p3(i,j,k)=ml_aklay(k)+ml_bklay(k)*ml_ps(i,j)
            enddo
         enddo
      enddo

c     Calculate 3d virtual temperature
      print*,'C TV'
      do k=1,ml_nz
         do i=1,ml_nx
            do j=1,ml_ny
               ml_tv3(i,j,k) = (ml_t3(i,j,k)+tzero)*
     >                         (1.+kappa*ml_q3(i,j,k))
            enddo
         enddo
      enddo

c     -----------------------------------------------------------------
c     Calculate geopotential 
c     -----------------------------------------------------------------

c     Integrate hydrostatic equation towards layers
      print*,'C HYDROSTATIC EQUATION (LAYERS)'
      do i=1,ml_nx
         do j=1,ml_ny

c           Make the virtual temperature profile available 
            do k=1,ml_nz
               p1 (ml_nz-k+1)=ml_p3 (i,j,k)
               tv1(ml_nz-k+1)=ml_tv3(i,j,k)
            enddo
            call spline (p1,tv1,ml_nz,1.e30,1.e30,spline_tv1)

c           Loop over all model levels
            do k=1,ml_nz

c              Get pressure at the grid point
               p = ml_aklay(k)+ml_bklay(k)*ml_ps(i,j)

c              Find nearest pressure level which is above topography
               if (mode.eq.1) then
                  lmin=pl_nz
                  do l=1,pl_nz
                     if ((abs(p-pl_p3(i,j,l))).lt.
     >                   (abs(p-pl_p3(i,j,lmin)))
     >                    .and.
     >                    (pl_p3(i,j,l).lt.ml_ps(i,j)) ) then
                        lmin=l
                     endif
                  enddo
               else if (mode.eq.2) then
                  lmin=1
               endif

c              Integrate hydrostatic equation from this level to the grid point
               p0 = pl_p3(i,j,lmin)
               n  = nint(abs(p-p0)/dpmin)
               if (n.lt.1) n=1
               dp = (p-p0)/real(n)

               pu  = p0
               z   = pl_z3(i,j,lmin)
               call splint(p1,tv1,spline_tv1,ml_nz,pu,tvu)
               do l=1,n
                  po  = pu+dp
                  call splint(p1,tv1,spline_tv1,ml_nz,po,tvo)
                  z   = z + rdg*0.5*(tvu+tvo)*alog(pu/po)
                  tvu = tvo
                  pu  = po
               enddo
               
c              Set the geopotential at the grid point
               ml_zlay3(i,j,k) = z
              
            enddo

         enddo

      enddo

c     -----------------------------------------------------------------
c     Calculate height of topography 
c     -----------------------------------------------------------------

      if (mode.eq.1) then 
         print*,'C TOPOGRAPHY'
         do i=1,ml_nx
            do j=1,ml_ny
               
c              Make the z/p profile available 
               do k=1,ml_nz
                  p1(ml_nz-k+1)=ml_p3(i,j,k)
                  z1(ml_nz-k+1)=ml_zlay3(i,j,k)
               enddo

c              Cubic spline interpolation
               call spline (p1,z1,ml_nz,1.e30,1.e30,spline_z1) 
               call splint (p1,z1,spline_z1,ml_nz,ml_ps(i,j),ml_zb(i,j))
               
            enddo
         enddo
      endif

c     -----------------------------------------------------------------
c     Write the topography and geopotential to P file
c     -----------------------------------------------------------------

c     Open P file
      call cdfwopn(trim(ml_pfn),cdfid,ierr)   

c     Write orography (levels)
      if (mode.eq.1) then
         varname='ORO'
         print*,'W ',trim(varname),' ',trim(ml_pfn)
         isok=0
         call check_varok (isok,varname,ml_vnam,ml_nvars)
         if (isok.eq.0) then
            ml_vardim(3)=1
            call putdef(cdfid,varname,ml_ndim,ml_mdv,ml_vardim,
     >                  ml_varmin,ml_varmax,ml_stag,ierr)         
            ml_vardim(3)=ml_nz
            if (ierr.ne.0) goto 997
         endif
         call putdat(cdfid,varname,ml_time,1,ml_zb,ierr)
         if (ierr.ne.0) goto 997
      endif

c     Write geopotential on layers
      varname='Z_DIA'
      print*,'W ',trim(varname),' ',trim(ml_pfn)
      isok=0
      call check_varok (isok,varname,ml_vnam,ml_nvars)
      if (isok.eq.0) then
         ml_stag(3)=-0.5
         call putdef(cdfid,varname,ml_ndim,ml_mdv,ml_vardim,
     >               ml_varmin,ml_varmax,ml_stag,ierr)         
         if (ierr.ne.0) goto 997
      endif
      call putdat(cdfid,varname,ml_time,0,ml_zlay3,ierr)
      if (ierr.ne.0) goto 997

c     Close P file
      call clscdf(cdfid,ierr)

c     -----------------------------------------------------------------
c     Exception handling and format specs
c     -----------------------------------------------------------------

      stop

 998  print*,'Z: Problems with input from m level'
      stop

 997  print*,'Z: Problems with output on m level'
      stop

 996  print*,'F: Problems with input from m level'
      stop

 995  print*,'F: Problems with output on z level'
      stop


      end

c     -------------------------------------------------------------
c     Natural cubic spline
c     -------------------------------------------------------------

      SUBROUTINE spline(x,y,n,yp1,ypn,y2)
      INTEGER n,NMAX
      REAL yp1,ypn,x(n),y(n),y2(n)
      PARAMETER (NMAX=500)
      INTEGER i,k
      REAL p,qn,sig,un,u(NMAX)
      if (yp1.gt..99e30) then
        y2(1)=0.
        u(1)=0.
      else
        y2(1)=-0.5
        u(1)=(3./(x(2)-x(1)))*((y(2)-y(1))/(x(2)-x(1))-yp1)
      endif
      do 11 i=2,n-1
        sig=(x(i)-x(i-1))/(x(i+1)-x(i-1))
        p=sig*y2(i-1)+2.
        y2(i)=(sig-1.)/p
        u(i)=(6.*((y(i+1)-y(i))/(x(i+
     *1)-x(i))-(y(i)-y(i-1))/(x(i)-x(i-1)))/(x(i+1)-x(i-1))-sig*
     *u(i-1))/p
11    continue
      if (ypn.gt..99e30) then
        qn=0.
        un=0.
      else
        qn=0.5
        un=(3./(x(n)-x(n-1)))*(ypn-(y(n)-y(n-1))/(x(n)-x(n-1)))
      endif
      y2(n)=(un-qn*u(n-1))/(qn*y2(n-1)+1.)
      do 12 k=n-1,1,-1
        y2(k)=y2(k)*y2(k+1)+u(k)
12    continue
      return
      END


      SUBROUTINE splint(xa,ya,y2a,n,x,y)
      INTEGER n
      REAL x,y,xa(n),y2a(n),ya(n)
      INTEGER k,khi,klo
      REAL a,b,h
      klo=1
      khi=n
1     if (khi-klo.gt.1) then
        k=(khi+klo)/2
        if(xa(k).gt.x)then
          khi=k
        else
          klo=k
        endif
      goto 1
      endif
      h=xa(khi)-xa(klo)
      if (h.eq.0.) pause 'bad xa input in splint'
      a=(xa(khi)-x)/h
      b=(x-xa(klo))/h
      y=a*ya(klo)+b*ya(khi)+((a**3-a)*y2a(klo)+(b**3-b)*y2a(khi))*(h**
     *2)/6.
      return
      END

c     ----------------------------------------------------------------
c     Check whether variable is found on netcdf file
c     ----------------------------------------------------------------

      subroutine check_varok (isok,varname,varlist,nvars)

c     Check whether the variable <varname> is in the list <varlist(nvars)>. 
c     If this is the case, <isok> is incremented by 1. Otherwise <isok> 
c     keeps its value.

      implicit none

c     Declaraion of subroutine parameters
      integer      isok
      integer      nvars
      character*80 varname
      character*80 varlist(nvars)

c     Auxiliary variables
      integer      i

c     Main
      do i=1,nvars
         if (trim(varname).eq.trim(varlist(i))) isok=isok+1
      enddo

      end