Subversion Repositories pvinversion.ecmwf

Rev

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

      PROGRAM inv_prep

c     ********************************************************************************
c     * CALCULATE REFERENCE PROFILE, CORIOLIS PARAMETER AND GRID PARAMETERS          *
c     * Rene Fehlmann 1994 / Code re-organization: Michael Sprenger, 2006            *
c     ********************************************************************************

c     --------------------------------------------------------------------------------
c     Declaration of variables, parameters, externals and common blocks
c     --------------------------------------------------------------------------------

      implicit none

c     Input and output file
      character*80   pvsrcfile,referfile
      integer        mode
      real           radius

c     Grid parameters
      integer        nx,ny,nz
      real           xmin,ymin,zmin,xmax,ymax,zmax
      real           dx,dy,dz
      real           mdv

c     Reference state
      real,   allocatable,dimension (:) :: nsqref
      real,   allocatable,dimension (:) :: thetaref
      real,   allocatable,dimension (:) :: rhoref
      real,   allocatable,dimension (:) :: pressref
      real,   allocatable,dimension (:) :: zref
      real    pressn,thetan

c     3d fields for calculation of reference profile
      real,allocatable,dimension (:,:,:) :: th,rho,nsq,p,z

c     2d weight for mean
      real,allocatable,dimension (:,:) :: weight

c     Auxiliary variables
      integer      i,j,k
      integer      stat
      integer      jj,kk
      character*80 varname
      integer      istep
      real         sum,max,min
      integer      cnt,nmd
      integer      step
      integer      i0,j0
      real         lon0,lat0,lon1,lat1,dist
      integer      vardim(4),ndim,cdfid,ierr
      real         varmin(4),varmax(4),stag(4)
      real         mdv
      character*80 name

c     Externals
      real      sdis
      external  sdis

c     --------------------------------------------------------------------------------
c     Input
c     --------------------------------------------------------------------------------

      print*,'********************************************************'
      print*,'* ref_grid                                             *'
      print*,'********************************************************'

c     Read parameter file
      open(10,file='fort.10')
       read(10,*) pvsrcfile
       read(10,*) referfile
       read(10,*) name,radius
       if ( trim(name).ne.'REF_R') stop
      close(10) 
      print*
      print*,trim(pvsrcfile)
      print*,trim(referfile)
      print*,radius
      print*

c     Get lat/lon gid parameters from input file
      call read_dim (nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv,
     >               pvsrcfile)
      print*,'Read_Dim: nx,ny,nz         = ',nx,ny,nz
      print*,'          dx,dy,dz         = ',dx,dy,dz
      print*,'          xmin,ymin,zmin   = ',xmin,ymin,zmin
      print*,'          mdv              = ',mdv
      print*
      xmax = xmin + real(nx-1) * dx
      ymax = ymin + real(ny-1) * dy

c     Count from 0, not from 1: consistent with <inv_cart.f>.
      nx=nx-1
      ny=ny-1
      nz=nz-1

c     Allocate memory for reference profile
      allocate(rhoref  (0:2*nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating rhoref'
      allocate(pressref(0:2*nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating pressref'
      allocate(thetaref(0:2*nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating thetaref'
      allocate(nsqref  (0:2*nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating nsqref'
      allocate(zref    (0:2*nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating zref'
         
c     Allocate memory for calculatation of reference profile
      allocate(th (0:nx,0:ny,0:nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating th'
      allocate(rho(0:nx,0:ny,0:nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating rho'
      allocate(p  (0:nx,0:ny,0:nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating p'
      allocate(nsq(0:nx,0:ny,0:nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating nsq'
      allocate(z(0:nx,0:ny,0:nz),STAT=stat)
      if (stat.ne.0) print*,'error allocating z'

c     Allocate memory for weight
      allocate(weight(0:nx,0:ny),STAT=stat)
      if (stat.ne.0) print*,'error allocating weight'

c     --------------------------------------------------------------------------------
c     Calculate the reference profile and put it onto netcdf file
c     --------------------------------------------------------------------------------

c     Read data from file
      varname='TH'
      call read_inp (th,varname,pvsrcfile,
     >               nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv)
      varname='RHO'
      call read_inp (rho,varname,pvsrcfile,
     >               nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv)
      varname='NSQ'
      call read_inp (nsq,varname,pvsrcfile,
     >               nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv)
      varname='P'
      call read_inp (p,varname,pvsrcfile,
     >               nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv)

c     Init the height field (not really necessary, but code becomes more symmetrical)
      do i=0,nx
         do j=0,ny
            do k=0,nz
               z(i,j,k)=zmin+real(k)*dz
            enddo
         enddo
      enddo

c     Define the weight
      if ( radius.lt.0 ) then
        do i=0,nx
          do j=0,ny
              weight(i,j) = 1.
          enddo
        enddo
      else
        i0 = nx/2
        j0 = ny/2
        lon0 = xmin + real(i0-1) * dx
        lat0 = ymin + real(j0-1) * dy
        weight(i0,j0)=1.
        do i=0,nx
          do j=0,ny
             lon1 = xmin + real(i-1) * dx
             lat1 = ymin + real(j-1) * dy
             dist = sdis(lon0,lat0,lon1,lat1)
             if ( dist.lt.radius ) then
                weight(i,j) = 1.
             else
                weight(i,j) = 0.
             endif
          enddo
        enddo
      endif

c     Determine the reference profile (mean over domain, split levels and layers)
      call mean(zref,    z,  nx,ny,nz,mdv,weight)
      call mean(nsqref,  nsq,nx,ny,nz,mdv,weight)
      call mean(rhoref,  rho,nx,ny,nz,mdv,weight)
      call mean(thetaref,th, nx,ny,nz,mdv,weight)
      call mean(pressref,p,  nx,ny,nz,mdv,weight)

c     Write reference file to netcdf file
      call write_ref (nsqref,rhoref,thetaref,pressref,zref,
     >                nz,referfile)      

c     Write some info
      print*
      print*,'Ref:            z         p       rho       nsq     theta'
      step=2*nz/10
      if (step.lt.1) step=1
      do k=0,2*nz,step
         write(*,'(8x,f10.1,2f10.2,f10.6,f10.2)')
     >         zref(k),pressref(k),rhoref(k),nsqref(k),thetaref(k) 
      enddo
      print*

c     Write weighht to REF file
      call cdfwopn(trim(referfile),cdfid,ierr)
      varname='WEIGHT'
      vardim(1)=nx+1
      vardim(2)=ny+1
      vardim(3)=1
      vardim(4)=1
      varmin(1)=xmin
      varmin(2)=ymin
      varmin(3)=0.
      varmax(1)=xmax
      varmax(2)=ymax
      varmax(3)=0.
      ndim=3
      mdv=-999.
      call putdef(cdfid,varname,ndim,mdv,vardim,
     >            varmin,varmax,stag,ierr)
      call putdat(cdfid,varname,0.,1,weight,ierr)

c     --------------------------------------------------------------------------------
c     Format specifications
c     --------------------------------------------------------------------------------

 111  format (5f20.9)
 106  format (2f20.9)
          
      end

c     --------------------------------------------------------------------------
c     Spherical distance between lat/lon points
c     --------------------------------------------------------------------------

      real function sdis(xp,yp,xq,yq)
c
c     calculates spherical distance (in km) between two points given
c     by their spherical coordinates (xp,yp) and (xq,yq), respectively.
c
      real      re
      parameter (re=6370.)
      real      pi180
      parameter (pi180=3.14159/180.)
      real      xp,yp,xq,yq,arg

      arg=sin(pi180*yp)*sin(pi180*yq)+
     >    cos(pi180*yp)*cos(pi180*yq)*cos(pi180*(xp-xq))
      if (arg.lt.-1.) arg=-1.
      if (arg.gt.1.) arg=1.

      sdis=re*acos(arg)

      end

c     ********************************************************************************
c     * NETCDF INPUT AND OUTPUT                                                      *
c     ********************************************************************************


c     --------------------------------------------------------------------------------
c     Write reference file to netcdf
c     --------------------------------------------------------------------------------

      SUBROUTINE write_ref (nsqref,rhoref,thetaref,pressref,zref,
     >                      nz,pvsrcfile) 

c     Write the reference profile to file
c
c         thetaref             : Reference potential temperature (K)
c         pressref             : Reference pressure (Pa)
c         rhoref               : Reference density (kg/m^3)
c         nsqref               : Stratification (s^-1)
c         zref                 : Reference height (m)
c         nz                   : Grid dimension in z direction
c         pvsrcfile            : Output file

      implicit   none

c     Declaration of subroutine parameters
      integer              nx,ny,nz
      real                 nsqref  (0:2*nz)
      real                 thetaref(0:2*nz)
      real                 rhoref  (0:2*nz)
      real                 pressref(0:2*nz)
      real                 zref    (0:2*nz)
      character*80         pvsrcfile

c     Numerical and physical parameters
      real                 eps
      parameter            (eps=0.01)

c     Auxiliary variables
      integer              cdfid,stat
      integer              vardim(4)
      real                 misdat
      integer              ndimin
      real                 varmin(4),varmax(4),stag(4)
      integer              i,j,k,nf1
      integer              ntimes
      real                 time(200)  
      character*80         vnam(100),varname
      integer              nvars
      integer              isok,ierr

c     Get grid description from topography
      call cdfopn(pvsrcfile,cdfid,stat)
      if (stat.ne.0) goto 997
      call getvars(cdfid,nvars,vnam,stat)
      if (stat.ne.0) goto 997
      isok=0
      varname='ORO'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) goto 997
      call getdef(cdfid,varname,ndimin,misdat,vardim,
     >            varmin,varmax,stag,stat)    
      if (stat.ne.0) goto 997
      time(1)=0.
      call gettimes(cdfid,time,ntimes,stat)  
      if (stat.ne.0) goto 997
      call clscdf(cdfid,stat)
      if (stat.ne.0) goto 997

c     Open output netcdf file
      call cdfwopn(pvsrcfile,cdfid,stat)
      if (stat.ne.0) goto 997
      
c     Create the variable if necessary
      call getvars(cdfid,nvars,vnam,stat)
      if (stat.ne.0) goto 997
      isok=0
      varname='NSQREF'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) then
         vardim(1)=1
         vardim(2)=1
         vardim(3)=2*nz+1
         call putdef(cdfid,varname,ndimin,misdat,vardim,
     >               varmin,varmax,stag,stat)
         if (stat.ne.0) goto 997
      endif
      isok=0
      varname='RHOREF'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) then
         vardim(1)=1
         vardim(2)=1
         vardim(3)=2*nz+1
         call putdef(cdfid,varname,ndimin,misdat,vardim,
     >               varmin,varmax,stag,stat)
         if (stat.ne.0) goto 997
      endif
      isok=0
      varname='PREREF'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) then
         vardim(1)=1
         vardim(2)=1
         vardim(3)=2*nz+1
         call putdef(cdfid,varname,ndimin,misdat,vardim,
     >               varmin,varmax,stag,stat)
         if (stat.ne.0) goto 997
      endif
      isok=0
      varname='THETAREF'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) then
         vardim(1)=1
         vardim(2)=1
         vardim(3)=2*nz+1
         call putdef(cdfid,varname,ndimin,misdat,vardim,
     >               varmin,varmax,stag,stat)
         if (stat.ne.0) goto 997
      endif
      isok=0
      varname='ZREF'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) then
         vardim(1)=1
         vardim(2)=1
         vardim(3)=2*nz+1
         call putdef(cdfid,varname,ndimin,misdat,vardim,
     >               varmin,varmax,stag,stat)
         if (stat.ne.0) goto 997
      endif

c     Write data
      varname='NSQREF'
      print*,'W NSQREF     ',trim(pvsrcfile)
      call putdat(cdfid,varname,time(1),0,nsqref,stat)
      if (stat.ne.0) goto 997
      varname='RHOREF'
      print*,'W RHOREF     ',trim(pvsrcfile)
      call putdat(cdfid,varname,time(1),0,rhoref,stat)
      if (stat.ne.0) goto 997
      varname='THETAREF'
      print*,'W THETAREF   ',trim(pvsrcfile)
      call putdat(cdfid,varname,time(1),0,thetaref,stat)
      if (stat.ne.0) goto 997
      varname='PREREF'
      print*,'W PREREF     ',trim(pvsrcfile)
      call putdat(cdfid,varname,time(1),0,pressref,stat)
      if (stat.ne.0) goto 997
      varname='ZREF'
      print*,'W ZREF       ',trim(pvsrcfile)
      call putdat(cdfid,varname,time(1),0,zref,stat)
      if (stat.ne.0) goto 997

c     Close output netcdf file
      call clscdf(cdfid,stat)
      if (stat.ne.0) goto 997

      return

c     Exception handling
 997  print*,'Write_Ref: Problem with input netcdf file... Stop'
      stop

      end


c     --------------------------------------------------------------------------------
c     Read input fields for reference profile
c     --------------------------------------------------------------------------------

      SUBROUTINE read_inp (field,fieldname,pvsrcfile,
     >                     nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv)

c     Read <fieldname> from netcdf file <pvsrcfile> into <field>. The grid is specified 
c     by <nx,ny,nz,dx,dy,dz,xmin,ymin,zmin>. A check is performed whether the input 
c     files are consitent with this grid. The missing data value is set to <mdv>.

      implicit   none

c     Declaration of subroutine parameters
      integer              nx,ny,nz
      real                 field(0:nx,0:ny,0:nz)
      character*80         fieldname
      character*80         pvsrcfile
      real                 dx,dy,dz,xmin,ymin,zmin
      real                 mdv

c     Numerical and physical parameters
      real                 eps
      parameter            (eps=0.01)

c     Auxiliary variables
      integer              cdfid,stat,cdfid99
      integer              vardim(4)
      real                 misdat
      real                 varmin(4),varmax(4),stag(4)
      integer              ndimin,outid,i,j,k
      real                 max_th
      real                 tmp(nx,ny,nz)
      integer              ntimes
      real                 time(200)       
      integer              nvars
      character*80         vnam(100),varname
      integer              isok

c     Open the input netcdf file
      call cdfopn(pvsrcfile,cdfid,stat)
      if (stat.ne.0) goto 998

c     Check whether needed variables are on file
      call getvars(cdfid,nvars,vnam,stat)
      if (stat.ne.0) goto 998
      isok=0
      varname=trim(fieldname)
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) goto 998

c     Get the grid parameters 
      call getdef(cdfid,varname,ndimin,misdat,vardim,
     >            varmin,varmax,stag,stat)    
      if (stat.ne.0) goto 998
      time(1)=0.
      call gettimes(cdfid,time,ntimes,stat)  
      if (stat.ne.0) goto 998

c     Check whether grid parameters are consistent
      if ( (vardim(1).ne.(nx+1)).or.
     >     (vardim(2).ne.(ny+1)).or.
     >     (vardim(3).ne.(nz+1)).or.
     >     (abs(varmin(1)-xmin).gt.eps).or.
     >     (abs(varmin(2)-ymin).gt.eps).or.
     >     (abs(varmin(3)-zmin).gt.eps).or.
     >     (abs((varmax(1)-varmin(1))/real(vardim(1)-1)-dx).gt.eps).or.
     >     (abs((varmax(2)-varmin(2))/real(vardim(2)-1)-dy).gt.eps).or.
     >     (abs((varmax(3)-varmin(3))/real(vardim(3)-1)-dz).gt.eps) ) 
     >then
         print*,'Input grid inconsitency...'
         print*,'  Nx      = ',vardim(1),nx+1
         print*,'  Ny      = ',vardim(2),ny+1
         print*,'  Nz      = ',vardim(3),nz+1
         print*,'  Varminx = ',varmin(1),xmin
         print*,'  Varminy = ',varmin(2),ymin
         print*,'  Varminz = ',varmin(3),zmin
         print*,'  Dx      = ',(varmax(1)-varmin(1))/real(nx-1),dx
         print*,'  Dy      = ',(varmax(2)-varmin(2))/real(ny-1),dy
         print*,'  Dz      = ',(varmax(3)-varmin(3))/real(nz-1),dz
         goto 998
      endif

c     Load variables
      call getdef(cdfid,varname,ndimin,misdat,vardim,
     >            varmin,varmax,stag,stat)
      if (stat.ne.0) goto 998
      call getdat(cdfid,varname,time(1),0,field,stat)
      print*,'R ',trim(varname),' ',trim(pvsrcfile)
      if (stat.ne.0) goto 998

c     Close input netcdf file
      call clscdf(cdfid,stat)
      if (stat.ne.0) goto 998

c     Set missing data value to <mdv>
      do i=1,nx
         do j=1,ny
            do k=1,nz
               if (abs(field(i,j,k)-misdat).lt.eps) then
                  field(i,j,k)=mdv
               endif
            enddo
         enddo
      enddo

      return

c     Exception handling
 998  print*,'Read_Inp: Problem with input netcdf file... Stop'
      stop

      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)>. If this is 
C     the case, <isok> is incremented by 1. Otherwise <isok> 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

c     --------------------------------------------------------------------------------
c     Get grid parameters
c     --------------------------------------------------------------------------------

      subroutine read_dim (nx,ny,nz,dx,dy,dz,xmin,ymin,zmin,mdv,
     >                     pvsrcfile)

c     Get the grid parameters from the variable <TH> on the input file <pvsrcfile>.
c     The grid parameters are
c        nx,ny,nz                : Number of grid points in x, y and z direction
c        xmin,ymin,zmin          : Minimum domain coordinates in x, y and z direction
c        xmax,ymax,zmax          : Maximal domain coordinates in x, y and z direction
c        dx,dy,dz                : Horizontal and vertical resolution
c     Additionally, it is checked whether the vertical grid is equally spaced. If ok,
c     the grid paramters are transformed from lon/lat to distance (in meters)

      implicit none

c     Declaration of subroutine parameters
      character*80   pvsrcfile
      integer        nx,ny,nz
      real           dx,dy,dz
      real           xmin,ymin,zmin,xmax,ymax,zmax
      real           mdv

c     Numerical epsilon and other physical/geoemtrical parameters
      real           eps
      parameter      (eps=0.01)

c     Auxiliary variables
      integer        cdfid,cstid
      integer        ierr
      character*80   vnam(100),varname
      integer        nvars
      integer        isok
      integer        vardim(4)
      real           misdat
      real           varmin(4),varmax(4),stag(4)
      real           aklev(1000),bklev(1000),aklay(1000),bklay(1000)
      real           dh
      character*80   csn
      integer        ndim
      integer        i

c     Get all grid parameters
      call cdfopn(pvsrcfile,cdfid,ierr)
      if (ierr.ne.0) goto 998
      call getvars(cdfid,nvars,vnam,ierr)
      if (ierr.ne.0) goto 998
      isok=0
      varname='T'
      call check_varok(isok,varname,vnam,nvars)
      if (isok.eq.0) goto 998
      call getcfn(cdfid,csn,ierr)
      if (ierr.ne.0) goto 998
      call cdfopn(csn,cstid,ierr)
      if (ierr.ne.0) goto 998
      call getdef(cdfid,varname,ndim,misdat,vardim,varmin,varmax,
     >            stag,ierr)
      if (ierr.ne.0) goto 998
      nx=vardim(1)
      ny=vardim(2)
      nz=vardim(3)
      xmin=varmin(1)
      ymin=varmin(2)
      zmin=varmin(3)
      call getlevs(cstid,nz,aklev,bklev,aklay,bklay,ierr)
      if (ierr.ne.0) goto 998
      call getgrid(cstid,dx,dy,ierr)
      if (ierr.ne.0) goto 998
      xmax=varmax(1)
      ymax=varmax(2)
      zmax=varmax(3)
      dz=(zmax-zmin)/real(nz-1)
      call clscdf(cstid,ierr)
      if (ierr.ne.0) goto 998
      call clscdf(cdfid,ierr)
      if (ierr.ne.0) goto 998

c     Check whether the grid is equallay spaced in the vertical
      do i=1,nz-1
         dh=aklev(i+1)-aklev(i)
         if (abs(dh-dz).gt.eps) then
            print*,'Aklev: Vertical grid must be equally spaced... Stop'
            print*,(aklev(i),i=1,nz)
            stop
         endif
         dh=aklay(i+1)-aklay(i)
         if (abs(dh-dz).gt.eps) then
            print*,'Aklay: Vertical grid must be equally spaced... Stop'
            print*,(aklay(i),i=1,nz)
            stop
         endif
      enddo

c     Set missing data value
      mdv=misdat

      return

c     Exception handling
 998  print*,'Read_Dim: Problem with input netcdf file... Stop'
      stop

      end


c     ********************************************************************************
c     * DEFINE REFERENCE PROFILE                                                     *
c     ********************************************************************************

c     --------------------------------------------------------------------------------
c     Calculate area mean
c     --------------------------------------------------------------------------------

      SUBROUTINE mean(a,m,nx,ny,nz,mdv,weight)

c     Calculate the area-mean of <m> and save it on <a>.

      implicit none

c     Declaration of subroutine parameters
      real       mdv
      integer    nx,ny,nz
      real       m(0:nx,0:ny,0:nz),a(0:2*nz)
      real       weight(0:nx,0:ny)

c     Numerical epsilon
      real       eps
      parameter  (eps=0.01)

c     Auxiliary varaibles
      real       mea(0:nz)
      integer    i,j,k,kk
      real       counter

c     Determine the mean over all levels (handle missing data)
      do k=0,nz
         mea(k)=0.
         counter=0.
         do i=0,nx
            do j=0,ny
               if (abs(m(i,j,k)-mdv).gt.eps) then
                  mea(k)=mea(k)+m(i,j,k)*weight(i,j)
                  counter=counter+weight(i,j)
               endif
               
            enddo
         enddo
         if (counter.gt.0) then
            mea(k)=mea(k)/counter
         else
            mea(k)=mdv
         endif
      enddo

c     Prepare the output array: split layers and levels
      do k=0,nz-1
         kk=2*k
         a(kk)=mea(k)
         a(kk+1)=0.5*(mea(k)+mea(k+1))
      enddo
      a(2*nz)=mea(nz)
      
      end