Ignore:
Timestamp:
Oct 22, 2018 5:32:49 PM (5 years ago)
Author:
eckhard
Message:

inifor: Added computation of geostrophic winds from COSMO input

File:
1 edited

Legend:

Unmodified
Added
Removed
  • palm/trunk/UTIL/inifor/src/transform.f90

    r3183 r3395  
    2626! -----------------
    2727! $Id$
     28! Switched addressing of averaging regions from index bounds to list of columns
     29! Added routines for the computation of geostrophic winds including:
     30!    - the downward extrapolation of density (linear) and
     31!    - pressure (hydrostatic equation) and
     32!    - rotation of geostrophic wind components to PALM frame of reference
     33!
     34! 3183 2018-07-27 14:25:55Z suehring
    2835! Introduced new PALM grid stretching
    2936! Removed unnecessary subroutine parameters
     
    5158    USE control
    5259    USE defs,                                                                  &
    53         ONLY: TO_DEGREES, TO_RADIANS, PI, dp
     60        ONLY: G, TO_DEGREES, TO_RADIANS, PI, dp
    5461    USE types
    5562    USE util,                                                                  &       
     
    176183
    177184
    178     SUBROUTINE average_2d(in_arr, out_arr, imin, imax, jmin, jmax)
    179        REAL(dp), INTENT(IN)  ::  in_arr(0:,0:,0:)
    180        REAL(dp), INTENT(OUT) ::  out_arr(0:)
    181        INTEGER, INTENT(IN)   ::  imin, imax, jmin, jmax
    182 
    183        INTEGER  ::  i, j, k
     185    SUBROUTINE average_2d(in_arr, out_arr, ii, jj)
     186       REAL(dp), INTENT(IN)              ::  in_arr(0:,0:,0:)
     187       REAL(dp), INTENT(OUT)             ::  out_arr(0:)
     188       INTEGER, INTENT(IN), DIMENSION(:) ::  ii, jj
     189
     190       INTEGER  ::  i, j, k, l
    184191       REAL(dp) ::  ni
    185        
    186        IF (imin < 0)  CALL abort('average_2d', "imin < 0.")
    187        IF (jmin < 0)  CALL abort('average_2d', "jmin < 0.")
    188        IF (imax > UBOUND(in_arr, 1))  CALL abort('average_2d', "imax out of i bound.")
    189        IF (jmax > UBOUND(in_arr, 2))  CALL abort('average_2d', "jmax out of j bound.")
     192
     193       IF (SIZE(ii) .NE. SIZE(jj))  THEN
     194          message = "Length of 'ii' and 'jj' index lists do not match." //     &
     195             NEW_LINE(' ') // "ii has " // str(SIZE(ii)) // " elements, " //        &
     196             NEW_LINE(' ') // "jj has " // str(SIZE(jj)) // "."
     197          CALL abort('average_2d', message)
     198       END IF
    190199
    191200       DO k = 0, UBOUND(out_arr, 1)
    192201
    193202          out_arr(k) = 0.0_dp
    194           DO j = jmin, jmax
    195           DO i = imin, imax
    196              out_arr(k) = out_arr(k) + in_arr(i, j, k)
     203          DO l = 1, UBOUND(ii, 1)
     204             i = ii(l)
     205             j = jj(l)
     206             out_arr(k) = out_arr(k) +&
     207                         in_arr(i, j, k)
    197208          END DO
    198           END DO
    199 
    200        END DO
    201    
    202        ! devide by number of grid points
    203        ni = 1.0_dp / ( (imax - imin + 1) * (jmax - jmin + 1) )
     209
     210       END DO
     211
     212       ni = 1.0_dp / SIZE(ii)
    204213       out_arr(:) = out_arr(:) * ni
    205214
     
    212221       REAL(dp), DIMENSION(:,:,:), INTENT(OUT) ::  palm_array
    213222       REAL(dp), DIMENSION(:,:,:), ALLOCATABLE ::  intermediate_array
    214        INTEGER ::  nx, ny, nz
     223       INTEGER ::  nx, ny, nlev
    215224
    216225       nx = palm_intermediate % nx
    217226       ny = palm_intermediate % ny
    218        nz = palm_intermediate % nz ! nlev
     227       nlev = palm_intermediate % nz ! nlev
    219228
    220229       ! Interpolate from COSMO-DE to intermediate grid. Allocating with one
    221230       ! less point in the vertical, since scalars like T have 50 instead of 51
    222231       ! points in COSMO-DE.
    223        ALLOCATE(intermediate_array(0:nx, 0:ny, 0:nz-1)) !
     232       ALLOCATE(intermediate_array(0:nx, 0:ny, 0:nlev-1)) !
    224233
    225234       CALL interpolate_2d(source_array, intermediate_array, palm_intermediate)
     
    234243
    235244
    236     SUBROUTINE average_profile(source_array, profile_array, imin, imax, jmin, jmax,&
    237                                palm_intermediate, palm_grid)
    238        TYPE(grid_definition), INTENT(IN)          ::  palm_intermediate, palm_grid
     245    SUBROUTINE average_profile(source_array, profile_array, avg_grid)
     246       TYPE(grid_definition), INTENT(IN)          ::  avg_grid
    239247       REAL(dp), DIMENSION(:,:,:), INTENT(IN)     ::  source_array
    240        INTEGER, INTENT(IN)                        ::  imin, imax, jmin, jmax
    241        REAL(dp), DIMENSION(0:,0:,0:), INTENT(OUT) ::  profile_array
    242        REAL(dp), DIMENSION(:,:,:), ALLOCATABLE    ::  intermediate_array
    243        INTEGER                                    ::  nx, ny, nz
    244 
    245        nx = palm_intermediate % nx
    246        ny = palm_intermediate % ny
    247        nz = palm_intermediate % nz
    248        ALLOCATE(intermediate_array(0:nx, 0:ny, 0:nz-1))
    249        intermediate_array(:,:,:) = 0.0_dp
    250 
    251        ! average input array to intermediate profile
    252        CALL average_2d(source_array, intermediate_array(0,0,:), imin, imax, jmin, jmax)
    253 
    254        ! vertically interpolate to ouput array
    255        CALL interpolate_1d(intermediate_array, profile_array, palm_grid)
    256 
    257        DEALLOCATE(intermediate_array)
     248       REAL(dp), DIMENSION(:), INTENT(OUT)        ::  profile_array
     249
     250       INTEGER ::  i_source, j_source, k_profile, k_source, l, m
     251
     252       REAL ::  ni_columns
     253
     254       profile_array(:) = 0.0_dp
     255
     256       DO l = 1, avg_grid % n_columns
     257          i_source = avg_grid % iii(l)
     258          j_source = avg_grid % jjj(l)
     259
     260          DO k_profile = avg_grid % k_min, UBOUND(profile_array, 1) ! PALM levels
     261
     262             DO m = 1, 2 ! vertical interpolation neighbours
     263
     264                k_source = avg_grid % kkk(l, k_profile, m)
     265
     266                profile_array(k_profile) = profile_array(k_profile)    &
     267                   + avg_grid % w(l, k_profile, m)                             &
     268                   * source_array(i_source, j_source, k_source)
     269
     270             END DO ! m, vertical interpolation neighbours
     271
     272          END DO    ! k_profile, PALM levels
     273
     274       END DO       ! l, horizontal neighbours
     275
     276       ni_columns = 1.0_dp / avg_grid % n_columns
     277       profile_array(:) = profile_array(:) * ni_columns
     278
     279       ! Extrapolate constant to the bottom
     280       profile_array(1:avg_grid % k_min-1) = profile_array(avg_grid % k_min)
    258281
    259282    END SUBROUTINE average_profile
    260283
     284
     285    SUBROUTINE extrapolate_density(rho, avg_grid)
     286       REAL(dp), DIMENSION(:), INTENT(INOUT) ::  rho
     287       TYPE(grid_definition), INTENT(IN)     ::  avg_grid
     288
     289       REAL(dp) ::  drhodz, dz, zk, rhok
     290       INTEGER  ::  k_min
     291
     292       k_min  = avg_grid % k_min
     293       zk     = avg_grid % z(k_min)
     294       rhok   = rho(k_min)
     295       dz     = avg_grid % z(k_min + 1) - avg_grid % z(k_min)
     296       drhodz = (rho(k_min + 1) - rho(k_min)) / dz
     297
     298       rho(1:k_min-1) = rhok + drhodz * (avg_grid % z(1:k_min-1) - zk)
     299
     300    END SUBROUTINE extrapolate_density
     301
     302
     303    SUBROUTINE extrapolate_pressure(p, rho, avg_grid)
     304       REAL(dp), DIMENSION(:), INTENT(IN)    ::  rho
     305       REAL(dp), DIMENSION(:), INTENT(INOUT) ::  p
     306       TYPE(grid_definition), INTENT(IN)     ::  avg_grid
     307
     308       REAL(dp) ::  drhodz, dz, zk, rhok
     309       INTEGER  ::  k, k_min
     310
     311       k_min = avg_grid % k_min
     312       zk    = avg_grid % z(k_min)
     313       rhok  = rho(k_min)
     314       dz    = avg_grid % z(k_min + 1) - avg_grid % z(k_min)
     315       drhodz = 0.5_dp * (rho(k_min + 1) - rho(k_min)) / dz
     316
     317       DO k = 1, k_min-1
     318          p(k) = constant_density_pressure(p(k_min), zk, rhok, drhodz,         &
     319                                           avg_grid % z(k), G)
     320       END DO
     321
     322    END SUBROUTINE extrapolate_pressure
     323
     324
     325!------------------------------------------------------------------------------!
     326! Description:
     327! ------------
     328!> Takes the averaged pressure profile <p> and sets the lowest entry to the
     329!> extrapolated pressure at the surface.
     330!------------------------------------------------------------------------------!
     331    SUBROUTINE get_surface_pressure(p, rho, avg_grid)
     332       REAL(dp), DIMENSION(:), INTENT(IN)    ::  rho
     333       REAL(dp), DIMENSION(:), INTENT(INOUT) ::  p
     334       TYPE(grid_definition), INTENT(IN)     ::  avg_grid
     335
     336       REAL(dp) ::  drhodz, dz, zk, rhok
     337       INTEGER  ::  k, k_min
     338
     339       k_min = avg_grid % k_min
     340       zk    = avg_grid % z(k_min)
     341       rhok  = rho(k_min)
     342       dz    = avg_grid % z(k_min + 1) - avg_grid % z(k_min)
     343       drhodz = 0.5_dp * (rho(k_min + 1) - rho(k_min)) / dz
     344
     345       p(1) = constant_density_pressure(p(k_min), zk, rhok, drhodz,            &
     346                                        0.0, G)
     347
     348    END SUBROUTINE get_surface_pressure
     349
     350
     351    FUNCTION constant_density_pressure(pk, zk, rhok, drhodz, z, g)  RESULT(p)
     352
     353       REAL(dp), INTENT(IN)  ::  pk, zk, rhok, drhodz, g, z
     354       REAL(dp) ::  p
     355
     356       p = pk + ( zk - z ) * g * ( rhok + 0.5*drhodz * (zk - z) )
     357
     358    END FUNCTION constant_density_pressure
     359
     360!-----------------------------------------------------------------------------!
     361! Description:
     362! -----------
     363!> This routine computes profiles of the two geostrophic wind components ug and
     364!> vg.
     365!-----------------------------------------------------------------------------!
     366    SUBROUTINE geostrophic_winds(p_north, p_south, p_east, p_west, rho, f3,    &
     367                                 Lx, Ly, phi_n, lam_n, phi_g, lam_g, ug, vg)
     368
     369    REAL(dp), DIMENSION(:), INTENT(IN)  ::  p_north, p_south, p_east, p_west,  &
     370                                            rho
     371    REAL(dp), INTENT(IN)                ::  f3, Lx, Ly, phi_n, lam_n, phi_g, lam_g
     372    REAL(dp), DIMENSION(:), INTENT(OUT) ::  ug, vg
     373    REAL(dp)                            ::  facx, facy
     374
     375    facx = 1.0_dp / (Lx * f3)
     376    facy = 1.0_dp / (Ly * f3)
     377    ug(:) = - facy / rho(:) * (p_north(:) - p_south(:))
     378    vg(:) =   facx / rho(:) * (p_east(:) - p_west(:))
     379
     380    CALL rotate_vector_field(                                                  &
     381       ug, vg, angle = meridian_convergence_rotated(phi_n, lam_n, phi_g, lam_g)&
     382    )
     383
     384    END SUBROUTINE geostrophic_winds
    261385
    262386
     
    419543       
    420544
     545    SUBROUTINE rotate_vector_field(x, y, angle)
     546       REAL(dp), DIMENSION(:), INTENT(INOUT) :: x, y  !< x and y coodrinate in arbitrary units
     547       REAL(dp), INTENT(IN)                  :: angle !< rotation angle [deg]
     548
     549       INTEGER  :: i
     550       REAL(dp) :: sine, cosine, v_rot(2), rotation(2,2)
     551
     552       sine = SIN(angle * TO_RADIANS)
     553       cosine = COS(angle * TO_RADIANS)
     554       ! RESAHPE() fills columns first, so the rotation matrix becomes
     555       !
     556       ! rotation = [ cosine   -sine  ]
     557       !            [  sine    cosine ]
     558       rotation = RESHAPE( (/cosine, sine, -sine, cosine/), (/2, 2/) )
     559
     560       DO i = LBOUND(x, 1), UBOUND(x, 1)
     561
     562          v_rot(:) = MATMUL(rotation, (/x(i), y(i)/))
     563
     564          x(i) = v_rot(1)
     565          y(i) = v_rot(2)
     566
     567       END DO
     568
     569    END SUBROUTINE rotate_vector_field
     570
     571
     572
     573!------------------------------------------------------------------------------!
     574! Description:
     575! ------------
     576!> This routine computes the local meridian convergence between a rotated-pole
     577!> and a geographical system using the Eq. (6) given in the DWD manual
     578!>
     579!>    Baldauf et al. (2018), Beschreibung des operationelle Kürzestfrist-
     580!>    vorhersagemodells COSMO-D2 und COSMO-D2-EPS und seiner Ausgabe in die
     581!>    Datenbanken des DWD.
     582!>    https://www.dwd.de/SharedDocs/downloads/DE/modelldokumentationen/nwv/cosmo_d2/cosmo_d2_dbbeschr_aktuell.pdf?__blob=publicationFile&v=2
     583!>
     584    FUNCTION meridian_convergence_rotated(phi_n, lam_n, phi_g, lam_g)          &
     585       RESULT(delta)
     586
     587       REAL(dp), INTENT(IN) ::  phi_n, lam_n, phi_g, lam_g
     588       REAL(dp)             ::  delta
     589
     590       delta = atan2( COS(phi_n) * SIN(lam_n - lam_g),                         &
     591                      COS(phi_g) * SIN(phi_n) -                                &
     592                      SIN(phi_g) * COS(phi_n) * COS(lam_n - lam_g) )
     593
     594    END FUNCTION meridian_convergence_rotated
    421595
    422596!------------------------------------------------------------------------------!
     
    512686
    513687   
    514     SUBROUTINE find_vertical_neighbours_and_weights(palm_grid, palm_intermediate)
     688    SUBROUTINE find_vertical_neighbours_and_weights_interp( palm_grid,         &
     689                                                            palm_intermediate )
    515690       TYPE(grid_definition), INTENT(INOUT) ::  palm_grid
    516691       TYPE(grid_definition), INTENT(IN)    ::  palm_intermediate
     
    528703
    529704       ! in each column of the fine grid, find vertical neighbours of every cell
     705       DO j = 0, ny
    530706       DO i = 0, nx
    531        DO j = 0, ny
    532707
    533708          k_intermediate = 0
     
    536711          column_top  = palm_intermediate % h(i,j,nlev)
    537712
    538           ! scan through palm_grid column until and set neighbour indices in
     713          ! scan through palm_grid column and set neighbour indices in
    539714          ! case current_height is either below column_base, in the current
    540715          ! cell, or above column_top. Keep increasing current cell index until
     
    580755                   h_top    = palm_intermediate % h(i,j,k_intermediate+1)
    581756                   h_bottom = palm_intermediate % h(i,j,k_intermediate)
     757                   point_is_in_current_cell = (                                &
     758                      current_height >= h_bottom .AND.                         &
     759                      current_height <  h_top                                  &
     760                   )
     761                END DO
     762
     763                IF (k_intermediate > nlev-1)  THEN
     764                   message = "Index " // TRIM(str(k_intermediate)) //          &
     765                             " is above intermediate grid range."
     766                   CALL abort('find_vertical_neighbours', message)
     767                END IF
     768   
     769                palm_grid % kk(i,j,k,1) = k_intermediate
     770                palm_grid % kk(i,j,k,2) = k_intermediate + 1
     771
     772                ! copmute vertical weights
     773                weight = (h_top - current_height) / (h_top - h_bottom)
     774                palm_grid % w_verti(i,j,k,1) = weight
     775                palm_grid % w_verti(i,j,k,2) = 1.0_dp - weight
     776             END IF
     777
     778          END DO
     779
     780       END DO
     781       END DO
     782
     783    END SUBROUTINE find_vertical_neighbours_and_weights_interp
     784
     785
     786    SUBROUTINE find_vertical_neighbours_and_weights_average( avg_grid )
     787       TYPE(grid_definition), INTENT(INOUT) ::  avg_grid
     788
     789       INTEGER  ::  i, j, k_palm, k_intermediate, l, nlev
     790       LOGICAL  ::  point_is_below_grid, point_is_above_grid,                  &
     791                    point_is_in_current_cell
     792       REAL(dp) ::  current_height, column_base, column_top, h_top, h_bottom,  &
     793                    weight
     794
     795
     796       avg_grid % k_min = LBOUND(avg_grid % z, 1)
     797
     798       nlev = SIZE(avg_grid % cosmo_h, 3)
     799
     800       ! in each column of the fine grid, find vertical neighbours of every cell
     801       DO l = 1, avg_grid % n_columns
     802
     803          i = avg_grid % iii(l)
     804          j = avg_grid % jjj(l)
     805
     806          column_base = avg_grid % cosmo_h(i,j,1)
     807          column_top  = avg_grid % cosmo_h(i,j,nlev)
     808
     809          ! scan through avg_grid column until and set neighbour indices in
     810          ! case current_height is either below column_base, in the current
     811          ! cell, or above column_top. Keep increasing current cell index until
     812          ! the current cell overlaps with the current_height.
     813          k_intermediate = 1 !avg_grid % cosmo_h is indezed 1-based.
     814          DO k_palm = 1, avg_grid % nz
     815
     816             ! Memorize the top and bottom boundaries of the coarse cell and the
     817             ! current height within it
     818             current_height = avg_grid % z(k_palm) + avg_grid % z0
     819             h_top    = avg_grid % cosmo_h(i,j,k_intermediate+1)
     820             h_bottom = avg_grid % cosmo_h(i,j,k_intermediate)
     821
     822             point_is_above_grid = (current_height > column_top) !22000m, very unlikely
     823             point_is_below_grid = (current_height < column_base)
     824
     825             point_is_in_current_cell = (                                      &
     826                current_height >= h_bottom .AND.                               &
     827                current_height <  h_top                                        &
     828             )
     829
     830             ! set default weights
     831             avg_grid % w(l,k_palm,1:2) = 0.0_dp
     832
     833             IF (point_is_above_grid)  THEN
     834
     835                avg_grid % kkk(l,k_palm,1:2) = nlev
     836                avg_grid % w(l,k_palm,1:2) = - 2.0_dp
     837
     838                message = "PALM-4U grid extends above COSMO-DE model top."
     839                CALL abort('find_vertical_neighbours_and_weights_average', message)
     840
     841             ELSE IF (point_is_below_grid)  THEN
     842
     843                avg_grid % kkk(l,k_palm,1:2) = 0
     844                avg_grid % w(l,k_palm,1:2) = - 2.0_dp
     845                avg_grid % k_min = MAX(k_palm + 1, avg_grid % k_min)
     846             ELSE
     847                ! cycle through intermediate levels until current
     848                ! intermediate-grid cell overlaps with current_height
     849                DO WHILE (.NOT. point_is_in_current_cell .AND. k_intermediate <= nlev-1)
     850                   k_intermediate = k_intermediate + 1
     851
     852                   h_top    = avg_grid % cosmo_h(i,j,k_intermediate+1)
     853                   h_bottom = avg_grid % cosmo_h(i,j,k_intermediate)
    582854                   point_is_in_current_cell = (                                &
    583855                      current_height >= h_bottom .AND.                         &
     
    594866                END IF
    595867   
    596                 palm_grid % kk(i,j,k,1) = k_intermediate
    597                 palm_grid % kk(i,j,k,2) = k_intermediate + 1
     868                avg_grid % kkk(l,k_palm,1) = k_intermediate
     869                avg_grid % kkk(l,k_palm,2) = k_intermediate + 1
    598870
    599871                ! copmute vertical weights
    600872                weight = (h_top - current_height) / (h_top - h_bottom)
    601                 palm_grid % w_verti(i,j,k,1) = weight
    602                 palm_grid % w_verti(i,j,k,2) = 1.0_dp - weight
     873                avg_grid % w(l,k_palm,1) = weight
     874                avg_grid % w(l,k_palm,2) = 1.0_dp - weight
    603875             END IF
    604876
    605           END DO
    606 
    607        END DO
    608        END DO
    609 
    610     END SUBROUTINE find_vertical_neighbours_and_weights
     877          END DO ! k, PALM levels
     878       END DO ! l, averaging columns
     879 
     880    END SUBROUTINE find_vertical_neighbours_and_weights_average
    611881
    612882!------------------------------------------------------------------------------!
Note: See TracChangeset for help on using the changeset viewer.