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

📄 module spharmt.f90

📁 fortran程序
💻 F90
📖 第 1 页 / 共 5 页
字号:
module spharmt
!
! fortran95 spherical harmonic module.
! For gaussian grids and triangular truncation only.
!
! version 1.1  9/30/2003 (second version - now all fortran using fft99)
! Jeff Whitaker <Jeffrey.S.Whitaker@noaa.gov>

!-----------------------------------------------------------------------------
! to use this module, put "use spharmt" at top of main program
! (just after program statement).
! to initialize arrays needed to compute spherical harmonic transforms
! at T(ntrunc) resolution on a nlon x nlat gaussian grid, 
! do something like this:
! 
! type (sphere) :: sphere_dat
! parameter (nlon=192,nlat=nlon/2)
! parameter (ntrunc=62)
! rsphere = 6.3712e6
! call spharmt_init(sphere_dat,nlon,nlat,ntrunc,re)
 
! derived data type "sphere" contains stuff needed for legendre
! transforms and fft.  By creating multiple instances of sphere
! data types, spherical harmonic transforms on multiple grids can
! be done easily in the same code.
!
! Components of sphere derived data type are:
!
! NLON (integer) - number of longitudes
! NLAT (integer) - number of latitudes
! NTRUNC (integer) - triangular truncation limit
! RE (real) - radius of sphere (m).
! PNM (real pointer array dimension ((ntrunc+1)*(ntrunc+2)/2, nlat) - 
! Associated legendre polynomials.
! HNM (real pointer array, same size as pnm) = -(1 - x*x) * d(pnm)/dx  
! at x = sin(latitude).
! GAULATS (real pointer array dimension nlat) - sin(gaussian latitudes).
! WEIGHTS (real pointer array dimension nlat) - gaussian weights.
! GWRC (real pointer array dimension nlat) - gaussian weights divided by 
! rsphere*(1-x**2).
! INDXM (integer pointer array dimension (ntrunc+1)*(ntrunc+2)/2) - value of
! zonal wavenumber m corresponding to spectral array index 
! nm=1,(ntrunc+1)*(ntrunc+2)/2)
! INDXN (integer pointer array same size as indxm) - value of
! spherical harmonic degree n corresponding to spectral array index 
! nm=1,(ntrunc+1)*(ntrunc+2)/2)
! LAP (real pointer array dimension (ntrunc+1)*(ntrunc+2)/2) - 
! lapacian operator in spectral space = 
! -n*(n+1)/rsphere**2, where n is degree of spherical harmonic.
! ILAP (real pointer array same size as lap) - inverse laplacian operator in 
! spectral space.
! TRIGS, IFAX: arrays needed by Temperton FFT.
! ISINITIALIZED (logical) - true if derived data type has been
! initialized by call to spharm_init, false if not initialized.
! 

! public routines:
!
! SPHARMT_INIT(sphere_dat,nlon,nlat,ntrunc,re):  initialize a sphere object
! (sphere_dat - derived data type "sphere"). inputs are nlon (number of unique
! longitudes), nlat (number of gaussian latitudes), and re 
! (radius of sphere in m). Must be called before anything else.
!
! SPHARMT_DESTROY(sphere_dat):  cleans up pointer arrays allocated by 
! spharmt_init.
!
! SPHARM(sphere_dat, ugrid, anm, idir):  spherical harmonic transform 
! (forward, i.e. grid to spectral, for idir=1 and backward for idir=-1). 
! Arguments are sphere derived data type (sphere_dat), gridded data (ugrid),
! complex spectral coefficients (anm), and flag specifying direction of 
! transform (idir).  See "Import Details" below for information 
! about indexing of grid and spectral arrays.

! GAULW(gaulats,weights): compute sin(gaussian latitudes) and gaussian
! weights.  Number of latitudes determined by size of input arrays.

! LEGEND(x,pnm,hnm): compute associated legendre functions (pnm) and
! their derivates hnm = (X**2-1)*D(PNM)/DX at x = sin(latitude). The
! input arrays pnm and hnm should have dimension (ntrunc+1)*(ntrunc+2)/2, 
! where ntrunc is triangular truncation limit (see Import Details below
! for a description of the spectral indexing).

! GETUV(sphere_dat,vrtnm,divnm,ug,vg): get U,V (u*cos(lat),v*cos(lat) on grid) 
! from spectral coefficients of vorticity and divergence. 
! Input:  sphere_dat, spectral coeffs. of vort and div (vrtnm,divnm).
! Output: gridded U,V (ug,vg).
!
! GETVRTDIV(sphere_dat,vrtnm,divnm,ug,vg): get spectral coefficients of 
! vorticity and divergence (vrtnm,divnm) from gridded U,V (ug,vg).
!
! COSGRAD(sphere_dat,chinm,uchig,vchig): compute cos(lat)*vector gradient.
! Inputs: sphere_dat, spectral coefficient array (chinm).
! Outputs: longitudinal and latitudinal components of gradient on the gaussian
! grid (uchig,vchig).

! SPECSMOOTH(sphere_dat,datagrid,smooth): isotropic spectral smoothing.
! Inputs: sphere_dat, smooth(ntrunc+1) (smoothing factor as a function
! of spherical harmonic degree), datagrid (gridded data to be smoothed).
! Outputs: datagrid (smoothed gridded data).
!
! SUMNM(sphere_dat,am,bm,anm,isign1,isign2):  
! given the arrays of fourier coeffs, am and bm,
! compute the complex spherical harmonic coeffs (anm) of:  
! isign1*( (1./rsphere*(1.-x**2))*d(ag)/d(lon) + (isign2/rsphere)*d(bg)/dx )
! where ag and bg are the grid pt counterparts of am,bm,
! isign1,isign2 are +1 or -1, rsphere is radius of sphere, x=sin(lat))
! am,bm can be computed from gridded data (ag,bg) using RFFT.
!
! RFFT(sphere_dat, data, coeff, idir): computes fourier harmonics in zonal
! direction of a gridded array. idir=+1 for forward (grid
! to fourier) and -1 for backward (fourier to grid) transform.
! data (nlon,nlat) contains gridded data, coeff (ntrunc+1,nlat) contains
! complex zonal fourier harmonics.
! 
! Important Details:
!
! The gridded data is assumed to be oriented such that i=1 is the Greenwich
! meridian and j=1 is the northernmost point. Grid indices increase eastward 
! and southward. The grid increment in longitude is 2*pi/nlon radians.
! For example nlon = 72 for a five degree grid. nlon must be greater than or
! equal to 4. The efficiency of the computation is improved when nlon is a
! product of small prime numbers.

! The spectral data is assumed to be in a complex array of dimension
! (NTRUNC+1)*(NTRUNC+2)/2. NTRUNC is the triangular truncation limit (NTRUNC=42
! for T42). NTRUNC must be <= nlon/2. Coefficients are ordered so that first
! (nm=1) is m=0,n=0, second is m=0,n=1, nm=mtrunc is m=0,n=mtrunc, nm=mtrunc+1 
! is m=1,n=1, etc. In Fortran90 syntax, values of m (degree) and n (order)
! corresponding as a function of the index nm are:

! indxm = (/((m,n=m,mtrunc),m=0,mtrunc)/)
! indxn = (/((n,n=m,mtrunc),m=0,mtrunc)/)

! Conversely, the index nm as a function of m and n is:

! nm = sum((/(i,i=mtrunc+1,mtrunc-m+2,-1)/))+n-m+1

! The associated legendre polynomials are normalized so that the integral  
! (pbar(n,m,theta)**2)*sin(theta) on the interval theta=0 to theta=pi is 1, 
! where: pbar(m,n,theta) = sqrt((2*n+1)*factorial(n-m)/(2*factorial(n+m)))
! *sin(theta)**m/(2**n*factorial(n)) times the (n+m)th derivative of 
! (x**2-1)**n with respect to x=cos(theta).

! note: theta = 0.5*pi - phi, where phi is latitude and theta is colatitude,
! Therefore, cos(theta) = sin(phi) and sin(theta) = cos(phi).

! Note that pbar(0,0,theta)=sqrt(2)/2, 
! and pbar(1,0,theta)=0.5*sqrt(6.)*sin(lat).
!----------------------------------------------------------------------------

! explicit typing

 implicit none

! everything private to module, unless otherwise specified.

 private
 public :: sphere,spharmt_init,spharmt_destroy,spharm,rfft,cosgrad,&
           specsmooth,getuv,getvrtdiv,sumnm,gaulw,legend
 
 type sphere

! rsphere is radius of sphere in m.
   real    :: rsphere = 0.

! nlons is number of longitudes (not including cyclic point).
   integer :: nlons = 0

! nlats is number of gaussian latitudes.
   integer :: nlats = 0

! for fft.
   real, dimension(:), pointer :: trigs
   integer, dimension(:), pointer :: ifax

! ntrunc is triangular truncation limit (e.g. 42 for T42 truncation)
   integer :: ntrunc = 0

! gaulats is sin(gaussian lats), weights are gaussian weights,
! gwrc is gaussian weights divided by re*cos(lat)**2, lap is 
! the laplacian operatore -n*(n+1)/re**2 (where n is the degree 
! of the spherical harmonic),
! ilap is the inverse laplacian (1/lap, set to zero for n=0).
   real, dimension(:), pointer :: gaulats, weights, gwrc, lap, ilap

! pnm is associated legendre polynomials, hnm is -(1-x**2)d(pnm)/dx,
! where x = gaulats.
   real, dimension(:,:), pointer :: pnm, hnm

! indxm is zonal wavenumber index, indxn is spherical harmonic degree index.
   integer, dimension(:), pointer :: indxm, indxn
   logical :: isinitialized=.FALSE.

 end type sphere

 contains

 subroutine spharmt_init(sphere_dat,nlon,nlat,ntrunc,re)

! initialize a sphere object.

   integer, intent(in) :: nlon,nlat,ntrunc
   real, intent(in) :: re
   type (sphere), intent(inout) :: sphere_dat
   real, dimension(:), allocatable :: pnm_tmp,hnm_tmp
   double precision, dimension(:), allocatable :: gaulats_tmp,weights_tmp
   integer :: nmdim,m,n,j

   sphere_dat%nlons = nlon
   sphere_dat%nlats = nlat
   sphere_dat%ntrunc = ntrunc
   sphere_dat%rsphere = re

   nmdim = (ntrunc+1)*(ntrunc+2)/2

   allocate(gaulats_tmp(nlat))
   allocate(weights_tmp(nlat))
   call gaulw(gaulats_tmp,weights_tmp)
   allocate(sphere_dat%gwrc(nlat))
   sphere_dat%gwrc = weights_tmp/(dble(re)*(1.d0-gaulats_tmp**2))
   allocate(sphere_dat%gaulats(nlat))
   allocate(sphere_dat%weights(nlat))
   sphere_dat%gaulats = gaulats_tmp
   sphere_dat%weights = weights_tmp
   deallocate(weights_tmp)

   allocate(sphere_dat%indxm(nmdim))
   allocate(sphere_dat%indxn(nmdim))
   allocate(sphere_dat%lap(nmdim))
   allocate(sphere_dat%ilap(nmdim))

   sphere_dat%indxm = (/((m,n=m,ntrunc),m=0,ntrunc)/)
   sphere_dat%indxn = (/((n,n=m,ntrunc),m=0,ntrunc)/)
   sphere_dat%lap(:)=-real(sphere_dat%indxn(:))*real(sphere_dat%indxn(:)+1)/re**2
   sphere_dat%ilap(1) = 0.
   sphere_dat%ilap(2:nmdim) = 1./sphere_dat%lap(2:nmdim)

   allocate(sphere_dat%pnm(nmdim,nlat))
   allocate(sphere_dat%hnm(nmdim,nlat))
   allocate(pnm_tmp(nmdim))
   allocate(hnm_tmp(nmdim))
   do j=1,nlat
      call legend(gaulats_tmp(j),pnm_tmp,hnm_tmp)
      sphere_dat%pnm(:,j) = pnm_tmp(:)
      sphere_dat%hnm(:,j) = hnm_tmp(:)
   enddo
   deallocate(gaulats_tmp)
   deallocate(pnm_tmp)
   deallocate(hnm_tmp)

   allocate(sphere_dat%trigs((3*nlon/2)+1))
   allocate(sphere_dat%ifax(13))
   call set99(sphere_dat%trigs,sphere_dat%ifax,nlon)

   sphere_dat%isinitialized = .TRUE.

 end subroutine spharmt_init

 subroutine spharmt_destroy(sphere_dat)

! deallocate pointers in sphere object.

   type (sphere), intent(inout) :: sphere_dat

   if (.not. sphere_dat%isinitialized) return

   deallocate(sphere_dat%gaulats)
   deallocate(sphere_dat%weights)
   deallocate(sphere_dat%gwrc)
   deallocate(sphere_dat%pnm)
   deallocate(sphere_dat%hnm)
   deallocate(sphere_dat%indxm)
   deallocate(sphere_dat%indxn)
   deallocate(sphere_dat%lap)
   deallocate(sphere_dat%ilap)
   deallocate(sphere_dat%trigs)
   deallocate(sphere_dat%ifax)

 end subroutine spharmt_destroy

 subroutine gaulw(sinlats, wts)

! compute sin of gaussian latitudes and gaussian weights.
! uses the iterative method presented in Numerical Recipes.

 double precision, intent (inout), dimension(:) :: sinlats, wts

 integer :: itermax
 integer :: i, iter, j, nlat, nprec
 double precision :: pi, pp, p1, p2, p3, z, z1, converg, ten


 ten = 10.d0
 converg = ten * EPSILON(ten)

 nprec = precision(converg)
 converg = .1**nprec

 itermax = 10

 pi = 4.0*ATAN(1.0)

 nlat = size(sinlats)
 if (size(sinlats) .ne. size(wts)) then
   print *, 'sinlats and wts must be same size in gaulw!'
   stop
 end if

 do i=1,nlat
   z = cos(pi*(i - 0.25)/(nlat + 0.5))
   do iter=1,itermax
     p1 = 1.0
     p2 = 0.0

     do j=1,nlat
        p3 = p2
        p2 = p1
        p1 = ((2.0*j - 1.0)*z*p2 - (j - 1.0)*p3)/j
     end do

     pp = nlat*(z*p1 - p2)/(z*z - 1.0E+00)
     z1 = z
     z  = z1 - p1/pp
     if(ABS(z - z1) .LT. converg) go to 10
   end do
   print *, 'abscissas failed to converge in itermax iterations'
   print *, 'stopping in gaulw!'
   stop

10 continue

   sinlats (i)     = z
   wts (i)     = 2.0/((1.0 - z*z)*pp*pp)

 end do

 return
 end subroutine gaulw

 SUBROUTINE LEGEND(X,PMN,HMN)
!
!     THIS SUBROUTINE COMPUTES ASSOCIATED LEGENDRE  
!     FUNCTIONS, PMN, AND THE DERIVATIVE QUANTITY 
!     HMN = -(1 - X*X) * D(PMN)/DX  AT X = COS( COLATITUDE )  
!     GIVEN SOME COLATITUDE IN THE DOMAIN.
!
!     ACCURACY: 
!
!     THE RECURSION RELATION EMPLOYED IS LESS ACCURATE
!     NEAR THE POLES FOR M + N .GE. ROUGHLY 75 BECAUSE THE RECURSION
!     INVOLVES THE DIFFERENCE OF NEARLY EQUAL NUMBERS, SO
!     THE ASSOCIATED LEGENDRE FUNCTIONS ARE COMPUTED USING DOUBLE
!     PRECISION ACCUMULATORS. 
!     SEE BELOUSOV, TABLES OF NORMALIZED ASSOCIATED LEGENDRE  
!     POLYNOMIALS, C. 1962, FOR INFORMATION ON THE ACCURATE 
!     COMPUTATION OF ASSOCIATED LEGENDRE FUNCTIONS.
 
      real, dimension(:), intent(inout) ::  PMN,  HMN
      double precision, intent(in) :: x
      integer :: m,n,nm,i,nmax,np1,nmstrt,j,nmdim,ntrunc
      double precision :: A, B, PROD, SINSQ, &
      eps,epsnmp1,EPSPMN, PMNJ, PMNJM1, PMNJM2
 
 
!**** SET PARAMETERS FOR ENTRY INTO THE RECURSIVE FORMULAE. 
 
 
      SINSQ = 1.D0 - X * X
 
      A     = 1.D0
      B     = 0.D0
      PROD  = 1.D0
 
      nmdim = size(pmn)
      ntrunc = nint((-1.+sqrt(1+8*float(nmdim)))/2.)-1
      if (size(pmn) .ne. size(hmn)) then
         print *, 'pnm and hnm must be same size in subroutine legend!'
         stop
      end if
 
!**** LOOP FOR THE 'M' INDEX. 
 
      NMSTRT = 0
      DO I = 1, NTRUNC+1
 
         M    = (I - 1)
         NMAX = NTRUNC+1-M
 
!        WHEN M=0 (I=1), STANDARD LEGENDRE POLYNOMIALS ARE
!        GENERATED. 
 
         IF (M .NE. 0) THEN
               A    = A + 2.D0
               B    = B + 2.D0
               PROD = PROD * SINSQ * A / B 
         END IF
 
!****    GENERATE PMN AND HMN FOR J = 1 AND 2.
 
         PMNJM2   = SQRT(0.5D0 * PROD)
         NM = NMSTRT + 1
         PMN(NM) = PMNJM2

⌨️ 快捷键说明

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