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

Last change on this file since 4004 was 3866, checked in by eckhard, 5 years ago

inifor: Use PALM's working precision; improved error handling, coding style, and comments

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