source: palm/trunk/UTIL/inifor/src/inifor_transform.f90 @ 4550

Last change on this file since 4550 was 4523, checked in by eckhard, 4 years ago

fixed constant-density pressure extrapolation, respect integer working precision

  • Property svn:keywords set to Id
File size: 58.7 KB
RevLine 
[3447]1!> @file src/inifor_transform.f90
[2696]2!------------------------------------------------------------------------------!
[2718]3! This file is part of the PALM model system.
[2696]4!
[2718]5! PALM is free software: you can redistribute it and/or modify it under the
6! terms of the GNU General Public License as published by the Free Software
7! Foundation, either version 3 of the License, or (at your option) any later
[2696]8! version.
9!
[2718]10! PALM is distributed in the hope that it will be useful, but WITHOUT ANY
11! WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12! A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
[2696]13!
14! You should have received a copy of the GNU General Public License along with
15! PALM. If not, see <http://www.gnu.org/licenses/>.
16!
[4481]17! Copyright 2017-2020 Leibniz Universitaet Hannover
18! Copyright 2017-2020 Deutscher Wetterdienst Offenbach
[2696]19!------------------------------------------------------------------------------!
20!
21! Current revisions:
22! -----------------
[3183]23!
24!
25! Former revisions:
26! -----------------
27! $Id: inifor_transform.f90 4523 2020-05-07 15:58:16Z raasch $
[4523]28! bugfix: pressure extrapolation
29! respect integer working precision (iwp) specified in inifor_defs.f90
30! moved fill_water_cells() routine here
31! remove unused routine, appropriately renamed constand_density_pressure()
32!
33!
34! 4481 2020-03-31 18:55:54Z maronga
[3866]35! Use PALM's working precision
36! Improved coding style
37!
38!
39! 3785 2019-03-06 10:41:14Z eckhard
[3779]40! Remove basic state pressure before computing geostrophic wind
41!  - Introduced new level-based profile averaging routine that does not rely on
42!    external weights average_profile()
43!  - Renamed original weights-based routine average_profile() ->
44!    interp_average_profile()
45! Average geostrophic wind components on coarse COSMO levels instead of fine PALM levels
46!  - Introduced new profile interpolation routine for interpolating single
47!    profiles from COSMO to PALM levels
48!  - Renamed original array variant interpolate_1d() -> interpolate_1d_arr()
49!
50!
51!
52! 3716 2019-02-05 17:02:38Z eckhard
[3716]53! Include out-of-bounds error message in log
54!
55!
56! 3680 2019-01-18 14:54:12Z knoop
[3678]57! Check if set of averaging columns is empty
58!
59!
60! 3618 2018-12-10 13:25:22Z eckhard
[3618]61! Prefixed all INIFOR modules with inifor_, removed unused variables
62!
63!
64! 3615 2018-12-10 07:21:03Z raasch
[3615]65! bugfix: abort replaced by inifor_abort
66!
67! 3614 2018-12-10 07:05:46Z raasch
[3614]68! unused variables removed
69!
70! 3613 2018-12-07 18:20:37Z eckhard
[3613]71! Use averaged heights profile for level-based averaging instead of modified
72!    COSMO heights array
73!
74!
75! 3557 2018-11-22 16:01:22Z eckhard
[3557]76! Updated documentation
77!
78!
79! 3537 2018-11-20 10:53:14Z eckhard
[3534]80! bugfix: working precision added
81!
82! 3447 2018-10-29 15:52:54Z eckhard
[3447]83! Renamed source files for compatibilty with PALM build system
84!
85!
86! 3395 2018-10-22 17:32:49Z eckhard
[3395]87! Switched addressing of averaging regions from index bounds to list of columns
88! Added routines for the computation of geostrophic winds including:
89!    - the downward extrapolation of density (linear) and
90!    - pressure (hydrostatic equation) and
91!    - rotation of geostrophic wind components to PALM frame of reference
92!
93! 3183 2018-07-27 14:25:55Z suehring
[3182]94! Introduced new PALM grid stretching
95! Removed unnecessary subroutine parameters
96! Renamed kcur to k_intermediate
[2696]97!
98!
[3183]99! 3182 2018-07-27 13:36:03Z suehring
[2696]100! Initial revision
101!
102!
103!
104! Authors:
105! --------
[3557]106!> @author Eckhard Kadasch (Deutscher Wetterdienst, Offenbach)
[2696]107!
108! Description:
109! ------------
110!> The transform module provides INIFOR's low-level transformation and
111!> interpolation routines. The rotated-pole transformation routines phirot2phi,
112!> phi2phirot, rlarot2rla, rla2rlarot, uv2uvrot, and uvrot2uv are adapted from
113!> int2lm's utility routines.
114!------------------------------------------------------------------------------!
[3680]115#if defined ( __netcdf )
[3618]116 MODULE inifor_transform
[2696]117
[3618]118    USE inifor_control
119    USE inifor_defs,                                                           &
[4523]120        ONLY: BETA, G, P_SL, PI, RD, T_SL, TO_DEGREES, TO_RADIANS, WATER_ID,   &
121              iwp, wp
[3618]122    USE inifor_types
123    USE inifor_util,                                                           &
[3779]124        ONLY: get_basic_state, real_to_str, str
[2696]125
126    IMPLICIT NONE
127
128 CONTAINS
129
[3779]130
[3866]131 SUBROUTINE interpolate_1d(in_arr, out_arr, outgrid)
132    TYPE(grid_definition), INTENT(IN) ::  outgrid
133    REAL(wp), INTENT(IN)              ::  in_arr(:)
134    REAL(wp), INTENT(OUT)             ::  out_arr(:)
[3779]135
[4523]136    INTEGER(iwp) :: k, l, nz
[3779]137
[3866]138    nz = UBOUND(out_arr, 1)
[3779]139
[3866]140    DO  k = nz, LBOUND(out_arr, 1), -1
[3779]141
142!
[3866]143!--    TODO: Remove IF clause and extrapolate based on a critical vertical
144!--    TODO: index marking the lower bound of COSMO-DE data coverage.
145!--    Check for negative interpolation weights indicating grid points
146!--    below COSMO-DE domain and extrapolate from the top in such cells.
147       IF (outgrid%w(1,k,1) < -1.0_wp .AND. k < nz)  THEN
148          out_arr(k) = out_arr(k+1)
149       ELSE
150          out_arr(k) = 0.0_wp
151          DO  l = 1, 2
152             out_arr(k) = out_arr(k) +                                      &
153                 outgrid%w(1,k,l) * in_arr(outgrid%kkk(1,k,l) )
154          ENDDO
155       ENDIF
156    ENDDO
[3779]157
[3866]158 END SUBROUTINE interpolate_1d
[3779]159
160
[2696]161!------------------------------------------------------------------------------!
162! Description:
163! ------------
164!> Interpolates linearly in the vertical direction in very column (i,j) of the
165!> output array outvar(i,j,:) using values of the source array invar. In cells
166!> that are outside the COSMO-DE domain, indicated by negative interpolation
167!> weights, extrapolate constantly from the cell above.
168!>
169!> Input parameters:
170!> -----------------
171!> invar : Array of source data
172!>
[3866]173!> outgrid%kk : Array of vertical neighbour indices. kk(i,j,k,:) contain the
[2696]174!>     indices of the two vertical neighbors of PALM-4U point (i,j,k) on the
175!>     input grid corresponding to the source data invar.
176!>
[3866]177!> outgrid%w_verti : Array of weights for vertical linear interpolation
[2696]178!>     corresponding to neighbour points indexed by kk.
179!>
180!> Output papameters:
181!> ------------------
182!> outvar : Array of interpolated data
183!------------------------------------------------------------------------------!
[3866]184 SUBROUTINE interpolate_1d_arr(in_arr, out_arr, outgrid)
185    TYPE(grid_definition), INTENT(IN) ::  outgrid
186    REAL(wp), INTENT(IN)              ::  in_arr(0:,0:,0:)
187    REAL(wp), INTENT(OUT)             ::  out_arr(0:,0:,:)
[2696]188
[4523]189    INTEGER(iwp) :: i, j, k, l, nz
[2696]190
[3866]191    nz = UBOUND(out_arr, 3)
[2696]192
[3866]193    DO  j = LBOUND(out_arr, 2), UBOUND(out_arr, 2)
194    DO  i = LBOUND(out_arr, 1), UBOUND(out_arr, 1)
195    DO  k = nz, LBOUND(out_arr, 3), -1
[2696]196
[3557]197!
[3866]198!--    TODO: Remove IF clause and extrapolate based on a critical vertical
199!--    TODO: index marking the lower bound of COSMO-DE data coverage.
200!--    Check for negative interpolation weights indicating grid points
201!--    below COSMO-DE domain and extrapolate from the top in such cells.
202       IF (outgrid%w_verti(i,j,k,1) < -1.0_wp .AND. k < nz)  THEN
203          out_arr(i,j,k) = out_arr(i,j,k+1)
204       ELSE
205          out_arr(i,j,k) = 0.0_wp
206          DO  l = 1, 2
207             out_arr(i,j,k) = out_arr(i,j,k) +                              &
208                 outgrid%w_verti(i,j,k,l) *                               &
209                 in_arr(i,j,outgrid%kk(i,j,k, l) )
210          ENDDO
211       ENDIF
212    ENDDO
213    ENDDO
214    ENDDO
215 END SUBROUTINE interpolate_1d_arr
[2696]216
217
218!------------------------------------------------------------------------------!
219! Description:
220! ------------
221!> Interpolates bi-linearly in horizontal planes on every k level of the output
222!> array outvar(:,:,k) using values of the source array invar(:,:,:). The source
223!> (invar) and interpolation array (outvar) need to have matching dimensions.
224!>
225!> Input parameters:
226!> -----------------
227!> invar : Array of source data
228!>
[3866]229!> outgrid%ii,%jj : Array of neighbour indices in x and y direction.
[2696]230!>     ii(i,j,k,:), and jj(i,j,k,:) contain the four horizontal neighbour points
231!>     of PALM-4U point (i,j,k) on the input grid corresponding to the source
232!>     data invar. (The outgrid carries the relationship with the ingrid in the
[3779]233!      form of the interpolation weights.)
[2696]234!>
[3866]235!> outgrid%w_horiz: Array of weights for horizontal bi-linear interpolation
[2696]236!>     corresponding to neighbour points indexed by ii and jj.
237!>
238!> Output papameters:
239!> ------------------
240!> outvar : Array of interpolated data
241!------------------------------------------------------------------------------!
[3866]242 SUBROUTINE interpolate_2d(invar, outvar, outgrid, ncvar)
[3557]243!
[3866]244!-- I index 0-based for the indices of the outvar to be consistent with the
245!-- outgrid indices and interpolation weights.
246    TYPE(grid_definition), INTENT(IN)  ::  outgrid
247    REAL(wp), INTENT(IN)               ::  invar(0:,0:,0:)
248    REAL(wp), INTENT(OUT)              ::  outvar(0:,0:,0:)
249    TYPE(nc_var), INTENT(IN), OPTIONAL ::  ncvar
[2696]250
[4523]251    INTEGER(iwp) ::  i, j, k, l
[2696]252
[3557]253!
[3866]254!-- TODO: check if input dimensions are consistent, i.e. ranges are correct
255    IF ( UBOUND(outvar, 3) .GT. UBOUND(invar, 3) )  THEN
256        message = "Output array for '" // TRIM(ncvar%name) // "' has ' more levels (" // &
[4523]257           TRIM(str(UBOUND(outvar, 3, kind=iwp))) // ") than input variable ("//&
258           TRIM(str(UBOUND(invar, 3, kind=iwp))) // ")."
[3866]259        CALL inifor_abort('interpolate_2d', message)
260    ENDIF
[2696]261
[3866]262    DO  k = 0, UBOUND(outvar, 3)
263    DO  j = 0, UBOUND(outvar, 2)
264    DO  i = 0, UBOUND(outvar, 1)
265       outvar(i,j,k) = 0.0_wp
266       DO  l = 1, 4
267         
268          outvar(i,j,k) = outvar(i,j,k) +                                   &
269             outgrid%w_horiz(i,j,l) * invar( outgrid%ii(i,j,l),         & 
270                                               outgrid%jj(i,j,l),         &
271                                               k )
[3785]272       ENDDO
[3866]273    ENDDO
274    ENDDO
275    ENDDO
[2696]276       
[3866]277 END SUBROUTINE interpolate_2d
[2696]278
279
[3557]280!------------------------------------------------------------------------------!
281! Description:
282! ------------
283!> Compute the horizontal average of the in_arr(:,:,:) and store it in
284!> out_arr(:)
285!------------------------------------------------------------------------------!
[3866]286 SUBROUTINE average_2d(in_arr, out_arr, ii, jj)
[4523]287    REAL(wp), INTENT(IN)                   ::  in_arr(0:,0:,0:)
288    REAL(wp), INTENT(OUT)                  ::  out_arr(0:)
289    INTEGER(iwp), INTENT(IN), DIMENSION(:) ::  ii, jj
[2696]290
[4523]291    INTEGER(iwp) ::  i, j, k, l
292    REAL(wp)     ::  ni
[2696]293
[3866]294    IF (SIZE(ii) /= SIZE(jj))  THEN
295       message = "Length of 'ii' and 'jj' index lists do not match." //     &
[4523]296          NEW_LINE(' ') // "ii has " // str(SIZE(ii, kind=iwp)) // " elements, " //   &
297          NEW_LINE(' ') // "jj has " // str(SIZE(jj, kind=iwp)) // "."
[3866]298       CALL inifor_abort('average_2d', message)
299    ENDIF
[3395]300
[3866]301    IF (SIZE(ii) == 0)  THEN
302       message = "No columns to average over; " //                          &
303                 "size of index lists 'ii' and 'jj' is zero."
304       CALL inifor_abort('average_2d', message)
305    ENDIF
[3678]306
[3866]307    DO  k = 0, UBOUND(out_arr, 1)
[2696]308
[3866]309       out_arr(k) = 0.0_wp
310       DO  l = 1, UBOUND(ii, 1)
311          i = ii(l)
312          j = jj(l)
313          out_arr(k) = out_arr(k) + in_arr(i, j, k)
[3785]314       ENDDO
[3395]315
[3866]316    ENDDO
[2696]317
[3866]318    ni = 1.0_wp / SIZE(ii)
319    out_arr(:) = out_arr(:) * ni
[2696]320
[3866]321 END SUBROUTINE average_2d
[2696]322
[3866]323
[3557]324!------------------------------------------------------------------------------!
325! Description:
326! ------------
327!> Three-dimensional interpolation driver. Interpolates from the source_array to
328!> the given palm_grid.
329!>
330!> The routine separates horizontal and vertical
331!> interpolation. In the horizontal interpolation step, the source_array data is
332!> interpolated along COSMO grid levels onto the intermediate grid (vertically
333!> as coarse as COSMO, horizontally as fine as PALM).
334!------------------------------------------------------------------------------!
[3866]335 SUBROUTINE interpolate_3d(source_array, palm_array, palm_intermediate, palm_grid)
336    TYPE(grid_definition), INTENT(IN) ::  palm_intermediate, palm_grid
337    REAL(wp), DIMENSION(:,:,:), INTENT(IN)  ::  source_array
338    REAL(wp), DIMENSION(:,:,:), INTENT(OUT) ::  palm_array
339    REAL(wp), DIMENSION(:,:,:), ALLOCATABLE ::  intermediate_array
[4523]340    INTEGER(iwp) ::  nx, ny, nlev
[2696]341
[3866]342    nx = palm_intermediate%nx
343    ny = palm_intermediate%ny
344    nlev = palm_intermediate%nz
[2696]345
[3557]346!
[3866]347!-- Interpolate from COSMO to intermediate grid. Allocating with one
348!-- less point in the vertical, since scalars like T have 50 instead of 51
349!-- points in COSMO.
350    ALLOCATE(intermediate_array(0:nx, 0:ny, 0:nlev-1)) !
[2696]351
[3866]352    CALL interpolate_2d(source_array, intermediate_array, palm_intermediate)
[2696]353
[3557]354!
[3866]355!-- Interpolate from intermediate grid to palm_grid grid, includes
356!-- extrapolation for cells below COSMO domain.
357    CALL interpolate_1d_arr(intermediate_array, palm_array, palm_grid)
[2696]358
[3866]359    DEALLOCATE(intermediate_array)
[2696]360
[3866]361 END SUBROUTINE interpolate_3d
[2696]362
363
[3557]364!------------------------------------------------------------------------------!
365! Description:
366! ------------
367!> Average data horizontally from the source_array over the region given by the
368!> averaging grid 'avg_grid' and store the result in 'profile_array'.
369!------------------------------------------------------------------------------!
[3866]370 SUBROUTINE interp_average_profile(source_array, profile_array, avg_grid)
371    TYPE(grid_definition), INTENT(IN)          ::  avg_grid
372    REAL(wp), DIMENSION(:,:,:), INTENT(IN)     ::  source_array
373    REAL(wp), DIMENSION(:), INTENT(OUT)        ::  profile_array
[2696]374
[4523]375    INTEGER(iwp) ::  i_source, j_source, k_profile, k_source, l, m
[2696]376
[3866]377    REAL ::  ni_columns
[2696]378
[3866]379    profile_array(:) = 0.0_wp
[2696]380
[3866]381    DO  l = 1, avg_grid%n_columns
382       i_source = avg_grid%iii(l)
383       j_source = avg_grid%jjj(l)
[2696]384
[3557]385!
[3866]386!--    Loop over PALM levels
387       DO  k_profile = avg_grid%k_min, UBOUND(profile_array, 1)
[3395]388
[3557]389!
[3866]390!--       Loop over vertical interpolation neighbours
391          DO  m = 1, 2
[3395]392
[3866]393             k_source = avg_grid%kkk(l, k_profile, m)
[3395]394
[3866]395             profile_array(k_profile) = profile_array(k_profile)            &
396                + avg_grid%w(l, k_profile, m)                             &
397                * source_array(i_source, j_source, k_source)
[3557]398!
[3866]399!--       Loop over vertical interpolation neighbours m
[3785]400          ENDDO
[3395]401
[3557]402!
[3866]403!--    Loop over PALM levels k_profile
[3785]404       ENDDO
[3395]405
[3866]406!
407!-- Loop over horizontal neighbours l
408    ENDDO
[3395]409
[3866]410    ni_columns = 1.0_wp / avg_grid%n_columns
411    profile_array(:) = profile_array(:) * ni_columns
412
[3557]413!
[3866]414!-- Constant extrapolation to the bottom
415    profile_array(1:avg_grid%k_min-1) = profile_array(avg_grid%k_min)
[3395]416
[3866]417 END SUBROUTINE interp_average_profile
[3779]418
419
420!------------------------------------------------------------------------------!
421! Description:
422! ------------
423!> Average data horizontally from the source_array over the region given by the
424!> averaging grid 'avg_grid' and store the result in 'profile_array'.
425!------------------------------------------------------------------------------!
[3866]426 SUBROUTINE average_profile( source_array, profile_array, avg_grid )
[3779]427
[3866]428    TYPE(grid_definition), INTENT(IN)          ::  avg_grid
429    REAL(wp), DIMENSION(:,:,:), INTENT(IN)     ::  source_array 
430    REAL(wp), DIMENSION(:), INTENT(OUT)        ::  profile_array
[3779]431
[4523]432    INTEGER(iwp) ::  i_source, j_source, l, nz, nlev
[3779]433
[3866]434    REAL(wp) ::  ni_columns
[3779]435
[3866]436    nlev = SIZE( source_array, 3 )
437    nz   = SIZE( profile_array, 1 )
[3779]438
[3866]439    IF ( nlev /= nz )  THEN
440       message = "Lengths of input and output profiles do not match: " //   &
441                 "cosmo_pressure(" // TRIM( str( nlev ) ) //                &
442                 "), profile_array(" // TRIM( str( nz ) )  // ")."
443       CALL inifor_abort('average_pressure_perturbation', message)
444    ENDIF
445   
446    profile_array(:) = 0.0_wp
[3779]447
[3866]448    DO  l = 1, avg_grid%n_columns
[3779]449
[3866]450       i_source = avg_grid%iii(l)
451       j_source = avg_grid%jjj(l)
[3779]452
[3866]453       profile_array(:) = profile_array(:)                                  &
454                        + source_array(i_source, j_source, :)
[3779]455
[3866]456    ENDDO
[3779]457
[3866]458    ni_columns = 1.0_wp / avg_grid%n_columns
459    profile_array(:) = profile_array(:) * ni_columns
[3779]460
[3866]461 END SUBROUTINE average_profile
[2696]462
463
[3557]464!------------------------------------------------------------------------------!
465! Description:
466! ------------
[3779]467!> This is a sister routine to average_profile() and differes from it in that
468!> it removes the COSMO basic state pressure from the input array before
469!> averaging.
470!------------------------------------------------------------------------------!
[3866]471 SUBROUTINE average_pressure_perturbation( cosmo_pressure, profile_array,   &
472                                           cosmo_grid, avg_grid )
[3779]473
[3866]474    TYPE(grid_definition), INTENT(IN)          ::  cosmo_grid, avg_grid
475    REAL(wp), DIMENSION(:,:,:), INTENT(IN)     ::  cosmo_pressure
476    REAL(wp), DIMENSION(:), INTENT(OUT)        ::  profile_array
[3779]477
[4523]478    INTEGER(iwp) ::  i_source, j_source, l, nz, nlev
[3779]479
[3866]480    REAL(wp)                            ::  ni_columns
481    REAL(wp), DIMENSION(:), ALLOCATABLE ::  basic_state_pressure
[3779]482
[3866]483    nlev = SIZE( cosmo_pressure, 3 )
484    nz   = SIZE( profile_array, 1 )
[3779]485
[3866]486    IF ( nlev /= nz )  THEN
487       message = "Lengths of input and output profiles do not match: " //   &
488                 "cosmo_pressure(" // TRIM( str( nlev ) ) //                &
489                 "), profile_array(" // TRIM( str( nz ) )  // ")."
490       CALL inifor_abort('average_pressure_perturbation', message)
491    ENDIF
[3779]492
[3866]493    ALLOCATE( basic_state_pressure(nz) )
494    profile_array(:) = 0.0_wp
[3779]495
[3866]496    DO  l = 1, avg_grid%n_columns
497       i_source = avg_grid%iii(l)
498       j_source = avg_grid%jjj(l)
[3779]499
500!
[3866]501!--    Compute pressure perturbation by removing COSMO basic state pressure
502       CALL get_basic_state( cosmo_grid%hfl(i_source,j_source,:), BETA,   &
503                             P_SL, T_SL, RD, G, basic_state_pressure )
[3779]504
[3866]505       profile_array(:) = profile_array(:)                                  &
506                        + cosmo_pressure(i_source, j_source, :)             &
507                        - basic_state_pressure(:)
[3779]508
509!
[3866]510!-- Loop over horizontal neighbours l
511    ENDDO
[3779]512
[3866]513    DEALLOCATE( basic_state_pressure )
[3779]514
[3866]515    ni_columns = 1.0_wp / avg_grid%n_columns
516    profile_array(:) = profile_array(:) * ni_columns
[3779]517
[3866]518 END SUBROUTINE average_pressure_perturbation
[3779]519
520
521!------------------------------------------------------------------------------!
522! Description:
523! ------------
[4523]524!> Computes average soil values in COSMO-DE water cells from neighbouring
525!> non-water cells. This is done as a preprocessing step for the COSMO-DE
526!> soil input arrays, which contain unphysical values for water cells.
527!>
528!> This routine computes the average of up to all nine neighbouring cells
529!> or keeps the original value, if not at least one non-water neightbour
530!> is available.
531!>
532!> By repeatedly applying this step, soil data can be extrapolated into
533!> 'water' regions occupying multiple cells, one cell per iteration.
534!>
535!> Input parameters:
536!> -----------------
537!> soiltyp : 2d map of COSMO-DE soil types
538!> nz : number of layers in the COSMO-DE soil
539!> niter : number iterations
540!>
541!> Output parameters:
542!> ------------------
543!> array : the soil array (i.e. water content or temperature)
[3557]544!------------------------------------------------------------------------------!
[4523]545 SUBROUTINE fill_water_cells(soiltyp, array, nz, niter)
546    INTEGER(iwp), DIMENSION(:,:,:), INTENT(IN) :: soiltyp
547    REAL(wp), DIMENSION(:,:,:), INTENT(INOUT)  :: array
548    INTEGER(iwp), INTENT(IN)                   :: nz, niter
[2696]549
[4523]550    REAL(wp), DIMENSION(nz)                    :: column
551    INTEGER(iwp), DIMENSION(:,:), ALLOCATABLE  :: old_soiltyp, new_soiltyp
552    INTEGER(iwp)                               :: l, i, j, nx, ny, n_cells, ii, jj, iter
553    INTEGER(iwp), DIMENSION(8)                 :: di, dj
[3395]554
[4523]555    nx = SIZE(array, 1)
556    ny = SIZE(array, 2)
557    di = (/ -1, -1, -1, 0,  0,  1, 1, 1 /)
558    dj = (/ -1,  0,  1, -1, 1, -1, 0, 1 /)
[3395]559
[4523]560    ALLOCATE(old_soiltyp(SIZE(soiltyp,1), &
561                         SIZE(soiltyp,2) ))
[3395]562
[4523]563    ALLOCATE(new_soiltyp(SIZE(soiltyp,1), &
564                         SIZE(soiltyp,2) ))
[3395]565
[4523]566    old_soiltyp(:,:) = soiltyp(:,:,1)
567    new_soiltyp(:,:) = soiltyp(:,:,1)
[3395]568
[4523]569    DO  iter = 1, niter
[3395]570
[4523]571       DO  j = 1, ny
572       DO  i = 1, nx
573       
574          IF (old_soiltyp(i,j) == WATER_ID)  THEN
[3395]575
[4523]576             n_cells = 0
577             column(:) = 0.0_wp
578             DO  l = 1, SIZE(di)
[3395]579
[4523]580                ii = MIN(nx, MAX(1_iwp, i + di(l)))
581                jj = MIN(ny, MAX(1_iwp, j + dj(l)))
582
583                IF (old_soiltyp(ii,jj) .NE. WATER_ID)  THEN
584                   n_cells = n_cells + 1
585                   column(:) = column(:) + array(ii,jj,:)
586                ENDIF
587
588             ENDDO
589
590!
591!--          Overwrite if at least one non-water neighbour cell is available
592             IF (n_cells > 0)  THEN
593                array(i,j,:) = column(:) / n_cells
594                new_soiltyp(i,j) = 0
595             ENDIF
596
597          ENDIF
598
599       ENDDO
600       ENDDO
601
602       old_soiltyp(:,:) = new_soiltyp(:,:)
603
[3866]604    ENDDO
[3395]605
[4523]606    DEALLOCATE(old_soiltyp, new_soiltyp)
[3395]607
[4523]608 END SUBROUTINE fill_water_cells
[3395]609
[4523]610
[3395]611!------------------------------------------------------------------------------!
612! Description:
613! ------------
614!> Takes the averaged pressure profile <p> and sets the lowest entry to the
615!> extrapolated pressure at the surface.
616!------------------------------------------------------------------------------!
[3866]617 SUBROUTINE get_surface_pressure(p, rho, avg_grid)
618    REAL(wp), DIMENSION(:), INTENT(IN)    ::  rho
619    REAL(wp), DIMENSION(:), INTENT(INOUT) ::  p
620    TYPE(grid_definition), INTENT(IN)     ::  avg_grid
[3395]621
[4523]622    REAL(wp)     ::  drhodz, dz, zk, rhok
623    INTEGER(iwp) ::  k_min
[3395]624
[3866]625    k_min = avg_grid%k_min
626    zk    = avg_grid%z(k_min)
627    rhok  = rho(k_min)
628    dz    = avg_grid%z(k_min + 1) - avg_grid%z(k_min)
[4523]629    drhodz = ( rho(k_min + 1) - rho(k_min) ) / dz
[3395]630
[4523]631    p(1) = linear_density_pressure( p(k_min), zk, rhok, drhodz,                &
632                                    z = 0.0_wp, g=G )
[3395]633
[3866]634 END SUBROUTINE get_surface_pressure
[3395]635
636
[4523]637 FUNCTION linear_density_pressure(pk, zk, rhok, drhodz, z, g)  RESULT(p)
[3395]638
[3866]639    REAL(wp), INTENT(IN)  ::  pk, zk, rhok, drhodz, g, z
640    REAL(wp) ::  p
[3395]641
[4523]642    p = pk + ( zk - z ) * g * ( rhok - 0.5_wp * drhodz * (zk - z) )
[3395]643
[4523]644 END FUNCTION linear_density_pressure
[3395]645
[2696]646!-----------------------------------------------------------------------------!
647! Description:
648! -----------
[3395]649!> This routine computes profiles of the two geostrophic wind components ug and
650!> vg.
651!-----------------------------------------------------------------------------!
[3866]652 SUBROUTINE geostrophic_winds(p_north, p_south, p_east, p_west, rho, f3,    &
653                              Lx, Ly, phi_n, lam_n, phi_g, lam_g, ug, vg)
[3395]654
[3866]655    REAL(wp), DIMENSION(:), INTENT(IN)  ::  p_north, p_south, p_east, p_west,  &
[3395]656                                            rho
[3866]657    REAL(wp), INTENT(IN)                ::  f3, Lx, Ly, phi_n, lam_n, phi_g, lam_g
658    REAL(wp), DIMENSION(:), INTENT(OUT) ::  ug, vg
659    REAL(wp)                            ::  facx, facy
[3395]660
[3866]661    facx = 1.0_wp / (Lx * f3)
662    facy = 1.0_wp / (Ly * f3)
[3395]663    ug(:) = - facy / rho(:) * (p_north(:) - p_south(:))
664    vg(:) =   facx / rho(:) * (p_east(:) - p_west(:))
665
666    CALL rotate_vector_field(                                                  &
667       ug, vg, angle = meridian_convergence_rotated(phi_n, lam_n, phi_g, lam_g)&
668    )
669
[3866]670 END SUBROUTINE geostrophic_winds
[3395]671
672
673!-----------------------------------------------------------------------------!
674! Description:
675! -----------
[2696]676!> This routine computes the inverse Plate Carree projection, i.e. in projects
677!> Cartesian coordinates (x,y) onto a sphere. It returns the latitude and
678!> lngitude of a geographical system centered at x0 and y0.
679!-----------------------------------------------------------------------------!
[3866]680 SUBROUTINE inv_plate_carree(x, y, x0, y0, r, lat, lon)
681    REAL(wp), INTENT(IN)  ::  x(:), y(:), x0, y0, r
682    REAL(wp), INTENT(OUT) ::  lat(:), lon(:)
683   
684    REAL(wp) :: ri
[2696]685
[3557]686!
[3866]687!-- TODO check dimensions of lat/lon and y/x match
[2696]688
[3866]689    ri = 1.0_wp / r
690   
691    lat(:) = (y(:) - y0) * ri
692    lon(:) = (x(:) - x0) * ri
693 END SUBROUTINE 
[2696]694
695
696!-----------------------------------------------------------------------------!
697! Description:
698! ------------
699!> Computes the reverse Plate-Carree projection of a x or y position on a
700!> Cartesian grid.
701!>
702!> Input parameters:
703!> -----------------
704!> xy : x or y coordinate of the Cartasian grid point [m].
705!>
706!> xy0 : x or y coordinate that coincides with the origin of the underlying
707!>     sperical system (crossing point of the equator and prime meridian) [m].
708!>
709!> r : Radius of the of the underlying sphere, e.g. EARTH_RADIUS [m].
710!>
711!> Returns:
712!> --------
713!> project : Longitude (in case xy = x) or latitude (xy = y) of the given input
714!>     coordinate xy.
715!------------------------------------------------------------------------------!
[3866]716 ELEMENTAL REAL(wp) FUNCTION project(xy, xy0, r)
717    REAL(wp), INTENT(IN)  ::  xy, xy0, r
718    REAL(wp) :: ri
[2696]719
[3557]720!
[3866]721!-- If this elemental function is called with a large array as xy, it is
722!-- computationally more efficient to precompute the inverse radius and
723!-- then muliply.
724    ri = 1.0_wp / r
[2696]725
[3866]726    project = (xy - xy0) * ri
[2696]727
[3866]728 END FUNCTION project
[2696]729
730
[3557]731!------------------------------------------------------------------------------!
732! Description:
733! ------------
734!> For a rotated-pole system with the origin at geographical latitude 'phi_c',
735!> compute the geographical latitude of its rotated north pole.
736!------------------------------------------------------------------------------!
[3866]737 REAL(wp) FUNCTION phic_to_phin(phi_c)
738     REAL(wp), INTENT(IN) ::  phi_c
[2696]739
[3866]740     phic_to_phin = 0.5_wp * PI - ABS(phi_c)
[2696]741
[3866]742 END FUNCTION phic_to_phin
[2696]743
744   
[3557]745!------------------------------------------------------------------------------!
746! Description:
747! ------------
748!> For a rotated-pole system with the origin at geographical latitude 'phi_c'
749!> and longitude 'lam_c', compute the geographical longitude of its rotated
750!> north pole.
751!------------------------------------------------------------------------------!
[3866]752 REAL(wp) FUNCTION lamc_to_lamn(phi_c, lam_c)
753    REAL(wp), INTENT(IN) ::  phi_c, lam_c
754     
755    lamc_to_lamn = lam_c
756    IF (phi_c > 0.0_wp)  THEN
757       lamc_to_lamn = lam_c - SIGN(PI, lam_c)
758    ENDIF
[2696]759
[3866]760 END FUNCTION lamc_to_lamn
[2696]761
762
[3557]763!------------------------------------------------------------------------------!
764! Description:
765! ------------
766!> Set gamma according to whether PALM domain is in the northern or southern
767!> hemisphere of the COSMO rotated-pole system. Gamma assumes either the
768!> value 0 or PI and is needed to work around around a bug in the
769!> rotated-pole coordinate transformations.
770!------------------------------------------------------------------------------!
[3866]771 REAL(wp) FUNCTION gamma_from_hemisphere(phi_cg, phi_ref)
772    REAL(wp), INTENT(IN) ::  phi_cg
773    REAL(wp), INTENT(IN) ::  phi_ref
[3557]774
[3866]775    LOGICAL ::  palm_origin_is_south_of_cosmo_origin
776   
777    palm_origin_is_south_of_cosmo_origin = (phi_cg < phi_ref)
[2696]778
[3866]779    IF (palm_origin_is_south_of_cosmo_origin)  THEN
780        gamma_from_hemisphere = PI
781    ELSE
782        gamma_from_hemisphere = 0.0_wp
783    ENDIF
784 END FUNCTION gamma_from_hemisphere
[2696]785
786
787!------------------------------------------------------------------------------!
788! Description:
789! ------------
790!> Computes the geographical coordinates corresponding to the given rotated-pole
791!> coordinates.
792!>
793!> In INIFOR, this routine is used to convert coordinates between two
794!> rotated-pole systems: COSMO-DE's rotated-pole system, and one centred at the
795!> PALM-4U domain centre. In this case, the PALM-4U system is thought of as the
796!> rotated-pole system and the routine is used to rotate back to COSMO-DE's
797!> system which is thought of as the geographical one.
798!>
799!> Input parameters:
800!> -----------------
801!> phir(:), lamr(: ): latitudes and longitudes of the rotated-pole grid
802!>
803!> phip, lamp: latitude and longitude of the rotated north pole
804!>
805!> gam: "angle between the north poles. If [gam] is not present, the other
806!>       system is the real geographical system." (original phiro2rot
807!>       description)
808!>
809!> Output parameters:
810!> ------------------
811!> phi(:,:), lam(:,:): geographical latitudes and logitudes
812!------------------------------------------------------------------------------!
[3866]813 SUBROUTINE rotate_to_cosmo(phir, lamr, phip, lamp, phi, lam, gam)
814    REAL(wp), INTENT(IN)  ::  phir(0:), lamr(0:), phip, lamp, gam
815    REAL(wp), INTENT(OUT) ::  phi(0:,0:), lam(0:,0:)
[2696]816
[4523]817    INTEGER(iwp) ::  i, j
[3866]818   
819    IF ( SIZE(phi, 1) .NE. SIZE(lam, 1) .OR. &
820         SIZE(phi, 2) .NE. SIZE(lam, 2) )  THEN
821       PRINT *, "inifor: rotate_to_cosmo: Dimensions of phi and lambda do not match. Dimensions are:"
822       PRINT *, "inifor: rotate_to_cosmo: phi: ", SIZE(phi, 1), SIZE(phi, 2)
823       PRINT *, "inifor: rotate_to_cosmo: lam: ", SIZE(lam, 1), SIZE(lam, 2)
824       STOP
825    ENDIF
[2696]826
[3866]827    IF ( SIZE(phir) .NE. SIZE(phi, 2) .OR. &
828         SIZE(lamr) .NE. SIZE(phi, 1) )  THEN
829       PRINT *, "inifor: rotate_to_cosmo: Dimensions of phir and lamr do not match. Dimensions are:"
830       PRINT *, "inifor: rotate_to_cosmo: phir: ", SIZE(phir), SIZE(phi, 2)
831       PRINT *, "inifor: rotate_to_cosmo: lamr: ", SIZE(lamr), SIZE(phi, 1)
832       STOP
833    ENDIF
834   
835    DO  j = 0, UBOUND(phir, 1)
836       DO  i = 0, UBOUND(lamr, 1)
[2696]837
[3866]838          phi(i,j) = phirot2phi(phir(j) * TO_DEGREES,                       &
839                                lamr(i) * TO_DEGREES,                       &
840                                phip * TO_DEGREES,                          &
841                                gam  * TO_DEGREES) * TO_RADIANS
[2696]842
[3866]843          lam(i,j) = rlarot2rla(phir(j) * TO_DEGREES,                       &
844                                lamr(i) * TO_DEGREES,                       &
845                                phip * TO_DEGREES,                          &
846                                lamp * TO_DEGREES,                          &
847                                gam  * TO_DEGREES) * TO_RADIANS
[2696]848
[3785]849       ENDDO
[3866]850    ENDDO
[2696]851
[3866]852 END SUBROUTINE rotate_to_cosmo
[3182]853       
[2696]854
[3557]855!------------------------------------------------------------------------------!
856! Description:
857! ------------
858!> Rotate the given vector field (x(:), y(:)) by the given 'angle'.
859!------------------------------------------------------------------------------!
[3866]860 SUBROUTINE rotate_vector_field(x, y, angle)
861    REAL(wp), DIMENSION(:), INTENT(INOUT) :: x, y  !< x and y coodrinate in arbitrary units
862    REAL(wp), INTENT(IN)                  :: angle !< rotation angle [deg]
[2696]863
[4523]864    INTEGER(iwp) :: i
865    REAL(wp)     :: sine, cosine, v_rot(2), rotation(2,2)
[3395]866
[3866]867    sine = SIN(angle * TO_RADIANS)
868    cosine = COS(angle * TO_RADIANS)
[3557]869!
[3866]870!-- RESAHPE() fills columns first, so the rotation matrix becomes
871!--
872!-- rotation = [ cosine   -sine  ]
873!--            [  sine    cosine ]
874    rotation = RESHAPE( (/cosine, sine, -sine, cosine/), (/2, 2/) )
[3395]875
[3866]876    DO  i = LBOUND(x, 1), UBOUND(x, 1)
[3395]877
[3866]878       v_rot(:) = MATMUL(rotation, (/x(i), y(i)/))
[3395]879
[3866]880       x(i) = v_rot(1)
881       y(i) = v_rot(2)
[3395]882
[3866]883    ENDDO
[3395]884
[3866]885 END SUBROUTINE rotate_vector_field
[3395]886
887
888
[2696]889!------------------------------------------------------------------------------!
890! Description:
891! ------------
[3395]892!> This routine computes the local meridian convergence between a rotated-pole
893!> and a geographical system using the Eq. (6) given in the DWD manual
894!>
895!>    Baldauf et al. (2018), Beschreibung des operationelle Kürzestfrist-
896!>    vorhersagemodells COSMO-D2 und COSMO-D2-EPS und seiner Ausgabe in die
897!>    Datenbanken des DWD.
898!>    https://www.dwd.de/SharedDocs/downloads/DE/modelldokumentationen/nwv/cosmo_d2/cosmo_d2_dbbeschr_aktuell.pdf?__blob=publicationFile&v=2
[3557]899!------------------------------------------------------------------------------!
[3866]900 FUNCTION meridian_convergence_rotated(phi_n, lam_n, phi_g, lam_g)          &
901    RESULT(delta)
[3395]902
[3866]903    REAL(wp), INTENT(IN) ::  phi_n, lam_n, phi_g, lam_g
904    REAL(wp)             ::  delta
[3395]905
[3866]906    delta = atan2( COS(phi_n) * SIN(lam_n - lam_g),                         &
907                   COS(phi_g) * SIN(phi_n) -                                &
908                   SIN(phi_g) * COS(phi_n) * COS(lam_n - lam_g) )
[3395]909
[3866]910 END FUNCTION meridian_convergence_rotated
[3395]911
912!------------------------------------------------------------------------------!
913! Description:
914! ------------
[2696]915!> Compute indices of PALM-4U grid point neighbours in the target
916!> system (COSMO-DE) by rounding up and down. (i,j) are the indices of
917!> the PALM-4U grid and (ii(i,j,1-4), jj(i,j,1-4)) contain the indices
918!> of the its four neigbouring points in the COSMO-DE grid.
919!>
920!>
921!>                     COSMO-DE grid
922!>                     -------------
923!>           jj, lat
[3182]924!>              ^        j
925!>              |         \          i
[2696]926!>  jj(i,j,2/3) + ... 2 ---\--------/------ 3
927!>              |     | ^   \      /        |
928!>              |     | |wp  \    /         |
929!>              |     | v     \  /          |
930!>       latpos + ............ o/ (i,j)     |
931!>              |     |        :            |
932!>              |     |        :<----wl---->|
933!>  jj(i,j,1/4) + ... 1 -------:----------- 4
934!>              |     :        :            :
935!>              |     :        :            :
936!>              |     :      lonpos         :
937!>              L-----+--------+------------+------> ii, lon
938!>               ii(i,j,1/2)        ii(i,j,3/4)
939!>
940!>
941!> Input parameters:
942!> -----------------
943!> source_lat, source_lon : (rotated-pole) coordinates of the source grid (e.g.
944!>    COSMO-DE)
945!>
946!> source_dxi, source_dyi : inverse grid spacings of the source grid.
947!>
948!> target_lat, target_lon : (rotated-pole) coordinates of the target grid (e.g.
949!>    COSMO-DE)
950!>
951!> Output parameters:
952!> ------------------
953!> palm_ii, palm_jj : x and y index arrays of horizontal neighbour columns
954!>
955!------------------------------------------------------------------------------!
[3866]956 SUBROUTINE find_horizontal_neighbours(cosmo_lat, cosmo_lon,                &
957                                       palm_clat, palm_clon,                &
958                                       palm_ii, palm_jj)
[2696]959
[4523]960    REAL(wp), DIMENSION(0:), INTENT(IN)            ::  cosmo_lat, cosmo_lon
961    REAL(wp), DIMENSION(0:,0:), INTENT(IN)         ::  palm_clat, palm_clon
962    REAL(wp)                                       ::  cosmo_dxi, cosmo_dyi
963    INTEGER(iwp), DIMENSION(0:,0:,1:), INTENT(OUT) ::  palm_ii, palm_jj
[2696]964
[4523]965    REAL(wp)     ::  lonpos, latpos, lon0, lat0
966    INTEGER(iwp) ::  i, j
[2696]967
[3866]968    lon0 = cosmo_lon(0)
969    lat0 = cosmo_lat(0)
970    cosmo_dxi = 1.0_wp / (cosmo_lon(1) - cosmo_lon(0))
971    cosmo_dyi = 1.0_wp / (cosmo_lat(1) - cosmo_lat(0))
[2696]972
[3866]973    DO  j = 0, UBOUND(palm_clon, 2)!palm_grid%ny
974    DO  i = 0, UBOUND(palm_clon, 1)!palm_grid%nx
[3557]975!
[3866]976!--    Compute the floating point index corrseponding to PALM-4U grid point
977!--    location along target grid (COSMO-DE) axes.
978       lonpos = (palm_clon(i,j) - lon0) * cosmo_dxi
979       latpos = (palm_clat(i,j) - lat0) * cosmo_dyi
[2696]980
[3866]981       IF (lonpos < 0.0_wp .OR. latpos < 0.0_wp)  THEN
982          message = "lonpos or latpos out of bounds " //                    &
983             "while finding interpolation neighbours!" // NEW_LINE(' ') //  &
984             "          (i,j) = (" //                                       &
985             TRIM(str(i)) // ", " // TRIM(str(j)) // ")" // NEW_LINE(' ') //&
986             "          lonpos " // TRIM(real_to_str(lonpos*TO_DEGREES)) // &
987             ", latpos " // TRIM(real_to_str(latpos*TO_DEGREES)) // NEW_LINE(' ') // &
988             "          lon0 " // TRIM(real_to_str(lon0  *TO_DEGREES)) //   &
989             ", lat0   " // TRIM(real_to_str(lat0*TO_DEGREES)) // NEW_LINE(' ') // &
990             "          PALM lon " // TRIM(real_to_str(palm_clon(i,j)*TO_DEGREES)) // &
991             ", PALM lat " // TRIM(real_to_str(palm_clat(i,j)*TO_DEGREES))
992          CALL inifor_abort('find_horizontal_neighbours', message)
993       ENDIF
[2696]994
[3866]995       palm_ii(i,j,1) = FLOOR(lonpos)
996       palm_ii(i,j,2) = FLOOR(lonpos)
997       palm_ii(i,j,3) = CEILING(lonpos)
998       palm_ii(i,j,4) = CEILING(lonpos)
[2696]999
[3866]1000       palm_jj(i,j,1) = FLOOR(latpos)
1001       palm_jj(i,j,2) = CEILING(latpos)
1002       palm_jj(i,j,3) = CEILING(latpos)
1003       palm_jj(i,j,4) = FLOOR(latpos)
1004    ENDDO
1005    ENDDO
[2696]1006
[3866]1007 END SUBROUTINE find_horizontal_neighbours
[2696]1008
1009   
[3557]1010!------------------------------------------------------------------------------!
1011! Description:
1012! ------------
1013!> Computes linear vertical interpolation neighbour indices and weights for each
1014!> column of the given palm grid.
1015!------------------------------------------------------------------------------!
[4523]1016 SUBROUTINE find_vertical_neighbours_and_weights_interp( palm_grid,            &
1017                                                         palm_intermediate )
[3866]1018    TYPE(grid_definition), INTENT(INOUT) ::  palm_grid
1019    TYPE(grid_definition), INTENT(IN)    ::  palm_intermediate
[2696]1020
[4523]1021    INTEGER(iwp) ::  i, j, k, nx, ny, nz, nlev, k_intermediate
1022    LOGICAL      ::  point_is_below_grid, point_is_above_grid,                 &
1023                     point_is_in_current_cell
1024    REAL(wp)     ::  current_height, column_base, column_top, h_top, h_bottom, &
1025                     weight
[2696]1026
[3866]1027    nx   = palm_grid%nx
1028    ny   = palm_grid%ny
1029    nz   = palm_grid%nz
1030    nlev = palm_intermediate%nz
[2696]1031
[3557]1032!
[3866]1033!-- in each column of the fine grid, find vertical neighbours of every cell
1034    DO  j = 0, ny
1035    DO  i = 0, nx
[2696]1036
[3866]1037       k_intermediate = 0
[2696]1038
[3866]1039       column_base = palm_intermediate%h(i,j,0)
1040       column_top  = palm_intermediate%h(i,j,nlev)
[2696]1041
[3557]1042!
[3866]1043!--    scan through palm_grid column and set neighbour indices in
1044!--    case current_height is either below column_base, in the current
1045!--    cell, or above column_top. Keep increasing current cell index until
1046!--    the current cell overlaps with the current_height.
1047       DO  k = 1, nz
[2696]1048
[3557]1049!
[3866]1050!--       Memorize the top and bottom boundaries of the coarse cell and the
1051!--       current height within it
1052          current_height = palm_grid%z(k) + palm_grid%z0
1053          h_top    = palm_intermediate%h(i,j,k_intermediate+1)
1054          h_bottom = palm_intermediate%h(i,j,k_intermediate)
[2696]1055
[3866]1056          point_is_above_grid = (current_height > column_top) !22000m, very unlikely
1057          point_is_below_grid = (current_height < column_base)
[2696]1058
[3866]1059          point_is_in_current_cell = (                                      &
1060             current_height >= h_bottom .AND.                               &
1061             current_height <  h_top                                        &
1062          )
[2696]1063
[3557]1064!
[3866]1065!--       set default weights
1066          palm_grid%w_verti(i,j,k,1:2) = 0.0_wp
[2696]1067
[3866]1068          IF (point_is_above_grid)  THEN
[2696]1069
[3866]1070             palm_grid%kk(i,j,k,1:2) = nlev
1071             palm_grid%w_verti(i,j,k,1:2) = - 2.0_wp
[2696]1072
[3866]1073             message = "PALM-4U grid extends above COSMO-DE model top."
1074             CALL inifor_abort('find_vertical_neighbours_and_weights', message)
[3182]1075
[3866]1076          ELSE IF (point_is_below_grid)  THEN
[2696]1077
[3866]1078             palm_grid%kk(i,j,k,1:2) = 0
1079             palm_grid%w_verti(i,j,k,1:2) = - 2.0_wp
[2696]1080
[3866]1081          ELSE
[3557]1082!
[3866]1083!--          cycle through intermediate levels until current
1084!--          intermediate-grid cell overlaps with current_height
1085             DO  WHILE (.NOT. point_is_in_current_cell .AND. k_intermediate <= nlev-1)
1086                k_intermediate = k_intermediate + 1
[2696]1087
[3866]1088                h_top    = palm_intermediate%h(i,j,k_intermediate+1)
1089                h_bottom = palm_intermediate%h(i,j,k_intermediate)
1090                point_is_in_current_cell = (                                &
1091                   current_height >= h_bottom .AND.                         &
1092                   current_height <  h_top                                  &
1093                )
1094             ENDDO
[2696]1095
[3866]1096             IF (k_intermediate > nlev-1)  THEN
1097                message = "Index " // TRIM(str(k_intermediate)) //          &
1098                          " is above intermediate grid range."
1099                CALL inifor_abort('find_vertical_neighbours', message)
1100             ENDIF
[2696]1101   
[3866]1102             palm_grid%kk(i,j,k,1) = k_intermediate
1103             palm_grid%kk(i,j,k,2) = k_intermediate + 1
[2696]1104
[3557]1105!
[3866]1106!--          compute vertical weights
1107             weight = (h_top - current_height) / (h_top - h_bottom)
1108             palm_grid%w_verti(i,j,k,1) = weight
1109             palm_grid%w_verti(i,j,k,2) = 1.0_wp - weight
1110          ENDIF
[2696]1111
[3785]1112       ENDDO
[2696]1113
[3866]1114    ENDDO
1115    ENDDO
[2696]1116
[3866]1117 END SUBROUTINE find_vertical_neighbours_and_weights_interp
[3395]1118
[3866]1119
[3557]1120!------------------------------------------------------------------------------!
1121! Description:
1122! ------------
1123!> Computes linear vertical interpolation neighbour indices and weights for each
1124!> column of the given averaging grid.
1125!>
1126!> The difference to the _interp variant of this routine lies in how columns
1127!> are adressed. While the _interp variant loops over all PALM grid columns
1128!> given by combinations of all index indices (i,j), this routine loops over a
1129!> subset of COSMO columns, which are sequantlially stored in the index lists
1130!> iii(:) and jjj(:).
1131!------------------------------------------------------------------------------!
[3866]1132 SUBROUTINE find_vertical_neighbours_and_weights_average(                   &
1133    avg_grid, level_based_averaging                                         &
1134 )
[3395]1135
[3866]1136    TYPE(grid_definition), INTENT(INOUT), TARGET ::  avg_grid
1137    LOGICAL                                      ::  level_based_averaging
[3395]1138
[4523]1139    INTEGER(iwp)      ::  i, j, k_palm, k_intermediate, l, nlev
[3866]1140    LOGICAL           ::  point_is_below_grid, point_is_above_grid,         &
1141                          point_is_in_current_cell
1142    REAL(wp)          ::  current_height, column_base, column_top, h_top,   &
1143                          h_bottom, weight
1144    REAL(wp), POINTER ::  cosmo_h(:,:,:)
[3395]1145
[3613]1146
[3866]1147    avg_grid%k_min = LBOUND(avg_grid%z, 1)
[3395]1148
[3866]1149    nlev = SIZE(avg_grid%cosmo_h, 3)
[3395]1150
[3866]1151    IF (level_based_averaging)  THEN
1152       cosmo_h => avg_grid%h
1153    ELSE
1154       cosmo_h => avg_grid%cosmo_h
1155    ENDIF
1156
1157!
1158!-- in each column of the fine grid, find vertical neighbours of every cell
1159    DO  l = 1, avg_grid%n_columns
1160
[3613]1161       IF (level_based_averaging)  THEN
[3866]1162          i = 1
1163          j = 1
[3613]1164       ELSE
[3866]1165          i = avg_grid%iii(l)
1166          j = avg_grid%jjj(l)
[3785]1167       ENDIF
[3613]1168
[3866]1169       column_base = cosmo_h(i,j,1)
1170       column_top  = cosmo_h(i,j,nlev)
[3395]1171
[3557]1172!
[3866]1173!--    scan through avg_grid column until and set neighbour indices in
1174!--    case current_height is either below column_base, in the current
1175!--    cell, or above column_top. Keep increasing current cell index until
1176!--    the current cell overlaps with the current_height.
1177       k_intermediate = 1 !avg_grid%cosmo_h is indezed 1-based.
1178       DO  k_palm = 1, avg_grid%nz
[3395]1179
[3557]1180!
[3866]1181!--       Memorize the top and bottom boundaries of the coarse cell and the
1182!--       current height within it
1183          current_height = avg_grid%z(k_palm) + avg_grid%z0
1184          h_top    = cosmo_h(i,j,k_intermediate+1)
1185          h_bottom = cosmo_h(i,j,k_intermediate)
[3395]1186
[3557]1187!
[3866]1188!--       COSMO column top is located at 22000m, point_is_above_grid is very
1189!--       unlikely.
1190          point_is_above_grid = (current_height > column_top)
1191          point_is_below_grid = (current_height < column_base)
[3395]1192
[3866]1193          point_is_in_current_cell = (                                      &
1194             current_height >= h_bottom .AND.                               &
1195             current_height <  h_top                                        &
1196          )
[3395]1197
[3557]1198!
[3866]1199!--       set default weights
1200          avg_grid%w(l,k_palm,1:2) = 0.0_wp
[3395]1201
[3866]1202          IF (point_is_above_grid)  THEN
[3395]1203
[3866]1204             avg_grid%kkk(l,k_palm,1:2) = nlev
1205             avg_grid%w(l,k_palm,1:2) = - 2.0_wp
[3395]1206
[3866]1207             message = "PALM-4U grid extends above COSMO-DE model top."
1208             CALL inifor_abort('find_vertical_neighbours_and_weights_average', message)
[3395]1209
[3866]1210          ELSE IF (point_is_below_grid)  THEN
[3395]1211
[3866]1212             avg_grid%kkk(l,k_palm,1:2) = 0
1213             avg_grid%w(l,k_palm,1:2) = - 2.0_wp
1214             avg_grid%k_min = MAX(k_palm + 1, avg_grid%k_min)
1215          ELSE
[3557]1216!
[3866]1217!--          cycle through intermediate levels until current
1218!--          intermediate-grid cell overlaps with current_height
1219             DO  WHILE (.NOT. point_is_in_current_cell .AND. k_intermediate <= nlev-1)
1220                k_intermediate = k_intermediate + 1
[3395]1221
[3866]1222                h_top    = cosmo_h(i,j,k_intermediate+1)
1223                h_bottom = cosmo_h(i,j,k_intermediate)
1224                point_is_in_current_cell = (                                &
1225                   current_height >= h_bottom .AND.                         &
1226                   current_height <  h_top                                  &
1227                )
1228             ENDDO
[3395]1229
[3557]1230!
[3866]1231!--          k_intermediate = 48 indicates the last section (indices 48 and 49), i.e.
1232!--          k_intermediate = 49 is not the beginning of a valid cell.
1233             IF (k_intermediate > nlev-1)  THEN
1234                message = "Index " // TRIM(str(k_intermediate)) //          &
1235                          " is above intermediate grid range."
1236                CALL inifor_abort('find_vertical_neighbours', message)
1237             ENDIF
[3395]1238   
[3866]1239             avg_grid%kkk(l,k_palm,1) = k_intermediate
1240             avg_grid%kkk(l,k_palm,2) = k_intermediate + 1
[3395]1241
[3557]1242!
[3866]1243!--          compute vertical weights
1244             weight = (h_top - current_height) / (h_top - h_bottom)
1245             avg_grid%w(l,k_palm,1) = weight
1246             avg_grid%w(l,k_palm,2) = 1.0_wp - weight
1247          ENDIF
[3395]1248
[3557]1249!
[3866]1250!--    Loop over PALM levels k
1251       ENDDO
[3557]1252
1253!
[3866]1254!--    Loop over averaging columns l
1255    ENDDO
[3395]1256 
[3866]1257 END SUBROUTINE find_vertical_neighbours_and_weights_average
[3395]1258
[2696]1259!------------------------------------------------------------------------------!
1260! Description:
1261! ------------
1262!> Compute the four weights for horizontal bilinear interpolation given the
1263!> coordinates clon(i,j) clat(i,j) of the PALM-4U grid in the COSMO-DE
1264!> rotated-pole grid and the neightbour indices ii(i,j,1-4) and jj(i,j,1-4).
1265!>
1266!> Input parameters:
1267!> -----------------
[3866]1268!> palm_grid%clon : longitudes of PALM-4U scalars (cell centres) in COSMO-DE's rotated-pole grid [rad]
[2696]1269!>
[3866]1270!> palm_grid%clat : latitudes of PALM-4U cell centres in COSMO-DE's rotated-pole grid [rad]
[2696]1271!>
[3866]1272!> cosmo_grid%lon : rotated-pole longitudes of scalars (cell centres) of the COSMO-DE grid [rad]
[2696]1273!>
[3866]1274!> cosmo_grid%lat : rotated-pole latitudes of scalars (cell centers) of the COSMO-DE grid [rad]
[2696]1275!>
[3866]1276!> cosmo_grid%dxi : inverse grid spacing in the first dimension [m^-1]
[2696]1277!>
[3866]1278!> cosmo_grid%dyi : inverse grid spacing in the second dimension [m^-1]
[2696]1279!>
1280!> Output parameters:
1281!> ------------------
[3866]1282!> palm_grid%w_horiz(:,:,1-4) : weights for bilinear horizontal interpolation
[2696]1283!
1284!                               COSMO-DE grid
1285!                               -------------
1286!                     jj, lat
1287!                        ^        j
1288!                        |         \          i
1289!            jj(i,j,2/3) + ... 2 ---\--------/------ 3
1290!                        |     | ^   \      /        |
1291!                        |     | |wp  \    /         |
1292!                        |     | v     \  /          |
1293!                 latpos + ............ o/ (i,j)     |
1294!                        |     |        :            |
1295!                        |     |        :<----wl---->|
1296!            jj(i,j,1/4) + ... 1 -------:----------- 4
1297!                        |     :        :            :
1298!                        |     :        :            :
1299!                        |     :      lonpos         :
1300!                        L-----+--------+------------+------> ii, lon
1301!                         ii(i,j,1/2)        ii(i,j,3/4)
1302!         
[3557]1303!------------------------------------------------------------------------------!
[3866]1304 SUBROUTINE compute_horizontal_interp_weights(cosmo_lat, cosmo_lon,         &
1305    palm_clat, palm_clon, palm_ii, palm_jj, palm_w_horiz)
[2696]1306       
[4523]1307    REAL(wp), DIMENSION(0:), INTENT(IN)           ::  cosmo_lat, cosmo_lon
1308    REAL(wp)                                      ::  cosmo_dxi, cosmo_dyi
1309    REAL(wp), DIMENSION(0:,0:), INTENT(IN)        ::  palm_clat, palm_clon
1310    INTEGER(iwp), DIMENSION(0:,0:,1:), INTENT(IN) ::  palm_ii, palm_jj
[2696]1311
[3866]1312    REAL(wp), DIMENSION(0:,0:,1:), INTENT(OUT) ::  palm_w_horiz
[2696]1313
[4523]1314    REAL(wp)     ::  wlambda, wphi
1315    INTEGER(iwp) ::  i, j
[2696]1316
[3866]1317    cosmo_dxi = 1.0_wp / (cosmo_lon(1) - cosmo_lon(0))
1318    cosmo_dyi = 1.0_wp / (cosmo_lat(1) - cosmo_lat(0))
[3182]1319
[3866]1320    DO  j = 0, UBOUND(palm_clon, 2)
1321    DO  i = 0, UBOUND(palm_clon, 1)
1322   
[3557]1323!
[3866]1324!--    weight in lambda direction
1325       wlambda = ( cosmo_lon(palm_ii(i,j,4)) - palm_clon(i,j) ) * cosmo_dxi
[2696]1326
[3557]1327!
[3866]1328!--    weight in phi direction
1329       wphi = ( cosmo_lat(palm_jj(i,j,2)) - palm_clat(i,j) ) * cosmo_dyi
[2696]1330
[3866]1331       IF (wlambda > 1.0_wp .OR. wlambda < 0.0_wp)  THEN
1332           message = "Horizontal weight wlambda = " // TRIM(real_to_str(wlambda)) //   &
1333                     " is out bounds."
1334           CALL inifor_abort('compute_horizontal_interp_weights', message)
1335       ENDIF
1336       IF (wphi > 1.0_wp .OR. wphi < 0.0_wp)  THEN
1337           message = "Horizontal weight wphi = " // TRIM(real_to_str(wphi)) //   &
1338                     " is out bounds."
1339           CALL inifor_abort('compute_horizontal_interp_weights', message)
1340       ENDIF
[2696]1341
[3866]1342       palm_w_horiz(i,j,1) = wlambda * wphi
1343       palm_w_horiz(i,j,2) = wlambda * (1.0_wp - wphi)
1344       palm_w_horiz(i,j,3) = (1.0_wp - wlambda) * (1.0_wp - wphi)
1345       palm_w_horiz(i,j,4) = 1.0_wp - SUM( palm_w_horiz(i,j,1:3) )
[2696]1346
[3866]1347    ENDDO
1348    ENDDO
[2696]1349       
[3866]1350 END SUBROUTINE compute_horizontal_interp_weights
[2696]1351
1352
1353!------------------------------------------------------------------------------!
1354! Description:
1355! ------------
1356!> Interpolates u and v components of velocities located at cell faces to the
1357!> cell centres by averaging neighbouring values.
1358!>
1359!> This routine is designed to be used with COSMO-DE arrays where there are the
1360!> same number of grid points for scalars (centres) and velocities (faces). In
1361!> COSMO-DE the velocity points are staggared one half grid spaceing up-grid
1362!> which means the first centre point has to be omitted and is set to zero.
[3557]1363!------------------------------------------------------------------------------!
[3866]1364 SUBROUTINE centre_velocities(u_face, v_face, u_centre, v_centre)
1365    REAL(wp), DIMENSION(0:,0:,0:), INTENT(IN)  ::  u_face, v_face
1366    REAL(wp), DIMENSION(0:,0:,0:), INTENT(OUT) ::  u_centre, v_centre
[4523]1367    INTEGER(iwp) ::  nx, ny
[2696]1368
[3866]1369    nx = UBOUND(u_face, 1)
1370    ny = UBOUND(u_face, 2)
[2696]1371
[3866]1372    u_centre(0,:,:)  = 0.0_wp
1373    u_centre(1:,:,:) = 0.5_wp * ( u_face(0:nx-1,:,:) + u_face(1:,:,:) )
[2696]1374
[3866]1375    v_centre(:,0,:)  = 0.0_wp
1376    v_centre(:,1:,:) = 0.5_wp * ( v_face(:,0:ny-1,:) + v_face(:,1:,:) )
1377 END SUBROUTINE centre_velocities
[2696]1378
1379
[3557]1380!------------------------------------------------------------------------------!
1381! Description:
1382! ------------
1383!> Compute the geographical latitude of a point given in rotated-pole cordinates
1384!------------------------------------------------------------------------------!
[3866]1385 FUNCTION phirot2phi (phirot, rlarot, polphi, polgam)
1386 
1387    REAL(wp), INTENT (IN) ::  polphi      !< latitude of the rotated north pole
1388    REAL(wp), INTENT (IN) ::  phirot      !< latitude in the rotated system
1389    REAL(wp), INTENT (IN) ::  rlarot      !< longitude in the rotated system
1390    REAL(wp), INTENT (IN) ::  polgam      !< angle between the north poles of the systems
[2696]1391
[3866]1392    REAL(wp)              ::  phirot2phi  !< latitude in the geographical system
[2696]1393   
[3866]1394    REAL(wp)              ::  zsinpol, zcospol, zphis, zrlas, zarg, zgam
1395 
1396    zsinpol = SIN(polphi * TO_RADIANS)
1397    zcospol = COS(polphi * TO_RADIANS)
1398    zphis   = phirot * TO_RADIANS
[2696]1399
[3866]1400    IF (rlarot > 180.0_wp)  THEN
1401       zrlas = rlarot - 360.0_wp
1402    ELSE
1403       zrlas = rlarot
1404    ENDIF
1405    zrlas = zrlas * TO_RADIANS
1406 
1407    IF (polgam /= 0.0_wp)  THEN
1408       zgam = polgam * TO_RADIANS
1409       zarg = zsinpol * SIN (zphis) +                                       &
1410              zcospol * COS(zphis) * ( COS(zrlas) * COS(zgam) -             &
1411                                       SIN(zgam)  * SIN(zrlas) )
1412    ELSE
1413       zarg = zcospol * COS (zphis) * COS (zrlas) + zsinpol * SIN (zphis)
1414    ENDIF
1415   
1416    phirot2phi = ASIN (zarg) * TO_DEGREES
1417 
1418 END FUNCTION phirot2phi
[2696]1419
1420
[3557]1421!------------------------------------------------------------------------------!
1422! Description:
1423! ------------
1424!> Compute the geographical latitude of a point given in rotated-pole cordinates
1425!------------------------------------------------------------------------------!
[3866]1426 FUNCTION phi2phirot (phi, rla, polphi, pollam)
1427 
1428    REAL(wp), INTENT (IN) ::  polphi !< latitude of the rotated north pole
1429    REAL(wp), INTENT (IN) ::  pollam !< longitude of the rotated north pole
1430    REAL(wp), INTENT (IN) ::  phi    !< latitude in the geographical system
1431    REAL(wp), INTENT (IN) ::  rla    !< longitude in the geographical system
[2696]1432   
[3866]1433    REAL(wp) ::  phi2phirot          !< longitude in the rotated system
1434   
1435    REAL(wp) ::  zsinpol, zcospol, zlampol, zphi, zrla, zarg1, zarg2, zrla1
1436   
1437    zsinpol = SIN(polphi * TO_RADIANS)
1438    zcospol = COS(polphi * TO_RADIANS)
1439    zlampol = pollam * TO_RADIANS
1440    zphi    = phi * TO_RADIANS
[2696]1441
[3866]1442    IF (rla > 180.0_wp)  THEN
1443       zrla1 = rla - 360.0_wp
1444    ELSE
1445       zrla1 = rla
1446    ENDIF
1447    zrla = zrla1 * TO_RADIANS
[2696]1448   
[3866]1449    zarg1 = SIN(zphi) * zsinpol
1450    zarg2 = COS(zphi) * zcospol * COS(zrla - zlampol)
1451   
1452    phi2phirot = ASIN(zarg1 + zarg2) * TO_DEGREES
1453 
1454 END FUNCTION phi2phirot
[2696]1455
1456
[3557]1457!------------------------------------------------------------------------------!
1458! Description:
1459! ------------
1460!> Compute the geographical longitude of a point given in rotated-pole cordinates
1461!------------------------------------------------------------------------------!
[3866]1462 FUNCTION rlarot2rla(phirot, rlarot, polphi, pollam, polgam)
1463 
1464    REAL(wp), INTENT (IN) ::  polphi !< latitude of the rotated north pole
1465    REAL(wp), INTENT (IN) ::  pollam !< longitude of the rotated north pole
1466    REAL(wp), INTENT (IN) ::  phirot !< latitude in the rotated system
1467    REAL(wp), INTENT (IN) ::  rlarot !< longitude in the rotated system
1468    REAL(wp), INTENT (IN) ::  polgam !< angle between the north poles of the systems
[2696]1469   
[3866]1470    REAL(wp) ::  rlarot2rla          !< latitude in the geographical system
1471   
1472    REAL(wp) ::  zsinpol, zcospol, zlampol, zphis, zrlas, zarg1, zarg2, zgam
1473   
1474    zsinpol = SIN(TO_RADIANS * polphi)
1475    zcospol = COS(TO_RADIANS * polphi)
1476    zlampol = TO_RADIANS * pollam
1477    zphis   = TO_RADIANS * phirot
[2696]1478
[3866]1479    IF (rlarot > 180.0_wp)  THEN
1480       zrlas = rlarot - 360.0_wp
1481    ELSE
1482       zrlas = rlarot
1483    ENDIF
1484    zrlas   = TO_RADIANS * zrlas
1485   
1486    IF (polgam /= 0.0_wp)  THEN
1487       zgam  = TO_RADIANS * polgam
1488       zarg1 = SIN(zlampol) * (zcospol * SIN(zphis) - zsinpol*COS(zphis) *  &
1489               (COS(zrlas) * COS(zgam) - SIN(zrlas) * SIN(zgam)) ) -        &
1490               COS(zlampol) * COS(zphis) * ( SIN(zrlas) * COS(zgam) +       &
1491                                             COS(zrlas) * SIN(zgam) )
1492   
1493       zarg2 = COS (zlampol) * (zcospol * SIN(zphis) - zsinpol*COS(zphis) * &
1494               (COS(zrlas) * COS(zgam) - SIN(zrlas) * SIN(zgam)) ) +        &
1495               SIN(zlampol) * COS(zphis) * ( SIN(zrlas) * COS(zgam) +       &
1496                                             COS(zrlas) * SIN(zgam) )
1497    ELSE
1498       zarg1   = SIN (zlampol) * (-zsinpol * COS(zrlas) * COS(zphis)  +     &
1499                                   zcospol *              SIN(zphis)) -     &
1500                 COS (zlampol) *             SIN(zrlas) * COS(zphis)
1501       zarg2   = COS (zlampol) * (-zsinpol * COS(zrlas) * COS(zphis)  +     &
1502                                   zcospol *              SIN(zphis)) +     &
1503                 SIN (zlampol) *             SIN(zrlas) * COS(zphis)
1504    ENDIF
1505   
1506    IF (zarg2 == 0.0_wp)  zarg2 = 1.0E-20_wp
1507   
1508    rlarot2rla = ATAN2(zarg1,zarg2) * TO_DEGREES
1509     
1510 END FUNCTION rlarot2rla
[2696]1511
1512
[3557]1513!------------------------------------------------------------------------------!
1514! Description:
1515! ------------
1516!> Compute the rotated-pole longitude of a point given in geographical cordinates
1517!------------------------------------------------------------------------------!
[3866]1518 FUNCTION rla2rlarot ( phi, rla, polphi, pollam, polgam )
[2696]1519
[3866]1520    REAL(wp), INTENT (IN) ::  polphi !< latitude of the rotated north pole
1521    REAL(wp), INTENT (IN) ::  pollam !< longitude of the rotated north pole
1522    REAL(wp), INTENT (IN) ::  phi    !< latitude in geographical system
1523    REAL(wp), INTENT (IN) ::  rla    !< longitude in geographical system
1524    REAL(wp), INTENT (IN) ::  polgam !< angle between the north poles of the systems
1525   
1526    REAL(wp) ::  rla2rlarot    !< latitude in the the rotated system
1527   
1528    REAL(wp) ::  zsinpol, zcospol, zlampol, zphi, zrla, zarg1, zarg2, zrla1
1529   
1530    zsinpol = SIN(polphi * TO_RADIANS)
1531    zcospol = COS(polphi * TO_RADIANS)
1532    zlampol = pollam * TO_RADIANS
1533    zphi    = phi * TO_RADIANS
[2696]1534
[3866]1535    IF (rla > 180.0_wp)  THEN
1536       zrla1 = rla - 360.0_wp
1537    ELSE
1538       zrla1 = rla
1539    ENDIF
1540    zrla = zrla1 * TO_RADIANS
1541   
1542    zarg1 = - SIN (zrla-zlampol) * COS(zphi)
1543    zarg2 = - zsinpol * COS(zphi) * COS(zrla-zlampol) + zcospol * SIN(zphi)
1544   
1545    IF (zarg2 == 0.0_wp)  zarg2 = 1.0E-20_wp
1546   
1547    rla2rlarot = ATAN2 (zarg1,zarg2) * TO_DEGREES
1548   
1549    IF (polgam /= 0.0_wp )  THEN
1550       rla2rlarot = polgam + rla2rlarot
1551       IF (rla2rlarot > 180._wp)  rla2rlarot = rla2rlarot - 360.0_wp
1552    ENDIF
1553   
1554 END FUNCTION rla2rlarot
[2696]1555
1556
[3557]1557!------------------------------------------------------------------------------!
1558! Description:
1559! ------------
1560!> Rotate the given velocity vector (u,v) from the geographical to the
1561!> rotated-pole system
1562!------------------------------------------------------------------------------!
[3866]1563 SUBROUTINE uv2uvrot(u, v, rlat, rlon, pollat, pollon, urot, vrot)
1564 
1565    REAL(wp), INTENT (IN)  ::  u, v           !< wind components in the true geographical system
1566    REAL(wp), INTENT (IN)  ::  rlat, rlon     !< coordinates in the true geographical system
1567    REAL(wp), INTENT (IN)  ::  pollat, pollon !< latitude and longitude of the north pole of the rotated grid
[2696]1568   
[3866]1569    REAL(wp), INTENT (OUT) ::  urot, vrot     !< wind components in the rotated grid             
[2696]1570   
[3866]1571    REAL (wp) ::  zsinpol, zcospol, zlonp, zlat, zarg1, zarg2, znorm
1572   
1573    zsinpol = SIN(pollat * TO_RADIANS)
1574    zcospol = COS(pollat * TO_RADIANS)
1575    zlonp   = (pollon-rlon) * TO_RADIANS
1576    zlat    = rlat * TO_RADIANS
1577   
1578    zarg1 = zcospol * SIN(zlonp)
1579    zarg2 = zsinpol * COS(zlat) - zcospol * SIN(zlat) * COS(zlonp)
1580    znorm = 1.0_wp / SQRT(zarg1*zarg1 + zarg2*zarg2)
1581   
1582    urot = u * zarg2 * znorm - v * zarg1 * znorm
1583    vrot = u * zarg1 * znorm + v * zarg2 * znorm
1584 
1585 END SUBROUTINE uv2uvrot
[2696]1586
1587
[3557]1588!------------------------------------------------------------------------------!
1589! Description:
1590! ------------
1591!> Rotate the given velocity vector (urot, vrot) from the rotated-pole to the
1592!> geographical system
1593!------------------------------------------------------------------------------!
[3866]1594 SUBROUTINE uvrot2uv (urot, vrot, rlat, rlon, pollat, pollon, u, v)
1595 
1596    REAL(wp), INTENT(IN) ::  urot, vrot     !< wind components in the rotated grid
1597    REAL(wp), INTENT(IN) ::  rlat, rlon     !< latitude and longitude in the true geographical system
1598    REAL(wp), INTENT(IN) ::  pollat, pollon !< latitude and longitude of the north pole of the rotated grid
[2696]1599   
[3866]1600    REAL(wp), INTENT(OUT) ::  u, v          !< wind components in the true geographical system
[2696]1601   
[3866]1602    REAL(wp) ::  zsinpol, zcospol, zlonp, zlat, zarg1, zarg2, znorm
1603 
1604    zsinpol = SIN(pollat * TO_RADIANS)
1605    zcospol = COS(pollat * TO_RADIANS)
1606    zlonp   = (pollon-rlon) * TO_RADIANS
1607    zlat    = rlat * TO_RADIANS
1608 
1609    zarg1 = zcospol * SIN(zlonp)
1610    zarg2 = zsinpol * COS(zlat) - zcospol * SIN(zlat) * COS(zlonp)
1611    znorm = 1.0_wp / SQRT(zarg1*zarg1 + zarg2*zarg2)
1612 
1613    u =   urot * zarg2 * znorm + vrot * zarg1 * znorm
1614    v = - urot * zarg1 * znorm + vrot * zarg2 * znorm
1615 
1616 END SUBROUTINE uvrot2uv
[2696]1617
[3618]1618 END MODULE inifor_transform
[3680]1619#endif
[2696]1620
Note: See TracBrowser for help on using the repository browser.