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

Last change on this file since 4778 was 4756, checked in by eckhard, 4 years ago

inifor: Fixed an error in surface pressure extrapolation

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