!> @file turbulence_closure_mod.f90 !------------------------------------------------------------------------------! ! This file is part of the PALM model system. ! ! PALM is free software: you can redistribute it and/or modify it under the ! terms of the GNU General Public License as published by the Free Software ! Foundation, either version 3 of the License, or (at your option) any later ! version. ! ! PALM is distributed in the hope that it will be useful, but WITHOUT ANY ! WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR ! A PARTICULAR PURPOSE. See the GNU General Public License for more details. ! ! You should have received a copy of the GNU General Public License along with ! PALM. If not, see . ! ! Copyright 2017-2017 Leibniz Universitaet Hannover !--------------------------------------------------------------------------------! ! ! Current revisions: ! ----------------- ! ! ! Former revisions: ! ----------------- ! $Id: turbulence_closure_mod.f90 2716 2017-12-29 16:35:59Z kanani $ ! Corrected "Former revisions" section ! ! 2701 2017-12-15 15:40:50Z suehring ! Changes from last commit documented ! ! 2698 2017-12-14 18:46:24Z suehring ! Bugfix in get_topography_top_index ! ! 2696 2017-12-14 17:12:51Z kanani ! Initial revision ! ! ! ! ! Authors: ! -------- ! @author Tobias Gronemeier ! ! ! Description: ! ------------ !> This module contains the available turbulence closures for PALM. !> !> !> @todo test initialization for all possibilities !> add OpenMP directives whereever possible !> remove debug output variables (dummy1, dummy2, dummy3) !> @note !> @bug TKE-e closure still crashes due to too small dt !------------------------------------------------------------------------------! MODULE turbulence_closure_mod #if defined( __nopointer ) USE arrays_3d, & ONLY: diss, diss_p, dzu, e, e_p, kh, km, l_grid, & mean_inflow_profiles, prho, pt, tdiss_m, te_m, tend, u, v, vpt, w #else USE arrays_3d, & ONLY: diss, diss_1, diss_2, diss_3, diss_p, dzu, e, e_1, e_2, e_3, & e_p, kh, km, l_grid, mean_inflow_profiles, prho, pt, tdiss_m, & te_m, tend, u, v, vpt, w #endif USE control_parameters, & ONLY: constant_diffusion, dt_3d, e_init, humidity, inflow_l, & initializing_actions, intermediate_timestep_count, & intermediate_timestep_count_max, kappa, km_constant, les_mw, & ocean, prandtl_number, prho_reference, pt_reference, rans_mode, & rans_tke_e, rans_tke_l, simulated_time, timestep_scheme, & turbulence_closure, turbulent_inflow, use_upstream_for_tke, & vpt_reference, ws_scheme_sca USE advec_ws, & ONLY: advec_s_ws USE advec_s_bc_mod, & ONLY: advec_s_bc USE advec_s_pw_mod, & ONLY: advec_s_pw USE advec_s_up_mod, & ONLY: advec_s_up USE cpulog, & ONLY: cpu_log, log_point, log_point_s USE indices, & ONLY: nbgp, nxl, nxlg, nxr, nxrg, nyn, nyng, nys, nysg, & nzb, nzb_s_inner, nzb_u_inner, nzb_v_inner, nzb_w_inner, nzt, & wall_flags_0 USE kinds USE pegrid USE plant_canopy_model_mod, & ONLY: pcm_tendency, plant_canopy USE statistics, & ONLY: hom, hom_sum, statistic_regions USE user_actions_mod, & ONLY: user_actions IMPLICIT NONE REAL(wp) :: c_1 = 1.44_wp !< model constant for RANS mode REAL(wp) :: c_2 = 1.92_wp !< model constant for RANS mode REAL(wp) :: c_3 = 1.44_wp !< model constant for RANS mode REAL(wp) :: c_h = 0.0015_wp !< model constant for RANS mode REAL(wp) :: c_m !< constant used for diffusion coefficient and dissipation (dependent on mode RANS/LES) REAL(wp) :: c_mu = 0.09_wp !< model constant for RANS mode REAL(wp) :: l_max !< maximum length scale for Blackadar mixing length REAL(wp) :: sig_e = 1.0_wp !< factor to calculate Ke from Km REAL(wp) :: sig_diss = 1.3_wp !< factor to calculate K_diss from Km INTEGER(iwp) :: surf_e !< end index of surface elements at given i-j position INTEGER(iwp) :: surf_s !< start index of surface elements at given i-j position REAL(wp), DIMENSION(:), ALLOCATABLE :: l_black !< mixing length according to Blackadar REAL(wp), DIMENSION(:,:,:), ALLOCATABLE, SAVE :: dummy1 !< debug output variable REAL(wp), DIMENSION(:,:,:), ALLOCATABLE, SAVE :: dummy2 !< debug output variable REAL(wp), DIMENSION(:,:,:), ALLOCATABLE, SAVE :: dummy3 !< debug output variable PUBLIC c_m, dummy1, dummy2, dummy3 ! !-- PALM interfaces: !-- Input parameter checks to be done in check_parameters INTERFACE tcm_check_parameters MODULE PROCEDURE tcm_check_parameters END INTERFACE tcm_check_parameters ! !-- Data output checks for 2D/3D data to be done in check_parameters INTERFACE tcm_check_data_output MODULE PROCEDURE tcm_check_data_output END INTERFACE tcm_check_data_output ! !-- Definition of data output quantities INTERFACE tcm_define_netcdf_grid MODULE PROCEDURE tcm_define_netcdf_grid END INTERFACE tcm_define_netcdf_grid ! !-- Averaging of 3D data for output INTERFACE tcm_3d_data_averaging MODULE PROCEDURE tcm_3d_data_averaging END INTERFACE tcm_3d_data_averaging ! !-- Data output of 2D quantities INTERFACE tcm_data_output_2d MODULE PROCEDURE tcm_data_output_2d END INTERFACE tcm_data_output_2d ! !-- Data output of 3D data INTERFACE tcm_data_output_3d MODULE PROCEDURE tcm_data_output_3d END INTERFACE tcm_data_output_3d ! !-- Initialization actions INTERFACE tcm_init MODULE PROCEDURE tcm_init END INTERFACE tcm_init ! !-- Initialization of arrays INTERFACE tcm_init_arrays MODULE PROCEDURE tcm_init_arrays END INTERFACE tcm_init_arrays ! !-- Initialization of TKE production term INTERFACE production_e_init MODULE PROCEDURE production_e_init END INTERFACE production_e_init ! !-- Prognostic equations for TKE and TKE dissipation rate INTERFACE tcm_prognostic MODULE PROCEDURE tcm_prognostic MODULE PROCEDURE tcm_prognostic_ij END INTERFACE tcm_prognostic ! !-- Production term for TKE INTERFACE production_e MODULE PROCEDURE production_e MODULE PROCEDURE production_e_ij END INTERFACE production_e ! !-- Diffusion term for TKE INTERFACE diffusion_e MODULE PROCEDURE diffusion_e MODULE PROCEDURE diffusion_e_ij END INTERFACE diffusion_e ! !-- Diffusion term for TKE dissipation rate INTERFACE diffusion_diss MODULE PROCEDURE diffusion_diss MODULE PROCEDURE diffusion_diss_ij END INTERFACE diffusion_diss ! !-- Mixing length for LES case INTERFACE mixing_length_les MODULE PROCEDURE mixing_length_les END INTERFACE mixing_length_les ! !-- Mixing length for RANS case INTERFACE mixing_length_rans MODULE PROCEDURE mixing_length_rans END INTERFACE mixing_length_rans ! !-- Calculate diffusivities INTERFACE tcm_diffusivities MODULE PROCEDURE tcm_diffusivities END INTERFACE tcm_diffusivities ! !-- Swapping of time levels (required for prognostic variables) INTERFACE tcm_swap_timelevel MODULE PROCEDURE tcm_swap_timelevel END INTERFACE tcm_swap_timelevel SAVE PRIVATE ! !-- Add INTERFACES that must be available to other modules (alphabetical order) PUBLIC production_e_init, tcm_3d_data_averaging, tcm_check_data_output, & tcm_check_parameters, tcm_data_output_2d, tcm_data_output_3d, & tcm_define_netcdf_grid, tcm_diffusivities, tcm_init, & tcm_init_arrays, tcm_prognostic, tcm_swap_timelevel CONTAINS !------------------------------------------------------------------------------! ! Description: ! ------------ !> Check parameters routine for turbulence closure module. !------------------------------------------------------------------------------! SUBROUTINE tcm_check_parameters USE control_parameters, & ONLY: message_string, neutral, turbulent_inflow, turbulent_outflow IMPLICIT NONE ! !-- Define which turbulence closure is going to be used IF ( rans_mode ) THEN c_m = 0.4_wp !according to Detering and Etling (1985) SELECT CASE ( TRIM( turbulence_closure ) ) CASE ( 'TKE-l' ) rans_tke_l = .TRUE. CASE ( 'TKE-e' ) rans_tke_e = .TRUE. IF ( INDEX( initializing_actions, 'set_1d-model_profiles' ) & == 0 ) THEN message_string = 'Initializing without 1D model while ' // & 'using TKE-e closure&' // & 'is not possible at the moment!' CALL message( 'tcm_check_parameters', 'TG0005', 1, 2, 0, 6, 0 ) ENDIF CASE DEFAULT message_string = 'Unknown turbulence closure: ' // & TRIM( turbulence_closure ) CALL message( 'tcm_check_parameters', 'TG0001', 1, 2, 0, 6, 0 ) END SELECT message_string = 'RANS mode is still in development! ' // & '&Not all features of PALM are yet compatible '// & 'with RANS mode. &Use at own risk!' CALL message( 'tcm_check_parameters', 'TG0003', 0, 1, 0, 6, 0 ) ELSE c_m = 0.1_wp !according to Lilly (1967) and Deardorff (1980) SELECT CASE ( TRIM( turbulence_closure ) ) CASE ( 'Moeng_Wyngaard' ) les_mw = .TRUE. CASE DEFAULT message_string = 'Unknown turbulence closure: ' // & TRIM( turbulence_closure ) CALL message( 'tcm_check_parameters', 'TG0001', 1, 2, 0, 6, 0 ) END SELECT ENDIF IF ( rans_tke_e ) THEN IF ( turbulent_inflow .OR. turbulent_outflow ) THEN message_string = 'turbulent inflow/outflow is not yet '// & 'implemented for TKE-e closure' CALL message( 'tcm_check_parameters', 'TG0002', 1, 2, 0, 6, 0 ) ENDIF ENDIF END SUBROUTINE tcm_check_parameters !------------------------------------------------------------------------------! ! Description: ! ------------ !> Check data output. !------------------------------------------------------------------------------! SUBROUTINE tcm_check_data_output( var, unit, i, ilen, k ) USE control_parameters, & ONLY: data_output, message_string IMPLICIT NONE CHARACTER (LEN=*) :: unit !< CHARACTER (LEN=*) :: var !< INTEGER(iwp) :: i !< INTEGER(iwp) :: ilen !< INTEGER(iwp) :: k !< SELECT CASE ( TRIM( var ) ) CASE ( 'diss' ) IF ( .NOT. rans_tke_e ) THEN message_string = 'output of "' // TRIM( var ) // '" requi' // & 'res TKE-e closure for RANS mode.' CALL message( 'tcm_check_data_output', 'TG0101', 1, 2, 0, 6, 0 ) ENDIF unit = 'm2/s3' CASE ( 'dummy2', 'dummy3', 'dummy1' ) unit = '?' CASE ( 'kh', 'km' ) unit = 'm2/s' CASE DEFAULT unit = 'illegal' END SELECT END SUBROUTINE tcm_check_data_output !------------------------------------------------------------------------------! ! Description: ! ------------ !> Define appropriate grid for netcdf variables. !> It is called out from subroutine netcdf. !------------------------------------------------------------------------------! SUBROUTINE tcm_define_netcdf_grid( var, found, grid_x, grid_y, grid_z ) IMPLICIT NONE CHARACTER (LEN=*), INTENT(OUT) :: grid_x !< CHARACTER (LEN=*), INTENT(OUT) :: grid_y !< CHARACTER (LEN=*), INTENT(OUT) :: grid_z !< CHARACTER (LEN=*), INTENT(IN) :: var !< LOGICAL, INTENT(OUT) :: found !< found = .TRUE. ! !-- Check for the grid SELECT CASE ( TRIM( var ) ) CASE ( 'diss', 'diss_xy', 'diss_xz', 'diss_yz' ) grid_x = 'x' grid_y = 'y' grid_z = 'zu' CASE ( 'dummy2', 'dummy3', 'dummy1' ) !### remove later grid_x = 'x' grid_y = 'y' grid_z = 'zu' CASE ( 'kh', 'kh_xy', 'kh_xz', 'kh_yz' ) grid_x = 'x' grid_y = 'y' grid_z = 'zu' CASE ( 'km', 'km_xy', 'km_xz', 'km_yz' ) grid_x = 'x' grid_y = 'y' grid_z = 'zu' CASE DEFAULT found = .FALSE. grid_x = 'none' grid_y = 'none' grid_z = 'none' END SELECT END SUBROUTINE tcm_define_netcdf_grid !------------------------------------------------------------------------------! ! Description: ! ------------ !> Average 3D data. !------------------------------------------------------------------------------! SUBROUTINE tcm_3d_data_averaging( mode, variable ) USE averaging, & ONLY: diss_av, kh_av, km_av USE control_parameters, & ONLY: average_count_3d IMPLICIT NONE CHARACTER (LEN=*) :: mode !< CHARACTER (LEN=*) :: variable !< INTEGER(iwp) :: i !< INTEGER(iwp) :: j !< INTEGER(iwp) :: k !< IF ( mode == 'allocate' ) THEN SELECT CASE ( TRIM( variable ) ) CASE ( 'diss' ) IF ( .NOT. ALLOCATED( diss_av ) ) THEN ALLOCATE( diss_av(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ENDIF diss_av = 0.0_wp CASE ( 'kh' ) IF ( .NOT. ALLOCATED( kh_av ) ) THEN ALLOCATE( kh_av(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ENDIF kh_av = 0.0_wp CASE ( 'km' ) IF ( .NOT. ALLOCATED( km_av ) ) THEN ALLOCATE( km_av(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ENDIF km_av = 0.0_wp CASE DEFAULT CONTINUE END SELECT ELSEIF ( mode == 'sum' ) THEN SELECT CASE ( TRIM( variable ) ) CASE ( 'diss' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 diss_av(k,j,i) = diss_av(k,j,i) + diss(k,j,i) ENDDO ENDDO ENDDO CASE ( 'kh' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 kh_av(k,j,i) = kh_av(k,j,i) + kh(k,j,i) ENDDO ENDDO ENDDO CASE ( 'km' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 km_av(k,j,i) = km_av(k,j,i) + km(k,j,i) ENDDO ENDDO ENDDO CASE DEFAULT CONTINUE END SELECT ELSEIF ( mode == 'average' ) THEN SELECT CASE ( TRIM( variable ) ) CASE ( 'diss' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 diss_av(k,j,i) = diss_av(k,j,i) & / REAL( average_count_3d, KIND=wp ) ENDDO ENDDO ENDDO CASE ( 'kh' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 kh_av(k,j,i) = kh_av(k,j,i) & / REAL( average_count_3d, KIND=wp ) ENDDO ENDDO ENDDO CASE ( 'km' ) DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 km_av(k,j,i) = km_av(k,j,i) & / REAL( average_count_3d, KIND=wp ) ENDDO ENDDO ENDDO END SELECT ENDIF END SUBROUTINE tcm_3d_data_averaging !------------------------------------------------------------------------------! ! Description: ! ------------ !> Define 2D output variables. !------------------------------------------------------------------------------! SUBROUTINE tcm_data_output_2d( av, variable, found, grid, mode, local_pf, & two_d, nzb_do, nzt_do ) USE averaging, & ONLY: diss_av, kh_av, km_av IMPLICIT NONE CHARACTER (LEN=*) :: grid !< CHARACTER (LEN=*) :: mode !< CHARACTER (LEN=*) :: variable !< INTEGER(iwp) :: av !< INTEGER(iwp) :: i !< INTEGER(iwp) :: j !< INTEGER(iwp) :: k !< INTEGER(iwp) :: nzb_do !< INTEGER(iwp) :: nzt_do !< LOGICAL :: found !< LOGICAL :: two_d !< flag parameter that indicates 2D variables (horizontal cross sections) REAL(wp), DIMENSION(nxl:nxr,nys:nyn,nzb:nzt+1) :: local_pf !< local !< array to which output data is resorted to found = .TRUE. SELECT CASE ( TRIM( variable ) ) CASE ( 'diss_xy', 'diss_xz', 'diss_yz' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = diss(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = diss_av(k,j,i) ENDDO ENDDO ENDDO ENDIF IF ( mode == 'xy' ) grid = 'zu' CASE ( 'kh_xy', 'kh_xz', 'kh_yz' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = kh(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = kh_av(k,j,i) ENDDO ENDDO ENDDO ENDIF IF ( mode == 'xy' ) grid = 'zu' CASE ( 'km_xy', 'km_xz', 'km_yz' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = km(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb_do, nzt_do local_pf(i,j,k) = km_av(k,j,i) ENDDO ENDDO ENDDO ENDIF IF ( mode == 'xy' ) grid = 'zu' CASE DEFAULT found = .FALSE. grid = 'none' END SELECT END SUBROUTINE tcm_data_output_2d !------------------------------------------------------------------------------! ! Description: ! ------------ !> Define 3D output variables. !------------------------------------------------------------------------------! SUBROUTINE tcm_data_output_3d( av, variable, found, local_pf ) USE averaging, & ONLY: diss_av, kh_av, km_av IMPLICIT NONE CHARACTER (LEN=*) :: variable !< INTEGER(iwp) :: av !< INTEGER(iwp) :: i !< INTEGER(iwp) :: j !< INTEGER(iwp) :: k !< LOGICAL :: found !< REAL(sp), DIMENSION(nxl:nxr,nys:nyn,nzb:nzt+1) :: local_pf !< local !< array to which output data is resorted to found = .TRUE. SELECT CASE ( TRIM( variable ) ) CASE ( 'diss' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = diss(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = diss_av(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE ( 'kh' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = kh(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = kh_av(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE ( 'km' ) IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = km(k,j,i) ENDDO ENDDO ENDDO ELSE DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = km_av(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE ( 'dummy1' ) !### remove later IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = dummy1(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE ( 'dummy2' ) !### remove later IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = dummy2(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE ( 'dummy3' ) !### remove later IF ( av == 0 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb, nzt+1 local_pf(i,j,k) = dummy3(k,j,i) ENDDO ENDDO ENDDO ENDIF CASE DEFAULT found = .FALSE. END SELECT END SUBROUTINE tcm_data_output_3d !------------------------------------------------------------------------------! ! Description: ! ------------ !> Allocate arrays and assign pointers. !------------------------------------------------------------------------------! SUBROUTINE tcm_init_arrays USE microphysics_mod, & ONLY: collision_turbulence USE particle_attributes, & ONLY: use_sgs_for_particles, wang_kernel IMPLICIT NONE ALLOCATE( kh(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( km(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( dummy1(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) !### remove later ALLOCATE( dummy2(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( dummy3(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) IF ( rans_mode ) ALLOCATE( l_black(nzb:nzt+1) ) #if defined( __nopointer ) ALLOCATE( e(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( e_p(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( te_m(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) #else ALLOCATE( e_1(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( e_2(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( e_3(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) #endif IF ( rans_tke_e .OR. use_sgs_for_particles .OR. wang_kernel .OR. & collision_turbulence ) THEN #if defined( __nopointer ) ALLOCATE( diss(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) IF ( rans_tke_e ) THEN ALLOCATE( diss_p(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( tdiss_m(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ENDIF #else ALLOCATE( diss_1(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) IF ( rans_tke_e ) THEN ALLOCATE( diss_2(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ALLOCATE( diss_3(nzb:nzt+1,nysg:nyng,nxlg:nxrg) ) ENDIF #endif ENDIF #if ! defined( __nopointer ) ! !-- Initial assignment of pointers e => e_1; e_p => e_2; te_m => e_3 IF ( rans_tke_e .OR. use_sgs_for_particles .OR. & wang_kernel .OR. collision_turbulence ) THEN diss => diss_1 IF ( rans_tke_e ) THEN diss_p => diss_2; tdiss_m => diss_3 ENDIF ENDIF #endif END SUBROUTINE tcm_init_arrays !------------------------------------------------------------------------------! ! Description: ! ------------ !> Initialization of turbulence closure module. !------------------------------------------------------------------------------! SUBROUTINE tcm_init USE arrays_3d, & ONLY: ug, vg, zu USE control_parameters, & ONLY: complex_terrain, f, kappa, dissipation_1d, topography USE model_1d_mod, & ONLY: diss1d, e1d, kh1d, km1d, l1d USE surface_mod, & ONLY: get_topography_top_index_ji IMPLICIT NONE INTEGER(iwp) :: i !< loop index INTEGER(iwp) :: j !< loop index INTEGER(iwp) :: k !< loop index INTEGER(iwp) :: nz_s_shift !< INTEGER(iwp) :: nz_s_shift_l !< ! !-- Actions for initial runs IF ( TRIM( initializing_actions ) /= 'read_restart_data' .AND. & TRIM( initializing_actions ) /= 'cyclic_fill' ) THEN IF ( INDEX( initializing_actions, 'set_1d-model_profiles' ) /= 0 ) THEN ! !-- Transfer initial profiles to the arrays of the 3D model DO i = nxlg, nxrg DO j = nysg, nyng e(:,j,i) = e1d kh(:,j,i) = kh1d km(:,j,i) = km1d ENDDO ENDDO IF ( constant_diffusion ) THEN e = 0.0_wp ENDIF IF ( rans_tke_e ) THEN IF ( dissipation_1d == 'prognostic' ) THEN !### Why must this be checked? DO i = nxlg, nxrg !### Should 'diss' not always DO j = nysg, nyng !### be prognostic in case rans_tke_e? diss(:,j,i) = diss1d ENDDO ENDDO ELSE DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb+1, nzt diss(k,j,i) = e(k,j,i) * SQRT( e(k,j,i) ) / l1d(k) ENDDO ENDDO ENDDO ENDIF ENDIF ELSEIF ( INDEX(initializing_actions, 'set_constant_profiles') /= 0 .OR. & INDEX( initializing_actions, 'inifor' ) /= 0 ) THEN IF ( constant_diffusion ) THEN km = km_constant kh = km / prandtl_number e = 0.0_wp ELSEIF ( e_init > 0.0_wp ) THEN DO k = nzb+1, nzt km(k,:,:) = c_m * l_grid(k) * SQRT( e_init ) ENDDO km(nzb,:,:) = km(nzb+1,:,:) km(nzt+1,:,:) = km(nzt,:,:) kh = km / prandtl_number e = e_init ELSE IF ( .NOT. ocean ) THEN kh = 0.01_wp ! there must exist an initial diffusion, because km = 0.01_wp ! otherwise no TKE would be produced by the ! production terms, as long as not yet ! e = (u*/cm)**2 at k=nzb+1 ELSE kh = 0.00001_wp km = 0.00001_wp ENDIF e = 0.0_wp ENDIF ENDIF ! !-- Store initial profiles for output purposes etc. hom(:,1,23,:) = SPREAD( km(:,nys,nxl), 2, statistic_regions+1 ) hom(:,1,24,:) = SPREAD( kh(:,nys,nxl), 2, statistic_regions+1 ) ! !-- Initialize old and new time levels. te_m = 0.0_wp e_p = e IF ( rans_tke_e ) THEN tdiss_m = 0.0_wp diss_p = diss ENDIF ELSEIF ( TRIM( initializing_actions ) == 'read_restart_data' .OR. & TRIM( initializing_actions ) == 'cyclic_fill' ) & THEN ! !-- In case of complex terrain and cyclic fill method as initialization, !-- shift initial data in the vertical direction for each point in the !-- x-y-plane depending on local surface height IF ( complex_terrain .AND. & TRIM( initializing_actions ) == 'cyclic_fill' ) THEN DO i = nxlg, nxrg DO j = nysg, nyng nz_s_shift = get_topography_top_index_ji( j, i, 's' ) e(nz_s_shift:nzt+1,j,i) = e(0:nzt+1-nz_s_shift,j,i) km(nz_s_shift:nzt+1,j,i) = km(0:nzt+1-nz_s_shift,j,i) kh(nz_s_shift:nzt+1,j,i) = kh(0:nzt+1-nz_s_shift,j,i) ENDDO ENDDO ENDIF ! !-- Initialization of the turbulence recycling method IF ( TRIM( initializing_actions ) == 'cyclic_fill' .AND. & turbulent_inflow ) THEN mean_inflow_profiles(:,5) = hom_sum(:,8,0) ! e ! !-- In case of complex terrain, determine vertical displacement at inflow !-- boundary and adjust mean inflow profiles IF ( complex_terrain ) THEN IF ( nxlg <= 0 .AND. nxrg >= 0 .AND. nysg <= 0 .AND. nyng >= 0 ) THEN nz_s_shift_l = get_topography_top_index_ji( 0, 0, 's' ) ELSE nz_s_shift_l = 0 ENDIF #if defined( __parallel ) CALL MPI_ALLREDUCE(nz_s_shift_l, nz_s_shift, 1, MPI_INTEGER, & MPI_MAX, comm2d, ierr) #else nz_s_shift = nz_s_shift_l #endif mean_inflow_profiles(nz_s_shift:nzt+1,5) = hom_sum(0:nzt+1-nz_s_shift,8,0) ! e ENDIF ! !-- Use these mean profiles at the inflow (provided that Dirichlet !-- conditions are used) IF ( inflow_l ) THEN DO j = nysg, nyng DO k = nzb, nzt+1 e(k,j,nxlg:-1) = mean_inflow_profiles(k,5) ENDDO ENDDO ENDIF ENDIF ! !-- Inside buildings set TKE back to zero IF ( TRIM( initializing_actions ) == 'cyclic_fill' .AND. & topography /= 'flat' ) THEN ! !-- Inside buildings set TKE back to zero. !-- Other scalars (km, kh, diss, ...) are ignored at present, !-- maybe revise later. DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt e(k,j,i) = MERGE( e(k,j,i), 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) ) te_m(k,j,i) = MERGE( te_m(k,j,i), 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) ) ENDDO ENDDO ENDDO ENDIF ! !-- Initialize new time levels (only done in order to set boundary values !-- including ghost points) e_p = e ! !-- Allthough tendency arrays are set in prognostic_equations, they have !-- have to be predefined here because they are used (but multiplied with 0) !-- there before they are set. te_m = 0.0_wp ENDIF ! !-- Calculate mixing length according to Blackadar (1962) IF ( rans_mode ) THEN IF ( f /= 0.0_wp ) THEN l_max = 2.7E-4 * SQRT( ug(nzt+1)**2 + vg(nzt+1)**2 ) / & ABS( f ) + 1E-10_wp ELSE l_max = 30.0_wp ENDIF DO k = nzb, nzt l_black(k) = kappa * zu(k) / ( 1.0_wp + kappa * zu(k) / l_max ) ENDDO l_black(nzt+1) = l_black(nzt) ENDIF END SUBROUTINE tcm_init !------------------------------------------------------------------------------! ! Description: ! ------------ !> Initialize virtual velocities used later in production_e. !------------------------------------------------------------------------------! SUBROUTINE production_e_init USE arrays_3d, & ONLY: drho_air_zw, zu USE control_parameters, & ONLY: constant_flux_layer USE surface_mod, & ONLY : surf_def_h, surf_def_v, surf_lsm_h, surf_usm_h IMPLICIT NONE INTEGER(iwp) :: i !< grid index x-direction INTEGER(iwp) :: j !< grid index y-direction INTEGER(iwp) :: k !< grid index z-direction INTEGER(iwp) :: m !< running index surface elements IF ( constant_flux_layer ) THEN ! !-- Calculate a virtual velocity at the surface in a way that the !-- vertical velocity gradient at k = 1 (u(k+1)-u_0) matches the !-- Prandtl law (-w'u'/km). This gradient is used in the TKE shear !-- production term at k=1 (see production_e_ij). !-- The velocity gradient has to be limited in case of too small km !-- (otherwise the timestep may be significantly reduced by large !-- surface winds). !-- not available in case of non-cyclic boundary conditions. !-- WARNING: the exact analytical solution would require the determination !-- of the eddy diffusivity by km = u* * kappa * zp / phi_m. !-- Default surfaces, upward-facing !$OMP PARALLEL DO PRIVATE(i,j,k,m) DO m = 1, surf_def_h(0)%ns i = surf_def_h(0)%i(m) j = surf_def_h(0)%j(m) k = surf_def_h(0)%k(m) ! !-- Note, calculatione of u_0 and v_0 is not fully accurate, as u/v !-- and km are not on the same grid. Actually, a further !-- interpolation of km onto the u/v-grid is necessary. However, the !-- effect of this error is negligible. surf_def_h(0)%u_0(m) = u(k+1,j,i) + surf_def_h(0)%usws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) surf_def_h(0)%v_0(m) = v(k+1,j,i) + surf_def_h(0)%vsws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) IF ( ABS( u(k+1,j,i) - surf_def_h(0)%u_0(m) ) > & ABS( u(k+1,j,i) - u(k-1,j,i) ) & ) surf_def_h(0)%u_0(m) = u(k-1,j,i) IF ( ABS( v(k+1,j,i) - surf_def_h(0)%v_0(m) ) > & ABS( v(k+1,j,i) - v(k-1,j,i) ) & ) surf_def_h(0)%v_0(m) = v(k-1,j,i) ENDDO ! !-- Default surfaces, downward-facing surfaces !$OMP PARALLEL DO PRIVATE(i,j,k,m) DO m = 1, surf_def_h(1)%ns i = surf_def_h(1)%i(m) j = surf_def_h(1)%j(m) k = surf_def_h(1)%k(m) surf_def_h(1)%u_0(m) = u(k-1,j,i) - surf_def_h(1)%usws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) surf_def_h(1)%v_0(m) = v(k-1,j,i) - surf_def_h(1)%vsws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) IF ( ABS( surf_def_h(1)%u_0(m) - u(k-1,j,i) ) > & ABS( u(k+1,j,i) - u(k-1,j,i) ) & ) surf_def_h(1)%u_0(m) = u(k+1,j,i) IF ( ABS( surf_def_h(1)%v_0(m) - v(k-1,j,i) ) > & ABS( v(k+1,j,i) - v(k-1,j,i) ) & ) surf_def_h(1)%v_0(m) = v(k+1,j,i) ENDDO ! !-- Natural surfaces, upward-facing !$OMP PARALLEL DO PRIVATE(i,j,k,m) DO m = 1, surf_lsm_h%ns i = surf_lsm_h%i(m) j = surf_lsm_h%j(m) k = surf_lsm_h%k(m) ! !-- Note, calculatione of u_0 and v_0 is not fully accurate, as u/v !-- and km are not on the same grid. Actually, a further !-- interpolation of km onto the u/v-grid is necessary. However, the !-- effect of this error is negligible. surf_lsm_h%u_0(m) = u(k+1,j,i) + surf_lsm_h%usws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) surf_lsm_h%v_0(m) = v(k+1,j,i) + surf_lsm_h%vsws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) IF ( ABS( u(k+1,j,i) - surf_lsm_h%u_0(m) ) > & ABS( u(k+1,j,i) - u(k-1,j,i) ) & ) surf_lsm_h%u_0(m) = u(k-1,j,i) IF ( ABS( v(k+1,j,i) - surf_lsm_h%v_0(m) ) > & ABS( v(k+1,j,i) - v(k-1,j,i) ) & ) surf_lsm_h%v_0(m) = v(k-1,j,i) ENDDO ! !-- Urban surfaces, upward-facing !$OMP PARALLEL DO PRIVATE(i,j,k,m) DO m = 1, surf_usm_h%ns i = surf_usm_h%i(m) j = surf_usm_h%j(m) k = surf_usm_h%k(m) ! !-- Note, calculatione of u_0 and v_0 is not fully accurate, as u/v !-- and km are not on the same grid. Actually, a further !-- interpolation of km onto the u/v-grid is necessary. However, the !-- effect of this error is negligible. surf_usm_h%u_0(m) = u(k+1,j,i) + surf_usm_h%usws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) surf_usm_h%v_0(m) = v(k+1,j,i) + surf_usm_h%vsws(m) * & drho_air_zw(k-1) * & ( zu(k+1) - zu(k-1) ) / & ( km(k,j,i) + 1.0E-20_wp ) IF ( ABS( u(k+1,j,i) - surf_usm_h%u_0(m) ) > & ABS( u(k+1,j,i) - u(k-1,j,i) ) & ) surf_usm_h%u_0(m) = u(k-1,j,i) IF ( ABS( v(k+1,j,i) - surf_usm_h%v_0(m) ) > & ABS( v(k+1,j,i) - v(k-1,j,i) ) & ) surf_usm_h%v_0(m) = v(k-1,j,i) ENDDO ENDIF END SUBROUTINE production_e_init !------------------------------------------------------------------------------! ! Description: ! ------------ !> Prognostic equation for subgrid-scale TKE and TKE dissipation rate. !> Vector-optimized version !------------------------------------------------------------------------------! SUBROUTINE tcm_prognostic USE arrays_3d, & ONLY: ddzu USE control_parameters, & ONLY: f, scalar_advec, tsc USE surface_mod, & ONLY : surf_def_h, surf_def_v, surf_lsm_h, surf_lsm_v, surf_usm_h, & surf_usm_v IMPLICIT NONE INTEGER(iwp) :: i !< loop index INTEGER(iwp) :: j !< loop index INTEGER(iwp) :: k !< loop index INTEGER(iwp) :: m !< loop index INTEGER(iwp) :: surf_e !< end index of surface elements at given i-j position INTEGER(iwp) :: surf_s !< start index of surface elements at given i-j position REAL(wp) :: sbt !< wheighting factor for sub-time step REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: advec !< advection term of TKE tendency REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: produc !< production term of TKE tendency ! !-- If required, compute prognostic equation for turbulent kinetic !-- energy (TKE) IF ( .NOT. constant_diffusion ) THEN CALL cpu_log( log_point(16), 'tke-equation', 'start' ) sbt = tsc(2) IF ( .NOT. use_upstream_for_tke ) THEN IF ( scalar_advec == 'bc-scheme' ) THEN IF ( timestep_scheme(1:5) /= 'runge' ) THEN ! !-- Bott-Chlond scheme always uses Euler time step. Thus: sbt = 1.0_wp ENDIF tend = 0.0_wp CALL advec_s_bc( e, 'e' ) ENDIF ENDIF ! !-- TKE-tendency terms with no communication IF ( scalar_advec /= 'bc-scheme' .OR. use_upstream_for_tke ) THEN IF ( use_upstream_for_tke ) THEN tend = 0.0_wp CALL advec_s_up( e ) ELSE tend = 0.0_wp IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( ws_scheme_sca ) THEN CALL advec_s_ws( e, 'e' ) ELSE CALL advec_s_pw( e ) ENDIF ELSE CALL advec_s_up( e ) ENDIF ENDIF ENDIF IF ( rans_tke_e ) advec = tend CALL production_e ! !-- Save production term for prognostic equation of TKE dissipation rate IF ( rans_tke_e ) produc = tend - advec IF ( .NOT. humidity ) THEN IF ( ocean ) THEN CALL diffusion_e( prho, prho_reference ) ELSE CALL diffusion_e( pt, pt_reference ) ENDIF ELSE CALL diffusion_e( vpt, pt_reference ) ENDIF ! !-- Additional sink term for flows through plant canopies IF ( plant_canopy ) CALL pcm_tendency( 6 ) CALL user_actions( 'e-tendency' ) ! !-- Prognostic equation for TKE. !-- Eliminate negative TKE values, which can occur due to numerical !-- reasons in the course of the integration. In such cases the old TKE !-- value is reduced by 90%. DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt e_p(k,j,i) = e(k,j,i) + ( dt_3d * ( sbt * tend(k,j,i) + & tsc(3) * te_m(k,j,i) ) & ) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) & ) IF ( e_p(k,j,i) < 0.0_wp ) e_p(k,j,i) = 0.1_wp * e(k,j,i) ENDDO ENDDO ENDDO ! !-- Use special boundary condition in case of TKE-e closure IF ( rans_tke_e ) THEN DO i = nxl, nxr DO j = nys, nyn surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) e_p(k,j,i) = surf_def_h(0)%us(m)**2 / c_m**2 ENDDO ENDDO ENDDO ENDIF ! !-- Calculate tendencies for the next Runge-Kutta step IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( intermediate_timestep_count == 1 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt te_m(k,j,i) = tend(k,j,i) ENDDO ENDDO ENDDO ELSEIF ( intermediate_timestep_count < & intermediate_timestep_count_max ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt te_m(k,j,i) = -9.5625_wp * tend(k,j,i) & + 5.3125_wp * te_m(k,j,i) ENDDO ENDDO ENDDO ENDIF ENDIF CALL cpu_log( log_point(16), 'tke-equation', 'stop' ) ENDIF ! TKE equation ! !-- If required, compute prognostic equation for TKE dissipation rate IF ( rans_tke_e ) THEN CALL cpu_log( log_point(33), 'diss-equation', 'start' ) sbt = tsc(2) IF ( .NOT. use_upstream_for_tke ) THEN IF ( scalar_advec == 'bc-scheme' ) THEN IF ( timestep_scheme(1:5) /= 'runge' ) THEN ! !-- Bott-Chlond scheme always uses Euler time step. Thus: sbt = 1.0_wp ENDIF tend = 0.0_wp CALL advec_s_bc( diss, 'diss' ) ENDIF ENDIF ! !-- dissipation-tendency terms with no communication IF ( scalar_advec /= 'bc-scheme' .OR. use_upstream_for_tke ) THEN IF ( use_upstream_for_tke ) THEN tend = 0.0_wp CALL advec_s_up( diss ) ELSE tend = 0.0_wp IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( ws_scheme_sca ) THEN CALL advec_s_ws( diss, 'diss' ) ELSE CALL advec_s_pw( diss ) ENDIF ELSE CALL advec_s_up( diss ) ENDIF ENDIF ENDIF ! !-- Production of TKE dissipation rate DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt ! tend(k,j,i) = tend(k,j,i) + c_1 * diss(k,j,i) / ( e(k,j,i) + 1.0E-20_wp ) * produc(k) tend(k,j,i) = tend(k,j,i) + c_1 * c_mu * f / c_h & !### needs revision / surf_def_h(0)%us(surf_def_h(0)%start_index(j,i)) & * SQRT(e(k,j,i)) * produc(k,j,i) ENDDO ENDDO ENDDO CALL diffusion_diss ! !-- Additional sink term for flows through plant canopies ! IF ( plant_canopy ) CALL pcm_tendency( ? ) !### what to do with this? ! CALL user_actions( 'diss-tendency' ) !### not yet implemented ! !-- Prognostic equation for TKE dissipation. !-- Eliminate negative dissipation values, which can occur due to numerical !-- reasons in the course of the integration. In such cases the old !-- dissipation value is reduced by 90%. DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt diss_p(k,j,i) = diss(k,j,i) + ( dt_3d * ( sbt * tend(k,j,i) + & tsc(3) * tdiss_m(k,j,i) ) & ) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) & ) IF ( diss_p(k,j,i) < 0.0_wp ) & diss_p(k,j,i) = 0.1_wp * diss(k,j,i) ENDDO ENDDO ENDDO ! !-- Use special boundary condition in case of TKE-e closure DO i = nxl, nxr DO j = nys, nyn surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) diss_p(k,j,i) = surf_def_h(0)%us(m)**3 / kappa * ddzu(k) ENDDO ENDDO ENDDO ! !-- Calculate tendencies for the next Runge-Kutta step IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( intermediate_timestep_count == 1 ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt tdiss_m(k,j,i) = tend(k,j,i) ENDDO ENDDO ENDDO ELSEIF ( intermediate_timestep_count < & intermediate_timestep_count_max ) THEN DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt tdiss_m(k,j,i) = -9.5625_wp * tend(k,j,i) & + 5.3125_wp * tdiss_m(k,j,i) ENDDO ENDDO ENDDO ENDIF ENDIF CALL cpu_log( log_point(33), 'diss-equation', 'stop' ) ENDIF END SUBROUTINE tcm_prognostic !------------------------------------------------------------------------------! ! Description: ! ------------ !> Prognostic equation for subgrid-scale TKE and TKE dissipation rate. !> Cache-optimized version !------------------------------------------------------------------------------! SUBROUTINE tcm_prognostic_ij( i, j, i_omp, tn ) USE arrays_3d, & ONLY: ddzu, diss_l_diss, diss_l_e, diss_s_diss, diss_s_e, & flux_l_diss, flux_l_e, flux_s_diss, flux_s_e USE control_parameters, & ONLY: f, tsc USE surface_mod, & ONLY : surf_def_h, surf_def_v, surf_lsm_h, surf_lsm_v, surf_usm_h, & surf_usm_v IMPLICIT NONE INTEGER(iwp) :: i !< loop index x direction INTEGER(iwp) :: i_omp !< INTEGER(iwp) :: j !< loop index y direction INTEGER(iwp) :: k !< loop index z direction INTEGER(iwp) :: m !< loop index INTEGER(iwp) :: surf_e !< end index of surface elements at given i-j position INTEGER(iwp) :: surf_s !< start index of surface elements at given i-j position INTEGER(iwp) :: tn !< REAL(wp), DIMENSION(nzb:nzt+1) :: advec !< advection term of TKE tendency REAL(wp), DIMENSION(nzb:nzt+1) :: produc !< production term of TKE tendency ! !-- If required, compute prognostic equation for turbulent kinetic !-- energy (TKE) IF ( .NOT. constant_diffusion ) THEN ! !-- Tendency-terms for TKE tend(:,j,i) = 0.0_wp IF ( timestep_scheme(1:5) == 'runge' & .AND. .NOT. use_upstream_for_tke ) THEN IF ( ws_scheme_sca ) THEN CALL advec_s_ws( i, j, e, 'e', flux_s_e, diss_s_e, & flux_l_e, diss_l_e , i_omp, tn ) ELSE CALL advec_s_pw( i, j, e ) ENDIF ELSE CALL advec_s_up( i, j, e ) ENDIF advec(:) = tend(:,j,i) CALL production_e( i, j ) produc(:) = tend(:,j,i) - advec(:) IF ( .NOT. humidity ) THEN IF ( ocean ) THEN CALL diffusion_e( i, j, prho, prho_reference ) ELSE CALL diffusion_e( i, j, pt, pt_reference ) ENDIF ELSE CALL diffusion_e( i, j, vpt, pt_reference ) ENDIF ! !-- Additional sink term for flows through plant canopies IF ( plant_canopy ) CALL pcm_tendency( i, j, 6 ) CALL user_actions( i, j, 'e-tendency' ) ! !-- Prognostic equation for TKE. !-- Eliminate negative TKE values, which can occur due to numerical !-- reasons in the course of the integration. In such cases the old !-- TKE value is reduced by 90%. DO k = nzb+1, nzt e_p(k,j,i) = e(k,j,i) + ( dt_3d * ( tsc(2) * tend(k,j,i) + & tsc(3) * te_m(k,j,i) ) & ) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) & ) IF ( e_p(k,j,i) <= 0.0_wp ) e_p(k,j,i) = 0.1_wp * e(k,j,i) ENDDO ! !-- Use special boundary condition in case of TKE-e closure IF ( rans_tke_e ) THEN surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) e_p(k,j,i) = surf_def_h(0)%us(m)**2 / c_m**2 ENDDO ENDIF ! !-- Calculate tendencies for the next Runge-Kutta step IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( intermediate_timestep_count == 1 ) THEN DO k = nzb+1, nzt te_m(k,j,i) = tend(k,j,i) ENDDO ELSEIF ( intermediate_timestep_count < & intermediate_timestep_count_max ) THEN DO k = nzb+1, nzt te_m(k,j,i) = -9.5625_wp * tend(k,j,i) + & 5.3125_wp * te_m(k,j,i) ENDDO ENDIF ENDIF ENDIF ! TKE equation ! !-- If required, compute prognostic equation for TKE dissipation rate IF ( rans_tke_e ) THEN ! !-- Tendency-terms for dissipation tend(:,j,i) = 0.0_wp IF ( timestep_scheme(1:5) == 'runge' & .AND. .NOT. use_upstream_for_tke ) THEN IF ( ws_scheme_sca ) THEN CALL advec_s_ws( i, j, diss, 'diss', flux_s_diss, diss_s_diss, & flux_l_diss, diss_l_diss, i_omp, tn ) ELSE CALL advec_s_pw( i, j, diss ) ENDIF ELSE CALL advec_s_up( i, j, diss ) ENDIF ! !-- Production of TKE dissipation rate DO k = nzb+1, nzt ! tend(k,j,i) = tend(k,j,i) + c_1 * diss(k,j,i) / ( e(k,j,i) + 1.0E-20_wp ) * produc(k) tend(k,j,i) = tend(k,j,i) + c_1 * c_mu * f / c_h & !### needs revision / surf_def_h(0)%us(surf_def_h(0)%start_index(j,i)) & * SQRT(e(k,j,i)) * produc(k) ENDDO CALL diffusion_diss( i, j ) ! !-- Additional sink term for flows through plant canopies ! IF ( plant_canopy ) CALL pcm_tendency( i, j, ? ) !### not yet implemented ! CALL user_actions( i, j, 'diss-tendency' ) !### not yet implemented ! !-- Prognostic equation for TKE dissipation !-- Eliminate negative dissipation values, which can occur due to !-- numerical reasons in the course of the integration. In such cases !-- the old dissipation value is reduced by 90%. DO k = nzb+1, nzt diss_p(k,j,i) = diss(k,j,i) + ( dt_3d * ( tsc(2) * tend(k,j,i) + & tsc(3) * tdiss_m(k,j,i) ) & ) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 )& ) IF ( diss_p(k,j,i) <= 0.0_wp ) diss_p(k,j,i) = 0.1_wp * diss(k,j,i) ENDDO ! !-- Use special boundary condition in case of TKE-e closure surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) diss_p(k,j,i) = surf_def_h(0)%us(m)**3 / kappa * ddzu(k) ENDDO ! !-- Calculate tendencies for the next Runge-Kutta step IF ( timestep_scheme(1:5) == 'runge' ) THEN IF ( intermediate_timestep_count == 1 ) THEN DO k = nzb+1, nzt tdiss_m(k,j,i) = tend(k,j,i) ENDDO ELSEIF ( intermediate_timestep_count < & intermediate_timestep_count_max ) THEN DO k = nzb+1, nzt tdiss_m(k,j,i) = -9.5625_wp * tend(k,j,i) + & 5.3125_wp * tdiss_m(k,j,i) ENDDO ENDIF ENDIF ! IF ( intermediate_timestep_count == 1 ) dummy1(:,j,i) = e_p(:,j,i) ! IF ( intermediate_timestep_count == 2 ) dummy2(:,j,i) = e_p(:,j,i) ! IF ( intermediate_timestep_count == 3 ) dummy3(:,j,i) = e_p(:,j,i) ENDIF ! dissipation equation END SUBROUTINE tcm_prognostic_ij !------------------------------------------------------------------------------! ! Description: ! ------------ !> Production terms (shear + buoyancy) of the TKE. !> Vector-optimized version !> @warning The case with constant_flux_layer = F and use_surface_fluxes = T is !> not considered well! !------------------------------------------------------------------------------! SUBROUTINE production_e USE arrays_3d, & ONLY: ddzw, dd2zu, drho_air_zw, q, ql USE cloud_parameters, & ONLY: l_d_cp, l_d_r, pt_d_t, t_d_pt USE control_parameters, & ONLY: cloud_droplets, cloud_physics, constant_flux_layer, g, neutral, & rho_reference, use_single_reference_value, use_surface_fluxes, & use_top_fluxes USE grid_variables, & ONLY: ddx, dx, ddy, dy USE surface_mod, & ONLY : surf_def_h, surf_def_v, surf_lsm_h, surf_lsm_v, surf_usm_h, & surf_usm_v IMPLICIT NONE INTEGER(iwp) :: i !< running index x-direction INTEGER(iwp) :: j !< running index y-direction INTEGER(iwp) :: k !< running index z-direction INTEGER(iwp) :: l !< running index for different surface type orientation INTEGER(iwp) :: m !< running index surface elements INTEGER(iwp) :: surf_e !< end index of surface elements at given i-j position INTEGER(iwp) :: surf_s !< start index of surface elements at given i-j position REAL(wp) :: def !< REAL(wp) :: flag !< flag to mask topography REAL(wp) :: k1 !< REAL(wp) :: k2 !< REAL(wp) :: km_neutral !< diffusion coefficient assuming neutral conditions - used to compute shear production at surfaces REAL(wp) :: theta !< REAL(wp) :: temp !< REAL(wp) :: sign_dir !< sign of wall-tke flux, depending on wall orientation REAL(wp) :: usvs !< momentum flux u"v" REAL(wp) :: vsus !< momentum flux v"u" REAL(wp) :: wsus !< momentum flux w"u" REAL(wp) :: wsvs !< momentum flux w"v" REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dudx !< Gradient of u-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dudy !< Gradient of u-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dudz !< Gradient of u-component in z-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dvdx !< Gradient of v-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dvdy !< Gradient of v-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dvdz !< Gradient of v-component in z-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dwdx !< Gradient of w-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dwdy !< Gradient of w-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dwdz !< Gradient of w-component in z-direction DO i = nxl, nxr IF ( constant_flux_layer ) THEN ! !-- Calculate TKE production by shear. Calculate gradients at all grid !-- points first, gradients at surface-bounded grid points will be !-- overwritten further below. DO j = nys, nyn DO k = nzb+1, nzt dudx(k,j) = ( u(k,j,i+1) - u(k,j,i) ) * ddx dudy(k,j) = 0.25_wp * ( u(k,j+1,i) + u(k,j+1,i+1) - & u(k,j-1,i) - u(k,j-1,i+1) ) * ddy dudz(k,j) = 0.5_wp * ( u(k+1,j,i) + u(k+1,j,i+1) - & u(k-1,j,i) - u(k-1,j,i+1) ) * & dd2zu(k) dvdx(k,j) = 0.25_wp * ( v(k,j,i+1) + v(k,j+1,i+1) - & v(k,j,i-1) - v(k,j+1,i-1) ) * ddx dvdy(k,j) = ( v(k,j+1,i) - v(k,j,i) ) * ddy dvdz(k,j) = 0.5_wp * ( v(k+1,j,i) + v(k+1,j+1,i) - & v(k-1,j,i) - v(k-1,j+1,i) ) * & dd2zu(k) dwdx(k,j) = 0.25_wp * ( w(k,j,i+1) + w(k-1,j,i+1) - & w(k,j,i-1) - w(k-1,j,i-1) ) * ddx dwdy(k,j) = 0.25_wp * ( w(k,j+1,i) + w(k-1,j+1,i) - & w(k,j-1,i) - w(k-1,j-1,i) ) * ddy dwdz(k,j) = ( w(k,j,i) - w(k-1,j,i) ) * ddzw(k) ENDDO ENDDO ! !-- Position beneath wall !-- (2) - Will allways be executed. !-- 'bottom and wall: use u_0,v_0 and wall functions' DO j = nys, nyn ! !-- Compute gradients at north- and south-facing surfaces. !-- First, for default surfaces, then for urban surfaces. !-- Note, so far no natural vertical surfaces implemented DO l = 0, 1 surf_s = surf_def_v(l)%start_index(j,i) surf_e = surf_def_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_v(l)%k(m) usvs = surf_def_v(l)%mom_flux_tke(0,m) wsvs = surf_def_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k,j) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k,j) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_v(l)%start_index(j,i) surf_e = surf_lsm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_v(l)%k(m) usvs = surf_lsm_v(l)%mom_flux_tke(0,m) wsvs = surf_lsm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k,j) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k,j) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Urban surfaces surf_s = surf_usm_v(l)%start_index(j,i) surf_e = surf_usm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_v(l)%k(m) usvs = surf_usm_v(l)%mom_flux_tke(0,m) wsvs = surf_usm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k,j) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k,j) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ENDDO ! !-- Compute gradients at east- and west-facing walls DO l = 2, 3 surf_s = surf_def_v(l)%start_index(j,i) surf_e = surf_def_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_v(l)%k(m) vsus = surf_def_v(l)%mom_flux_tke(0,m) wsus = surf_def_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k,j) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k,j) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_v(l)%start_index(j,i) surf_e = surf_lsm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_v(l)%k(m) vsus = surf_lsm_v(l)%mom_flux_tke(0,m) wsus = surf_lsm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k,j) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k,j) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Urban surfaces surf_s = surf_usm_v(l)%start_index(j,i) surf_e = surf_usm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_v(l)%k(m) vsus = surf_usm_v(l)%mom_flux_tke(0,m) wsus = surf_usm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k,j) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k,j) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ENDDO ! !-- Compute gradients at upward-facing surfaces surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) ! !-- Please note, actually, an interpolation of u_0 and v_0 !-- onto the grid center would be required. However, this !-- would require several data transfers between 2D-grid and !-- wall type. The effect of this missing interpolation is !-- negligible. (See also production_e_init). dudz(k,j) = ( u(k+1,j,i) - surf_def_h(0)%u_0(m) ) * dd2zu(k) dvdz(k,j) = ( v(k+1,j,i) - surf_def_h(0)%v_0(m) ) * dd2zu(k) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) dudz(k,j) = ( u(k+1,j,i) - surf_lsm_h%u_0(m) ) * dd2zu(k) dvdz(k,j) = ( v(k+1,j,i) - surf_lsm_h%v_0(m) ) * dd2zu(k) ENDDO ! !-- Urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_h%k(m) dudz(k,j) = ( u(k+1,j,i) - surf_usm_h%u_0(m) ) * dd2zu(k) dvdz(k,j) = ( v(k+1,j,i) - surf_usm_h%v_0(m) ) * dd2zu(k) ENDDO ! !-- Compute gradients at downward-facing walls, only for !-- non-natural default surfaces surf_s = surf_def_h(1)%start_index(j,i) surf_e = surf_def_h(1)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(1)%k(m) dudz(k,j) = ( surf_def_h(1)%u_0(m) - u(k-1,j,i) ) * dd2zu(k) dvdz(k,j) = ( surf_def_h(1)%v_0(m) - v(k-1,j,i) ) * dd2zu(k) ENDDO ENDDO DO j = nys, nyn DO k = nzb+1, nzt def = 2.0_wp * ( dudx(k,j)**2 + dvdy(k,j)**2 + dwdz(k,j)**2 ) + & dudy(k,j)**2 + dvdx(k,j)**2 + dwdx(k,j)**2 + & dwdy(k,j)**2 + dudz(k,j)**2 + dvdz(k,j)**2 + & 2.0_wp * ( dvdx(k,j)*dudy(k,j) + dwdx(k,j)*dudz(k,j) + & dwdy(k,j)*dvdz(k,j) ) IF ( def < 0.0_wp ) def = 0.0_wp flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) tend(k,j,i) = tend(k,j,i) + km(k,j,i) * def * flag ENDDO ENDDO ELSE DO j = nys, nyn ! !-- Calculate TKE production by shear. Here, no additional !-- wall-bounded code is considered. !-- Why? DO k = nzb+1, nzt dudx(k,j) = ( u(k,j,i+1) - u(k,j,i) ) * ddx dudy(k,j) = 0.25_wp * ( u(k,j+1,i) + u(k,j+1,i+1) - & u(k,j-1,i) - u(k,j-1,i+1) ) * ddy dudz(k,j) = 0.5_wp * ( u(k+1,j,i) + u(k+1,j,i+1) - & u(k-1,j,i) - u(k-1,j,i+1) ) * & dd2zu(k) dvdx(k,j) = 0.25_wp * ( v(k,j,i+1) + v(k,j+1,i+1) - & v(k,j,i-1) - v(k,j+1,i-1) ) * ddx dvdy(k,j) = ( v(k,j+1,i) - v(k,j,i) ) * ddy dvdz(k,j) = 0.5_wp * ( v(k+1,j,i) + v(k+1,j+1,i) - & v(k-1,j,i) - v(k-1,j+1,i) ) * & dd2zu(k) dwdx(k,j) = 0.25_wp * ( w(k,j,i+1) + w(k-1,j,i+1) - & w(k,j,i-1) - w(k-1,j,i-1) ) * ddx dwdy(k,j) = 0.25_wp * ( w(k,j+1,i) + w(k-1,j+1,i) - & w(k,j-1,i) - w(k-1,j-1,i) ) * ddy dwdz(k,j) = ( w(k,j,i) - w(k-1,j,i) ) * & ddzw(k) def = 2.0_wp * ( & dudx(k,j)**2 + dvdy(k,j)**2 + dwdz(k,j)**2 & ) + & dudy(k,j)**2 + dvdx(k,j)**2 + dwdx(k,j)**2 + & dwdy(k,j)**2 + dudz(k,j)**2 + dvdz(k,j)**2 + & 2.0_wp * ( & dvdx(k,j)*dudy(k,j) + dwdx(k,j)*dudz(k,j) + & dwdy(k,j)*dvdz(k,j) & ) IF ( def < 0.0_wp ) def = 0.0_wp flag = MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 29 ) ) tend(k,j,i) = tend(k,j,i) + km(k,j,i) * def * flag ENDDO ENDDO ENDIF ! !-- If required, calculate TKE production by buoyancy IF ( .NOT. neutral ) THEN IF ( .NOT. humidity ) THEN IF ( ocean ) THEN ! !-- So far in the ocean no special treatment of density flux !-- in the bottom and top surface layer DO j = nys, nyn DO k = nzb+1, nzt tend(k,j,i) = tend(k,j,i) + & kh(k,j,i) * g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & ( prho(k+1,j,i) - prho(k-1,j,i) ) * & dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDDO ! !-- Treatment of near-surface grid points, at up- and down- !-- ward facing surfaces IF ( use_surface_fluxes ) THEN DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & drho_air_zw(k-1) * & surf_def_h(l)%shf(m) ENDDO ENDDO ENDIF IF ( use_top_fluxes ) THEN surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & drho_air_zw(k) * & surf_def_h(2)%shf(m) ENDDO ENDIF ENDDO ELSE DO j = nys, nyn DO k = nzb+1, nzt ! !-- Flag 9 is used to mask top fluxes, flag 30 to mask !-- surface fluxes tend(k,j,i) = tend(k,j,i) - & kh(k,j,i) * g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & ( pt(k+1,j,i) - pt(k-1,j,i) ) * & dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDDO IF ( use_surface_fluxes ) THEN ! !-- Default surfaces, up- and downward-facing DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) & * drho_air_zw(k-1) & * surf_def_h(l)%shf(m) ENDDO ENDDO ! !-- Natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) & * drho_air_zw(k-1) & * surf_lsm_h%shf(m) ENDDO ! !-- Urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_h%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) & * drho_air_zw(k-1) & * surf_usm_h%shf(m) ENDDO ENDIF IF ( use_top_fluxes ) THEN surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) & * drho_air_zw(k) & * surf_def_h(2)%shf(m) ENDDO ENDIF ENDDO ENDIF ELSE DO j = nys, nyn DO k = nzb+1, nzt ! !-- Flag 9 is used to mask top fluxes, flag 30 to mask !-- surface fluxes IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) tend(k,j,i) = tend(k,j,i) - kh(k,j,i) * & g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)-pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) & ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF tend(k,j,i) = tend(k,j,i) - kh(k,j,i) * & g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)-pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) & ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) tend(k,j,i) = tend(k,j,i) - & kh(k,j,i) * g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)- pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) - & pt(k,j,i) * ( ql(k+1,j,i) - & ql(k-1,j,i) ) ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDIF ENDDO ENDDO IF ( use_surface_fluxes ) THEN DO j = nys, nyn ! !-- Treat horizontal default surfaces DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_def_h(l)%shf(m) + & k2 * surf_def_h(l)%qsws(m) & ) * drho_air_zw(k-1) ENDDO ENDDO ! !-- Treat horizontal natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_lsm_h%shf(m) + & k2 * surf_lsm_h%qsws(m) & ) * drho_air_zw(k-1) ENDDO ! !-- Treat horizontal urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_usm_h%shf(m) + & k2 * surf_usm_h%qsws(m) & ) * drho_air_zw(k-1) ENDDO ENDDO ENDIF IF ( use_top_fluxes ) THEN DO j = nys, nyn surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_def_h(2)%shf(m) + & k2 * surf_def_h(2)%qsws(m) & ) * drho_air_zw(k) ENDDO ENDDO ENDIF ENDIF ENDIF ENDDO END SUBROUTINE production_e !------------------------------------------------------------------------------! ! Description: ! ------------ !> Production terms (shear + buoyancy) of the TKE. !> Cache-optimized version !> @warning The case with constant_flux_layer = F and use_surface_fluxes = T is !> not considered well! !------------------------------------------------------------------------------! SUBROUTINE production_e_ij( i, j ) USE arrays_3d, & ONLY: ddzw, dd2zu, drho_air_zw, q, ql USE cloud_parameters, & ONLY: l_d_cp, l_d_r, pt_d_t, t_d_pt USE control_parameters, & ONLY: cloud_droplets, cloud_physics, constant_flux_layer, g, neutral, & rho_reference, use_single_reference_value, use_surface_fluxes, & use_top_fluxes USE grid_variables, & ONLY: ddx, dx, ddy, dy USE surface_mod, & ONLY : surf_def_h, surf_def_v, surf_lsm_h, surf_lsm_v, surf_usm_h, & surf_usm_v IMPLICIT NONE INTEGER(iwp) :: i !< running index x-direction INTEGER(iwp) :: j !< running index y-direction INTEGER(iwp) :: k !< running index z-direction INTEGER(iwp) :: l !< running index for different surface type orientation INTEGER(iwp) :: m !< running index surface elements INTEGER(iwp) :: surf_e !< end index of surface elements at given i-j position INTEGER(iwp) :: surf_s !< start index of surface elements at given i-j position REAL(wp) :: def !< REAL(wp) :: flag !< flag to mask topography REAL(wp) :: k1 !< REAL(wp) :: k2 !< REAL(wp) :: km_neutral !< diffusion coefficient assuming neutral conditions - used to compute shear production at surfaces REAL(wp) :: theta !< REAL(wp) :: temp !< REAL(wp) :: sign_dir !< sign of wall-tke flux, depending on wall orientation REAL(wp) :: usvs !< momentum flux u"v" REAL(wp) :: vsus !< momentum flux v"u" REAL(wp) :: wsus !< momentum flux w"u" REAL(wp) :: wsvs !< momentum flux w"v" REAL(wp), DIMENSION(nzb+1:nzt) :: dudx !< Gradient of u-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dudy !< Gradient of u-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dudz !< Gradient of u-component in z-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dvdx !< Gradient of v-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dvdy !< Gradient of v-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dvdz !< Gradient of v-component in z-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dwdx !< Gradient of w-component in x-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dwdy !< Gradient of w-component in y-direction REAL(wp), DIMENSION(nzb+1:nzt) :: dwdz !< Gradient of w-component in z-direction IF ( constant_flux_layer ) THEN ! !-- Calculate TKE production by shear. Calculate gradients at all grid !-- points first, gradients at surface-bounded grid points will be !-- overwritten further below. DO k = nzb+1, nzt dudx(k) = ( u(k,j,i+1) - u(k,j,i) ) * ddx dudy(k) = 0.25_wp * ( u(k,j+1,i) + u(k,j+1,i+1) - & u(k,j-1,i) - u(k,j-1,i+1) ) * ddy dudz(k) = 0.5_wp * ( u(k+1,j,i) + u(k+1,j,i+1) - & u(k-1,j,i) - u(k-1,j,i+1) ) * dd2zu(k) dvdx(k) = 0.25_wp * ( v(k,j,i+1) + v(k,j+1,i+1) - & v(k,j,i-1) - v(k,j+1,i-1) ) * ddx dvdy(k) = ( v(k,j+1,i) - v(k,j,i) ) * ddy dvdz(k) = 0.5_wp * ( v(k+1,j,i) + v(k+1,j+1,i) - & v(k-1,j,i) - v(k-1,j+1,i) ) * dd2zu(k) dwdx(k) = 0.25_wp * ( w(k,j,i+1) + w(k-1,j,i+1) - & w(k,j,i-1) - w(k-1,j,i-1) ) * ddx dwdy(k) = 0.25_wp * ( w(k,j+1,i) + w(k-1,j+1,i) - & w(k,j-1,i) - w(k-1,j-1,i) ) * ddy dwdz(k) = ( w(k,j,i) - w(k-1,j,i) ) * ddzw(k) ENDDO ! !-- Compute gradients at north- and south-facing surfaces. !-- Note, no vertical natural surfaces so far. DO l = 0, 1 ! !-- Default surfaces surf_s = surf_def_v(l)%start_index(j,i) surf_e = surf_def_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_v(l)%k(m) usvs = surf_def_v(l)%mom_flux_tke(0,m) wsvs = surf_def_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_v(l)%start_index(j,i) surf_e = surf_lsm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_v(l)%k(m) usvs = surf_lsm_v(l)%mom_flux_tke(0,m) wsvs = surf_lsm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Urban surfaces surf_s = surf_usm_v(l)%start_index(j,i) surf_e = surf_usm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_v(l)%k(m) usvs = surf_usm_v(l)%mom_flux_tke(0,m) wsvs = surf_usm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( usvs**2 + wsvs**2 )**0.25_wp & * 0.5_wp * dy ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j-1,i), 0 ) ) dudy(k) = sign_dir * usvs / ( km_neutral + 1E-10_wp ) dwdy(k) = sign_dir * wsvs / ( km_neutral + 1E-10_wp ) ENDDO ENDDO ! !-- Compute gradients at east- and west-facing walls DO l = 2, 3 ! !-- Default surfaces surf_s = surf_def_v(l)%start_index(j,i) surf_e = surf_def_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_v(l)%k(m) vsus = surf_def_v(l)%mom_flux_tke(0,m) wsus = surf_def_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_v(l)%start_index(j,i) surf_e = surf_lsm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_v(l)%k(m) vsus = surf_lsm_v(l)%mom_flux_tke(0,m) wsus = surf_lsm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ! !-- Urban surfaces surf_s = surf_usm_v(l)%start_index(j,i) surf_e = surf_usm_v(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_v(l)%k(m) vsus = surf_usm_v(l)%mom_flux_tke(0,m) wsus = surf_usm_v(l)%mom_flux_tke(1,m) km_neutral = kappa * ( vsus**2 + wsus**2 )**0.25_wp & * 0.5_wp * dx ! !-- -1.0 for right-facing wall, 1.0 for left-facing wall sign_dir = MERGE( 1.0_wp, -1.0_wp, & BTEST( wall_flags_0(k,j,i-1), 0 ) ) dvdx(k) = sign_dir * vsus / ( km_neutral + 1E-10_wp ) dwdx(k) = sign_dir * wsus / ( km_neutral + 1E-10_wp ) ENDDO ENDDO ! !-- Compute gradients at upward-facing walls, first for !-- non-natural default surfaces surf_s = surf_def_h(0)%start_index(j,i) surf_e = surf_def_h(0)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(0)%k(m) ! !-- Please note, actually, an interpolation of u_0 and v_0 !-- onto the grid center would be required. However, this !-- would require several data transfers between 2D-grid and !-- wall type. The effect of this missing interpolation is !-- negligible. (See also production_e_init). dudz(k) = ( u(k+1,j,i) - surf_def_h(0)%u_0(m) ) * dd2zu(k) dvdz(k) = ( v(k+1,j,i) - surf_def_h(0)%v_0(m) ) * dd2zu(k) ENDDO ! !-- Natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) dudz(k) = ( u(k+1,j,i) - surf_lsm_h%u_0(m) ) * dd2zu(k) dvdz(k) = ( v(k+1,j,i) - surf_lsm_h%v_0(m) ) * dd2zu(k) ENDDO ! !-- Urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_h%k(m) dudz(k) = ( u(k+1,j,i) - surf_usm_h%u_0(m) ) * dd2zu(k) dvdz(k) = ( v(k+1,j,i) - surf_usm_h%v_0(m) ) * dd2zu(k) ENDDO ! !-- Compute gradients at downward-facing walls, only for !-- non-natural default surfaces surf_s = surf_def_h(1)%start_index(j,i) surf_e = surf_def_h(1)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(1)%k(m) dudz(k) = ( surf_def_h(1)%u_0(m) - u(k-1,j,i) ) * dd2zu(k) dvdz(k) = ( surf_def_h(1)%v_0(m) - v(k-1,j,i) ) * dd2zu(k) ENDDO DO k = nzb+1, nzt def = 2.0_wp * ( dudx(k)**2 + dvdy(k)**2 + dwdz(k)**2 ) + & dudy(k)**2 + dvdx(k)**2 + dwdx(k)**2 + & dwdy(k)**2 + dudz(k)**2 + dvdz(k)**2 + & 2.0_wp * ( dvdx(k)*dudy(k) + dwdx(k)*dudz(k) + dwdy(k)*dvdz(k) ) ! !-- Production term according to Kato and Launder (1993) ! def = SQRT( ( dudx(k) + dudy(k) + dudz(k) + & ! dvdx(k) + dvdy(k) + dvdz(k) + & ! dwdx(k) + dwdy(k) + dwdz(k) & ! )**4 - & ! ( dudx(k)**2 + dvdy(k)**2 + dwdz(k)**2 + & ! 2.0_wp * ( dudy(k) * dvdx(k) + & ! dudz(k) * dwdx(k) + & ! dvdz(k) * dwdy(k) ) & ! )**2 & ! ) IF ( def < 0.0_wp ) def = 0.0_wp flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) tend(k,j,i) = tend(k,j,i) + km(k,j,i) * def * flag ENDDO ELSE ! !-- Calculate TKE production by shear. Here, no additional !-- wall-bounded code is considered. !-- Why? DO k = nzb+1, nzt dudx(k) = ( u(k,j,i+1) - u(k,j,i) ) * ddx dudy(k) = 0.25_wp * ( u(k,j+1,i) + u(k,j+1,i+1) - & u(k,j-1,i) - u(k,j-1,i+1) ) * ddy dudz(k) = 0.5_wp * ( u(k+1,j,i) + u(k+1,j,i+1) - & u(k-1,j,i) - u(k-1,j,i+1) ) * dd2zu(k) dvdx(k) = 0.25_wp * ( v(k,j,i+1) + v(k,j+1,i+1) - & v(k,j,i-1) - v(k,j+1,i-1) ) * ddx dvdy(k) = ( v(k,j+1,i) - v(k,j,i) ) * ddy dvdz(k) = 0.5_wp * ( v(k+1,j,i) + v(k+1,j+1,i) - & v(k-1,j,i) - v(k-1,j+1,i) ) * dd2zu(k) dwdx(k) = 0.25_wp * ( w(k,j,i+1) + w(k-1,j,i+1) - & w(k,j,i-1) - w(k-1,j,i-1) ) * ddx dwdy(k) = 0.25_wp * ( w(k,j+1,i) + w(k-1,j+1,i) - & w(k,j-1,i) - w(k-1,j-1,i) ) * ddy dwdz(k) = ( w(k,j,i) - w(k-1,j,i) ) * ddzw(k) def = 2.0_wp * ( dudx(k)**2 + dvdy(k)**2 + dwdz(k)**2 ) + & dudy(k)**2 + dvdx(k)**2 + dwdx(k)**2 + & dwdy(k)**2 + dudz(k)**2 + dvdz(k)**2 + & 2.0_wp * ( dvdx(k)*dudy(k) + dwdx(k)*dudz(k) + dwdy(k)*dvdz(k) ) ! !-- Production term according to Kato and Launder (1993) ! def = SQRT( ( dudx(k) + dudy(k) + dudz(k) + & ! dvdx(k) + dvdy(k) + dvdz(k) + & ! dwdx(k) + dwdy(k) + dwdz(k) & ! )**4 - & ! ( dudx(k)**2 + dvdy(k)**2 + dwdz(k)**2 + & ! 2.0_wp * ( dudy(k) * dvdx(k) + & ! dudz(k) * dwdx(k) + & ! dvdz(k) * dwdy(k) ) & ! )**2 & ! ) IF ( def < 0.0_wp ) def = 0.0_wp flag = MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 29 ) ) tend(k,j,i) = tend(k,j,i) + km(k,j,i) * def * flag ENDDO ENDIF ! !-- If required, calculate TKE production by buoyancy IF ( .NOT. neutral ) THEN IF ( .NOT. humidity ) THEN IF ( ocean ) THEN ! !-- So far in the ocean no special treatment of density flux in !-- the bottom and top surface layer DO k = nzb+1, nzt tend(k,j,i) = tend(k,j,i) + & kh(k,j,i) * g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & ( prho(k+1,j,i) - prho(k-1,j,i) ) * & dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDDO IF ( use_surface_fluxes ) THEN ! !-- Default surfaces, up- and downward-facing DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & drho_air_zw(k-1) * & surf_def_h(l)%shf(m) ENDDO ENDDO ENDIF IF ( use_top_fluxes ) THEN surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( rho_reference, prho(k,j,i), & use_single_reference_value ) * & drho_air_zw(k) * & surf_def_h(2)%shf(m) ENDDO ENDIF ELSE DO k = nzb+1, nzt ! !-- Flag 9 is used to mask top fluxes, flag 30 to mask !-- surface fluxes tend(k,j,i) = tend(k,j,i) - & kh(k,j,i) * g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & ( pt(k+1,j,i) - pt(k-1,j,i) ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDDO IF ( use_surface_fluxes ) THEN ! !-- Default surfaces, up- and downward-facing DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & drho_air_zw(k-1) * & surf_def_h(l)%shf(m) ENDDO ENDDO ! !-- Natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & drho_air_zw(k-1) * & surf_lsm_h%shf(m) ENDDO ! !-- Urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_h%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & drho_air_zw(k-1) * & surf_usm_h%shf(m) ENDDO ENDIF IF ( use_top_fluxes ) THEN surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) tend(k,j,i) = tend(k,j,i) + g / & MERGE( pt_reference, pt(k,j,i), & use_single_reference_value ) * & drho_air_zw(k) * & surf_def_h(2)%shf(m) ENDDO ENDIF ENDIF ELSE DO k = nzb+1, nzt ! !-- Flag 9 is used to mask top fluxes, flag 30 to mask surface fluxes IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) tend(k,j,i) = tend(k,j,i) - kh(k,j,i) * g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)-pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) & ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF tend(k,j,i) = tend(k,j,i) - kh(k,j,i) * g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)-pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) & ) * dd2zu(k) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) * & MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) tend(k,j,i) = tend(k,j,i) - kh(k,j,i) * g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * ( pt(k+1,j,i)-pt(k-1,j,i) ) + & k2 * ( q(k+1,j,i) - q(k-1,j,i) ) - & pt(k,j,i) * ( ql(k+1,j,i) - & ql(k-1,j,i) ) ) * dd2zu(k) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 30 ) & ) & * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 9 ) & ) ENDIF ENDDO IF ( use_surface_fluxes ) THEN ! !-- Treat horizontal default surfaces, up- and downward-facing DO l = 0, 1 surf_s = surf_def_h(l)%start_index(j,i) surf_e = surf_def_h(l)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(l)%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_def_h(l)%shf(m) + & k2 * surf_def_h(l)%qsws(m) & ) * drho_air_zw(k-1) ENDDO ENDDO ! !-- Treat horizontal natural surfaces surf_s = surf_lsm_h%start_index(j,i) surf_e = surf_lsm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_lsm_h%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_lsm_h%shf(m) + & k2 * surf_lsm_h%qsws(m) & ) * drho_air_zw(k-1) ENDDO ! !-- Treat horizontal urban surfaces surf_s = surf_usm_h%start_index(j,i) surf_e = surf_usm_h%end_index(j,i) DO m = surf_s, surf_e k = surf_usm_h%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1 * surf_usm_h%shf(m) + & k2 * surf_usm_h%qsws(m) & ) * drho_air_zw(k-1) ENDDO ENDIF IF ( use_top_fluxes ) THEN surf_s = surf_def_h(2)%start_index(j,i) surf_e = surf_def_h(2)%end_index(j,i) DO m = surf_s, surf_e k = surf_def_h(2)%k(m) IF ( .NOT. cloud_physics .AND. .NOT. cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE IF ( cloud_physics ) THEN IF ( ql(k,j,i) == 0.0_wp ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) k2 = 0.61_wp * pt(k,j,i) ELSE theta = pt(k,j,i) + pt_d_t(k) * l_d_cp * ql(k,j,i) temp = theta * t_d_pt(k) k1 = ( 1.0_wp - q(k,j,i) + 1.61_wp * & ( q(k,j,i) - ql(k,j,i) ) * & ( 1.0_wp + 0.622_wp * l_d_r / temp ) ) / & ( 1.0_wp + 0.622_wp * l_d_r * l_d_cp * & ( q(k,j,i) - ql(k,j,i) ) / ( temp * temp ) ) k2 = theta * ( l_d_cp / temp * k1 - 1.0_wp ) ENDIF ELSE IF ( cloud_droplets ) THEN k1 = 1.0_wp + 0.61_wp * q(k,j,i) - ql(k,j,i) k2 = 0.61_wp * pt(k,j,i) ENDIF tend(k,j,i) = tend(k,j,i) + g / & MERGE( vpt_reference, vpt(k,j,i), & use_single_reference_value ) * & ( k1* surf_def_h(2)%shf(m) + & k2 * surf_def_h(2)%qsws(m) & ) * drho_air_zw(k) ENDDO ENDIF ENDIF ENDIF END SUBROUTINE production_e_ij !------------------------------------------------------------------------------! ! Description: ! ------------ !> Diffusion and dissipation terms for the TKE. !> Vector-optimized version !------------------------------------------------------------------------------! SUBROUTINE diffusion_e( var, var_reference ) USE arrays_3d, & ONLY: ddzu, ddzw, drho_air, rho_air_zw USE grid_variables, & ONLY: ddx2, ddy2 USE microphysics_mod, & ONLY: collision_turbulence USE particle_attributes, & ONLY: use_sgs_for_particles, wang_kernel USE surface_mod, & ONLY : bc_h IMPLICIT NONE INTEGER(iwp) :: i !< running index x direction INTEGER(iwp) :: j !< running index y direction INTEGER(iwp) :: k !< running index z direction INTEGER(iwp) :: m !< running index surface elements REAL(wp) :: flag !< flag to mask topography REAL(wp) :: l !< mixing length REAL(wp) :: ll !< adjusted l REAL(wp) :: var_reference !< #if defined( __nopointer ) REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: var !< #else REAL(wp), DIMENSION(:,:,:), POINTER :: var !< #endif REAL(wp), DIMENSION(nzb+1:nzt,nys:nyn) :: dissipation !< TKE dissipation ! !-- Calculate the tendency terms DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt ! !-- Calculate dissipation IF ( les_mw ) THEN CALL mixing_length_les( i, j, k, l, ll, var, var_reference ) dissipation(k,j) = ( 0.19_wp + 0.74_wp * l / ll ) & * e(k,j,i) * SQRT( e(k,j,i) ) / l ELSEIF ( rans_tke_l ) THEN CALL mixing_length_rans( i, j, k, l, ll, var, var_reference ) dissipation(k,j) = c_m**3 * e(k,j,i) * SQRT( e(k,j,i) ) / ll ELSEIF ( rans_tke_e ) THEN dissipation(k,j) = diss(k,j,i) ENDIF ! !-- Predetermine flag to mask topography flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) tend(k,j,i) = tend(k,j,i) & + ( & ( km(k,j,i)+km(k,j,i+1) ) * ( e(k,j,i+1)-e(k,j,i) ) & - ( km(k,j,i)+km(k,j,i-1) ) * ( e(k,j,i)-e(k,j,i-1) ) & ) * ddx2 * flag & + ( & ( km(k,j,i)+km(k,j+1,i) ) * ( e(k,j+1,i)-e(k,j,i) ) & - ( km(k,j,i)+km(k,j-1,i) ) * ( e(k,j,i)-e(k,j-1,i) ) & ) * ddy2 * flag & + ( & ( km(k,j,i)+km(k+1,j,i) ) * ( e(k+1,j,i)-e(k,j,i) ) * ddzu(k+1) & * rho_air_zw(k) & - ( km(k,j,i)+km(k-1,j,i) ) * ( e(k,j,i)-e(k-1,j,i) ) * ddzu(k) & * rho_air_zw(k-1) & ) * ddzw(k) * drho_air(k) * flag & - dissipation(k,j) * flag ENDDO ENDDO ! !-- Store dissipation if needed for calculating the sgs particle !-- velocities IF ( .NOT. rans_tke_e .AND. ( use_sgs_for_particles .OR. & wang_kernel .OR. collision_turbulence ) ) THEN DO j = nys, nyn DO k = nzb+1, nzt diss(k,j,i) = dissipation(k,j) * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) ) ENDDO ENDDO ENDIF ENDDO ! !-- Neumann boundary condition for dissipation diss(nzb,:,:) = diss(nzb+1,:,:) IF ( .NOT. rans_tke_e .AND. ( use_sgs_for_particles .OR. & wang_kernel .OR. collision_turbulence ) ) THEN ! !-- Upward facing surfaces DO m = 1, bc_h(0)%ns i = bc_h(0)%i(m) j = bc_h(0)%j(m) k = bc_h(0)%k(m) diss(k-1,j,i) = diss(k,j,i) ENDDO ! !-- Downward facing surfaces DO m = 1, bc_h(1)%ns i = bc_h(1)%i(m) j = bc_h(1)%j(m) k = bc_h(1)%k(m) diss(k+1,j,i) = diss(k,j,i) ENDDO ENDIF END SUBROUTINE diffusion_e !------------------------------------------------------------------------------! ! Description: ! ------------ !> Diffusion and dissipation terms for the TKE. !> Cache-optimized version !------------------------------------------------------------------------------! SUBROUTINE diffusion_e_ij( i, j, var, var_reference ) USE arrays_3d, & ONLY: ddzu, ddzw, drho_air, rho_air_zw USE grid_variables, & ONLY: ddx2, ddy2 USE microphysics_mod, & ONLY: collision_turbulence USE particle_attributes, & ONLY: use_sgs_for_particles, wang_kernel USE surface_mod, & ONLY : bc_h IMPLICIT NONE INTEGER(iwp) :: i !< running index x direction INTEGER(iwp) :: j !< running index y direction INTEGER(iwp) :: k !< running index z direction INTEGER(iwp) :: m !< running index surface elements INTEGER(iwp) :: surf_e !< End index of surface elements at (j,i)-gridpoint INTEGER(iwp) :: surf_s !< Start index of surface elements at (j,i)-gridpoint REAL(wp) :: flag !< flag to mask topography REAL(wp) :: l !< mixing length REAL(wp) :: ll !< adjusted l REAL(wp) :: var_reference !< #if defined( __nopointer ) REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: var !< #else REAL(wp), DIMENSION(:,:,:), POINTER :: var !< #endif REAL(wp), DIMENSION(nzb+1:nzt) :: dissipation !< dissipation of TKE ! !-- Calculate the mixing length (for dissipation) DO k = nzb+1, nzt ! !-- Predetermine flag to mask topography flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ! !-- Calculate dissipation... !-- ...in case of LES IF ( les_mw ) THEN CALL mixing_length_les( i, j, k, l, ll, var, var_reference ) dissipation(k) = ( 0.19_wp + 0.74_wp * l / ll ) & * e(k,j,i) * SQRT( e(k,j,i) ) / l ! !-- ...in case of RANS ELSEIF ( rans_tke_l ) THEN CALL mixing_length_rans( i, j, k, l, ll, var, var_reference ) dissipation(k) = c_m**3 * e(k,j,i) * SQRT( e(k,j,i) ) / ll ELSEIF ( rans_tke_e ) THEN dissipation(k) = diss(k,j,i) ENDIF ! !-- Calculate the tendency term tend(k,j,i) = tend(k,j,i) & + ( & ( km(k,j,i)+km(k,j,i+1) ) * ( e(k,j,i+1)-e(k,j,i) ) & - ( km(k,j,i)+km(k,j,i-1) ) * ( e(k,j,i)-e(k,j,i-1) ) & ) * ddx2 * flag / sig_e & + ( & ( km(k,j,i)+km(k,j+1,i) ) * ( e(k,j+1,i)-e(k,j,i) ) & - ( km(k,j,i)+km(k,j-1,i) ) * ( e(k,j,i)-e(k,j-1,i) ) & ) * ddy2 * flag / sig_e & + ( & ( km(k,j,i)+km(k+1,j,i) ) * ( e(k+1,j,i)-e(k,j,i) ) * ddzu(k+1) & * rho_air_zw(k) & - ( km(k,j,i)+km(k-1,j,i) ) * ( e(k,j,i)-e(k-1,j,i) ) * ddzu(k) & * rho_air_zw(k-1) & ) * ddzw(k) * drho_air(k) * flag / sig_e & - dissipation(k) * flag ENDDO ! !-- Store dissipation if needed for calculating the sgs particle velocities IF ( .NOT. rans_tke_e .AND. ( use_sgs_for_particles .OR. wang_kernel & .OR. collision_turbulence ) ) THEN DO k = nzb+1, nzt diss(k,j,i) = dissipation(k) * MERGE( 1.0_wp, 0.0_wp, & BTEST( wall_flags_0(k,j,i), 0 ) ) ENDDO ! !-- Neumann boundary condition for dissipation diss(nzb,:,:) = diss(nzb+1,:,:) !-- For each surface type determine start and end index (in case of elevated !-- topography several up/downward facing surfaces may exist. surf_s = bc_h(0)%start_index(j,i) surf_e = bc_h(0)%end_index(j,i) DO m = surf_s, surf_e k = bc_h(0)%k(m) diss(k-1,j,i) = diss(k,j,i) ENDDO ! !-- Downward facing surfaces surf_s = bc_h(1)%start_index(j,i) surf_e = bc_h(1)%end_index(j,i) DO m = surf_s, surf_e k = bc_h(1)%k(m) diss(k+1,j,i) = diss(k,j,i) ENDDO ENDIF END SUBROUTINE diffusion_e_ij !------------------------------------------------------------------------------! ! Description: ! ------------ !> Diffusion term for the TKE dissipation rate !> Vector-optimized version !------------------------------------------------------------------------------! SUBROUTINE diffusion_diss() USE arrays_3d, & ONLY: ddzu, ddzw, drho_air, rho_air_zw USE grid_variables, & ONLY: ddx2, ddy2 IMPLICIT NONE INTEGER(iwp) :: i !< running index x direction INTEGER(iwp) :: j !< running index y direction INTEGER(iwp) :: k !< running index z direction REAL(wp) :: flag !< flag to mask topography ! !-- Calculate the tendency terms DO i = nxl, nxr DO j = nys, nyn DO k = nzb+1, nzt ! !-- Predetermine flag to mask topography flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) tend(k,j,i) = tend(k,j,i) & + ( & ( km(k,j,i)+km(k,j,i+1) ) * ( diss(k,j,i+1)-diss(k,j,i) ) & - ( km(k,j,i)+km(k,j,i-1) ) * ( diss(k,j,i)-diss(k,j,i-1) ) & ) * ddx2 * flag & + ( & ( km(k,j,i)+km(k,j+1,i) ) * ( diss(k,j+1,i)-diss(k,j,i) ) & - ( km(k,j,i)+km(k,j-1,i) ) * ( diss(k,j,i)-diss(k,j-1,i) ) & ) * ddy2 * flag & + ( & ( km(k,j,i)+km(k+1,j,i) ) * ( diss(k+1,j,i)-diss(k,j,i) ) * ddzu(k+1) & * rho_air_zw(k) & - ( km(k,j,i)+km(k-1,j,i) ) * ( diss(k,j,i)-diss(k-1,j,i) ) * ddzu(k) & * rho_air_zw(k-1) & ) * ddzw(k) * drho_air(k) * flag & - c_2 * diss(k,j,i)**2 & / ( e(k,j,i) + 1.0E-20_wp ) * flag ENDDO ENDDO ENDDO END SUBROUTINE diffusion_diss !------------------------------------------------------------------------------! ! Description: ! ------------ !> Diffusion term for the TKE dissipation rate !> Cache-optimized version !------------------------------------------------------------------------------! SUBROUTINE diffusion_diss_ij( i, j ) USE arrays_3d, & ONLY: ddzu, ddzw, drho_air, rho_air_zw USE grid_variables, & ONLY: ddx2, ddy2 IMPLICIT NONE INTEGER(iwp) :: i !< running index x direction INTEGER(iwp) :: j !< running index y direction INTEGER(iwp) :: k !< running index z direction REAL(wp) :: flag !< flag to mask topography REAL(wp), DIMENSION(nzb+1:nzt) :: tend_temp ! !-- Calculate the mixing length (for dissipation) DO k = nzb+1, nzt ! !-- Predetermine flag to mask topography flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ! !-- Calculate the tendency term tend_temp(k) = ( & ( km(k,j,i)+km(k,j,i+1) ) * ( diss(k,j,i+1)-diss(k,j,i) ) & - ( km(k,j,i)+km(k,j,i-1) ) * ( diss(k,j,i)-diss(k,j,i-1) ) & ) * ddx2 * flag / sig_diss & + ( & ( km(k,j,i)+km(k,j+1,i) ) * ( diss(k,j+1,i)-diss(k,j,i) ) & - ( km(k,j,i)+km(k,j-1,i) ) * ( diss(k,j,i)-diss(k,j-1,i) ) & ) * ddy2 * flag / sig_diss & + ( & ( km(k,j,i)+km(k+1,j,i) ) * ( diss(k+1,j,i)-diss(k,j,i) ) * ddzu(k+1) & * rho_air_zw(k) & - ( km(k,j,i)+km(k-1,j,i) ) * ( diss(k,j,i)-diss(k-1,j,i) ) * ddzu(k) & * rho_air_zw(k-1) & ) * ddzw(k) * drho_air(k) * flag / sig_diss & - c_2 * diss(k,j,i)**2 & / ( e(k,j,i) + 1.0E-20_wp ) * flag tend(k,j,i) = tend(k,j,i) + tend_temp(k) ENDDO END SUBROUTINE diffusion_diss_ij !------------------------------------------------------------------------------! ! Description: ! ------------ !> Calculate mixing length for LES mode. !------------------------------------------------------------------------------! SUBROUTINE mixing_length_les( i, j, k, l, ll, var, var_reference ) USE arrays_3d, & ONLY: dd2zu, l_grid, l_wall USE control_parameters, & ONLY: atmos_ocean_sign, g, use_single_reference_value, & wall_adjustment, wall_adjustment_factor IMPLICIT NONE INTEGER(iwp) :: i !< loop index INTEGER(iwp) :: j !< loop index INTEGER(iwp) :: k !< loop index REAL(wp) :: dvar_dz !< vertical gradient of var REAL(wp) :: l !< mixing length REAL(wp) :: l_stable !< mixing length according to stratification REAL(wp) :: ll !< adjusted l_grid REAL(wp) :: var_reference !< var at reference height #if defined( __nopointer ) REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: var !< temperature #else REAL(wp), DIMENSION(:,:,:), POINTER :: var !< temperature #endif dvar_dz = atmos_ocean_sign * ( var(k+1,j,i) - var(k-1,j,i) ) * dd2zu(k) IF ( dvar_dz > 0.0_wp ) THEN IF ( use_single_reference_value ) THEN l_stable = 0.76_wp * SQRT( e(k,j,i) ) & / SQRT( g / var_reference * dvar_dz ) + 1E-5_wp ELSE l_stable = 0.76_wp * SQRT( e(k,j,i) ) & / SQRT( g / var(k,j,i) * dvar_dz ) + 1E-5_wp ENDIF ELSE l_stable = l_grid(k) ENDIF ! !-- Adjustment of the mixing length IF ( wall_adjustment ) THEN l = MIN( wall_adjustment_factor * l_wall(k,j,i), l_grid(k), l_stable ) ll = MIN( wall_adjustment_factor * l_wall(k,j,i), l_grid(k) ) ELSE l = MIN( l_grid(k), l_stable ) ll = l_grid(k) ENDIF END SUBROUTINE mixing_length_les !------------------------------------------------------------------------------! ! Description: ! ------------ !> Calculate mixing length for RANS mode. !------------------------------------------------------------------------------! SUBROUTINE mixing_length_rans( i, j, k, l, l_diss, var, var_reference ) USE arrays_3d, & ONLY: dd2zu USE control_parameters, & ONLY: atmos_ocean_sign, g, use_single_reference_value IMPLICIT NONE INTEGER(iwp) :: i !< loop index INTEGER(iwp) :: j !< loop index INTEGER(iwp) :: k !< loop index REAL(wp) :: duv2_dz2 !< squared vertical gradient of wind vector REAL(wp) :: dvar_dz !< vertical gradient of var REAL(wp) :: l !< mixing length REAL(wp) :: l_diss !< mixing length for dissipation REAL(wp) :: rif !< Richardson flux number REAL(wp) :: var_reference !< var at reference height #if defined( __nopointer ) REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: var !< temperature #else REAL(wp), DIMENSION(:,:,:), POINTER :: var !< temperature #endif dvar_dz = atmos_ocean_sign * ( var(k+1,j,i) - var(k-1,j,i) ) * dd2zu(k) duv2_dz2 = ( ( u(k+1,j,i) - u(k-1,j,i) ) * dd2zu(k) )**2 & + ( ( v(k+1,j,i) - v(k-1,j,i) ) * dd2zu(k) )**2 & + 1E-30_wp IF ( use_single_reference_value ) THEN rif = g / var_reference * dvar_dz / duv2_dz2 ELSE rif = g / var(k,j,i) * dvar_dz / duv2_dz2 ENDIF rif = MAX( rif, -5.0_wp ) rif = MIN( rif, 1.0_wp ) ! !-- Calculate diabatic mixing length using Dyer-profile functions IF ( rif >= 0.0_wp ) THEN l = l_black(k) / ( 1.0_wp + 5.0_wp * rif ) l_diss = l ELSE ! !-- In case of unstable stratification, use mixing length of neutral case !-- for l, but consider profile functions for l_diss l = l_black(k) l_diss = l_black(k) * SQRT( 1.0_wp - 16.0_wp * rif ) ENDIF END SUBROUTINE mixing_length_rans !------------------------------------------------------------------------------! ! Description: ! ------------ !> Computation of the turbulent diffusion coefficients for momentum and heat !> according to Prandtl-Kolmogorov. !------------------------------------------------------------------------------! SUBROUTINE tcm_diffusivities( var, var_reference ) USE control_parameters, & ONLY: e_min, outflow_l, outflow_n, outflow_r, outflow_s USE statistics, & ONLY : rmask, sums_l_l USE surface_mod, & ONLY : bc_h, surf_def_h IMPLICIT NONE INTEGER(iwp) :: i !< INTEGER(iwp) :: j !< INTEGER(iwp) :: k !< INTEGER(iwp) :: m !< INTEGER(iwp) :: n !< INTEGER(iwp) :: omp_get_thread_num !< INTEGER(iwp) :: sr !< INTEGER(iwp) :: tn !< REAL(wp) :: flag !< REAL(wp) :: l !< REAL(wp) :: ll !< REAL(wp) :: var_reference !< #if defined( __nopointer ) REAL(wp), DIMENSION(nzb:nzt+1,nysg:nyng,nxlg:nxrg) :: var !< #else REAL(wp), DIMENSION(:,:,:), POINTER :: var !< #endif ! !-- Default thread number in case of one thread tn = 0 ! !-- Initialization for calculation of the mixing length profile sums_l_l = 0.0_wp ! !-- Compute the turbulent diffusion coefficient for momentum !$OMP PARALLEL PRIVATE (i,j,k,l,ll,sr,tn,flag) !$ tn = omp_get_thread_num() ! !-- Introduce an optional minimum tke IF ( e_min > 0.0_wp ) THEN !$OMP DO DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb+1, nzt e(k,j,i) = MAX( e(k,j,i), e_min ) * & MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ENDDO ENDDO ENDDO ENDIF IF ( les_mw ) THEN !$OMP DO DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb+1, nzt flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ! !-- Determine the mixing length for LES closure CALL mixing_length_les( i, j, k, l, ll, var, var_reference ) ! !-- Compute diffusion coefficients for momentum and heat km(k,j,i) = c_m * l * SQRT( e(k,j,i) ) * flag kh(k,j,i) = ( 1.0_wp + 2.0_wp * l / ll ) * km(k,j,i) * flag ! !-- Summation for averaged profile (cf. flow_statistics) DO sr = 0, statistic_regions sums_l_l(k,sr,tn) = sums_l_l(k,sr,tn) + l * rmask(j,i,sr) & * flag ENDDO ENDDO ENDDO ENDDO ELSEIF ( rans_tke_l ) THEN !$OMP DO DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb+1, nzt flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ! !-- Mixing length for RANS mode with TKE-l closure CALL mixing_length_rans( i, j, k, l, ll, var, var_reference ) ! !-- Compute diffusion coefficients for momentum and heat km(k,j,i) = c_m * l * SQRT( e(k,j,i) ) * flag kh(k,j,i) = km(k,j,i) / prandtl_number * flag ! !-- Summation for averaged profile (cf. flow_statistics) DO sr = 0, statistic_regions sums_l_l(k,sr,tn) = sums_l_l(k,sr,tn) + l * rmask(j,i,sr) & * flag ENDDO ENDDO ENDDO ENDDO ELSEIF ( rans_tke_e ) THEN !$OMP DO DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb+1, nzt flag = MERGE( 1.0_wp, 0.0_wp, BTEST( wall_flags_0(k,j,i), 0 ) ) ! !-- Compute diffusion coefficients for momentum and heat km(k,j,i) = c_mu * e(k,j,i)**2 / ( diss(k,j,i) + 1.E-10 ) * flag kh(k,j,i) = km(k,j,i) / prandtl_number * flag ENDDO ENDDO ENDDO ENDIF sums_l_l(nzt+1,:,tn) = sums_l_l(nzt,:,tn) ! quasi boundary-condition for ! data output !$OMP END PARALLEL ! !-- Set vertical boundary values (Neumann conditions both at upward- and !-- downward facing walls. To set wall-boundary values, the surface data type !-- is applied. !-- Horizontal boundary conditions at vertical walls are not set because !-- so far vertical surfaces require usage of a Prandtl-layer where the boundary !-- values of the diffusivities are not needed. IF ( .NOT. rans_tke_e ) THEN ! !-- Upward-facing !$OMP PARALLEL DO PRIVATE( i, j, k ) DO m = 1, bc_h(0)%ns i = bc_h(0)%i(m) j = bc_h(0)%j(m) k = bc_h(0)%k(m) km(k-1,j,i) = km(k,j,i) kh(k-1,j,i) = kh(k,j,i) ENDDO ! !-- Downward facing surfaces !$OMP PARALLEL DO PRIVATE( i, j, k ) DO m = 1, bc_h(1)%ns i = bc_h(1)%i(m) j = bc_h(1)%j(m) k = bc_h(1)%k(m) km(k+1,j,i) = km(k,j,i) kh(k+1,j,i) = kh(k,j,i) ENDDO ELSE ! !-- Up- and downward facing surfaces DO n = 0, 1 DO m = 1, surf_def_h(n)%ns i = surf_def_h(n)%i(m) j = surf_def_h(n)%j(m) k = surf_def_h(n)%k(m) km(k,j,i) = kappa * surf_def_h(n)%us(m) * dzu(k) kh(k,j,i) = 1.35_wp * km(k,j,i) ENDDO ENDDO CALL exchange_horiz( km, nbgp ) CALL exchange_horiz( kh, nbgp ) ENDIF ! !-- Model top !$OMP PARALLEL DO DO i = nxlg, nxrg DO j = nysg, nyng km(nzt+1,j,i) = km(nzt,j,i) kh(nzt+1,j,i) = kh(nzt,j,i) ENDDO ENDDO ! !-- Set Neumann boundary conditions at the outflow boundaries in case of !-- non-cyclic lateral boundaries IF ( outflow_l ) THEN km(:,:,nxl-1) = km(:,:,nxl) kh(:,:,nxl-1) = kh(:,:,nxl) ENDIF IF ( outflow_r ) THEN km(:,:,nxr+1) = km(:,:,nxr) kh(:,:,nxr+1) = kh(:,:,nxr) ENDIF IF ( outflow_s ) THEN km(:,nys-1,:) = km(:,nys,:) kh(:,nys-1,:) = kh(:,nys,:) ENDIF IF ( outflow_n ) THEN km(:,nyn+1,:) = km(:,nyn,:) kh(:,nyn+1,:) = kh(:,nyn,:) ENDIF END SUBROUTINE tcm_diffusivities !------------------------------------------------------------------------------! ! Description: ! ------------ !> Swapping of timelevels. !------------------------------------------------------------------------------! SUBROUTINE tcm_swap_timelevel ( mod_count ) IMPLICIT NONE INTEGER(iwp) :: i !< loop index x direction INTEGER(iwp) :: j !< loop index y direction INTEGER(iwp) :: k !< loop index z direction INTEGER, INTENT(IN) :: mod_count !< #if defined( __nopointer ) IF ( .NOT. constant_diffusion ) THEN DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 e(k,j,i) = e_p(k,j,i) ENDDO ENDDO ENDDO ENDIF IF ( rans_tke_e ) THEN DO i = nxlg, nxrg DO j = nysg, nyng DO k = nzb, nzt+1 diss(k,j,i) = diss_p(k,j,i) ENDDO ENDDO ENDDO ENDIF #else SELECT CASE ( mod_count ) CASE ( 0 ) IF ( .NOT. constant_diffusion ) THEN e => e_1; e_p => e_2 ENDIF IF ( rans_tke_e ) THEN diss => diss_1; diss_p => diss_2 ENDIF CASE ( 1 ) IF ( .NOT. constant_diffusion ) THEN e => e_2; e_p => e_1 ENDIF IF ( rans_tke_e ) THEN diss => diss_2; diss_p => diss_1 ENDIF END SELECT #endif END SUBROUTINE tcm_swap_timelevel END MODULE turbulence_closure_mod