!--------------------------------------- LICENCE BEGIN -----------------------------------
!Environment Canada - Atmospheric Science and Technology License/Disclaimer,
! version 3; Last Modified: May 7, 2008.
!This is free but copyrighted software; you can use/redistribute/modify it under the terms
!of the Environment Canada - Atmospheric Science and Technology License/Disclaimer
!version 3 or (at your option) any later version that should be found at:
!http://collaboration.cmc.ec.gc.ca/science/rpn.comm/license.html
!
!This software 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 above mentioned License/Disclaimer for more details.
!You should have received a copy of the License/Disclaimer along with this software;
!if not, you can write to: EC-RPN COMM Group, 2121 TransCanada, suite 500, Dorval (Quebec),
!CANADA, H9P 1J3; or send e-mail to service.rpn@ec.gc.ca
!-------------------------------------- LICENCE END --------------------------------------
MODULE GAUSSGRID_MOD 6,3
use MathPhysConstants_mod
use EarthConstants_mod
use HorizontalCoord_mod
implicit none
save
private
! Public Variables
public :: gaus_rwt,gaus_conphy
! Public subroutines
public :: gaus_SetupFromHCO, gaus_setup, gaus_find_lat_index
real*8, allocatable :: gaus_rmu(:)
real*8, allocatable :: gaus_rwocs(:)
real*8, allocatable :: gaus_r1mu2(:)
real*8, allocatable :: gaus_rsqm2(:)
real*8, allocatable :: gaus_r1qm2(:)
real*8, allocatable :: gaus_r1mui(:)
real*8, allocatable :: gaus_r1mua(:)
real*8, allocatable :: gaus_rwt(:)
real*8, allocatable :: gaus_rlati(:)
real*8, allocatable :: gaus_rcolat(:)
real*8, allocatable :: gaus_conphy(:)
real*8, allocatable :: gaus_conima(:)
integer :: gaus_ni, gaus_nj
contains
!--------------------------------------------------------------------------
! gaus_SetupFromHCO
!--------------------------------------------------------------------------
subroutine gaus_SetupFromHCO(hco_in, lverbose_in) 3,1
!
!- Initialisation of Gaussian latitudes, weights and related quantities
! using the grid info from horizontalcoord_mod
!
implicit none
type(struct_hco), intent(in) :: hco_in
logical, optional :: lverbose_in
logical :: lverbose
integer :: njlath, jlat, my_mpi_id, my_mpi_idx_dummy, my_mpi_idy_dummy
real(8) :: zpisu2
if(present(lverbose_in)) then
lverbose = lverbose_in
else
lverbose = .false.
endif
!
!- 1. Tests on the input grid
!
if ( trim(hco_in % grtyp) /= 'G' ) then
write(*,*)
write(*,*) 'gaus_SetupFromHCO: The global analysis grid structure is not from'
write(*,*) ' a gaussian grid! ABORT'
write(*,*) 'hco_in % grtyp = ', trim(hco_in % grtyp)
stop
end if
if ( (hco_in % lat(2) - hco_in % lat(1)) > 0.0d0 ) then
write(*,*)
write(*,*) 'gaus_SetupFromHCO: The latitude of the global analysis grid '
write(*,*) ' are NOT ordered from North TO South'
stop
end if
!
!- 2. Dimension settings and Memory allocation
!
gaus_ni = hco_in % ni
gaus_nj = hco_in % nj
write(*,*) 'gaus_ni = ', gaus_ni
write(*,*) 'gaus_nj = ', gaus_nj
allocate(gaus_rmu(gaus_nj))
allocate(gaus_rwt(gaus_nj))
allocate(gaus_rwocs(gaus_nj))
allocate(gaus_r1mu2(gaus_nj))
allocate(gaus_rsqm2(gaus_nj))
allocate(gaus_rcolat(gaus_nj))
allocate(gaus_r1qm2(gaus_nj))
allocate(gaus_r1mui(gaus_nj))
allocate(gaus_r1mua(gaus_nj))
allocate(gaus_rlati((-1):(gaus_nj+2)))
! allocate(gaus_rlati(gaus_nj))
allocate(gaus_conphy(gaus_nj))
allocate(gaus_conima(gaus_nj))
call rpn_comm_mype(my_mpi_id, my_mpi_idx_dummy, my_mpi_idy_dummy)
njlath = (gaus_nj + 1)/2
if(my_mpi_id.eq.0) write(*,fmt='(//,6(" ***********"))')
if(my_mpi_id.eq.0) write(*,*)' SUGAUSSGRID: initialisation of Gaussian', &
' latitudes, weights, etc...'
if(my_mpi_id.eq.0) write(*,fmt='(6(" ***********"))')
! 1. GAUSSIAN LATITUDES AND WEIGHTS OVER AN HEMISPHERE
! -------------------------------------------------
call gauss8
(njlath,gaus_rmu(1),gaus_rwt(1),gaus_rsqm2(1),gaus_rcolat(1),gaus_rwocs(1) &
,gaus_r1qm2(1),gaus_r1mui(1),gaus_r1mu2(1))
do jlat = 1, njlath
gaus_rlati(jlat) = asin(gaus_rmu(jlat))
gaus_r1mua(jlat) = r1sa*gaus_r1mui(jlat)
enddo
! 2. COMPLETION FOR THE SOUTHERN HEMISPHERE
! --------------------------------------
do jlat = njlath +1, gaus_nj
gaus_rmu(jlat) = -gaus_rmu(2*njlath +1 - jlat)
gaus_rwocs(jlat) = gaus_rwocs(2*njlath +1 - jlat)
gaus_r1mu2(jlat) = gaus_r1mu2(2*njlath +1 - jlat)
gaus_rsqm2(jlat) = gaus_rsqm2(2*njlath +1 - jlat)
gaus_r1qm2(jlat) = gaus_r1qm2(2*njlath +1 - jlat)
gaus_r1mui(jlat) = gaus_r1mui(2*njlath +1 - jlat)
gaus_r1mua(jlat) = gaus_r1mua(2*njlath +1 - jlat)
gaus_rwt(jlat) = gaus_rwt(2*njlath +1 - jlat)
gaus_rlati(jlat) = - gaus_rlati (2*njlath +1 - jlat)
enddo
zpisu2 = MPC_PI_R8/2.d0
do jlat = 1, gaus_nj
gaus_rcolat(jlat) = zpisu2 - gaus_rlati(jlat)
enddo
!* 3. Overdimensioning for interpolation
gaus_rlati(-1) = MPC_PI_R8-gaus_rlati(1)
gaus_rlati(0) = MPC_PI_R8*.5d0
gaus_rlati(gaus_nj+1) = -MPC_PI_R8*.5d0
gaus_rlati(gaus_nj+2) = -MPC_PI_R8-gaus_rlati(gaus_nj)
do jlat = 1, gaus_nj
gaus_conphy(jlat) = ra*gaus_r1qm2(jlat)
gaus_conima(jlat) = r1sa*gaus_rsqm2(jlat)
enddo
!* 4. Print the content of GAUS
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(" JLAT:",4X," RLATI",8X,"RCOLAT",8X,"RMU",10X ,"RWT",12X,"RW0CS")')
do jlat = 1, gaus_nj
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(2X,I4,5(2X,G23.16))') &
jlat,gaus_rlati(jlat),gaus_rcolat(jlat), gaus_rmu(jlat) &
,gaus_rwt(jlat),gaus_rwocs(jlat)
enddo
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(//," JLAT:",4X,"R1MU2",8X,"RSQM2",9X,"R1QM2",10X,"R1MUI",10X,"R1MUA")')
do jlat = 1, gaus_nj
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(2X,I4,5(2X,G23.16))') jlat,gaus_r1mu2(jlat),gaus_rsqm2(jlat),gaus_r1qm2(jlat) &
,gaus_r1mui(jlat),gaus_r1mua(jlat)
enddo
end subroutine gaus_SetupFromHCO
!--------------------------------------------------------------------------
! gaus_Setup
!--------------------------------------------------------------------------
SUBROUTINE GAUS_SETUP(ni_out, nj_out, ni_in, nj_in, lverbose_in),1
!
! GAUS_SETUP - Initialisation of Gaussian latitudes, weights and related
! quantities
implicit none
integer, intent(OUT) :: ni_out, nj_out
integer, intent(IN), optional :: ni_in , nj_in
logical, intent(IN), optional :: lverbose_in
logical :: lverbose
integer :: ni,nj,nulnam,fnom,fclos,ierr
integer njlath,jlat,my_mpi_id,my_mpi_idx_dummy,my_mpi_idy_dummy
real*8 zpisu2
NAMELIST /NAMDIM/NI, NJ
if(present(lverbose_in)) then
lverbose = lverbose_in
else
lverbose = .false.
endif
if(present(ni_in).and.present(nj_in)) then
ni=ni_in
nj=nj_in
else
ni=240
nj=120
nulnam=0
ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
if(ierr < 0) then
write(*,*) 'gaus_setup: Failed to open flnml to obtain ni and nj: ierr=', ierr
call qqexit(1)
end if
read(nulnam,nml=namdim,iostat=ierr)
write(*,nml=namdim)
ierr=fclos(nulnam)
endif
gaus_ni=ni
gaus_nj=nj
ni_out=gaus_ni
nj_out=gaus_nj
allocate(gaus_rmu(nj))
allocate(gaus_rwt(nj))
allocate(gaus_rwocs(nj))
allocate(gaus_r1mu2(nj))
allocate(gaus_rsqm2(nj))
allocate(gaus_rcolat(nj))
allocate(gaus_r1qm2(nj))
allocate(gaus_r1mui(nj))
allocate(gaus_r1mua(nj))
allocate(gaus_rlati((-1):(nj+2)))
allocate(gaus_conphy(nj))
allocate(gaus_conima(nj))
call rpn_comm_mype(my_mpi_id, my_mpi_idx_dummy, my_mpi_idy_dummy)
njlath = (nj + 1)/2
if(my_mpi_id.eq.0) write(*,fmt='(//,6(" ***********"))')
if(my_mpi_id.eq.0) write(*,*)' SUGAUSSGRID: initialisation of Gaussian', &
' latitudes, weights, etc...'
if(my_mpi_id.eq.0) write(*,fmt='(6(" ***********"))')
! 1. GAUSSIAN LATITUDES AND WEIGHTS OVER AN HEMISPHERE
! -------------------------------------------------
call gauss8
(njlath,gaus_rmu(1),gaus_rwt(1),gaus_rsqm2(1),gaus_rcolat(1),gaus_rwocs(1) &
,gaus_r1qm2(1),gaus_r1mui(1),gaus_r1mu2(1))
do jlat = 1, njlath
gaus_rlati(jlat) = asin(gaus_rmu(jlat))
gaus_r1mua(jlat) = r1sa*gaus_r1mui(jlat)
enddo
! 2. COMPLETION FOR THE SOUTHERN HEMISPHERE
! --------------------------------------
do jlat = njlath +1, nj
gaus_rmu(jlat) = -gaus_rmu(2*njlath +1 - jlat)
gaus_rwocs(jlat) = gaus_rwocs(2*njlath +1 - jlat)
gaus_r1mu2(jlat) = gaus_r1mu2(2*njlath +1 - jlat)
gaus_rsqm2(jlat) = gaus_rsqm2(2*njlath +1 - jlat)
gaus_r1qm2(jlat) = gaus_r1qm2(2*njlath +1 - jlat)
gaus_r1mui(jlat) = gaus_r1mui(2*njlath +1 - jlat)
gaus_r1mua(jlat) = gaus_r1mua(2*njlath +1 - jlat)
gaus_rwt(jlat) = gaus_rwt(2*njlath +1 - jlat)
gaus_rlati(jlat) = - gaus_rlati (2*njlath +1 - jlat)
enddo
zpisu2 = MPC_PI_R8/2.d0
do jlat = 1, nj
gaus_rcolat(jlat) = zpisu2 - gaus_rlati(jlat)
enddo
!* 3. Overdimensioning for interpolation
gaus_rlati(-1) = MPC_PI_R8-gaus_rlati(1)
gaus_rlati(0) = MPC_PI_R8*.5d0
gaus_rlati(nj+1) = -MPC_PI_R8*.5d0
gaus_rlati(nj+2) = -MPC_PI_R8-gaus_rlati(nj)
do jlat = 1, nj
gaus_conphy(jlat) = ra*gaus_r1qm2(jlat)
gaus_conima(jlat) = r1sa*gaus_rsqm2(jlat)
enddo
!* 4. Print the content of GAUS
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(" JLAT:",4X," RLATI",8X,"RCOLAT",8X,"RMU",10X ,"RWT",12X,"RW0CS")')
do jlat = 1, nj
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(2X,I4,5(2X,G23.16))') &
jlat,gaus_rlati(jlat),gaus_rcolat(jlat), gaus_rmu(jlat) &
,gaus_rwt(jlat),gaus_rwocs(jlat)
enddo
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(//," JLAT:",4X,"R1MU2",8X,"RSQM2",9X,"R1QM2",10X,"R1MUI",10X,"R1MUA")')
do jlat = 1, nj
if(lverbose.and.my_mpi_id.eq.0) write(*,fmt='(2X,I4,5(2X,G23.16))') jlat,gaus_r1mu2(jlat),gaus_rsqm2(jlat),gaus_r1qm2(jlat) &
,gaus_r1mui(jlat),gaus_r1mua(jlat)
enddo
END SUBROUTINE GAUS_SETUP
SUBROUTINE GAUSS8(NRACP,RACP,PG,SIA,RAD,PGSSIN2,SINM1,SINM2,SIN2) 3,8
!C *****************************************************************
!C CALCULE LES NRACP RACINES POSITIVES DU POLYNOME DE LEGENDRE DE
!C DEGRE 2*NRACP (ICI-APRES NOTE PN) DEFINI SUR L INTERVALLE DES
!C COLATITUDES ALLANT DE 0 (POLE NORD) A PI (POLE SUD). ON SAIT QUE
!C LES 2*NRACP RACINES SONT ANTI-SYMETRIQUES P/R A L EQUATEUR PI/2,
!C ETANT POSITIVES ENTRE COLAT=0 ET COLAT =PI/2.
!C ON CALCULE ENSUITE LES POIDS DE GAUSS ASSOCIES AUX COLATITUDES
!C GAUSSIENNES (ICI APRES NOTEES CG), AINSI QU UN CERTAIN NOMBRE DE
!C FONCTIONS DE CG DEFINIES PLUS LOIN. ON RAPPELLE ENFIN QUE LA LATI-
!C TUDE LAT=COLAT-PI/2, ET DONC QUE SIN(LAT)=COS(COLAT).
!C NRACP : NOMBRE DE RACINES POSITIVES DU POLYNOME DE LEGENDRE
!C : DE DEGRE 2*NRACP.
!C RACP(I) : RACINES DE PN, =SIN(LG)=COS(CG).
!C PG(I) : POIDS DE GAUSS CORRESPONDANTS.
!C SIA(I) : SIN(CG)=COS(LG).
!C RAD(I) : COLATITUDE CG EN RADIANS.
!C PGSSIN2(I) : POIDS DE GAUSS / (SIN(CG))**2.
!C SINM1(I) : (SIN(CG))**-1.
!C SINM2(I) : (SIN(CG))**-2.
!C VOIR NST 8, CHAP. A, PP.1-7, ET APPENDICE D12, PP. 26-27.
!C VERSION REVISEE PAR MICHEL BELAND, 9 DECEMBRE 1980.
!C VERSION "REAL*8" ... P. KOCLAS AVRIL 1993...
!C *****************************************************************
IMPLICIT NONE
INTEGER NRACP
REAL*8 RACP(*),PG(*),SIA(*),RAD(*),PGSSIN2(*),SINM1(*),SINM2(*),SIN2(*)
REAL*8 XLIM,PI,FI,FI1,FN,DOT,DN,DN1,A,B,C,G,GM,GP,GT,RACTEMP,GTEMP
INTEGER I,IR,IRM,IRP
!C
!C ON DEMANDE UNE PRECISION DE 1.E-13 POUR LES RACINES DE PN.
!C
XLIM=1.D-13
PI = 4.D0*ATAN(1.D0)
IR = 2*NRACP
FI=DBLE(IR)
FI1=FI+1.D0
FN=DBLE(NRACP)
!C
!C ON UTILISE UNE FORMULE ASYMPTOTIQUE POUR OBTENIR LES VALEURS
!C APPROXIMATIVES DES COLATITUDES GAUSSIENNES
!C CG(I) = (PI/2) * (2*I-1)/(2*NRACP).
!C VOIR ABRAMOWITZ AND STEGUN, P. 787, EQU. 22.16.6 .
!C
DO 20 I=1,NRACP
DOT=DBLE(I-1)
RACP(I)=-PI*.5D0*(DOT+.5D0)/FN + PI*.5D0
RACP(I) = SIN(RACP(I))
20 CONTINUE
!C
!C ON CALCULE ENSUITE LES CONSTANTES FACTEURS DE P(N+1) ET P(N-1)
!C DANS L EXPRESSION DE LA PSEUDO-DERIVEE DE PN.
!C
DN = FI/SQRT(4.D0*FI*FI-1.D0)
DN1=FI1/SQRT(4.D0*FI1*FI1-1.D0)
A = DN1*FI
B = DN*FI1
IRP = IR + 1
IRM = IR -1
!C
!C ON EMPLOIE ENSUITE UNE METHODE DE NEWTON POUR AUGMENTER LA PREC.
!C SI RACTEMP EST UNE SOL. APPROXIMATIVE DE PN(RACP)=0., ALORS LA
!C SEQUENCE RACTEMP(I+1)=RACTEMP(I)-PN(RACTEMP(I))/DER.PN(RACTEMP(I))
!C CONVERGE VERS RACP DE FACON QUADRATIQUE.
!C VOIR ABRAMOWITZ AND STEGUN, P.18, EQU. 3.9.5.
!C ORDLEG CALCULE LA VALEUR DE PN (RACP) , NORMALISE.
!C
DO 50 I=1,NRACP
42 CALL ORDLEG8
(G,RACP(I),IR)
CALL ORDLEG8
(GM,RACP(I),IRM)
CALL ORDLEG8
(GP,RACP(I),IRP)
GT = (A*GP-B*GM)/(RACP(I)*RACP(I)-1.D0)
RACTEMP = RACP(I) - G/GT
GTEMP = RACP(I) - RACTEMP
RACP(I) = RACTEMP
IF( ABS(GTEMP).GT.XLIM) GO TO 42
50 CONTINUE
!C
!C ON CALCULE ENSUITE LES POIDS DE GAUSS SELON L ALGORITHME
!C PG(I) = 2./[(1.-RACP(I)**2)*(DER.PN(RACP(I)))**2].
!C VOIR ABRAMOWITZ AND STEGUN, P.887, EQU. 25.4.29.
!C NOTE: ON DOIT MULTIPLIER LA PRECEDENTE FORMULE PAR UN FACTEUR
!C DE DENORMALISATION, LES PN DONNES PAR ORDLEG ETANT NORMALISES.
!C ON SE SERT D UNE FORMULE DE RECURRENCE POUR LA DERIVEE DE PN.
!C
DO 60 I=1,NRACP
A=2.D0*(1.-RACP(I)**2)
CALL ORDLEG8
(B,RACP(I),IRM)
B = B*B*FI*FI
PG(I)=A*(FI-.5D0)/B
RAD(I) = ACOS(RACP(I))
SIA(I) = SIN(RAD(I))
C=(SIA(I))**2
SINM1(I) = 1.D0/SIA(I)
SINM2(I) = 1.D0/C
PGSSIN2(I) =PG(I)/C
SIN2(I)=C
60 CONTINUE
RETURN
END SUBROUTINE GAUSS8
function gaus_find_lat_index(zlao)
!
!
! gaus_find_lat_index (previously ISRCHILA)
! - Locate index of first latitude to the north of ZLAO
!
! Author: Luc Fillion RPN/AES Jan 1993
!
implicit none
real*8 :: zlao
integer :: ifound, j, gaus_find_lat_index
100 continue
gaus_find_lat_index = 0
ifound = 0
j = -1
101 j = j + 1
if(zlao.ge.gaus_rlati(j)) then
gaus_find_lat_index = j - 1
ifound = 1
endif
if(ifound.eq.0.and.(j.lt.(gaus_nj+2))) go to 101
end function gaus_find_lat_index
END MODULE GAUSSGRID_MOD