diff --git a/compile.sh b/compile.sh index 107662793561f45fcded8574cf3e2f2f41fa07fb..c04fab77b69f92e2f4813facd9f5a81304bdf331 100755 --- a/compile.sh +++ b/compile.sh @@ -2,8 +2,16 @@ gfortran -c -cpp -Wuninitialized srcF/sfx_phys_const.f90 gfortran -c -cpp -Wuninitialized srcF/sfx_common.f90 +gfortran -c -cpp -Wuninitialized srcF/sfx_data.f90 + +gfortran -c -cpp -Wuninitialized srcF/sfx_roughness.f90 + +gfortran -c -cpp -Wuninitialized srcF/sfx_log_param.f90 +gfortran -c -cpp -Wuninitialized srcF/sfx_log.f90 + gfortran -c -cpp -Wuninitialized srcF/sfx_esm_param.f90 gfortran -c -cpp -Wuninitialized srcF/sfx_esm.f90 + gfortran -c -cpp -Wuninitialized srcF/sfx_main.f90 -gfortran -o sfx.exe sfx_main.o sfx_esm.o sfx_esm_param.o sfx_common.o sfx_phys_const.o +gfortran -o sfx.exe sfx_main.o sfx_log.o sfx_log_param.o sfx_esm.o sfx_esm_param.o sfx_roughness.o sfx_data.o sfx_common.o sfx_phys_const.o rm *.o *.mod diff --git a/makefile b/makefile index 05e2436e4687416b83d0c1a259264bc626949fed..913ada0ce2fdb39f87657db9b8ec57fcae3ca142 100644 --- a/makefile +++ b/makefile @@ -10,7 +10,7 @@ ifeq ($(COMPILER),gnu) FC = gfortran endif -OBJ_F90 = sfx_phys_const.o sfx_common.o sfx_esm_param.o sfx_esm.o sfx_main.o +OBJ_F90 = sfx_phys_const.o sfx_common.o sfx_data.o sfx_roughness.o sfx_log_param.o sfx_log.o sfx_esm_param.o sfx_esm.o sfx_main.o OBJ_F = OBJ = $(OBJ_F90) $(OBJ_F) diff --git a/srcF/sfx_data.f90 b/srcF/sfx_data.f90 new file mode 100644 index 0000000000000000000000000000000000000000..7ab691c9f4fa73904f1e96290d4856cf6ff25d22 --- /dev/null +++ b/srcF/sfx_data.f90 @@ -0,0 +1,82 @@ +module sfx_data + !> @brief surface flux module data + + ! macro defs. + ! -------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------- + + ! modules used + ! -------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------- + + ! directives list + ! -------------------------------------------------------------------------------- + implicit none + private + ! -------------------------------------------------------------------------------- + + ! public interface + ! -------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + type, public :: meteoDataType + !> @brief meteorological input for surface flux calculation + + real :: h !> constant flux layer height [m] + real :: U !> abs(wind speed) at 'h' [m/s] + real :: dT !> difference between potential temperature at 'h' and at surface [K] + real :: Tsemi !> semi-sum of potential temperature at 'h' and at surface [K] + real :: dQ !> difference between humidity at 'h' and at surface [g/g] + real :: z0_m !> surface aerodynamic roughness (should be < 0 for water bodies surface) + end type + + type, public :: meteoDataVecType + !> @brief meteorological input for surface flux calculation + !> &details using arrays as input + + real, allocatable :: h(:) !> constant flux layer height [m] + real, allocatable :: U(:) !> abs(wind speed) at 'h' [m/s] + real, allocatable :: dT(:) !> difference between potential temperature at 'h' and at surface [K] + real, allocatable :: Tsemi(:) !> semi-sum of potential temperature at 'h' and at surface [K] + real, allocatable :: dQ(:) !> difference between humidity at 'h' and at surface [g/g] + real, allocatable :: z0_m(:) !> surface aerodynamic roughness (should be < 0 for water bodies surface) + end type + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + type, public :: sfxDataType + !> @brief surface flux output data + + real :: zeta !> = z/L [n/d] + real :: Rib !> bulk Richardson number [n/d] + real :: Re !> Reynolds number [n/d] + real :: B !> = log(z0_m / z0_h) [n/d] + real :: z0_m !> aerodynamic roughness [m] + real :: z0_t !> thermal roughness [m] + real :: Rib_conv_lim !> Ri-bulk convection critical value [n/d] + real :: Cm !> transfer coefficient for momentum [n/d] + real :: Ct !> transfer coefficient for heat [n/d] + real :: Km !> eddy viscosity coeff. at h [m^2/s] + real :: Pr_t_inv !> inverse turbulent Prandtl number at h [n/d] + end type + + type, public :: sfxDataVecType + !> @brief surface flux output data + !> &details using arrays as output + + real, allocatable :: zeta(:) !> = z/L [n/d] + real, allocatable :: Rib(:) !> bulk Richardson number [n/d] + real, allocatable :: Re(:) !> Reynolds number [n/d] + real, allocatable :: B(:) !> = log(z0_m / z0_h) [n/d] + real, allocatable :: z0_m(:) !> aerodynamic roughness [m] + real, allocatable :: z0_t(:) !> thermal roughness [m] + real, allocatable :: Rib_conv_lim(:) !> Ri-bulk convection critical value [n/d] + real, allocatable :: Cm(:) !> transfer coefficient for momentum [n/d] + real, allocatable :: Ct(:) !> transfer coefficient for heat [n/d] + real, allocatable :: Km(:) !> eddy viscosity coeff. at h [m^2/s] + real, allocatable :: Pr_t_inv(:) !> inverse turbulent Prandtl number at h [n/d] + end type + ! -------------------------------------------------------------------------------- + +end module sfx_data \ No newline at end of file diff --git a/srcF/sfx_esm.f90 b/srcF/sfx_esm.f90 index a611bd2e4a159e88e4b914e0b6062e31b4db2833..2633a8481953c3891745f781e034b615491631a8 100644 --- a/srcF/sfx_esm.f90 +++ b/srcF/sfx_esm.f90 @@ -12,6 +12,8 @@ module sfx_esm #ifdef SFX_CHECK_NAN use sfx_common #endif + use sfx_data + use sfx_roughness use sfx_esm_param ! -------------------------------------------------------------------------------- @@ -27,66 +29,6 @@ module sfx_esm public :: get_surface_fluxes_vec ! -------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------- - type, public :: meteoDataType - !> @brief meteorological input for surface flux calculation - - real :: h !> constant flux layer height [m] - real :: U !> abs(wind speed) at 'h' [m/s] - real :: dT !> difference between potential temperature at 'h' and at surface [K] - real :: Tsemi !> semi-sum of potential temperature at 'h' and at surface [K] - real :: dQ !> difference between humidity at 'h' and at surface [g/g] - real :: z0_m !> surface aerodynamic roughness (should be < 0 for water bodies surface) - end type - - type, public :: meteoDataVecType - !> @brief meteorological input for surface flux calculation - !> &details using arrays as input - - real, allocatable :: h(:) !> constant flux layer height [m] - real, allocatable :: U(:) !> abs(wind speed) at 'h' [m/s] - real, allocatable :: dT(:) !> difference between potential temperature at 'h' and at surface [K] - real, allocatable :: Tsemi(:) !> semi-sum of potential temperature at 'h' and at surface [K] - real, allocatable :: dQ(:) !> difference between humidity at 'h' and at surface [g/g] - real, allocatable :: z0_m(:) !> surface aerodynamic roughness (should be < 0 for water bodies surface) - end type - ! -------------------------------------------------------------------------------- - - ! -------------------------------------------------------------------------------- - type, public :: sfxDataType - !> @brief surface flux output data - - real :: zeta !> = z/L [n/d] - real :: Rib !> bulk Richardson number [n/d] - real :: Re !> Reynolds number [n/d] - real :: B !> = log(z0_m / z0_h) [n/d] - real :: z0_m !> aerodynamic roughness [m] - real :: z0_t !> thermal roughness [m] - real :: Rib_conv_lim !> Ri-bulk convection critical value [n/d] - real :: Cm !> transfer coefficient for momentum [n/d] - real :: Ct !> transfer coefficient for heat [n/d] - real :: Km !> eddy viscosity coeff. at h [m^2/s] - real :: Pr_t_inv !> inverse turbulent Prandtl number at h [n/d] - end type - - type, public :: sfxDataVecType - !> @brief surface flux output data - !> &details using arrays as output - - real, allocatable :: zeta(:) !> = z/L [n/d] - real, allocatable :: Rib(:) !> bulk Richardson number [n/d] - real, allocatable :: Re(:) !> Reynolds number [n/d] - real, allocatable :: B(:) !> = log(z0_m / z0_h) [n/d] - real, allocatable :: z0_m(:) !> aerodynamic roughness [m] - real, allocatable :: z0_t(:) !> thermal roughness [m] - real, allocatable :: Rib_conv_lim(:) !> Ri-bulk convection critical value [n/d] - real, allocatable :: Cm(:) !> transfer coefficient for momentum [n/d] - real, allocatable :: Ct(:) !> transfer coefficient for heat [n/d] - real, allocatable :: Km(:) !> eddy viscosity coeff. at h [m^2/s] - real, allocatable :: Pr_t_inv(:) !> inverse turbulent Prandtl number at h [n/d] - end type - ! -------------------------------------------------------------------------------- - ! -------------------------------------------------------------------------------- type, public :: numericsType integer :: maxiters_convection = 10 !> maximum (actual) number of iterations in convection @@ -569,51 +511,4 @@ contains end subroutine ! -------------------------------------------------------------------------------- - ! charnock roughness definition - ! -------------------------------------------------------------------------------- - subroutine get_charnock_roughness(z0_m, u_dyn0, U, h, maxiters) - ! ---------------------------------------------------------------------------- - real, intent(out) :: z0_m !> aerodynamic roughness [m] - real, intent(out) :: u_dyn0 !> dynamic velocity in neutral conditions [m/s] - - real, intent(in) :: h !> constant flux layer height [m] - real, intent(in) :: U !> abs(wind speed) [m/s] - integer, intent(in) :: maxiters !> maximum number of iterations - ! ---------------------------------------------------------------------------- - - ! --- local variables - real :: Uc ! wind speed at h_charnock [m/s] - - real :: a, b, c, c_min - real :: f - - integer :: i, j - ! ---------------------------------------------------------------------------- - - Uc = U - a = 0.0 - b = 25.0 - c_min = log(h_charnock) / kappa - - do i = 1, maxiters - f = c1_charnock - 2.0 * log(Uc) - do j = 1, maxiters - c = (f + 2.0 * log(b)) / kappa - ! looks like the check should use U10 instead of U - ! but note that a1 should be set = 0 in cycle beforehand - if (U <= 8.0e0) a = log(1.0 + c2_charnock * ((b / Uc)**3)) / kappa - c = max(c - a, c_min) - b = c - end do - z0_m = h_charnock * exp(-c * kappa) - z0_m = max(z0_m, 0.000015e0) - Uc = U * log(h_charnock / z0_m) / log(h / z0_m) - end do - - ! --- define dynamic velocity in neutral conditions - u_dyn0 = Uc / c - - end subroutine - ! -------------------------------------------------------------------------------- - end module sfx_esm \ No newline at end of file diff --git a/srcF/sfx_esm_param.f90 b/srcF/sfx_esm_param.f90 index 3aec59df790a95e63a61dec30cc353c1a5ae24cd..0f9e253919ff842374631fd9bb79243efcec1836 100644 --- a/srcF/sfx_esm_param.f90 +++ b/srcF/sfx_esm_param.f90 @@ -51,22 +51,4 @@ module sfx_esm_param real, parameter :: B_max_land = 2.0 real, parameter :: B_max_ocean = 8.0 - !> Charnock parameters - !> z0 = Re_visc_min * (nu / u_dyn) + gamma_c * (u_dyn^2 / g) - real, parameter :: gamma_c = 0.0144 - real, parameter :: Re_visc_min = 0.111 - - real, parameter :: h_charnock = 10.0 - real, parameter :: c1_charnock = log(h_charnock * (g / gamma_c)) - real, parameter :: c2_charnock = Re_visc_min * nu_air * c1_charnock - end module sfx_esm_param - - - - - - - - - diff --git a/srcF/sfx_log.f90 b/srcF/sfx_log.f90 new file mode 100644 index 0000000000000000000000000000000000000000..a3c34655dd813972c3d95d1b4bc92f7697c49a86 --- /dev/null +++ b/srcF/sfx_log.f90 @@ -0,0 +1,258 @@ +module sfx_log + !> @brief simple log-roughness surface flux module + + ! macro defs. + ! -------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------- + + ! modules used + ! -------------------------------------------------------------------------------- +#ifdef SFX_CHECK_NAN + use sfx_common +#endif + use sfx_data + use sfx_roughness + use sfx_log_param + ! -------------------------------------------------------------------------------- + + ! directives list + ! -------------------------------------------------------------------------------- + implicit none + private + ! -------------------------------------------------------------------------------- + + ! public interface + ! -------------------------------------------------------------------------------- + public :: get_surface_fluxes + public :: get_surface_fluxes_vec + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + type, public :: numericsType + integer :: maxiters_charnock = 10 !> maximum (actual) number of iterations in charnock roughness + end type + ! -------------------------------------------------------------------------------- + +contains + + ! -------------------------------------------------------------------------------- + subroutine get_surface_fluxes_vec(sfx, meteo, numerics, n) + !> @brief surface flux calculation for array data + !> @details contains C/C++ & CUDA interface + ! ---------------------------------------------------------------------------- + type (sfxDataVecType), intent(inout) :: sfx + + type (meteoDataVecType), intent(in) :: meteo + type (numericsType), intent(in) :: numerics + integer, intent(in) :: n + ! ---------------------------------------------------------------------------- + + ! --- local variables + type (meteoDataType) meteo_cell + type (sfxDataType) sfx_cell + integer i + ! ---------------------------------------------------------------------------- + + do i = 1, n + meteo_cell = meteoDataType(& + h = meteo%h(i), & + U = meteo%U(i), dT = meteo%dT(i), Tsemi = meteo%Tsemi(i), dQ = meteo%dQ(i), & + z0_m = meteo%z0_m(i)) + + call get_surface_fluxes(sfx_cell, meteo_cell, numerics) + + call push_sfx_data(sfx, sfx_cell, i) + end do + + end subroutine get_surface_fluxes_vec + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + subroutine push_sfx_data(sfx, sfx_cell, idx) + !> @brief helper subroutine for copying data in sfxDataVecType + ! ---------------------------------------------------------------------------- + type (sfxDataVecType), intent(inout) :: sfx + type (sfxDataType), intent(in) :: sfx_cell + integer, intent(in) :: idx + ! ---------------------------------------------------------------------------- + + sfx%zeta(idx) = sfx_cell%zeta + sfx%Rib(idx) = sfx_cell%Rib + sfx%Re(idx) = sfx_cell%Re + sfx%B(idx) = sfx_cell%B + sfx%z0_m(idx) = sfx_cell%z0_m + sfx%z0_t(idx) = sfx_cell%z0_t + sfx%Rib_conv_lim(idx) = sfx_cell%Rib_conv_lim + sfx%Cm(idx) = sfx_cell%Cm + sfx%Ct(idx) = sfx_cell%Ct + sfx%Km(idx) = sfx_cell%Km + sfx%Pr_t_inv(idx) = sfx_cell%Pr_t_inv + + end subroutine push_sfx_data + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + subroutine get_surface_fluxes(sfx, meteo, numerics) + !> @brief surface flux calculation for single cell + !> @details contains C/C++ interface + ! ---------------------------------------------------------------------------- +#ifdef SFX_CHECK_NAN + use ieee_arithmetic +#endif + + type (sfxDataType), intent(out) :: sfx + + type (meteoDataType), intent(in) :: meteo + type (numericsType), intent(in) :: numerics + ! ---------------------------------------------------------------------------- + + ! --- meteo derived datatype name shadowing + ! ---------------------------------------------------------------------------- + real :: h !> constant flux layer height [m] + real :: U !> abs(wind speed) at 'h' [m/s] + real :: dT !> difference between potential temperature at 'h' and at surface [K] + real :: Tsemi !> semi-sum of potential temperature at 'h' and at surface [K] + real :: dQ !> difference between humidity at 'h' and at surface [g/g] + real :: z0_m !> surface aerodynamic roughness (should be < 0 for water bodies surface) + ! ---------------------------------------------------------------------------- + + ! --- local variables + ! ---------------------------------------------------------------------------- + real z0_t !> thermal roughness [m] + real B !> = ln(z0_m / z0_t) [n/d] + real h0_m, h0_t !> = h / z0_m, h / z0_h [n/d] + + real u_dyn0 !> dynamic velocity in neutral conditions [m/s] + real Re !> roughness Reynolds number = u_dyn0 * z0_m / nu [n/d] + + real zeta !> = z/L [n/d] + real Rib !> bulk Richardson number + + real psi_m, psi_h !> universal functions (momentum) & (heat) [n/d] + real phi_m, phi_h !> stability functions (momentum) & (heat) [n/d] + + real Km !> eddy viscosity coeff. at h [m^2/s] + real Pr_t_inv !> invese Prandt number [n/d] + + real Cm, Ct !> transfer coeff. for (momentum) & (heat) [n/d] + + integer surface_type !> surface type = (ocean || land) + + +#ifdef SFX_CHECK_NAN + real NaN +#endif + ! ---------------------------------------------------------------------------- + +#ifdef SFX_CHECK_NAN + ! --- checking if arguments are finite + if (.not.(is_finite(meteo%U).and.is_finite(meteo%Tsemi).and.is_finite(meteo%dT).and.is_finite(meteo%dQ) & + .and.is_finite(meteo%z0_m).and.is_finite(meteo%h))) then + + NaN = ieee_value(0.0, ieee_quiet_nan) ! setting NaN + sfx = sfxDataType(zeta = NaN, Rib = NaN, & + Re = NaN, B = NaN, z0_m = NaN, z0_t = NaN, & + Rib_conv_lim = NaN, & + Cm = NaN, Ct = NaN, Km = NaN, Pr_t_inv = NaN) + return + end if +#endif + + ! --- shadowing names for clarity + U = meteo%U + Tsemi = meteo%Tsemi + dT = meteo%dT + dQ = meteo%dQ + h = meteo%h + z0_m = meteo%z0_m + + ! --- define surface type + if (z0_m < 0.0) then + surface_type = surface_ocean + else + surface_type = surface_land + end if + + if (surface_type == surface_ocean) then + ! --- define surface roughness [momentum] & dynamic velocity in neutral conditions + call get_charnock_roughness(z0_m, u_dyn0, U, h, numerics%maxiters_charnock) + ! --- define relative height + h0_m = h / z0_m + endif + if (surface_type == surface_land) then + ! --- define relative height + h0_m = h / z0_m + ! --- define dynamic velocity in neutral conditions + u_dyn0 = U * kappa / log(h0_m) + end if + + ! --- define B = log(z0_m / z0_h) + Re = u_dyn0 * z0_m / nu_air + if(Re <= Re_rough_min) then + B = B1_rough * alog(B3_rough * Re) + B2_rough + else + ! *: B4 takes into account Re value at z' ~ O(10) z0 + B = B4_rough * (Re**B2_rough) + end if + + ! --- apply max restriction based on surface type + if (surface_type == surface_ocean) then + B = min(B, B_max_ocean) + endif + if (surface_type == surface_land) then + B = min(B, B_max_land) + end if + + ! --- define roughness [thermal] + z0_t = z0_m / exp(B) + ! --- define relative height [thermal] + h0_t = h / z0_t + + ! --- define Ri-bulk + Rib = (g / Tsemi) * h * (dT + 0.61e0 * Tsemi * dQ) / U**2 + + ! --- get the fluxes + ! ---------------------------------------------------------------------------- + call get_psi_neutral(psi_m, psi_h, zeta, h0_m, h0_t, B) + + phi_m = 1.0 + phi_h = 1.0 / Pr_t_0_inv + + ! --- define transfer coeff. (momentum) & (heat) + Cm = kappa / psi_m + Ct = kappa / psi_h + + ! --- define eddy viscosity & inverse Prandtl number + Km = kappa * Cm * U * h / phi_m + Pr_t_inv = phi_m / phi_h + + ! --- setting output + sfx = sfxDataType(zeta = zeta, Rib = Rib, & + Re = Re, B = B, z0_m = z0_m, z0_t = z0_t, & + Rib_conv_lim = 0.0, & + Cm = Cm, Ct = Ct, Km = Km, Pr_t_inv = Pr_t_inv) + + end subroutine get_surface_fluxes + ! -------------------------------------------------------------------------------- + + ! universal functions + ! -------------------------------------------------------------------------------- + subroutine get_psi_neutral(psi_m, psi_h, zeta, h0_m, h0_t, B) + !> @brief universal functions (momentum) & (heat): neutral case + ! ---------------------------------------------------------------------------- + real, intent(out) :: psi_m, psi_h !> universal functions + real, intent(out) :: zeta !> = z/L + + real, intent(in) :: h0_m, h0_t !> = z/z0_m, z/z0_h + real, intent(in) :: B !> = log(z0_m / z0_h) + ! ---------------------------------------------------------------------------- + + zeta = 0.0 + psi_m = log(h0_m) + psi_h = log(h0_t) / Pr_t_0_inv + if (abs(B) < 1.0e-10) psi_h = psi_m / Pr_t_0_inv + + end subroutine + ! -------------------------------------------------------------------------------- + +end module sfx_log \ No newline at end of file diff --git a/srcF/sfx_log_param.f90 b/srcF/sfx_log_param.f90 new file mode 100644 index 0000000000000000000000000000000000000000..3918880c9093be206167a72917d920491cd9d42a --- /dev/null +++ b/srcF/sfx_log_param.f90 @@ -0,0 +1,40 @@ +module sfx_log_param + !> @brief log-roughness surface flux model parameters + !> @details all in SI units + + ! modules used + ! -------------------------------------------------------------------------------- + use sfx_phys_const + ! -------------------------------------------------------------------------------- + + ! directives list + ! -------------------------------------------------------------------------------- + implicit none + ! -------------------------------------------------------------------------------- + + ! *: add lake surface & add surface type as input value + integer, public, parameter :: surface_ocean = 0 !> ocean surface + integer, public, parameter :: surface_land = 1 !> land surface + + !> von Karman constant [n/d] + real, parameter :: kappa = 0.40 + !> inverse Prandtl (turbulent) number in neutral conditions [n/d] + real, parameter :: Pr_t_0_inv = 1.15 + + !> Re fully roughness minimum value [n/d] + real, parameter :: Re_rough_min = 16.3 + + !> roughness model coeff. [n/d] + !> --- transitional mode + !> B = log(z0_m / z0_t) = B1 * log(B3 * Re) + B2 + real, parameter :: B1_rough = 5.0 / 6.0 + real, parameter :: B2_rough = 0.45 + real, parameter :: B3_rough = kappa * Pr_m + !> --- fully rough mode (Re > Re_rough_min) + !> B = B4 * Re^(B2) + real, parameter :: B4_rough =(0.14 * (30.0**B2_rough)) * (Pr_m**0.8) + + real, parameter :: B_max_land = 2.0 + real, parameter :: B_max_ocean = 8.0 + +end module sfx_log_param diff --git a/srcF/sfx_main.f90 b/srcF/sfx_main.f90 index 96a435cf954d1dd8e4535822c0d6b716c994fe0c..e65c55ee3efb7c3bb58736fa52bfa1ead3ddcdfe 100644 --- a/srcF/sfx_main.f90 +++ b/srcF/sfx_main.f90 @@ -4,9 +4,14 @@ program sfx_main ! -------------------------------------------------------------------------------- use sfx_phys_const use sfx_common - - use sfx_esm_param - use sfx_esm + use sfx_data + + use sfx_esm, only: & + get_surface_fluxes_vec_esm => get_surface_fluxes_vec, & + numericsType_esm => numericsType + use sfx_log, only: & + get_surface_fluxes_vec_log => get_surface_fluxes_vec, & + numericsType_log => numericsType ! -------------------------------------------------------------------------------- ! directives list @@ -15,17 +20,19 @@ program sfx_main ! -------------------------------------------------------------------------------- !> dataset ID: - !> = 0, USER - !> = 1, MOSAiC dataset - !> = 2, IRGASON dataset - !> = 3, SHEBA dataset integer :: dataset_id character(len = 256) :: dataset_name - integer, parameter :: dataset_MOSAiC = 1 - integer, parameter :: dataset_IRGASON = 2 + integer, parameter :: dataset_MOSAiC = 1 !> MOSAiC campaign + integer, parameter :: dataset_IRGASON = 2 !> IRGASON data integer, parameter :: dataset_SHEBA = 3 !> please spell 'SHIBA' integer, parameter :: dataset_USER = 4 !> used defined dataset + !> sfx model ID: + integer :: model_id + character(len = 256) :: model_name + integer, parameter :: model_esm = 0 !> ESM model + integer, parameter :: model_log = 1 !> LOG simplified model + ! input/output data ! -------------------------------------------------------------------------------- type(meteoDataVecType) :: meteo !> meteorological data (input) @@ -33,7 +40,8 @@ program sfx_main type(sfxDataVecType) :: sfx !> surface fluxes (output) - type(numericsType) :: numerics !> surface flux module numerics parameters + type(numericsType_esm) :: numerics_esm !> surface flux module (ESM) numerics parameters + type(numericsType_log) :: numerics_log !> surface flux module (LOG) numerics parameters integer :: num !> number of 'cells' in input @@ -49,11 +57,15 @@ program sfx_main integer :: num_args character(len = 128) :: arg + character(len = 128), parameter :: arg_key_model = '--model' character(len = 128), parameter :: arg_key_dataset = '--dataset' character(len = 128), parameter :: arg_key_output = '--output' character(len = 128), parameter :: arg_key_nmax = '--nmax' character(len = 128), parameter :: arg_key_help = '--help' + character(len = 128), parameter :: arg_key_esm = 'esm' + character(len = 128), parameter :: arg_key_log = 'log' + character(len = 128), parameter :: arg_key_mosaic = 'mosaic' character(len = 128), parameter :: arg_key_irgason = 'irgason' character(len = 128), parameter :: arg_key_sheba = 'sheba' @@ -70,7 +82,8 @@ program sfx_main ! -------------------------------------------------------------------------------- - !> @brief define dataset + !> @brief define model & dataset + model_id = model_esm !> default = ESM dataset_id = dataset_MOSAiC !> default = MOSAiC is_output_set = 0 @@ -82,6 +95,8 @@ program sfx_main write(*, *) ' sfx model, usage:' write(*, *) ' --help ' write(*, *) ' print usage options ' + write(*, *) ' --model [key]' + write(*, *) ' key = esm || log' write(*, *) ' --dataset [key]' write(*, *) ' key = mosaic || irgason || sheba || user [files]' write(*, *) ' files = in-common-file in-file out-file' @@ -91,6 +106,21 @@ program sfx_main write(*, *) ' max number of data points > 0 ' stop end if + if (trim(arg) == trim(arg_key_model)) then + if (i == num_args) then + write(*, *) ' FAILURE! > missing model [key] argument' + stop + end if + call get_command_argument(i + 1, arg) + if (trim(arg) == trim(arg_key_esm)) then + model_id = model_esm + else if (trim(arg) == trim(arg_key_log)) then + model_id = model_log + else + write(*, *) ' FAILURE! > unknown model [key]: ', trim(arg) + stop + end if + end if if (trim(arg) == trim(arg_key_dataset)) then if (i == num_args) then write(*, *) ' FAILURE! > missing dataset [key] argument' @@ -144,7 +174,17 @@ program sfx_main end do - !> @brief set filenames & data for specific dataset + !> @brief set name for specific model + if (model_id == model_esm) then + model_name = "ESM" + else if (model_id == model_log) then + model_name = "LOG" + else + write(*, *) ' FAILURE! > unknown model id: ', model_id + stop + end if + + !> @brief set name & filenames for specific dataset if (dataset_id == dataset_MOSAiC) then dataset_name = 'MOSAiC' @@ -166,9 +206,11 @@ program sfx_main dataset_name = 'USER' else write(*, *) ' FAILURE! > unknown dataset id: ', dataset_id + stop end if write(*, *) ' Running SFX model' + write(*, *) ' model = ', trim(model_name) write(*, *) ' dataset = ', trim(dataset_name) write(*, *) ' filename[IN-COMMON] = ', trim(filename_in_common) write(*, *) ' filename[IN] = ', trim(filename_in) @@ -239,7 +281,11 @@ program sfx_main !> @brief calling flux module - call get_surface_fluxes_vec(sfx, meteo, numerics, num) + if (model_id == model_esm) then + call get_surface_fluxes_vec_esm(sfx, meteo, numerics_esm, num) + else if (model_id == model_log) then + call get_surface_fluxes_vec_log(sfx, meteo, numerics_log, num) + end if !> @brief write output data diff --git a/srcF/sfx_roughness.f90 b/srcF/sfx_roughness.f90 new file mode 100644 index 0000000000000000000000000000000000000000..8e01cc9438299d1371783caa4ba81b51c12de45d --- /dev/null +++ b/srcF/sfx_roughness.f90 @@ -0,0 +1,88 @@ +module sfx_roughness + !> @brief surface roughness parameterizations + + ! macro defs. + ! -------------------------------------------------------------------------------- + ! -------------------------------------------------------------------------------- + + ! modules used + ! -------------------------------------------------------------------------------- + use sfx_phys_const + ! -------------------------------------------------------------------------------- + + ! directives list + ! -------------------------------------------------------------------------------- + implicit none + private + ! -------------------------------------------------------------------------------- + + ! -------------------------------------------------------------------------------- + real, parameter :: kappa = 0.40 !> von Karman constant [n/d] + ! -------------------------------------------------------------------------------- + + ! public interface + ! -------------------------------------------------------------------------------- + public :: get_charnock_roughness + ! -------------------------------------------------------------------------------- + + !> Charnock parameters + !> z0 = Re_visc_min * (nu / u_dyn) + gamma_c * (u_dyn^2 / g) + ! -------------------------------------------------------------------------------- + real, parameter :: gamma_c = 0.0144 + real, parameter :: Re_visc_min = 0.111 + + real, parameter :: h_charnock = 10.0 + real, parameter :: c1_charnock = log(h_charnock * (g / gamma_c)) + real, parameter :: c2_charnock = Re_visc_min * nu_air * c1_charnock + ! -------------------------------------------------------------------------------- + +contains + + ! charnock roughness definition + ! -------------------------------------------------------------------------------- + subroutine get_charnock_roughness(z0_m, u_dyn0, U, h, maxiters) + ! ---------------------------------------------------------------------------- + real, intent(out) :: z0_m !> aerodynamic roughness [m] + real, intent(out) :: u_dyn0 !> dynamic velocity in neutral conditions [m/s] + + real, intent(in) :: h !> constant flux layer height [m] + real, intent(in) :: U !> abs(wind speed) [m/s] + integer, intent(in) :: maxiters !> maximum number of iterations + ! ---------------------------------------------------------------------------- + + ! --- local variables + real :: Uc ! wind speed at h_charnock [m/s] + + real :: a, b, c, c_min + real :: f + + integer :: i, j + ! ---------------------------------------------------------------------------- + + Uc = U + a = 0.0 + b = 25.0 + c_min = log(h_charnock) / kappa + + do i = 1, maxiters + f = c1_charnock - 2.0 * log(Uc) + do j = 1, maxiters + c = (f + 2.0 * log(b)) / kappa + ! looks like the check should use U10 instead of U + ! but note that a1 should be set = 0 in cycle beforehand + if (U <= 8.0e0) a = log(1.0 + c2_charnock * ((b / Uc)**3)) / kappa + c = max(c - a, c_min) + b = c + end do + z0_m = h_charnock * exp(-c * kappa) + z0_m = max(z0_m, 0.000015e0) + Uc = U * log(h_charnock / z0_m) / log(h / z0_m) + end do + + ! --- define dynamic velocity in neutral conditions + u_dyn0 = Uc / c + + end subroutine + ! -------------------------------------------------------------------------------- + +end module sfx_roughness