source: LMDZ6/trunk/tools/make_sso/make_sso_SpherePack.f90 @ 5087

Last change on this file since 5087 was 5084, checked in by Laurent Fairhead, 12 months ago

Reverting to r4065. Updating fortran standard broke too much stuff. Will do it by smaller chunks
AB, LF

File size: 48.3 KB
Line 
1!-------------------------------------------------------------------------------
2!
3PROGRAM make_sso
4!
5!-------------------------------------------------------------------------------
6! Purpose: Project ETOPO file (GMT4 axes conventions) on spherical harmonics.
7!-------------------------------------------------------------------------------
8  USE netcdf
9!  USE sphpack
10  IMPLICIT NONE
11!-------------------------------------------------------------------------------
12! Local variables:
13  REAL,    PARAMETER :: xpi=4.*ATAN(1.),         &    !--- pi
14                        hp=xpi/2., d2r=xpi/180., &    !--- pi, pi/2, pi/180 
15                        REarth=2.E7/xpi,         &    !--- Earth radius
16                        eps=1.E-6                     !--- Tolerance for angles
17  !=== Transition length for spectral filters (to avoid side lobes)
18!  REAL,    PARAMETER :: del=2000./REarth              !--- On unit sphere
19  !=== .TRUE./.FALSE.: Fast/slow routine with heavy O(N^3)/light O(N^2) storage
20  LOGICAL, PARAMETER :: lfast=.TRUE.                  !--- Fast/slow routine
21
22!--- From the input arguments
23  CHARACTER(LEN=256), ALLOCATABLE :: args(:)          !--- All the arguments
24  CHARACTER(LEN=2048):: call_seq                      !--- Calling sequence
25  INTEGER            :: nargs                         !--- Input args number
26  CHARACTER(LEN=256) :: f_in                          !--- Input file name
27  CHARACTER(LEN=256) :: vnam                          !--- Height variable name
28  CHARACTER(LEN=256) :: res_in                        !--- Input resolution
29  CHARACTER(LEN=256) :: fmsk                          !--- Mask file or keyword
30  !=== Filter on output field, of resolution=ffactor*output resolution
31  LOGICAL            :: lfilter=.TRUE.                !--- Filter activation
32  REAL               :: ffactor                       !--- Filter resol. factor
33  REAL               :: tfactor                       !--- Transition factor
34  INTEGER            :: nlon_ou, nlat_ou              !--- Output grid resol.
35
36!--- Quantities related to input field grid
37  CHARACTER(LEN=256) :: lonn, latn                    !--- Axes vars names
38  CHARACTER(LEN=256) :: lonu, latu                    !--- Angular units
39  REAL, ALLOCATABLE  :: lon_in(:), lat_in(:)          !--- Cells centers
40  INTEGER            :: nlon_in,   nlat_in            !--- Input  grid dims
41  INTEGER            :: igrid_in(2)                   !--- Grid identification
42  REAL               :: dlon, dlat                    !--- Domain sizes
43  REAL               :: D1, del1                      !--- Resol. at equator
44  REAL               :: al                            !--- Longitude ratio
45  LOGICAL            :: lcyc                          !--- T: redundant longitude
46  LOGICAL            :: lXdec, lYinc                  !--- T: dec/increasing lon/lat
47  REAL, ALLOCATABLE, DIMENSION(:,:) ::v,h , a , b,mski!--- Heights, coeffs, mask
48  REAL, ALLOCATABLE, DIMENSION(:,:,:) ::hs, as, bs, & !--- Same, filtered
49    dxZ, dyZ, dxZ2, dyZ2, dxyZ, Xk, Xl, Xm, Xp, Xq, dH!--- Grads, correlations
50
51!--- Quantities related to output grid
52  CHARACTER(LEN=256) :: f_ou                          !--- Output file name
53  CHARACTER(LEN=256) :: res_ou                        !--- Output resolution
54  INTEGER(KIND=1), ALLOCATABLE :: mask(:,:)           !--- Boolean out land mask
55  REAL, ALLOCATABLE  :: lon_ou(:), lat_ou(:), tmp1(:) !--- Output grid coords
56  INTEGER            :: igrid_ou(2)                   !--- Grid identification
57  REAL               :: D2, del2                      !--- Resol. at equator
58  REAL, ALLOCATABLE, DIMENSION(:,:) ::   msko, &      !--- Fractional land mask
59                                           h0, &      !--- Large scale orography
60                   Zphi, Zsig, Zthe, Zgam, mu, tmp2   !--- Geopo, SSO parameters
61
62!--- Miscellanous Netcdf-related variables
63  INTEGER :: fID, xID, loID, phiID, sigID, theID, picID, dIDs(4), nn
64  INTEGER :: vID, yID, laID, meaID, gamID, mskID, valID, muID, ix180
65  LOGICAL :: ll
66
67!--- Miscellanous SPHEREPACK-specific quantities
68  INTEGER :: l1, idh, mdab, lsh, la1, la2, lwa, lsvmin, lsave, ierr, intl
69  INTEGER :: l2, jdh, ndab, lvh, lb1, lb2, lwb, lwkmin, lwork, ldwork
70  REAL, ALLOCATABLE :: wsha(:), wsave(:), wvh (:)
71  REAL, ALLOCATABLE :: wshs(:), dwork(:), work(:)
72  INTEGER :: k, n, nt, isym
73
74  CHARACTER(LEN=256) :: sub, fnam, msg, arg
75
76!-------------------------------------------------------------------------------
77  sub='make_sso'
78
79!=== DEAL WITH INPUT ARGUMENTS =================================================
80  call_seq='./'//TRIM(sub)//'.e'
81  nargs=COMMAND_ARGUMENT_COUNT(); ALLOCATE(args(nargs))
82!--- MANUAL DISPLAYED IF NO ARGUMENTS ARE PROVIDED
83  IF(nargs==0) THEN
84    WRITE(*,*)''
85    WRITE(*,*)'General calling sequence:'
86    WRITE(*,*)''
87    WRITE(*,*)TRIM(call_seq)//'  -i <file>  -v <var>  -res <nlon> <nlat>'      &
88      //'  -m <mask>  -f <ffac>  -t <tfac>'
89    WRITE(*,*)''
90    WRITE(*,*)'Where:'
91    WRITE(*,*)''
92    WRITE(*,*)' <file>:   input file name'
93    WRITE(*,*)' <var> :   height variable name'
94    WRITE(*,*)' <nlon>:   number of distinct longitude points of output grid'
95    WRITE(*,*)' <nlat>:   number of distinct latitude  points of output grid'
96    WRITE(*,*)' <mask>:   can be:  1. a mask file (o2a.nc)'
97    WRITE(*,*)'                    2. "noro": computed with grid_noro'
98    WRITE(*,*)'           default: 3. "spec": computed internally'
99    WRITE(*,*)' <ffac>:   filtering  width: ffac*Do         (Do:output resol.)'
100    WRITE(*,*)' <tfac>:   transition width: Di+tfac*(Do-Di) (Di: input resol.)'
101    WRITE(*,*)
102    WRITE(*,*)'Note that latitudes grid contains both poles.'
103    WRITE(*,*)
104    STOP
105  END IF
106!--- ARGUMENTS PARSING
107  DO k=1,nargs
108    CALL GET_COMMAND_ARGUMENT(NUMBER=k,VALUE=args(k))
109    call_seq=TRIM(call_seq)//' '//TRIM(args(k))
110  END DO
111  k=1
112  f_in=''; nlon_ou=0; nlat_ou=0; vnam=''; fmsk=''; ffactor=2.0; tfactor=0.
113  DO
114    k=k+1; ll=.FALSE.; arg=args(k-1)
115    SELECT CASE(arg)
116      CASE('-i');   f_in=args(k); msg='Missing file "'//TRIM(f_in)//'".'
117                    ll=NF90_OPEN(f_in,NF90_NOWRITE,fID)/=NF90_NOERR
118                    IF(.NOT.ll) n=NF90_CLOSE(fID)
119      CASE('-res'); nlon_ou=str2int(args(k)); k=k+1
120                    nlat_ou=str2int(args(k))
121      CASE('-v');   vnam=args(k)
122      CASE('-m');   fmsk=args(k)
123      CASE('-f');   ffactor=str2real(args(k))
124      CASE('-t');   tfactor=str2real(args(k))
125      CASE DEFAULT; msg='Unrecognized argument "'//TRIM(arg)//'".'; ll=.TRUE.
126    END SELECT
127    IF(ll) THEN; WRITE(*,*)msg; STOP; END IF
128    k=k+1
129    IF(k>nargs) EXIT
130  END DO
131!--- CHECK OUTPUT GRID IS DEFINED
132  IF(TRIM(fmsk)=='') fmsk='spec'
133  IF(ALL(['noro','spec']/=fmsk)) THEN
134    msg='Missing or wrong "-m" option ; can be "noro", "spec" or a mask file'
135    CALL err(NF90_OPEN(fmsk,NF90_NOWRITE,fID)/=NF90_NOERR,msg)
136    CALL nc(NF90_INQ_VARID(fID,"MaskOcean",vID),"MaskOcean")    !--- MASK ID
137    CALL nc(NF90_INQUIRE_VARIABLE(fID,vID,dimids=dIDs))         !--- DIMS IDS
138    CALL nc(NF90_INQUIRE_DIMENSION(fID,dIDs(1),len=nlon_ou),'x')!--- NB LONG
139    CALL nc(NF90_INQUIRE_DIMENSION(fID,dIDs(2),len=nlat_ou),'y')!--- NB LAT
140    CALL nc(NF90_CLOSE(fID))
141  END IF
142  IF(nlon_ou<=0.OR.nlat_ou<=0) THEN
143    WRITE(*,*)'Missing or wrong "-res" arguments for output grid'; STOP
144  END IF
145
146!=== READ THE INPUT FIELD =======================================================
147  CALL nc(NF90_OPEN(f_in,NF90_NOWRITE,fID))
148  WRITE(*,*)'>> Reading variable "'//TRIM(vnam)//'" from "'//TRIM(f_in)//'"...'
149
150!--- GET HEIGHT VARIABLE AND ITS DIMENSIONS
151  CALL nc(NF90_INQ_VARID(fID,vnam,vID))
152  CALL nc(NF90_INQUIRE_VARIABLE (fID,vID,dimids=dIDs))
153  CALL nc(NF90_INQUIRE_DIMENSION(fID,dIDs(1),len=nlon_in,name=lonn))
154  CALL nc(NF90_INQUIRE_DIMENSION(fID,dIDs(2),len=nlat_in,name=latn))
155  WRITE(*,*)TRIM(vnam)//' is '//TRIM(lonn)//'('//TRIM(int2str(nlon_in))//')*'   &
156                              //TRIM(latn)//'('//TRIM(int2str(nlat_in))//')'
157  nlon_in=nlon_in-1                                   !--- DISTINCT POINTS NUMBER
158  ALLOCATE(lon_in(nlon_in+1),lat_in(nlat_in),h(nlon_in+1,nlat_in))
159  CALL nc(NF90_INQ_VARID(fID,lonn,loID)        ,lonn)
160  CALL nc(NF90_INQ_VARID(fID,latn,laID)        ,latn)
161  CALL nc(NF90_GET_VAR  (fID,loID,lon_in)      ,lonn)
162  CALL nc(NF90_GET_VAR  (fID,laID,lat_in)      ,latn)
163  CALL nc(NF90_GET_ATT  (fID,loID,'units',lonu),lonn)
164  CALL nc(NF90_GET_ATT  (fID,laID,'units',latu),latn)
165  CALL nc(NF90_GET_VAR  (fID,vID,h(:,:),[1,1],[nlon_in+1,nlat_in]),vnam)
166  CALL nc(NF90_CLOSE(fID))
167
168!--- CHECK WETHER GRID IS CORRECT (GLOBAL DOMAIN, IDENTIFIED UNITS...)
169  dlon=ABS(lon_in(nlon_in+1)-lon_in(1)); dlat=ABS(lat_in(nlat_in)-lat_in(1))
170  CALL err(lonu(1:6)/='degree','Invalid longitudes unit: "'//TRIM(lonu)//'"')
171  CALL err(latu(1:6)/='degree','Invalid latitudes unit: "' //TRIM(latu)//'"')
172  CALL err(ABS(dlon-360.)>eps,'Longitudes domain is not global.')
173  CALL err(ABS(dlat-180.)>eps, 'Latitudes domain is not global.')
174
175!--- ENSURE LONGITUDES ARE INCREASING AND LATITUDES ARE DECREASING
176  lXdec=lon_in(1)>lon_in(nlon_in)
177  lYinc=lat_in(1)<lat_in(nlat_in)
178  IF(lXdec) THEN; lon_in=lon_in(nlon_in+1:1:-1); h(:,:)=h(nlon_in+1:1:-1,:); END IF
179  IF(lYinc) THEN; lat_in=lat_in(nlat_in  :1:-1); h(:,:)=h(:,nlat_in  :1:-1); END IF
180
181!--- ENSURE LONGITUDES ARE BETWEEN -180 and 180deg WITHOUT REDUNDANT END POINT
182  DO ix180=1,nlon_in+1; IF(lon_in(ix180)>=180.) EXIT; END DO
183  IF(ix180>=nlon_in+1) THEN
184    lon_in(:)=[lon_in(ix180:nlon_in)-360.,lon_in(1:ix180)]
185    ALLOCATE(tmp2(nlon_in+1,nlat_in)); nn=nlon_in-ix180+1
186    tmp2(1:nn,:)=h(ix180:nlon_in,:); tmp2(nn+1:nlon_in+1,:)=h(1:ix180,:)
187    DEALLOCATE(tmp2)
188  END IF
189
190!--- REMOVE OCEAN ; FLIP FIELD (SPHEREPACK: LAT FIRST) ; INPUT BOOLEAN MASK
191  WHERE(h(:,:)<0.) h(:,:)=0
192  idh=nlat_in; jdh=nlon_in; CALL flip(h)
193  ALLOCATE(mski(idh,jdh)); mski(:,:)=0.; WHERE(h(:,:)>0.) mski(:,:)=1.
194
195!=== SPHERICAL HARMONICS ANALYSIS ===============================================
196!--- Allocate work vector (min. length depends on used routines)
197  nt=2                                                        ! MAX nt
198  l1=MIN(nlat_in,(nlon_in+2)/2)
199  IF(MODULO(nlon_in,2)==1) l1=MIN(nlat_in,(nlon_in+1)/2)
200  l2=(nlat_in+1)/2
201  IF(lfast) THEN
202    lwork=2*(nlat_in+1)                                       ! shaeci/shseci
203    lwork=MAX(lwork,nlat_in*(nt*nlon_in+MAX(3*l2,nlon_in)))   ! shaec /shaes
204    lwork=MAX(lwork,4*(nlat_in+1))                            ! vhseci
205    lwork=MAX(lwork,nlat_in*(2*nt*nlon_in+MAX(6*l2,nlon_in))+nlat_in*(2*l1*nt+1)) ! gradec
206    lsh=2*nlat_in*l2+3*(  (l1-2  )*(nlat_in+nlat_in-l1-1))/2+nlon_in+15 ! shaec /shsec
207    lvh=4*nlat_in*l2+3*MAX(l1-2,0)*(nlat_in+nlat_in-l1-1)   +nlon_in+15 ! vhseci/gradec
208  ELSE
209    lwork=5*nlat_in*l2+3*((l1-2)*(nlat_in+nlat_in-l1-1))/2    ! shaesi/shsesi
210    lwork=MAX(lwork,(nt+1)*nlat_in*nlon_in)                   ! shaes /shses
211    lwork=MAX(lwork,3*(MAX(l1-2,0)*(nlat_in+nlat_in-l1-1))/2+5*l2*nlat_in)     ! vhsesi
212    lwork=MAX(lwork,nlat_in*((2*nt+1)*nlon_in+2*l1*nt+1))     ! grades
213    lsh=(l1*l2*(nlat_in+nlat_in-l1+1))/2+nlon_in+15           ! shaes /shses
214    lvh= l1*l2*(nlat_in+nlat_in-l1+1)   +nlon_in+15           ! vhsesi/grades
215  END IF
216  ALLOCATE(work(lwork))
217  ALLOCATE(wsha(lsh),wshs(lsh),wvh(lvh))
218
219!--- Allocate spectral coefficients vectors
220  mdab=l1; ndab=nlat_in; IF(.NOT.lfast) mdab=MIN(nlat_in,(nlon_in+2)/2)
221  ALLOCATE(a(mdab,ndab),b(mdab,ndab))
222  isym=0; nt=1                                                !--- Symetries, fields nb
223
224!--- FIELD PROJECTION ON THE SPHERICAL HARMONICS BASE.
225  WRITE(*,*)'>> Projecting fields on the spherical harmonics...'
226  IF(lfast) THEN
227    CALL shaeci(nlat_in,nlon_in,wsha,lsh,work,lwork,ierr)
228    CALL error('shaeci',ierr)
229    CALL shaec(nlat_in,nlon_in,isym,nt,h,idh,jdh,a,b,mdab,ndab,wsha,lsh,work,lwork,ierr)
230    CALL error('shaec',ierr)
231  ELSE
232    CALL shaesi(nlat_in,nlon_in,wsha,lsh,work,lwork,ierr)
233    CALL error('shaesi',ierr)
234    CALL shaes(nlat_in,nlon_in,isym,nt,h,idh,jdh,a,b,mdab,ndab,wsha,lsh,work,lwork,ierr)
235    CALL error('shaes',ierr)
236  END IF
237
238!--- ALIASING DAMPLING FILTER
239!  WRITE(*,*)'>> Bi-Laplacian filtering to avoid Gibbs phenomenon...'
240!  CALL diffusion_filter(a,b)
241
242!=== INPUT/OUTPUT RESOLUTIONS AT EQUATOR (normalized with Earth radius)
243!--- The area of a cell at (lam,phi) of size(dl,dp) is: dl.SIN(dp).COS(phi)
244!--- At equator, Res=SQRT(dl.SIN(dp)) ; with dl=2.pi/nlon and dp=pi/nlat:
245!---             Res=SQRT(2.pi.SIN(pi/nlat)/nlon)~pi*SQRT(2/(nlon*nlat))
246!--- Note: nlat is the slices number = latitude points number -1
247  D1=SQRT(2.*xpi*SIN(xpi/(nlat_in-1))/nlon_in)   !--- Input  grid
248  D2=SQRT(2.*xpi*SIN(xpi/(nlat_ou-1))/nlon_ou)   !--- Output grid
249
250!=== FILTER FACTOR EQUAL TO ZERO => NO FILTERING
251  lfilter=ffactor/=0.
252
253!=== DEFAULT TRANSITION SCALE: INPUT GRID RESOLUTION
254  WRITE(*,*)'tfactor=',tfactor
255  del1=D1; del2=D1+(D2-D1)*tfactor
256  WRITE(*,*)'2*D1,del1,D2,del2=',2*D1,del1,D2,del2
257
258!=== SCALES SEPARATION AT THE OUTPUT GRID RESOLUTION ============================
259  nt=2
260  WRITE(*,*)'>> Scales separation at '//TRIM(real2str(D2/d2r))//' degrees (deta'&
261      //'ils below '//TRIM(real2str(60*D1/d2r))//' minutes removed)...'
262  ALLOCATE(as(mdab,ndab,nt),bs(mdab,ndab,nt))
263  DO n=1,nt; as(:,:,n)=a; bs(:,:,n)=b; END DO
264  CALL  lowpass_filter(as(:,:,1),bs(:,:,1),          D2,del2)   !--- LARGE SCALES
265  CALL bandpass_filter(as(:,:,2),bs(:,:,2),2*D1,del1,D2,del2)   !--- SMALL SCALES
266
267!--- BACKWARD TRANSFORM FOR LARGE AND SUB-CELL SCALES OROGRAPHYS
268  WRITE(*,*)'>> Backward transform of large and sub-cell scales orographies...'
269  ALLOCATE(hs(idh,jdh,nt))
270  IF(lfast) THEN
271    CALL shseci(nlat_in,nlon_in,wshs,lsh,work,lwork,ierr)
272    CALL error('shseci',ierr)
273    CALL shsec(nlat_in,nlon_in,isym,nt,hs,idh,jdh,as,bs,mdab,ndab,wshs,lsh,work,lwork,ierr)
274    CALL error('shsec',ierr)
275  ELSE
276    CALL shsesi(nlat_in,nlon_in,wshs,lsh,work,lwork,ierr)
277    CALL error('shsesi',ierr)
278    CALL shses(nlat_in,nlon_in,isym,nt,hs,idh,jdh,as,bs,mdab,ndab,wshs,lsh,work,lwork,ierr)
279    CALL error('shses',ierr)
280  END IF
281  WHERE(mski==0.); hs(:,:,1)=0.; hs(:,:,2)=0.; END WHERE
282  hs(:,:,2)=hs(:,:,2)**2
283
284!=== COMPUTE GRADIENT ; n=1: large scale ; n=2: sub-cell grid ===================
285  WRITE(*,*)'>> Computing large and sub-cell scales gradients...'
286  ALLOCATE(dxZ(idh,jdh,nt),dyZ(idh,jdh,nt))
287  IF(lfast) THEN
288    CALL vhseci(nlat_in,nlon_in,wvh,lvh,work,lwork,ierr)
289    CALL error('vhseci',ierr)
290    DO n=1,2
291      CALL gradec(nlat_in,nlon_in,isym,1,dyZ(:,:,n),dxZ(:,:,n),idh,jdh,         &
292                           as(:,:,n),bs(:,:,n),mdab,ndab,wvh,lvh,work,lwork,ierr)
293    END DO
294    CALL error('gradec',ierr)
295  ELSE
296    CALL vhsesi(nlat_in,nlon_in,wvh,lvh,work,lwork,ierr)
297    CALL error('vhsesi',ierr)
298    DO n=1,2
299      CALL gradec(nlat_in,nlon_in,isym,1,dyZ(:,:,n),dxZ(:,:,n),idh,jdh,as(:,:,n),&
300                                   bs(:,:,n),mdab,ndab,wvh,lvh,work,lwork,ierr)
301    END DO
302    CALL error('grades',ierr)
303  END IF
304  DEALLOCATE(as,bs)
305  dxZ=dxZ/REarth; dyZ=dyZ/REarth
306  IF(lat_in(nlat_in)>lat_in(1)) dyZ=-dyZ
307  WHERE(mski==0.)
308    dxZ(:,:,1)=0.; dyZ(:,:,1)=0.
309    dxZ(:,:,2)=0.; dyZ(:,:,2)=0.
310  END WHERE
311  ALLOCATE(dH(idh,jdh,6))
312  dH(:,:,1)=dxZ(:,:,1)*dxZ(:,:,1); dH(:,:,4)=dxZ(:,:,2)*dxZ(:,:,2)
313  dH(:,:,2)=dyZ(:,:,1)*dyZ(:,:,1); dH(:,:,5)=dyZ(:,:,2)*dyZ(:,:,2)
314  dH(:,:,3)=dxZ(:,:,1)*dyZ(:,:,1); dH(:,:,6)=dxZ(:,:,2)*dyZ(:,:,2)
315  DEALLOCATE(dxZ,dyZ)
316
317!===============================================================================
318  IF(lfilter) THEN    !=== FILTER TO REMOVE ALIASING ===========================
319!===============================================================================
320    WRITE(*,*)'>> Smoothing large and sub-cell scales fields at '//            &
321             &TRIM(real2str(ffactor*D2/d2r))//' degrees...'
322    CALL spatial_filter_m(dH       ,ffactor*D2,del2)
323    CALL spatial_filter_1(hs(:,:,2),ffactor*D2,del2)
324  END IF
325  DEALLOCATE(work)
326!===============================================================================
327
328!=== DEAL WITH OUTPUT GRID =====================================================
329  intl=0
330!--- OUTPUT GRID (CONTAINS A LONGITUDE REDUNDANT POINT FOR NOW)
331  lon_ou = [(360.*DBLE(n)/DBLE(nlon_ou  ), n = 0, nlon_ou)] + lon_in(1)
332  lat_ou = [(180.*DBLE(n)/DBLE(nlat_ou-1), n = nlat_ou-1, 0, -1)] - 90.
333  la1 = MIN(nlat_in,(nlon_in+1)/2); la2 = (nlat_in+1)/2
334  lb1 = MIN(nlat_ou,(nlon_ou+1)/2); lb2 = (nlat_ou+1)/2
335  lwa = 4*nlat_in*la2+3*MAX(la1-2,0)*(2*nlat_in-la1-1)+la2+nlon_in+15
336  lwb = 4*nlat_ou*lb2+3*MAX(lb1-2,0)*(2*nlat_ou-lb1-1)+    nlon_ou+15
337  lsave=lwa+lwb
338  l1 = MAX(nlat_in,nlat_ou)
339  l2 = MAX(nlon_in,nlon_ou)
340  lwork  = 2*l1*(8*MIN(l1,(l2+2)/2)+4*l2+3)
341  ldwork = 2*l1*(l1+1)+1
342  ALLOCATE(wsave(lsave),work(lwork),dwork(ldwork))
343
344!--- Latitude grid: equally spaced partition of [-pi/2,pi/2]
345  igrid_in(:)=[-1,1]                                  !--- NP->SP, nlat*nlon
346  igrid_ou(:)=[-1,0]                                  !--- NP->SP, nlon*nlat
347
348!--- BUILD FRACTIONAL LAND MASK msko AND GEOPOTENTIAL ('noro' CASE)
349  SELECT CASE(fmsk)
350    CASE('noro')
351      WRITE(*,*)'>> Computing mask & geop (grid_noro compatible)...'
352      ALLOCATE(msko(nlon_ou+1,nlat_ou), Zphi(nlon_ou+1,nlat_ou))
353      CALL flip(h)
354      CALL grid_noro0(lon_in*d2r, lat_in*d2r, h,lon_ou*d2r, lat_ou*d2r, Zphi, msko)
355      msko = msko(1:nlon_ou,:)
356      Zphi = Zphi(1:nlon_ou,:)
357    CASE('spec')
358      WRITE(*,*)'>> Computing mask in spectral space...'
359      ALLOCATE(msko(nlon_ou,nlat_ou))
360      CALL trssph(intl, igrid_in, nlon_in, nlat_in, mski,                      &
361                        igrid_ou, nlon_ou, nlat_ou, msko, wsave, lsave, lsvmin,&
362                                     work, lwork, lwkmin, dwork, ldwork, ierr)
363      intl=1
364    CASE DEFAULT
365      WRITE(*,*)'>> Trying to read mask from "'//TRIM(fmsk)//'" file'
366      ALLOCATE(msko(nlon_ou,nlat_ou))
367      msg='Missing or wrong "-m" option ; can be "noro", "spec" or a mask file'
368      CALL err(NF90_OPEN(fmsk,NF90_NOWRITE,fID)/=NF90_NOERR,msg)
369      CALL nc(NF90_INQ_VARID(fID,"MaskOcean",vID),"MaskOcean")    !--- MASK ID
370      CALL nc(NF90_GET_VAR(fID,vID,msko(:,:)))                    !--- MASK
371      CALL nc(NF90_CLOSE(fID))
372      msko(:,:)=1.0-msko(:,:)
373  END SELECT
374  DEALLOCATE(mski,h)
375
376!--- GET RID OF REDUNDANT LONGITUDE (USELESS FOR SPHEREPACK)
377  lon_ou=lon_ou(1:nlon_ou)
378
379!=== REGRIDDING ON THE TARGET OUTPUT GRID ======================================
380  WRITE(*,*)'>> Regridding on the target grid...'
381
382!--- COMPUTE BOOLEAN OUTPUT MASK
383  ALLOCATE(mask(nlon_ou,nlat_ou)); mask=0_1; WHERE(msko>=0.5) mask=1_1
384
385!--- MEAN OROGRAPHY
386  ALLOCATE(h0(nlon_ou,nlat_ou))
387  CALL trssph(intl, igrid_in, nlon_in, nlat_in, hs(:,:,1),                     &
388                    igrid_ou, nlon_ou, nlat_ou, h0,                            &
389    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
390  WHERE(h0<0..OR.mask==0_1) h0(:,:)=0.
391
392!--- STANDARD DEVIATION OF SUB-CELL SCALES OROGRAPHY
393  ALLOCATE(mu(nlon_ou,nlat_ou))
394  CALL trssph(1, igrid_in, nlon_in, nlat_in, hs(:,:,2),                        &
395                 igrid_ou, nlon_ou, nlat_ou, mu,                               &
396    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
397  DEALLOCATE(hs)
398  WHERE(mu(:,:)<0..OR.mask==0_1) mu(:,:)=0.; mu=SQRT(mu)
399
400!--- SQUARE OF LARGE AND SUB-CELL SCALES ZONAL SLOPE
401  ALLOCATE(dxZ2(nlon_ou,nlat_ou,2))
402  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,1),                      &
403                 igrid_ou, nlon_ou, nlat_ou, dxZ2(:,:,1),                      &
404    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
405  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,4),                      &
406                 igrid_ou, nlon_ou, nlat_ou, dxZ2(:,:,2),                      &
407    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
408  WHERE(mask==0_1); dxZ2(:,:,1)=0.; dxZ2(:,:,2)=0.; END WHERE
409
410!--- SQUARE OF LARGE AND SUB-CELL SCALES MERIDIONAL SLOPE
411  ALLOCATE(dyZ2(nlon_ou,nlat_ou,2))
412  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,2),                      &
413                 igrid_ou, nlon_ou, nlat_ou, dyZ2(:,:,1),                      &
414    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
415  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,5),                      &
416                 igrid_ou, nlon_ou, nlat_ou, dyZ2(:,:,2),                      &
417    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
418  WHERE(mask==0_1); dyZ2(:,:,1)=0.; dyZ2(:,:,2)=0.; END WHERE
419
420!--- PRODUCT OF LARGE AND SUB-CELL SCALES MERIDIONAL AND ZONAL SLOPES
421  ALLOCATE(dxyZ(nlon_ou,nlat_ou,2))
422  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,3),                      &
423                 igrid_ou, nlon_ou, nlat_ou, dxyZ(:,:,1),                      &
424    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
425  CALL trssph(1, igrid_in, nlon_in, nlat_in, dH  (:,:,6),                      &
426                 igrid_ou, nlon_ou, nlat_ou, dxyZ(:,:,2),                      &
427    wsave, lsave, lsvmin, work, lwork, lwkmin, dwork, ldwork, ierr)
428  WHERE(mask==0_1); dxyZ(:,:,1)=0.; dxyZ(:,:,2)=0.; END WHERE
429  DEALLOCATE(dH)
430
431!--- COMPUTE LARGE AND SUB-CELL SCALES DERIVED QUANTITIES
432  ALLOCATE(Xk(nlon_ou,nlat_ou,2)); Xk=(dxZ2+dyZ2)/2.
433  ALLOCATE(Xl(nlon_ou,nlat_ou,2)); Xl=(dxZ2-dyZ2)/2.
434  DEALLOCATE(dxZ2,dyZ2)
435  ALLOCATE(Xm(nlon_ou,nlat_ou,2)); Xm=dxyZ
436  DEALLOCATE(dxyZ)
437  ALLOCATE(Xp(nlon_ou,nlat_ou,2),Xq(nlon_ou,nlat_ou,2))
438  Xp=Xk; Xq=Xk; Xk=SQRT(Xl**2+Xm**2); Xp=Xp-Xk; Xq=Xq+Xk
439  DEALLOCATE(Xk)
440  WHERE(ABS(Xm)<=1e-16) Xm=1e-16*SIGN(1.,Xm)
441  ALLOCATE(Zthe(nlon_ou,nlat_ou)); Zthe=0.5*ATAN2(Xm(:,:,1),Xl(:,:,1))/d2r
442  DEALLOCATE(Xm,Xl)
443  ALLOCATE(Zgam(nlon_ou,nlat_ou)); Zgam=0.
444  WHERE(Xq(:,:,1)>0..AND.Xp(:,:,1)>1.E-8) Zgam(:,:)=Xp(:,:,1)/Xq(:,:,1)
445  DEALLOCATE(Xp)
446  ALLOCATE(Zsig(nlon_ou,nlat_ou)); Zsig=SQRT(Xq(:,:,2))
447  WHERE(Xq(:,:,2)<=1.E-16) Zsig(:,:)=1.E-8
448  DEALLOCATE(Xq)
449  WHERE(mask==0_1); Zthe=0.; Zgam=0.; Zsig=0.; END WHERE
450
451!--- Save the data
452  WRITE(*,*)'>> Saving fields in a netcdf file...'
453  res_in=TRIM(int2str(nlon_in))//'x'//TRIM(int2str(nlat_in))
454  res_ou=TRIM(int2str(nlon_ou))//'x'//TRIM(int2str(nlat_ou))
455  f_ou='make_sso_'//TRIM(res_ou)//'_'//TRIM(f_in)
456  fnam=f_ou
457  CALL nc(NF90_CREATE(f_ou,NF90_CLOBBER,fID))
458
459  CALL nc(NF90_DEF_DIM(fID,'x',nlon_ou,xID))
460  CALL nc(NF90_DEF_VAR(fID,'x',NF90_REAL,xID,loID)      ,'x')
461  CALL nc(NF90_PUT_ATT(fID,loID,'long_name','Longitude'),'x')
462  CALL nc(NF90_PUT_ATT(fID,loID,'units','degrees_east') ,'x')
463
464  CALL nc(NF90_DEF_DIM(fID,'y',nlat_ou,yID))
465  CALL nc(NF90_DEF_VAR(fID,'y',NF90_REAL,yID,laID)      ,'y')
466  CALL nc(NF90_PUT_ATT(fID,laID,'long_name','Latitude') ,'y')
467  CALL nc(NF90_PUT_ATT(fID,laID,'units','degrees_north'),'y')
468
469  CALL nc(NF90_DEF_VAR(fID,'mask',NF90_REAL,[xID,yID],mskID),'mask')
470  IF(fmsk=='noro') &
471  CALL nc(NF90_DEF_VAR(fID,'Zphi',NF90_REAL,[xID,yID],phiID),'Zphi')
472  CALL nc(NF90_DEF_VAR(fID,'Zmea',NF90_REAL,[xID,yID],meaID),'Zmea')
473  CALL nc(NF90_DEF_VAR(fID,'mu'  ,NF90_REAL,[xID,yID], muID),'mu'  )
474  CALL nc(NF90_DEF_VAR(fID,'Zsig',NF90_REAL,[xID,yID],sigID),'Zsig')
475  CALL nc(NF90_DEF_VAR(fID,'Zgam',NF90_REAL,[xID,yID],gamID),'Zgam')
476  CALL nc(NF90_DEF_VAR(fID,'Zthe',NF90_REAL,[xID,yID],theID),'Zthe')
477  CALL nc(NF90_DEF_VAR(fID,'Zpic',NF90_REAL,[xID,yID],picID),'Zpic')
478  CALL nc(NF90_DEF_VAR(fID,'Zval',NF90_REAL,[xID,yID],valID),'Zval')
479
480  CALL nc(NF90_PUT_ATT(fID,mskID,'long_name','Fractional land mask'                      ),'mask')
481  IF(fmsk=='noro') &
482  CALL nc(NF90_PUT_ATT(fID,phiID,'long_name','Geopotential'                              ),'Zphi')
483  CALL nc(NF90_PUT_ATT(fID,meaID,'long_name','Mean orography'                            ),'Zmea')
484  CALL nc(NF90_PUT_ATT(fID, muID,'long_name','Std deviation of sub-cell scales orography'),'mu'  )
485  CALL nc(NF90_PUT_ATT(fID,sigID,'long_name','Slope along principal axis'                ),'Zsig')
486  CALL nc(NF90_PUT_ATT(fID,gamID,'long_name','Anisotropy (aspect ratio)'                 ),'Zgam')
487  CALL nc(NF90_PUT_ATT(fID,theID,'long_name','Orientation (principal axis)'              ),'Zthe')
488  CALL nc(NF90_PUT_ATT(fID,picID,'long_name','Maximum height'                            ),'Zpic')
489  CALL nc(NF90_PUT_ATT(fID,valID,'long_name','Minimum height'                            ),'Zval')
490
491  CALL nc(NF90_PUT_ATT(fID,mskID,'units','none'   ),'mask')
492  IF(fmsk=='noro') &
493  CALL nc(NF90_PUT_ATT(fID,phiID,'units','m'      ),'Zphi')
494  CALL nc(NF90_PUT_ATT(fID,meaID,'units','m'      ),'Zmea')
495  CALL nc(NF90_PUT_ATT(fID, muID,'units','m'      ),'mu'  )
496  CALL nc(NF90_PUT_ATT(fID,sigID,'units','m/m'    ),'Zsig')
497  CALL nc(NF90_PUT_ATT(fID,gamID,'units','none'   ),'Zgam')
498  CALL nc(NF90_PUT_ATT(fID,theID,'units','degrees'),'Zthe')
499  CALL nc(NF90_PUT_ATT(fID,picID,'units','m'      ),'Zpic')
500  CALL nc(NF90_PUT_ATT(fID,valID,'units','m'      ),'Zval')
501
502  CALL nc(NF90_PUT_ATT(fID,NF90_GLOBAL,'Conventions','COARDS/CF-1.0'))
503  CALL nc(NF90_PUT_ATT(fID,NF90_GLOBAL,'Initial_Grid',TRIM(res_in)))
504  CALL nc(NF90_PUT_ATT(fID,NF90_GLOBAL,'history',TRIM(call_seq)))
505  CALL nc(NF90_ENDDEF(fID))
506
507  CALL nc(NF90_PUT_VAR(fID, loID,lon_ou),'x' )
508  CALL nc(NF90_PUT_VAR(fID, laID,lat_ou),'y' )
509  CALL nc(NF90_PUT_VAR(fID,mskID,msko),'mask')
510  IF(fmsk=='noro') &
511  CALL nc(NF90_PUT_VAR(fID,phiID,Zphi),'Zphi')
512  CALL nc(NF90_PUT_VAR(fID,meaID,h0  ),'Zmea')
513  CALL nc(NF90_PUT_VAR(fID, muID,mu  ),'mu'  )
514  CALL nc(NF90_PUT_VAR(fID,sigID,Zsig),'Zsig')
515  CALL nc(NF90_PUT_VAR(fID,gamID,Zgam),'Zgam')
516  CALL nc(NF90_PUT_VAR(fID,theID,Zthe),'Zthe')
517  CALL nc(NF90_PUT_VAR(fID,picID,       h0+2*mu ),'Zpic')
518  CALL nc(NF90_PUT_VAR(fID,valID,MAX(0.,h0-2*mu)),'Zval')
519  CALL nc(NF90_CLOSE(fID))
520  WRITE(*,*)'Finished.'
521
522CONTAINS
523
524
525!-------------------------------------------------------------------------------
526!
527SUBROUTINE diffusion_filter(a,b)
528!
529!-------------------------------------------------------------------------------
530! Purpose: ECMWF bi-laplacian diffusion filter to limit aliasing.
531! Note that one third of the frequencies is skipped to allow some accuracy for
532! derived quantities computation.
533!-------------------------------------------------------------------------------
534  IMPLICIT NONE
535!-------------------------------------------------------------------------------
536! Arguments:
537  REAL, INTENT(INOUT) :: a(:,:), b(:,:)
538!-------------------------------------------------------------------------------
539! Local variables:
540  INTEGER :: m
541  REAL, ALLOCATABLE :: fn(:)
542!-------------------------------------------------------------------------------
543  ALLOCATE(fn(ndab)); fn=[(REAL(m*(m-1)),m=1,ndab)]
544  a(1,:)=a(1,:)/(1+4.*fn(:)/fn(ndab))**2
545  b(1,:)=b(1,:)/(1+4.*fn(:)/fn(ndab))**2
546  DO m=1,ndab
547    IF(m<=2*ndab/3) THEN
548      a(m,:)=a(m,:)/(1+5.*fn(:)/fn(ndab))**2
549      b(m,:)=b(m,:)/(1+5.*fn(:)/fn(ndab))**2
550    ELSE
551      a(m,:)=0.
552      b(m,:)=0.
553    END IF
554  END DO
555
556END SUBROUTINE diffusion_filter
557!
558!-------------------------------------------------------------------------------
559
560
561!-------------------------------------------------------------------------------
562!
563SUBROUTINE lowpass_filter(a,b,d,dd)
564!
565!-------------------------------------------------------------------------------
566! Purpose: ECMWF lowpass filter.
567!-------------------------------------------------------------------------------
568  IMPLICIT NONE
569!-------------------------------------------------------------------------------
570! Arguments:
571  REAL, INTENT(INOUT) :: a(:,:), b(:,:)
572  REAL, INTENT(IN)    :: d, dd
573!-------------------------------------------------------------------------------
574! Local variables:
575  INTEGER :: m, na
576  REAL, ALLOCATABLE  :: h(:)
577!-------------------------------------------------------------------------------
578  na=SIZE(a,1)
579  IF(ALLOCATED(h)) THEN; IF(SIZE(h)/=na) DEALLOCATE(h); END IF
580  IF(.NOT.ALLOCATED(h)) ALLOCATE(h(na))
581  CALL lowpass(d,dd,h)
582  DO m=1,SIZE(h); a(m,:)=a(m,:)*h; b(m,:)=b(m,:)*h; END DO
583
584END SUBROUTINE lowpass_filter
585!
586!-------------------------------------------------------------------------------
587
588
589!-------------------------------------------------------------------------------
590!
591SUBROUTINE bandpass_filter(a,b,d1,dd1,d2,dd2)
592!
593!-------------------------------------------------------------------------------
594! Purpose: ECMWF bandpass filter. Calls lowpass filter with 2 different scales.
595!-------------------------------------------------------------------------------
596  IMPLICIT NONE
597!-------------------------------------------------------------------------------
598! Arguments:
599  REAL, INTENT(INOUT) :: a(:,:), b(:,:)
600  REAL, INTENT(IN)    :: d1, dd1, d2, dd2
601!-------------------------------------------------------------------------------
602! Local variables:
603  INTEGER :: m, na
604  REAL, ALLOCATABLE  :: h(:), hh(:)
605!-------------------------------------------------------------------------------
606  na=SIZE(a,1)
607  IF(ALLOCATED(hh)) THEN; IF(SIZE(hh)/=na) DEALLOCATE(hh); END IF
608  IF(ALLOCATED(h )) THEN; IF(SIZE(h )/=na) DEALLOCATE(h ); END IF
609  IF(.NOT.ALLOCATED(hh)) ALLOCATE(hh(na))
610  IF(.NOT.ALLOCATED(h )) ALLOCATE(h (na))
611  CALL lowpass(d1,dd1,hh)
612  CALL lowpass(d2,dd2,h ); hh=hh-h
613  DO m=1,SIZE(h); a(m,:)=a(m,:)*hh; b(m,:)=b(m,:)*hh; END DO
614
615END SUBROUTINE bandpass_filter
616!
617!-------------------------------------------------------------------------------
618
619
620!-------------------------------------------------------------------------------
621!
622SUBROUTINE lowpass(d,dd,h)
623!
624!-------------------------------------------------------------------------------
625  IMPLICIT NONE
626!-------------------------------------------------------------------------------
627! Arguments:
628  REAL, INTENT(IN)    :: d, dd   ! d is DELTA/REarth ; dd is delta/REarth
629  REAL, INTENT(INOUT) :: h(:)
630!-------------------------------------------------------------------------------
631  INTEGER :: m, n
632  REAL    :: hd
633!-------------------------------------------------------------------------------
634  n=SIZE(h); hd=0.5*d
635  h(:)=[((SIN(m*(hd-dd))/m+(COS(hp+m*hd)*SIN(hp+m*dd))/(hp/dd+m)               &
636        + SIN(m*(hd+dd))/m+(COS(hp-m*hd)*SIN(hp-m*dd))/(hp/dd-m))**2/d/d,m=1,n)]
637
638END SUBROUTINE lowpass
639!
640!-------------------------------------------------------------------------------
641
642
643!-------------------------------------------------------------------------------
644!
645SUBROUTINE spatial_filter_1(u,D,dd)
646!
647!-------------------------------------------------------------------------------
648! Purpose: Spatial filter ; using spectral lowpass filter.
649!-------------------------------------------------------------------------------
650  IMPLICIT NONE
651!-------------------------------------------------------------------------------
652! Arguments:
653  REAL, INTENT(INOUT) :: u(:,:)
654  REAL, INTENT(IN)    :: D, dd
655!-------------------------------------------------------------------------------
656! Local variables:
657  REAL, ALLOCATABLE  :: uu(:,:,:)
658!-------------------------------------------------------------------------------
659  ALLOCATE(uu(SIZE(u,1),SIZE(u,2),1)); uu(:,:,1)=u
660  CALL spatial_filter_m(uu,D,dd)
661  u=uu(:,:,1)
662  DEALLOCATE(uu)
663
664END SUBROUTINE spatial_filter_1
665!
666!-------------------------------------------------------------------------------
667
668
669!-------------------------------------------------------------------------------
670!
671SUBROUTINE spatial_filter_m(u,D,dd)
672!
673!-------------------------------------------------------------------------------
674! Purpose: Spatial filter ; using spectral lowpass filter.
675!-------------------------------------------------------------------------------
676  IMPLICIT NONE
677!-------------------------------------------------------------------------------
678! Arguments:
679  REAL, INTENT(INOUT) :: u(:,:,:)
680  REAL, INTENT(IN)    :: D, dd
681!-------------------------------------------------------------------------------
682! Local variables:
683  INTEGER :: ku
684  REAL, ALLOCATABLE  :: aa(:,:,:),bb(:,:,:)
685!-------------------------------------------------------------------------------
686  ku=SIZE(u,3)                                   !--- NUMBER OF FIELDS TO SMOOTH
687  ALLOCATE(aa(mdab,ndab,ku),bb(mdab,ndab,ku))
688
689!--- PROJECTION OF THE SUB-CELL SCALES FIELDS ON THE SPHERICAL HARMONICS BASE.
690  IF(lfast) THEN
691    CALL shaec(nlat_in,nlon_in,0,ku,u,idh,jdh,aa,bb,mdab,ndab,wsha,lsh,work,lwork,ierr)
692    CALL error('shaec',ierr)
693  ELSE
694    CALL shaes(nlat_in,nlon_in,0,ku,u,idh,jdh,aa,bb,mdab,ndab,wsha,lsh,work,lwork,ierr)
695    CALL error('shaes',ierr)
696  END IF
697
698!--- LOW PASS FILTER AT THE OUTPUT GRID RESOLUTION
699  DO n=1,ku; CALL lowpass_filter(aa(:,:,n),bb(:,:,n),D,dd); END DO
700
701!--- BACKWARD TRANSFORM FOR MEAN SUB-CELL SCALES PARAMETERS
702  IF(lfast) THEN
703    CALL shsec(nlat_in,nlon_in,0,ku,u,idh,jdh,aa,bb,mdab,ndab,wshs,lsh,work,lwork,ierr)
704    CALL error('shsec',ierr)
705  ELSE
706    CALL shses(nlat_in,nlon_in,0,ku,u,idh,jdh,aa,bb,mdab,ndab,wshs,lsh,work,lwork,ierr)
707    CALL error('shses',ierr)
708  END IF
709  DEALLOCATE(aa,bb)
710
711END SUBROUTINE spatial_filter_m
712!
713!-------------------------------------------------------------------------------
714
715
716!-------------------------------------------------------------------------------
717!
718SUBROUTINE grid_noro0(xin,yin,zin,xou,you,zphi,mask)
719!
720!-------------------------------------------------------------------------------
721! Purpose: Sub-cell scales orographic parameters coomputation. Angles in radians
722! Notes: Input coordinates contain a longitude periodic redundant point.
723!-------------------------------------------------------------------------------
724  IMPLICIT NONE
725!-------------------------------------------------------------------------------
726! Arguments:
727  REAL, INTENT(IN)  :: xin(:), yin(:)   !--- INPUT  COORDINATES (nxi)   (nyi)
728  REAL, INTENT(IN)  :: zin(:,:)         !--- INPUT  FIELD       (nxi,    nyi)
729  REAL, INTENT(IN)  :: xou(:), you(:)   !--- OUTPUT COORDINATES (nxo+1) (nyo)
730  REAL, INTENT(OUT) :: zphi(:,:)        !--- GEOPOTENTIAL       (nxo+1,  nyo)
731  REAL, INTENT(OUT) :: mask(:,:)        !--- FRACTIONAL MASK    (nxo+1,  nyo)
732!-------------------------------------------------------------------------------
733! Local variables:
734  REAL, ALLOCATABLE :: xi(:), xo_w(:), yo_s(:), zi(:,:), num_tot(:,:)
735  REAL, ALLOCATABLE :: yi(:), xo_e(:), yo_n(:),  w(:,:), num_lan(:,:)
736  REAL    :: dx, dy, wx, wy, zx, zy, ex, ey
737  INTEGER :: nxi, nxo, i, ii, nn
738  INTEGER :: nyi, nyo, j, jj, di
739!-------------------------------------------------------------------------------
740  nxi=SIZE(xin); nxo=SIZE(xou)-1; ex=SIGN(1.,xin(nxi)-xin(1))
741  nyi=SIZE(yin); nyo=SIZE(you);   ey=SIGN(1.,yin(nyi)-yin(1))
742
743!--- MARGIN TO ENSURE OUTPUT CELLS HAVE MATCHING INPUT CELLS
744  di=CEILING(1.+MAXVAL([xou(2:nxo+1)-xou(1:nxo)])/MINVAL([xin(2:nxi)-xin(1:nxi-1)]))
745
746!--- EXTENSION OF THE INPUT DATABASE TO PROCEED COMPUTATIONS AT BOUNDARIES
747  ALLOCATE(xi(nxi+2*di)); xi(:)=[xin(nxi-di:nxi-1)-2.*xpi,xin,xin(2:di+1)+2.*xpi]
748  ALLOCATE(yi(nyi+2));    yi(:)=[2.*yin(1)-yin(2),yin,2.*yin(nyi)-yin(nyi-1)]
749
750  ALLOCATE(zi(nxi+2*di,nyi+2))
751  zi(1      +di:nxi+  di,2:nyi+1)=zin(:,:)
752  zi(1         :      di,2:nyi+1)=zin(nxi-di:nxi-1,:)
753  zi(1+nxi  +di:nxi+2*di,2:nyi+1)=zin(2     :di+1 ,:)
754  zi(1         :nxi/2+di,      1)=zi (1+nxi/2:nxi  +di,    2)
755  zi(1+nxi/2+di:nxi+2*di,      1)=zi (1      :nxi/2+di,    2)
756  zi(1         :nxi/2+di,  nyi+2)=zi (1+nxi/2:nxi  +di,nyi+1)
757  zi(1+nxi/2+di:nxi+2*di,  nyi+2)=zi (1      :nxi/2+di,nyi+1)
758
759!  write(*,'(a,2f10.5,a,2f10.5)')'xin=',xin(1:2),'...',xin(nxi-1:nxi)
760!  write(*,'(a,2f10.5,a,2f10.5)')'yin=',yin(1:2),'...',yin(nyi-1:nyi)
761!  write(*,'(a,2f10.5,a,2f10.5)')'xou=',xou(1:2),'...',xou(nxo:nxo+1)
762!  write(*,'(a,2f10.5,a,2f10.5)')'you=',you(1:2),'...',you(nyo-1:nyo)
763!  write(*,*)'xi=',xi(1:di+1),'...',xi(nxi+di:nxi+2*di)
764!  write(*,'(a,3f10.5,a,3f10.5)')'yi=',yi(1:3),'...',yi(nyi:nyi+2)
765
766!--- COMPUTE LIMITS OF MODEL GRIDPOINT AREA (REGULAR GRID) bdac
767  ALLOCATE(xo_w(nxo+1)); xo_w=(xou+[2.*xou(    1)-xou(  2),xou(1:nxo  )])/2.0
768  ALLOCATE(yo_s(nyo  )); yo_s=(you+[2.*you(    1)-you(  2),you(1:nyo-1)])/2.0
769  ALLOCATE(xo_e(nxo+1)); xo_e=(xou+[xou(2:nxo+1),2.*xou(nxo+1)-xou(nxo)])/2.0
770  ALLOCATE(yo_n(nyo  )); yo_n=(you+[you(2:nyo  ),2.*you(nyo)-you(nyo-1)])/2.0
771
772!--- INITIALIZATIONS:
773  ALLOCATE(w(nxo+1,nyo)); w(:,:)=0.0; zphi(:,:)=0.0
774
775!--- SUMMATION OVER GRIDPOINT AREA
776  zx=2.*xpi/REAL(nxi)                            !--- LON CELL LENGTH (1-SPHERE)
777  dx=zx/2.                                       !--- HALF "
778  ALLOCATE(num_tot(nxo+1,nyo)); num_tot(:,:)=0.
779  ALLOCATE(num_lan(nxo+1,nyo)); num_lan(:,:)=0.
780  DO ii=1,nxo+1
781    DO jj=1,nyo
782      DO j=2,nyi+1
783        zy=xpi/REAL(nyi)*COS(yi(j))              !--- LAT CELL LENGTH (1-SPHERE)
784        dy=zy/2.
785        wy=MAX(0.,MIN(zy,MINVAL(dy+ey*[yo_n(jj)-yi(j),yi(j)-yo_s(jj)])))
786        IF(wy==0.) CYCLE
787        DO i=2,nxi+2*di-1
788          wx=MAX(0.,MIN(zx,MINVAL(dx+ex*[xo_e(ii)-xi(i),xi(i)-xo_w(ii)])*COS(yi(j))))
789          IF(wx==0.) CYCLE
790          num_tot(ii,jj)=num_tot(ii,jj)+1.0
791          IF(zi(i,j)>=1.) num_lan(ii,jj)=num_lan(ii,jj)+1.0
792          w   (ii,jj)=   w(ii,jj)+wx*wy
793          zphi(ii,jj)=zphi(ii,jj)+wx*wy*zi(i,j)
794        END DO
795      END DO
796    END DO
797  END DO
798
799!--- COMPUTE FRACTIONAL MASK AND GEOPOTENTIAL
800  WHERE(w(:,:)/=0.0) mask=num_lan(:,:)/num_tot(:,:)
801  nn=COUNT(w(:,:)==0.0)
802  IF(nn/=0) WRITE(*,*)'Problem with weight ; vanishing occurrences: ',nn
803  WHERE(w/=0.0) zphi=zphi/w
804  WHERE(w==0.0.OR.mask<0.1) zphi=0.0
805  zphi(nxo+1,:)=zphi(1,:)
806  zphi(:,  1)=SUM(w(1:nxo,  1)*zphi(1:nxo,  1))/SUM(w(1:nxo,  1))
807  zphi(:,nyo)=SUM(w(1:nxo,nyo)*zphi(1:nxo,nyo))/SUM(w(1:nxo,nyo))
808
809END SUBROUTINE grid_noro0
810!
811!-------------------------------------------------------------------------------
812
813
814!-------------------------------------------------------------------------------
815!
816SUBROUTINE nc(ncres,var)
817!
818!-------------------------------------------------------------------------------
819! Purpose: NetCDF errors handling.
820!-------------------------------------------------------------------------------
821  IMPLICIT NONE
822!-------------------------------------------------------------------------------
823! Arguments:
824  INTEGER,          INTENT(IN)           :: ncres
825  CHARACTER(LEN=*), OPTIONAL, INTENT(IN) :: var
826!-------------------------------------------------------------------------------
827! Local variables:
828  CHARACTER(LEN=256) :: msg
829!-------------------------------------------------------------------------------
830  IF(ncres/=NF90_NoErr) THEN
831    msg='Error in routine '//TRIM(sub)
832    IF(fnam/='')     msg=TRIM(msg)//' for file "'//TRIM(fnam)//'"'
833    IF(PRESENT(var)) msg=TRIM(msg)//'" and variable "'//TRIM(var)//'"'
834    WRITE(*,*)TRIM(msg)//': '//NF90_STRERROR(ncres); STOP
835  END IF
836
837END SUBROUTINE nc
838!
839!-------------------------------------------------------------------------------
840
841
842!-------------------------------------------------------------------------------
843!
844ELEMENTAL FUNCTION int2str(val)
845!
846!-------------------------------------------------------------------------------
847  IMPLICIT NONE
848!-------------------------------------------------------------------------------
849! Purpose: convert an integer into a string.
850!-------------------------------------------------------------------------------
851! Arguments:
852  CHARACTER(LEN=128)  :: int2str
853  INTEGER, INTENT(IN) :: val
854!-------------------------------------------------------------------------------
855! Local variable:
856  CHARACTER(LEN=128) :: s1
857!-------------------------------------------------------------------------------
858  WRITE(s1,*)val; int2str=TRIM(ADJUSTL(s1))
859
860END FUNCTION int2str
861!-------------------------------------------------------------------------------
862
863
864!-------------------------------------------------------------------------------
865!
866SUBROUTINE err(ierr,str)
867!
868!-------------------------------------------------------------------------------
869  IMPLICIT NONE
870!-------------------------------------------------------------------------------
871! Argument:
872  LOGICAL,          INTENT(IN) :: ierr
873  CHARACTER(LEN=*), INTENT(IN) :: str
874!-------------------------------------------------------------------------------
875  IF(.NOT.ierr) RETURN
876  WRITE(*,*)TRIM(str)
877  STOP
878
879END SUBROUTINE err
880!
881!-------------------------------------------------------------------------------
882
883
884!-------------------------------------------------------------------------------
885!
886SUBROUTINE error(sub,ierr)
887!
888!-------------------------------------------------------------------------------
889  IMPLICIT NONE
890!-------------------------------------------------------------------------------
891! Arguments:
892  CHARACTER(LEN=*), INTENT(IN) :: sub
893  INTEGER,          INTENT(IN) :: ierr
894!-------------------------------------------------------------------------------
895  IF(ierr==0) RETURN
896  WRITE(*,*)'In routine "'//TRIM(sub)//'": error in the specification of argume&
897      &nt nr. '//TRIM(int2str(ierr))
898  STOP
899END SUBROUTINE error
900!
901!-------------------------------------------------------------------------------
902
903
904!-------------------------------------------------------------------------------
905!
906ELEMENTAL FUNCTION real2str(val)
907!
908!-------------------------------------------------------------------------------
909  IMPLICIT NONE
910!-------------------------------------------------------------------------------
911! Purpose: convert an integer into a string.
912!-------------------------------------------------------------------------------
913! Arguments:
914  CHARACTER(LEN=128) :: real2str
915  REAL,   INTENT(IN) :: val
916  INTEGER :: r, s
917!-------------------------------------------------------------------------------
918                s=NINT(val); r=NINT(1000.*(val-REAL(s)))
919  IF(r<0) THEN; s= INT(val); r= INT(1000.*(val-REAL(s))); END IF
920  real2str=TRIM(ADJUSTL(int2str(s)))//'.'//TRIM(ADJUSTL(int2str(r)))
921
922END FUNCTION real2str
923!-------------------------------------------------------------------------------
924
925
926!-------------------------------------------------------------------------------
927!
928SUBROUTINE flip(a)
929!
930!-------------------------------------------------------------------------------
931  IMPLICIT NONE
932!-------------------------------------------------------------------------------
933! Arguments:
934  REAL, ALLOCATABLE, INTENT(INOUT) :: a(:,:)
935!-------------------------------------------------------------------------------
936! Local variables:
937  INTEGER :: k, n
938  REAL, ALLOCATABLE :: tmp(:,:)
939!-------------------------------------------------------------------------------
940  n=SIZE(a,2)
941  ALLOCATE(tmp(n,SIZE(a,1))); DO k=1,n; tmp(k,:)=a(:,k); END DO
942  CALL MOVE_ALLOC(FROM=tmp,TO=a)
943
944END SUBROUTINE flip
945!
946!-------------------------------------------------------------------------------
947
948
949!-------------------------------------------------------------------------------
950ELEMENTAL LOGICAL FUNCTION is_int(str) RESULT(out)
951!-------------------------------------------------------------------------------
952  IMPLICIT NONE
953!-------------------------------------------------------------------------------
954! Purpose: tests if str is a valid integer number
955!-------------------------------------------------------------------------------
956  CHARACTER(LEN=*), INTENT(IN) :: str
957  INTEGER :: k, k0
958!-------------------------------------------------------------------------------
959  out=LEN_TRIM(str)/=0; IF(.NOT.out) RETURN
960  k0=1; IF(INDEX('+-',str(1:1))/=0) k0=2
961  DO k=k0,LEN_TRIM(str); IF(INDEX('0123456789',str(k:k))==0) out=.FALSE.; END DO
962END FUNCTION is_int
963!-------------------------------------------------------------------------------
964
965!-------------------------------------------------------------------------------
966ELEMENTAL LOGICAL FUNCTION is_dec(str) RESULT(out)
967!-------------------------------------------------------------------------------
968  IMPLICIT NONE
969!-------------------------------------------------------------------------------
970! Purpose: tests if str is a valid decimal number or an integer
971!-------------------------------------------------------------------------------
972  CHARACTER(LEN=*), INTENT(IN) :: str
973  INTEGER :: ix
974!-------------------------------------------------------------------------------
975  out=LEN_TRIM(str)/=0; IF(.NOT.out) RETURN
976
977  !--- TEST IF THE NUMBER IS A PURE INTEGER
978  IF(is_int(str)) RETURN
979
980  !--- CHECK FOR POINT SEPARATOR (SHOULD HAVE A STRING INT BEFORE AND/OR AFTER
981  ix=INDEX(str,'.'); out=ix>0.AND.LEN_TRIM(str)/=1; IF(.NOT.out) RETURN
982
983  !--- CHECK SURROUNDING STRINGS ARE INTEGERS
984  out=is_int(str(1:ix-1)); IF(ix==LEN_TRIM(str)) RETURN
985  out=out.AND.is_int(str(ix+1:LEN_TRIM(str)))
986END FUNCTION is_dec
987!-------------------------------------------------------------------------------
988
989!-------------------------------------------------------------------------------
990ELEMENTAL LOGICAL FUNCTION is_num(str) RESULT(out)
991!-------------------------------------------------------------------------------
992  IMPLICIT NONE
993!-------------------------------------------------------------------------------
994! Purpose: test if str is a valid integer/decimal/scientific notation number
995!-------------------------------------------------------------------------------
996  CHARACTER(LEN=*),  INTENT(IN) :: str
997  INTEGER :: ix, ls
998!-------------------------------------------------------------------------------
999  out=LEN_TRIM(str)/=0; IF(.NOT.out) RETURN; ls=LEN_TRIM(str)
1000
1001  !--- TEST IF THE NUMBER IS A PURE INTEGER OR A PURE DECIMAL NUMBER
1002  IF(is_int(str)) RETURN
1003  IF(is_dec(str)) RETURN
1004
1005  !--- CHECK FOR "D" OR "E" SEPARATOR
1006  ix=MAX(INDEX(strLow(str),'d'),INDEX(strLow(str),'e')); out=ix>0
1007  IF(.NOT.out) RETURN
1008
1009  !--- CHECK SURROUNDING STRINGS ARE NUMBERS
1010  out=is_dec(str(1:ix-1)).AND.is_int(str(ix+1:ls))
1011END FUNCTION is_num
1012!-------------------------------------------------------------------------------
1013
1014!-------------------------------------------------------------------------------
1015INTEGER FUNCTION str2int(str) RESULT(out)
1016!-------------------------------------------------------------------------------
1017  IMPLICIT NONE
1018!-------------------------------------------------------------------------------
1019! Purpose: convert a string into an integer
1020!-------------------------------------------------------------------------------
1021  CHARACTER(LEN=*), INTENT(IN) :: str
1022  INTEGER :: k
1023!-------------------------------------------------------------------------------
1024  CALL err(.NOT.is_int(str),'Invalid integer "'//TRIM(str)//'"')
1025  out=0
1026  DO k=1,LEN_TRIM(str); out=10*out+INT(INDEX('0123456789',str(k:k))-1); END DO
1027END FUNCTION str2int
1028!-------------------------------------------------------------------------------
1029
1030!-------------------------------------------------------------------------------
1031REAL FUNCTION str2real(str) RESULT(out)
1032!-------------------------------------------------------------------------------
1033  IMPLICIT NONE
1034!-------------------------------------------------------------------------------
1035! Purpose: Convert a string into an real
1036!-------------------------------------------------------------------------------
1037  CHARACTER(LEN=*), INTENT(IN) :: str
1038  INTEGER :: ix, l
1039!-------------------------------------------------------------------------------
1040  CALL err(.NOT.is_num(str),'Invalid number "'//TRIM(str)//'"')
1041  ix=MAX(INDEX(strLow(str),'d'),INDEX(strLow(str),'e')); l=LEN_TRIM(str)
1042  IF(ix==0) out=dec2real(str)
1043  IF(ix/=0) out=dec2real(str(1:ix-1))*10.**str2int(str(ix+1:l))
1044END FUNCTION str2real
1045!-------------------------------------------------------------------------------
1046
1047!-------------------------------------------------------------------------------
1048ELEMENTAL REAL FUNCTION dec2real(str) RESULT(out)
1049!-------------------------------------------------------------------------------
1050  IMPLICIT NONE
1051!-------------------------------------------------------------------------------
1052! Purpose: convert a valid (already checked) string into an real
1053!-------------------------------------------------------------------------------
1054  CHARACTER(LEN=*), INTENT(IN) :: str
1055  INTEGER :: k, k0, ix
1056!-------------------------------------------------------------------------------
1057  out=0.
1058  k0=1; IF(INDEX('+-',str(1:1))/=0) k0=2; ix=INDEX(str,'.')     !--- CHECK SIGN
1059  DO k=k0,LEN_TRIM(str); IF(k==ix) CYCLE                        !--- SKIP .+-
1060    out=out*10.+REAL(INDEX('0123456789',str(k:k))-1)
1061  END DO
1062  IF(ix/=0) out=out*10.**(ix-LEN_TRIM(str))                     !--- COMA
1063  IF(k0==2) out=-out                                            !--- SIGN
1064END FUNCTION dec2real
1065!-------------------------------------------------------------------------------
1066
1067!-------------------------------------------------------------------------------
1068!
1069ELEMENTAL CHARACTER(LEN=256) FUNCTION strLow(str) RESULT(out)
1070!
1071!-------------------------------------------------------------------------------
1072  IMPLICIT NONE
1073!-------------------------------------------------------------------------------
1074! Purpose: Lower case conversion.
1075!-------------------------------------------------------------------------------
1076! Parameters:
1077  CHARACTER(LEN=*), INTENT(IN) :: str
1078!-------------------------------------------------------------------------------
1079! Local variable:
1080  INTEGER :: k, ix
1081!-------------------------------------------------------------------------------
1082  out=str
1083  DO k=1,LEN(str)
1084    ix=IACHAR(str(k:k)); IF(64<ix.AND.ix<91) out(k:k)=ACHAR(ix+32)
1085  END DO
1086
1087END FUNCTION strLow
1088!
1089!-------------------------------------------------------------------------------
1090
1091END PROGRAM make_sso
1092!
1093!-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.