From 5e47c9393baa1263ff023c5efad3574987fc1b5b Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 15 Jun 2020 12:06:48 +0200 Subject: [PATCH 01/31] Start at implemention realistic LSM --- src/modlsm.f90 | 157 +++++++++++++++++++++++++++++++++++++++++++++ src/modstartup.f90 | 5 ++ src/modsurface.f90 | 6 ++ src/program.f90 | 6 +- 4 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 src/modlsm.f90 diff --git a/src/modlsm.f90 b/src/modlsm.f90 new file mode 100644 index 00000000..4f8dbb51 --- /dev/null +++ b/src/modlsm.f90 @@ -0,0 +1,157 @@ +! +! Copyright (c) 2020-2020 Wageningen University and Research (WUR) +! +! This file is part of DALES +! +! DALES 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. +! +! DALES 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 DALES. If not, see . +! + +module modlsm + use netcdf + implicit none + public :: initlsm, lsm, exitlsm + + ! Land-surface / van Genuchten parameters from NetCDF input table. + real, allocatable :: & + theta_res(:), theta_wp(:), theta_fc(:), theta_sat(:), gamma_sat(:), vg_a(:), vg_l(:), vg_n(:) +contains + +! +! Initialise the land-surface model +! +subroutine initlsm + implicit none + + ! Read the soil parameter table + call read_soil_table +end subroutine initlsm + +subroutine lsm + implicit none +end subroutine lsm + +! +! Cleanup (deallocate) the land-surface model +! +subroutine exitlsm + implicit none + + deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_sat, vg_a, vg_l, vg_n ) +end subroutine exitlsm + +! +! Read the input table with the (van Genuchten) soil parameters +! +subroutine read_soil_table + implicit none + integer :: n, ncid, dimid, varid + + ! Open the NetCDF file and read the table size + print*,'Reading "van_genuchten_parameters.nc"' + call check( nf90_open('van_genuchten_parameters.nc', nf90_nowrite, ncid) ) + call check( nf90_inq_dimid(ncid, 'index', dimid) ) + call check( nf90_inquire_dimension(ncid, dimid, len=n) ) + + ! Allocate variables + allocate( & + theta_res(n), theta_wp(n), theta_fc(n), theta_sat(n), & + gamma_sat(n), vg_a(n), vg_l(n), vg_n(n) ) + + ! Read variables + call check( nf90_inq_varid(ncid, 'theta_res', varid) ) + call check( nf90_get_var(ncid, varid, theta_res) ) + + call check( nf90_inq_varid(ncid, 'theta_wp', varid) ) + call check( nf90_get_var(ncid, varid, theta_wp) ) + + call check( nf90_inq_varid(ncid, 'theta_fc', varid) ) + call check( nf90_get_var(ncid, varid, theta_fc) ) + + call check( nf90_inq_varid(ncid, 'theta_sat', varid) ) + call check( nf90_get_var(ncid, varid, theta_sat) ) + + call check( nf90_inq_varid(ncid, 'gamma_sat', varid) ) + call check( nf90_get_var(ncid, varid, gamma_sat) ) + + call check( nf90_inq_varid(ncid, 'alpha', varid) ) + call check( nf90_get_var(ncid, varid, vg_a) ) + + call check( nf90_inq_varid(ncid, 'l', varid) ) + call check( nf90_get_var(ncid, varid, vg_l) ) + + call check( nf90_inq_varid(ncid, 'n', varid) ) + call check( nf90_get_var(ncid, varid, vg_n) ) + + call check( nf90_close(ncid) ) + +end subroutine read_soil_table + +! +! Convert soil hydraulic head to soil water content, using van Genuchten parameterisation. +! +pure function psi_to_theta(theta_res, theta_sat, vg_a, vg_n, vg_m, psi) result(res) + implicit none + real, intent(in) :: theta_res, theta_sat, vg_a, vg_n, vg_m, psi + real :: res + + res = theta_res + (theta_sat - theta_res) * (1. / (1.+ abs(vg_a * psi)**vg_n))**vg_m +end function psi_to_theta + +! +! Convert soil water content to hydraulic head, using van Genuchten parameterisation. +! +pure function theta_to_psi(theta_res, theta_sat, vg_a, vg_n, vg_m, theta) result(res) + implicit none + real, intent(in) :: theta_res, theta_sat, vg_a, vg_n, vg_m, theta + real :: res + + res = -(vg_a**(-vg_n) * (-1. + ((theta_res - theta_sat)/(theta_res - theta))**(1./vg_m)))**(1./vg_n) +end function theta_to_psi + +! +! Calculate hydraulic diffusivity using van Genuchten parameterisation. +! +pure function calc_diffusivity_vg( & + theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res) result(res) + implicit none + real, intent(in) :: theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res + real :: res + + res = (1.-vg_m)*lambda_sat / (vg_a * vg_m * (theta_sat-theta_res)) * theta_norm**(vg_l-(1./vg_m)) * & + ( (1.-theta_norm**(1./vg_m))**(-vg_m) + (1.-theta_norm**(1/vg_m))**vg_m - 2. ) +end function calc_diffusivity_vg + +! +! Calculate hydraulic conductivity using van Genuchten parameterisation. +! +pure function calc_conductivity_vg(theta_norm, vg_l, vg_m, lambda_sat) result(res) + implicit none + real, intent(in) :: theta_norm, vg_l, vg_m, lambda_sat + real :: res + + res = lambda_sat * theta_norm**vg_l * ( 1.- (1.-theta_norm**(1./vg_m))**vg_m )**2. +end function calc_conductivity_vg + +! +! Check NetCDF calls +! +subroutine check(status) + integer, intent (in) :: status + if(status /= nf90_noerr) then + print *,'NetCDF error: ', trim(nf90_strerror(status)) + stop + end if +end subroutine check + +end module modlsm diff --git a/src/modstartup.f90 b/src/modstartup.f90 index b98534cc..1b59af6b 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -63,6 +63,7 @@ subroutine startup(path) use modforces, only : lforce_user use modsurfdata, only : z0,ustin,wtsurf,wqsurf,wsvsurf,ps,thls,isurf use modsurface, only : initsurface + use modlsm, only : initlsm use modfields, only : initfields use modpois, only : initpois use modradiation, only : initradiation @@ -268,6 +269,7 @@ subroutine startup(path) call initradiation call initchem call initsurface + call initlsm call initsubgrid call initmicrophysics @@ -348,6 +350,7 @@ subroutine checkinitvalues !isurf if (myid == 0) then select case (isurf) + case(-1) case(1) case(2,10) case(3:4) @@ -1143,11 +1146,13 @@ subroutine exitmodules use modradiation, only : exitradiation use modsubgrid, only : exitsubgrid use modsurface, only : exitsurface + use modlsm, only : exitlsm use modthermodynamics, only : exitthermodynamics call exittimedep call exitthermodynamics call exitsurface + call exitlsm call exitsubgrid call exitradiation call exitpois diff --git a/src/modsurface.f90 b/src/modsurface.f90 index b9934c03..9d372a1c 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -320,6 +320,8 @@ subroutine initsurface close(ifinput) else select case (isurf) + case (-1) + return case (1) ! Interactive land surface open (ifinput,file='surface.interactive.inp.'//cexpnr) ierr = 0 @@ -724,6 +726,10 @@ subroutine surface patchx = 0 patchy = 0 + if (isurf==-1) then + return + end if + if (isurf==10) then call surf_user return diff --git a/src/program.f90 b/src/program.f90 index 886aaded..6db67d45 100644 --- a/src/program.f90 +++ b/src/program.f90 @@ -107,7 +107,8 @@ program DALES use modboundary, only : boundary, grwdamp! JvdD ,tqaver use modthermodynamics, only : thermodynamics use modmicrophysics, only : microsources - use modsurface, only : surface + use modsurface, only : surface + use modlsm, only : lsm use modsubgrid, only : subgrid use modforces, only : forces, coriolis, lstend use modradiation, only : radiation @@ -211,8 +212,9 @@ program DALES call samptend(tend_rad) !----------------------------------------------------- -! 3.2 THE SURFACE LAYER +! 3.2 THE SURFACE LAYER / LAND-SURFACE !----------------------------------------------------- + call lsm call surface !----------------------------------------------------- From db40554172aa43424117c3df69d4c791d17efec3 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 29 Jun 2020 12:23:47 +0200 Subject: [PATCH 02/31] Added data structures for land-surface tiles --- src/modlsm.f90 | 107 ++++++++++++++++++++++++++++++++++++++++++--- src/modsurface.f90 | 35 +++++++-------- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 4f8dbb51..0b9c5bea 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -20,26 +20,77 @@ module modlsm use netcdf implicit none + public :: initlsm, lsm, exitlsm ! Land-surface / van Genuchten parameters from NetCDF input table. real, allocatable :: & theta_res(:), theta_wp(:), theta_fc(:), theta_sat(:), gamma_sat(:), vg_a(:), vg_l(:), vg_n(:) + + logical :: sw_lsm + logical :: sw_homogeneous + + ! Data structure for sub-grid tiles + type lsm_tile + ! Static properties: + real, allocatable :: z0m(:,:), z0h(:,:) + ! Dynamic tile fraction: + real, allocatable :: frac(:,:) + ! Monin-obukhov / surface layer: + real, allocatable :: obuk(:,:), ustar(:,:), ra(:,:) + ! Conductivity skin layer: + real, allocatable :: lambda_stable(:,:), lamda_unstable(:,:) + ! Surface fluxes: + real, allocatable :: H(:,:), LE(:,:), G(:,:), wthl(:,:), wqt(:,:) + ! Surface temperature and humidity + real, allocatable :: tskin(:,:), qskin(:,:) + end type lsm_tile + + type(lsm_tile) low_veg, high_veg, bare_soil, wet_skin + contains +subroutine lsm + implicit none +end subroutine lsm + ! ! Initialise the land-surface model ! subroutine initlsm + use modglobal, only : ifnamopt, fname_options, checknamelisterror + use modmpi, only : myid, comm3d, mpierr, mpi_logical + use modsurfdata, only : isurf implicit none - ! Read the soil parameter table - call read_soil_table -end subroutine initlsm + integer :: ierr -subroutine lsm - implicit none -end subroutine lsm + ! Read namelist + namelist /NAMLSM/ & + sw_lsm, sw_homogeneous + + if (myid == 0) then + open(ifnamopt, file=fname_options, status='old', iostat=ierr) + read(ifnamopt, NAMLSM, iostat=ierr) + write(6, NAMLSM) + close(ifnamopt) + end if + + ! Broadcast namelist values to all MPI tasks + call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) + + if (sw_lsm) then + ! Allocate required fields in modsurfacedata, + ! and arrays / tiles from this module + call allocate_fields + + ! Read the soil parameter table + call read_soil_table + + end if + +end subroutine initlsm ! ! Cleanup (deallocate) the land-surface model @@ -50,6 +101,50 @@ subroutine exitlsm deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_sat, vg_a, vg_l, vg_n ) end subroutine exitlsm +! +! Allocate all LSM fields +! +subroutine allocate_fields + use modglobal, only : i2, j2 + implicit none + + ! Allocate the tiled variables + call allocate_tile(low_veg) + call allocate_tile(high_veg) + call allocate_tile(bare_soil) + call allocate_tile(wet_skin) + +end subroutine allocate_fields + +! +! Allocate all fields of a LSM tile +! +subroutine allocate_tile(tile) + use modglobal, only : i2, j2 + implicit none + type(lsm_tile), intent(inout) :: tile + + ! Static properties: + allocate(tile%z0m (i2, j2)) + allocate(tile%z0h (i2, j2)) + ! Dynamic tile fraction: + allocate(tile%frac (i2, j2)) + ! Monin-obukhov / surface layer: + allocate(tile%obuk (i2, j2)) + allocate(tile%ustar(i2, j2)) + allocate(tile%ra (i2, j2)) + ! Surface fluxes: + allocate(tile%H (i2, j2)) + allocate(tile%LE (i2, j2)) + allocate(tile%G (i2, j2)) + allocate(tile%wthl (i2, j2)) + allocate(tile%wqt (i2, j2)) + ! Surface temperature and humidity + allocate(tile%tskin(i2, j2)) + allocate(tile%qskin(i2, j2)) + +end subroutine allocate_tile + ! ! Read the input table with the (van Genuchten) soil parameters ! diff --git a/src/modsurface.f90 b/src/modsurface.f90 index 9d372a1c..99d83aee 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -80,9 +80,10 @@ subroutine initsurface integer :: i,j,k, landindex, ierr, defined_landtypes, landtype_0 = -1 integer :: tempx,tempy - character(len=1500) :: readbuffer + character(len=1500) :: readbuffer + namelist/NAMSURFACE/ & !< Soil related variables - isurf,tsoilav, tsoildeepav, phiwav, rootfav, & + isurf, tsoilav, tsoildeepav, phiwav, rootfav, & ! Land surface related variables lmostlocal, lsmoothflux, lneutral, z0mav, z0hav, rsisurf2, Cskinav, lambdaskinav, albedoav, Qnetav, cvegav, Wlav, & ! Jarvis-Steward related variables @@ -163,7 +164,7 @@ subroutine initsurface call MPI_BCAST(phiwp , 1, MY_REAL , 0, comm3d, mpierr) call MPI_BCAST(R10 , 1, MY_REAL , 0, comm3d, mpierr) call MPI_BCAST(lsplitleaf , 1, MPI_LOGICAL, 0, comm3d, mpierr) - + call MPI_BCAST(land_use(1:mpatch,1:mpatch),mpatch*mpatch, MPI_INTEGER, 0, comm3d, mpierr) if(lCO2Ags .and. (.not. lrsAgs)) then @@ -320,8 +321,6 @@ subroutine initsurface close(ifinput) else select case (isurf) - case (-1) - return case (1) ! Interactive land surface open (ifinput,file='surface.interactive.inp.'//cexpnr) ierr = 0 @@ -696,7 +695,7 @@ subroutine initsurface if (lsplitleaf) then allocate(PARdirField (2:i1,2:j1)) allocate(PARdifField (2:i1,2:j1)) - endif + endif endif return end subroutine initsurface @@ -868,7 +867,7 @@ subroutine surface phimzf = phim(zf(1)/obl(i,j)) phihzf = phih(zf(1)/obl(i,j)) - + dudz (i,j) = ustar(i,j) * phimzf / (fkar*zf(1))*(upcu/horv) dvdz (i,j) = ustar(i,j) * phimzf / (fkar*zf(1))*(vpcv/horv) dthldz(i,j) = - thlflux(i,j) / ustar(i,j) * phihzf / (fkar*zf(1)) @@ -901,7 +900,7 @@ subroutine surface phimzf = phim(zf(1)/obl(i,j)) phihzf = phih(zf(1)/obl(i,j)) - + upcu = 0.5 * (u0(i,j,1) + u0(i+1,j,1)) + cu vpcv = 0.5 * (v0(i,j,1) + v0(i,j+1,1)) + cv horv = sqrt(upcu ** 2. + vpcv ** 2.) @@ -984,10 +983,10 @@ subroutine surface svflux(i,j,n) = wsvsurf(n) enddo endif - + phimzf = phim(zf(1)/obl(i,j)) phihzf = phih(zf(1)/obl(i,j)) - + dudz (i,j) = ustar(i,j) * phimzf / (fkar*zf(1))*(upcu/horv) dvdz (i,j) = ustar(i,j) * phimzf / (fkar*zf(1))*(vpcv/horv) dthldz(i,j) = - thlflux(i,j) / ustar(i,j) * phihzf / (fkar*zf(1)) @@ -1162,7 +1161,7 @@ subroutine getobl if(Rib > 0) L = 0.01 if(Rib < 0) L = -0.01 end if - + do while (.true.) iter = iter + 1 Lold = L @@ -1301,7 +1300,7 @@ subroutine getobl if(Rib > 0) L = 0.01 if(Rib < 0) L = -0.01 end if - + do while (.true.) iter = iter + 1 Lold = L @@ -1378,7 +1377,7 @@ end function psih ! stability function Phi for momentum. ! Many functional forms of Phi have been suggested, see e.g. Optis 2015 - ! Phi and Psi above are related by an integral and should in principle match, + ! Phi and Psi above are related by an integral and should in principle match, ! currently they do not. ! FJ 2018: For very stable situations, zeta > 1 add cap to phi - the linear expression is valid only for zeta < 1 function phim(zeta) @@ -1398,7 +1397,7 @@ function phim(zeta) return end function phim - ! stability function Phi for heat. + ! stability function Phi for heat. function phih(zeta) implicit none real :: phih @@ -1416,7 +1415,7 @@ function phih(zeta) return end function phih - + function E1(x) implicit none real :: E1 @@ -1428,7 +1427,7 @@ function E1(x) do k=1,99 !E1sum = E1sum + (-1.0) ** (k + 0.0) * x ** (k + 0.0) / ( (k + 0.0) * factorial(k) ) E1sum = E1sum + (-1.0 * x) ** k / ( k * factorial(k) ) ! FJ changed this for compilation with cray fortran - + end do E1 = -0.57721566490153286060 - log(x) - E1sum @@ -1668,7 +1667,7 @@ subroutine do_lsm real :: Ag, PARdir, PARdif !Variables for 2leaf AGS real :: MW_Air = 28.97 real :: MW_CO2 = 44 - + real :: sinbeta, kdrbl, kdf, kdr, ref, ref_dir real :: iLAI, fSL real :: PARdfU, PARdfD, PARdfT, PARdrU, PARdrD, PARdrT, dirPAR, difPAR @@ -1960,7 +1959,7 @@ subroutine do_lsm gc_inf = LAI(i,j) * sum(weight_g * gnet) else !lsplitleaf - + ! Calculate upscaling from leaf to canopy: net flow CO2 into the plant (An) AGSa1 = 1.0 / (1 - f0) Dstar = D0 / (AGSa1 * (f0 - fmin)) From 9e78c3d813a6e940f2a5658c3a5b0f0f47f5b6d9 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 29 Jun 2020 13:52:11 +0200 Subject: [PATCH 03/31] Added homogeneous initialisation of LSM --- src/modlsm.f90 | 147 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 126 insertions(+), 21 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 0b9c5bea..41408273 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -23,12 +23,7 @@ module modlsm public :: initlsm, lsm, exitlsm - ! Land-surface / van Genuchten parameters from NetCDF input table. - real, allocatable :: & - theta_res(:), theta_wp(:), theta_fc(:), theta_sat(:), gamma_sat(:), vg_a(:), vg_l(:), vg_n(:) - - logical :: sw_lsm - logical :: sw_homogeneous + logical :: sw_lsm ! On/off switch LSM ! Data structure for sub-grid tiles type lsm_tile @@ -39,15 +34,22 @@ module modlsm ! Monin-obukhov / surface layer: real, allocatable :: obuk(:,:), ustar(:,:), ra(:,:) ! Conductivity skin layer: - real, allocatable :: lambda_stable(:,:), lamda_unstable(:,:) + real, allocatable :: lambda_stable(:,:), lambda_unstable(:,:) ! Surface fluxes: real, allocatable :: H(:,:), LE(:,:), G(:,:), wthl(:,:), wqt(:,:) - ! Surface temperature and humidity + ! Surface temperature and humidity: real, allocatable :: tskin(:,:), qskin(:,:) + ! Vegetation properties: + real, allocatable :: lai(:,:), rs_min(:,:) end type lsm_tile type(lsm_tile) low_veg, high_veg, bare_soil, wet_skin + ! Land-surface / van Genuchten parameters from NetCDF input table. + real, allocatable :: & + theta_res(:), theta_wp(:), theta_fc(:), theta_sat(:), & + gamma_sat(:), vg_a(:), vg_l(:), vg_n(:) + contains subroutine lsm @@ -64,6 +66,7 @@ subroutine initlsm implicit none integer :: ierr + logical :: sw_homogeneous ! Read namelist namelist /NAMLSM/ & @@ -72,6 +75,7 @@ subroutine initlsm if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) read(ifnamopt, NAMLSM, iostat=ierr) + call checknamelisterror(ierr, ifnamopt, 'NAMLSM') write(6, NAMLSM) close(ifnamopt) end if @@ -81,10 +85,18 @@ subroutine initlsm call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) if (sw_lsm) then - ! Allocate required fields in modsurfacedata, - ! and arrays / tiles from this module + ! Allocate required fields in modsurfacedata, and arrays / tiles from this module: call allocate_fields + if (sw_homogeneous) then + ! Initialise homogeneous LSM from namelist input + call init_homogeneous + else + ! Initialise heterogeneous LSM from external input + print*,'ERROR: heterogeneous LSM not (yet) implemented!' + stop + end if + ! Read the soil parameter table call read_soil_table @@ -125,26 +137,119 @@ subroutine allocate_tile(tile) type(lsm_tile), intent(inout) :: tile ! Static properties: - allocate(tile%z0m (i2, j2)) - allocate(tile%z0h (i2, j2)) + allocate(tile%z0m(i2, j2)) + allocate(tile%z0h(i2, j2)) + ! Dynamic tile fraction: - allocate(tile%frac (i2, j2)) + allocate(tile%frac(i2, j2)) + ! Monin-obukhov / surface layer: - allocate(tile%obuk (i2, j2)) + allocate(tile%obuk(i2, j2)) allocate(tile%ustar(i2, j2)) - allocate(tile%ra (i2, j2)) + allocate(tile%ra(i2, j2)) + + ! Conductivity skin layer: + allocate(tile%lambda_stable(i2, j2)) + allocate(tile%lambda_unstable(i2, j2)) + ! Surface fluxes: - allocate(tile%H (i2, j2)) - allocate(tile%LE (i2, j2)) - allocate(tile%G (i2, j2)) - allocate(tile%wthl (i2, j2)) - allocate(tile%wqt (i2, j2)) - ! Surface temperature and humidity + allocate(tile%H(i2, j2)) + allocate(tile%LE(i2, j2)) + allocate(tile%G(i2, j2)) + allocate(tile%wthl(i2, j2)) + allocate(tile%wqt(i2, j2)) + + ! Surface temperature and humidity: allocate(tile%tskin(i2, j2)) allocate(tile%qskin(i2, j2)) + ! Vegetation properties: + allocate(tile%lai(i2, j2)) + allocate(tile%rs_min(i2, j2)) + end subroutine allocate_tile +! +! Initialise the LSM homogeneous from namelist input +! +subroutine init_homogeneous + use modglobal, only : ifnamopt, fname_options, checknamelisterror + use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real + implicit none + + integer :: ierr + real :: z0m_low, z0m_high, z0m_bare + real :: z0h_low, z0h_high, z0h_bare + real :: lambda_s_low, lambda_s_high, lambda_s_bare + real :: lambda_us_low, lambda_us_high, lambda_us_bare + real :: lai_low, lai_high + real :: rs_min_low, rs_min_high + + ! Read namelist + namelist /NAMLSM_HOMOGENEOUS/ & + z0m_low, z0m_high, z0m_bare, & + z0h_low, z0h_high, z0h_bare, & + lambda_s_low, lambda_s_high, lambda_s_bare, & + lambda_us_low, lambda_us_high, lambda_us_bare, & + lai_low, lai_high, & + rs_min_low, rs_min_high + + if (myid == 0) then + open(ifnamopt, file=fname_options, status='old', iostat=ierr) + read(ifnamopt, NAMLSM_HOMOGENEOUS, iostat=ierr) + call checknamelisterror(ierr, ifnamopt, 'NAMLSM_HOMOGENEOUS') + write(6, NAMLSM_HOMOGENEOUS) + close(ifnamopt) + end if + + ! Broadcast to all MPI tasks + call MPI_BCAST(z0m_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_bare, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(z0h_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_bare, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(lambda_s_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(lambda_s_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(lambda_s_bare, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(lambda_us_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(lambda_us_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(lambda_us_bare, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(lai_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(lai_high, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(rs_min_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(rs_min_high, 1, my_real, 0, comm3d, mpierr) + + ! Set values + low_veg %z0m(:,:) = z0m_low + high_veg %z0m(:,:) = z0m_high + bare_soil%z0m(:,:) = z0m_bare + + low_veg %z0h(:,:) = z0h_low + high_veg %z0h(:,:) = z0h_high + bare_soil%z0h(:,:) = z0h_bare + + low_veg %lambda_stable(:,:) = lambda_s_low + high_veg %lambda_stable(:,:) = lambda_s_high + bare_soil%lambda_stable(:,:) = lambda_s_bare + + low_veg %lambda_unstable(:,:) = lambda_us_low + high_veg %lambda_unstable(:,:) = lambda_us_high + bare_soil%lambda_unstable(:,:) = lambda_us_bare + + low_veg %lai(:,:) = lai_low + high_veg%lai(:,:) = lai_high + + low_veg %rs_min(:,:) = rs_min_low + high_veg%rs_min(:,:) = rs_min_high + +end subroutine init_homogeneous + ! ! Read the input table with the (van Genuchten) soil parameters ! From b7dd889e706cbda0c90471b3b2bf680684413807 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 29 Jun 2020 15:45:35 +0200 Subject: [PATCH 04/31] Added soil grid and initialisation of soil --- src/modlsm.f90 | 86 +++++++++++++++++++++++++++++++++++++++++++--- src/modstartup.f90 | 4 +-- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 41408273..d168fd3c 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -25,6 +25,16 @@ module modlsm logical :: sw_lsm ! On/off switch LSM + ! Soil grid + integer :: kmax_soil + real :: z_size_soil + real, allocatable :: z_soil(:), zh_soil(:) + real, allocatable :: dz_soil(:), dzh_soil(:) + real, allocatable :: dzi_soil(:), dzhi_soil(:) + + ! Soil properties + integer, allocatable :: soil_index(:,:,:) + ! Data structure for sub-grid tiles type lsm_tile ! Static properties: @@ -70,7 +80,9 @@ subroutine initlsm ! Read namelist namelist /NAMLSM/ & - sw_lsm, sw_homogeneous + sw_lsm, sw_homogeneous, z_soil, z_size_soil + + allocate(z_soil(kmax_soil)) if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) @@ -85,6 +97,9 @@ subroutine initlsm call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) if (sw_lsm) then + ! Create/calculate soil grid properties + call create_soil_grid + ! Allocate required fields in modsurfacedata, and arrays / tiles from this module: call allocate_fields @@ -93,8 +108,7 @@ subroutine initlsm call init_homogeneous else ! Initialise heterogeneous LSM from external input - print*,'ERROR: heterogeneous LSM not (yet) implemented!' - stop + stop 'Heterogeneous LSM not (yet) implemented!' end if ! Read the soil parameter table @@ -113,13 +127,58 @@ subroutine exitlsm deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_sat, vg_a, vg_l, vg_n ) end subroutine exitlsm +! +! Calculate soil grid properties +! +subroutine create_soil_grid + implicit none + integer :: k + + allocate(dz_soil (kmax_soil )) + allocate(dzi_soil (kmax_soil )) + allocate(zh_soil (kmax_soil+1)) + allocate(dzh_soil (kmax_soil+1)) + allocate(dzhi_soil(kmax_soil+1)) + + ! Half level heights + zh_soil(1) = z_size_soil + zh_soil(kmax_soil+1) = 0. + + do k=2, kmax_soil + zh_soil(k) = 0.5*(z_soil(k) + z_soil(k-1)) + end do + + ! Grid spacing full and half levels + do k=1, kmax_soil + dz_soil(k) = zh_soil(k+1) - zh_soil(k) + end do + + do k=2, kmax_soil + dzh_soil(k) = z_soil(k) - z_soil(k-1) + end do + + dzh_soil(1) = 2*(z_soil(1) - zh_soil(1)) + dzh_soil(kmax_soil+1) = 2*(-z_soil(kmax_soil)) + + ! Inverse grid spacings + dzi_soil(:) = 1./dz_soil(:) + dzhi_soil(:) = 1./dzh_soil(:) + +end subroutine create_soil_grid + ! ! Allocate all LSM fields ! subroutine allocate_fields use modglobal, only : i2, j2 + use modsurfdata, only : tsoil, phiw implicit none + ! Allocate soil variables + allocate(soil_index(i2, j2, kmax_soil)) + allocate(tsoil (i2, j2, kmax_soil)) + allocate(phiw (i2, j2, kmax_soil)) + ! Allocate the tiled variables call allocate_tile(low_veg) call allocate_tile(high_veg) @@ -175,9 +234,10 @@ end subroutine allocate_tile subroutine init_homogeneous use modglobal, only : ifnamopt, fname_options, checknamelisterror use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real + use modsurfdata, only : tsoil, phiw implicit none - integer :: ierr + integer :: ierr, k real :: z0m_low, z0m_high, z0m_bare real :: z0h_low, z0h_high, z0h_bare real :: lambda_s_low, lambda_s_high, lambda_s_bare @@ -185,6 +245,8 @@ subroutine init_homogeneous real :: lai_low, lai_high real :: rs_min_low, rs_min_high + real, allocatable :: t_soil_p(:), theta_soil_p(:) + ! Read namelist namelist /NAMLSM_HOMOGENEOUS/ & z0m_low, z0m_high, z0m_bare, & @@ -192,7 +254,10 @@ subroutine init_homogeneous lambda_s_low, lambda_s_high, lambda_s_bare, & lambda_us_low, lambda_us_high, lambda_us_bare, & lai_low, lai_high, & - rs_min_low, rs_min_high + rs_min_low, rs_min_high, & + t_soil_p, theta_soil_p + + allocate(t_soil_p(kmax_soil), theta_soil_p(kmax_soil)) if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) @@ -225,6 +290,9 @@ subroutine init_homogeneous call MPI_BCAST(rs_min_low, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(rs_min_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(t_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) + call MPI_BCAST(theta_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) + ! Set values low_veg %z0m(:,:) = z0m_low high_veg %z0m(:,:) = z0m_high @@ -248,6 +316,14 @@ subroutine init_homogeneous low_veg %rs_min(:,:) = rs_min_low high_veg%rs_min(:,:) = rs_min_high + do k=1, kmax_soil + tsoil(:,:,k) = t_soil_p(k) + phiw (:,:,k) = theta_soil_p(k) + end do + + ! Cleanup! + deallocate(t_soil_p, theta_soil_p) + end subroutine init_homogeneous ! diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 1b59af6b..62ec51dd 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -63,7 +63,7 @@ subroutine startup(path) use modforces, only : lforce_user use modsurfdata, only : z0,ustin,wtsurf,wqsurf,wsvsurf,ps,thls,isurf use modsurface, only : initsurface - use modlsm, only : initlsm + use modlsm, only : initlsm, kmax_soil use modfields, only : initfields use modpois, only : initpois use modradiation, only : initradiation @@ -93,7 +93,7 @@ subroutine startup(path) krandumin, krandumax, randu,& nprocx,nprocy namelist/DOMAIN/ & - itot,jtot,kmax,& + itot,jtot,kmax,kmax_soil,& xsize,ysize,& xlat,xlon,xyear,xday,xtime,ksp namelist/PHYSICS/ & From 693964183b27ac3efd5f7af62e4590cd48b0c5be Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 30 Jun 2020 09:52:26 +0200 Subject: [PATCH 05/31] Minor changes new LSM --- src/modlsm.f90 | 97 +++++++++++++++++++++++++-------------------- src/modstartup.f90 | 4 +- src/modsurface.f90 | 5 ++- src/modsurfdata.f90 | 6 +-- 4 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index d168fd3c..579ebd18 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -53,7 +53,8 @@ module modlsm real, allocatable :: lai(:,:), rs_min(:,:) end type lsm_tile - type(lsm_tile) low_veg, high_veg, bare_soil, wet_skin + ! Tiles for low veg (lv), high veg (hv), bare soil (bs), wet skin (ws): + type(lsm_tile) tile_lv, tile_hv, tile_bs, tile_ws ! Land-surface / van Genuchten parameters from NetCDF input table. real, allocatable :: & @@ -63,7 +64,12 @@ module modlsm contains subroutine lsm + use modsurfdata, only : thlflux, qtflux implicit none + + thlflux(:,:) = 0. + qtflux(:,:) = 0. + end subroutine lsm ! @@ -71,8 +77,8 @@ end subroutine lsm ! subroutine initlsm use modglobal, only : ifnamopt, fname_options, checknamelisterror - use modmpi, only : myid, comm3d, mpierr, mpi_logical - use modsurfdata, only : isurf + use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real + use modsurfdata, only : isurf, zsoil implicit none integer :: ierr @@ -80,9 +86,9 @@ subroutine initlsm ! Read namelist namelist /NAMLSM/ & - sw_lsm, sw_homogeneous, z_soil, z_size_soil + sw_lsm, sw_homogeneous, zsoil, z_size_soil - allocate(z_soil(kmax_soil)) + allocate(zsoil(kmax_soil)) if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) @@ -96,6 +102,9 @@ subroutine initlsm call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(z_soil, kmax_soil, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z_size_soil, 1, my_real, 0, comm3d, mpierr) + if (sw_lsm) then ! Create/calculate soil grid properties call create_soil_grid @@ -113,7 +122,6 @@ subroutine initlsm ! Read the soil parameter table call read_soil_table - end if end subroutine initlsm @@ -171,7 +179,8 @@ end subroutine create_soil_grid ! subroutine allocate_fields use modglobal, only : i2, j2 - use modsurfdata, only : tsoil, phiw + use modsurfdata, only : & + tsoil, phiw implicit none ! Allocate soil variables @@ -180,10 +189,10 @@ subroutine allocate_fields allocate(phiw (i2, j2, kmax_soil)) ! Allocate the tiled variables - call allocate_tile(low_veg) - call allocate_tile(high_veg) - call allocate_tile(bare_soil) - call allocate_tile(wet_skin) + call allocate_tile(tile_lv) + call allocate_tile(tile_hv) + call allocate_tile(tile_bs) + call allocate_tile(tile_ws) end subroutine allocate_fields @@ -196,35 +205,35 @@ subroutine allocate_tile(tile) type(lsm_tile), intent(inout) :: tile ! Static properties: - allocate(tile%z0m(i2, j2)) - allocate(tile%z0h(i2, j2)) + allocate(tile % z0m(i2, j2)) + allocate(tile % z0h(i2, j2)) ! Dynamic tile fraction: - allocate(tile%frac(i2, j2)) + allocate(tile % frac(i2, j2)) ! Monin-obukhov / surface layer: - allocate(tile%obuk(i2, j2)) - allocate(tile%ustar(i2, j2)) - allocate(tile%ra(i2, j2)) + allocate(tile % obuk(i2, j2)) + allocate(tile % ustar(i2, j2)) + allocate(tile % ra(i2, j2)) ! Conductivity skin layer: - allocate(tile%lambda_stable(i2, j2)) - allocate(tile%lambda_unstable(i2, j2)) + allocate(tile % lambda_stable(i2, j2)) + allocate(tile % lambda_unstable(i2, j2)) ! Surface fluxes: - allocate(tile%H(i2, j2)) - allocate(tile%LE(i2, j2)) - allocate(tile%G(i2, j2)) - allocate(tile%wthl(i2, j2)) - allocate(tile%wqt(i2, j2)) + allocate(tile % H(i2, j2)) + allocate(tile % LE(i2, j2)) + allocate(tile % G(i2, j2)) + allocate(tile % wthl(i2, j2)) + allocate(tile % wqt(i2, j2)) ! Surface temperature and humidity: - allocate(tile%tskin(i2, j2)) - allocate(tile%qskin(i2, j2)) + allocate(tile % tskin(i2, j2)) + allocate(tile % qskin(i2, j2)) ! Vegetation properties: - allocate(tile%lai(i2, j2)) - allocate(tile%rs_min(i2, j2)) + allocate(tile % lai(i2, j2)) + allocate(tile % rs_min(i2, j2)) end subroutine allocate_tile @@ -294,27 +303,27 @@ subroutine init_homogeneous call MPI_BCAST(theta_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) ! Set values - low_veg %z0m(:,:) = z0m_low - high_veg %z0m(:,:) = z0m_high - bare_soil%z0m(:,:) = z0m_bare + tile_lv % z0m(:,:) = z0m_low + tile_hv % z0m(:,:) = z0m_high + tile_bs % z0m(:,:) = z0m_bare - low_veg %z0h(:,:) = z0h_low - high_veg %z0h(:,:) = z0h_high - bare_soil%z0h(:,:) = z0h_bare + tile_lv % z0h(:,:) = z0h_low + tile_hv % z0h(:,:) = z0h_high + tile_bs % z0h(:,:) = z0h_bare - low_veg %lambda_stable(:,:) = lambda_s_low - high_veg %lambda_stable(:,:) = lambda_s_high - bare_soil%lambda_stable(:,:) = lambda_s_bare + tile_lv % lambda_stable(:,:) = lambda_s_low + tile_hv % lambda_stable(:,:) = lambda_s_high + tile_bs % lambda_stable(:,:) = lambda_s_bare - low_veg %lambda_unstable(:,:) = lambda_us_low - high_veg %lambda_unstable(:,:) = lambda_us_high - bare_soil%lambda_unstable(:,:) = lambda_us_bare + tile_lv % lambda_unstable(:,:) = lambda_us_low + tile_hv % lambda_unstable(:,:) = lambda_us_high + tile_bs % lambda_unstable(:,:) = lambda_us_bare - low_veg %lai(:,:) = lai_low - high_veg%lai(:,:) = lai_high + tile_lv % lai(:,:) = lai_low + tile_hv % lai(:,:) = lai_high - low_veg %rs_min(:,:) = rs_min_low - high_veg%rs_min(:,:) = rs_min_high + tile_lv % rs_min(:,:) = rs_min_low + tile_hv % rs_min(:,:) = rs_min_high do k=1, kmax_soil tsoil(:,:,k) = t_soil_p(k) diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 62ec51dd..5a9a9393 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -350,12 +350,12 @@ subroutine checkinitvalues !isurf if (myid == 0) then select case (isurf) - case(-1) case(1) case(2,10) case(3:4) if (wtsurf <-1e10) stop 'wtsurf not set' if (wqsurf <-1e10) stop 'wqsurf not set' + case(11) case default stop 'isurf out of range/not set' end select @@ -588,7 +588,7 @@ subroutine readinitfiles Wlm = Wl case(2) tskin = thls - case(3,4) + case(3,4,11) thls = thlprof(1) qts = qtprof(1) tskin = thls diff --git a/src/modsurface.f90 b/src/modsurface.f90 index 99d83aee..a4ced76c 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -538,7 +538,7 @@ subroutine initsurface enddo enddo end select - else + else ! not lhetero: if((z0mav == -1 .and. z0hav == -1) .and. (z0 .ne. -1)) then z0mav = z0 z0hav = z0 @@ -725,7 +725,8 @@ subroutine surface patchx = 0 patchy = 0 - if (isurf==-1) then + if (isurf==11) then + ! New LSM, handled by modlsm return end if diff --git a/src/modsurfdata.f90 b/src/modsurfdata.f90 index 277eda05..37b2d318 100644 --- a/src/modsurfdata.f90 +++ b/src/modsurfdata.f90 @@ -26,8 +26,6 @@ ! Copyright 1993-2009 Delft University of Technology, Wageningen University, Utrecht University, KNMI ! - - module modsurfdata ! implicit none @@ -38,13 +36,13 @@ module modsurfdata ! Soil properties ! Domain-uniform properties - integer, parameter :: ksoilmax = 4 !< Number of soil layers [-] + integer, parameter :: ksoilmax = 4 !< Number of soil layers [-] real :: lambdasat !< heat conductivity saturated soil [W/m/K] real :: Ke !< Kersten number [-] real, allocatable :: zsoil (:) !< Height of bottom soil layer from surface [m] - real, allocatable :: zsoilc (:) !< Height of center soil layer from surface [m] + real, allocatable :: zsoilc (:) !< Height of center soil layer from surface [m] real, allocatable :: dzsoil (:) !< Depth of soil layer [m] real, allocatable :: dzsoilh(:) !< Depth of soil layer between center of layers [m] From 6b5d8bea7a80934f920816d57c7b7e8d66e3474d Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 30 Jun 2020 11:08:45 +0200 Subject: [PATCH 06/31] Modified modlsmstat to work with the new LSM --- src/modgenstat.f90 | 3 ++ src/modlsm.f90 | 12 ++++-- src/modlsmstat.f90 | 101 +++++++++++++++++++++++++++++++-------------- src/modstat_nc.f90 | 11 +++++ 4 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/modgenstat.f90 b/src/modgenstat.f90 index 4a21bf67..ebaa970e 100644 --- a/src/modgenstat.f90 +++ b/src/modgenstat.f90 @@ -171,6 +171,7 @@ subroutine initgenstat cexpnr,dtav_glob,timeav_glob,dt_lim,btime,tres,lwarmstart,checknamelisterror use modstat_nc, only : lnetcdf, open_nc,define_nc,ncinfo,nctiminfo,writestat_dims_nc use modsurfdata, only : isurf,ksoilmax + use modlsm, only : kmax_soil implicit none @@ -419,6 +420,8 @@ subroutine initgenstat if (isurf==1) then call open_nc(fname, ncid,nrec,n3=kmax,ns=ksoilmax) + else if (isurf==11) then + call open_nc(fname, ncid,nrec,n3=kmax,ns=kmax_soil) else call open_nc(fname, ncid,nrec,n3=kmax) endif diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 579ebd18..b8e8fb95 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -78,7 +78,7 @@ end subroutine lsm subroutine initlsm use modglobal, only : ifnamopt, fname_options, checknamelisterror use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real - use modsurfdata, only : isurf, zsoil + use modsurfdata, only : isurf implicit none integer :: ierr @@ -86,9 +86,9 @@ subroutine initlsm ! Read namelist namelist /NAMLSM/ & - sw_lsm, sw_homogeneous, zsoil, z_size_soil + sw_lsm, sw_homogeneous, z_soil, z_size_soil - allocate(zsoil(kmax_soil)) + allocate(z_soil(kmax_soil)) if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) @@ -180,7 +180,7 @@ end subroutine create_soil_grid subroutine allocate_fields use modglobal, only : i2, j2 use modsurfdata, only : & - tsoil, phiw + tsoil, phiw, lambda, lambdas, gammas implicit none ! Allocate soil variables @@ -188,6 +188,10 @@ subroutine allocate_fields allocate(tsoil (i2, j2, kmax_soil)) allocate(phiw (i2, j2, kmax_soil)) + allocate(lambda (i2, j2, kmax_soil)) + allocate(lambdas (i2, j2, kmax_soil)) + allocate(gammas (i2, j2, kmax_soil)) + ! Allocate the tiled variables call allocate_tile(tile_lv) call allocate_tile(tile_hv) diff --git a/src/modlsmstat.f90 b/src/modlsmstat.f90 index 3737e4d2..89a32048 100644 --- a/src/modlsmstat.f90 +++ b/src/modlsmstat.f90 @@ -67,15 +67,16 @@ subroutine initlsmstat use modstat_nc, only : lnetcdf,define_nc,ncinfo use modgenstat, only : idtav_prof=>idtav, itimeav_prof=>itimeav,ncid_prof=>ncid use modsurfdata,only : ksoilmax,isurf + use modlsm, only : kmax_soil implicit none - integer ierr + integer ierr, kdim_soil namelist/NAMLSMSTAT/ & dtav,timeav,lstat dtav=dtav_glob;timeav=timeav_glob lstat = .false. - if (isurf /=1) return + if (.not. (isurf ==1 .or. isurf==11)) return if(myid==0)then open(ifnamopt,file=fname_options,status='old',iostat=ierr) read (ifnamopt,NAMLSMSTAT,iostat=ierr) @@ -105,17 +106,23 @@ subroutine initlsmstat stop 'dtav should be a integer multiple of dtmax' end if - allocate(phiwav(ksoilmax)) - allocate(tsoilav(ksoilmax)) - allocate(lambdaav(ksoilmax)) - allocate(lambdasav(ksoilmax)) - allocate(gammasav(ksoilmax)) + if (isurf == 1) then + kdim_soil = ksoilmax + else if (isurf == 11) then + kdim_soil = kmax_soil + end if + + allocate(phiwav(kdim_soil)) + allocate(tsoilav(kdim_soil)) + allocate(lambdaav(kdim_soil)) + allocate(lambdasav(kdim_soil)) + allocate(gammasav(kdim_soil)) - allocate(phiwmn(ksoilmax)) - allocate(tsoilmn(ksoilmax)) - allocate(lambdamn(ksoilmax)) - allocate(lambdasmn(ksoilmax)) - allocate(gammasmn(ksoilmax)) + allocate(phiwmn(kdim_soil)) + allocate(tsoilmn(kdim_soil)) + allocate(lambdamn(kdim_soil)) + allocate(lambdasmn(kdim_soil)) + allocate(gammasmn(kdim_soil)) phiwmn = 0.0 tsoilmn = 0.0 @@ -127,6 +134,7 @@ subroutine initlsmstat open (ifoutput,file='lsmstat.'//cexpnr,status='replace') close (ifoutput) end if + if (lnetcdf) then idtav = idtav_prof itimeav = itimeav_prof @@ -174,21 +182,29 @@ subroutine do_lsmstat use modmpi, only : slabsum use modglobal, only : ijtot,i1,j1,i2,j2 - use modsurfdata, only : ksoilmax,tsoil,phiw,lambda,lambdas,gammas + use modsurfdata, only : isurf,ksoilmax,tsoil,phiw,lambda,lambdas,gammas + use modlsm, only : kmax_soil implicit none + integer kdim_soil + if (isurf == 1) then + kdim_soil = ksoilmax + else if (isurf == 11) then + kdim_soil = kmax_soil + end if + tsoilav = 0. phiwav = 0. lambdaav = 0. lambdasav = 0. gammasav = 0. - call slabsum(tsoilav ,1,ksoilmax,tsoil ,1,i2,1,j2,1,ksoilmax,2,i1,2,j1,1,ksoilmax) - call slabsum(phiwav ,1,ksoilmax,phiw ,1,i2,1,j2,1,ksoilmax,2,i1,2,j1,1,ksoilmax) - call slabsum(lambdaav ,1,ksoilmax,lambda ,1,i2,1,j2,1,ksoilmax,2,i1,2,j1,1,ksoilmax) - call slabsum(lambdasav ,1,ksoilmax,lambdas ,1,i2,1,j2,1,ksoilmax,2,i1,2,j1,1,ksoilmax) - call slabsum(gammasav ,1,ksoilmax,gammas ,1,i2,1,j2,1,ksoilmax,2,i1,2,j1,1,ksoilmax) + call slabsum(tsoilav ,1,kdim_soil,tsoil ,1,i2,1,j2,1,kdim_soil,2,i1,2,j1,1,kdim_soil) + call slabsum(phiwav ,1,kdim_soil,phiw ,1,i2,1,j2,1,kdim_soil,2,i1,2,j1,1,kdim_soil) + call slabsum(lambdaav ,1,kdim_soil,lambda ,1,i2,1,j2,1,kdim_soil,2,i1,2,j1,1,kdim_soil) + call slabsum(lambdasav ,1,kdim_soil,lambdas ,1,i2,1,j2,1,kdim_soil,2,i1,2,j1,1,kdim_soil) + call slabsum(gammasav ,1,kdim_soil,gammas ,1,i2,1,j2,1,kdim_soil,2,i1,2,j1,1,kdim_soil) ! ADD SLAB AVERAGES TO TIME MEAN phiwmn = phiwmn + phiwav/ijtot @@ -206,11 +222,20 @@ subroutine writelsmstat use modglobal, only : cexpnr,ifoutput,rtimee use modstat_nc, only: lnetcdf, writestat_nc use modgenstat, only: ncid_prof=>ncid,nrec_prof=>nrec - use modsurfdata,only : ksoilmax,zsoilc + use modsurfdata,only : isurf, ksoilmax, zsoilc + use modlsm, only : kmax_soil, z_soil + implicit none - real,dimension(ksoilmax,nvar) :: vars - integer nsecs, nhrs, nminut,k + real, allocatable :: vars(:,:) + integer nsecs, nhrs, nminut,k, kdim_soil + + if (isurf == 1) then + kdim_soil = ksoilmax + else if (isurf == 11) then + kdim_soil = kmax_soil + end if + allocate(vars(kdim_soil, nvar)) nsecs = nint(rtimee) nhrs = int(nsecs/3600) @@ -237,15 +262,27 @@ subroutine writelsmstat '#--------------------------------------------------------------------------' & ,'#LEV HEIGHT T_SOIL SOIL MOIST HEAT COND. MOIST DIFF. MOIST COND.' & ,'# (M) (K) (M^3/M^3) (W/M/K) (M^2/S) (M/S) ' - do k=1,ksoilmax - write(ifoutput,'(I3,F8.4,F10.4,4E13.4)') & - k, zsoilc(k),& - tsoilmn(k),& - phiwmn(k),& - lambdamn(k),& - lambdasmn(k),& - gammasmn(k) - end do + if (isurf == 1) then + do k=1,kdim_soil + write(ifoutput,'(I3,F8.4,F10.4,4E13.4)') & + k, zsoilc(k),& + tsoilmn(k),& + phiwmn(k),& + lambdamn(k),& + lambdasmn(k),& + gammasmn(k) + end do + else if (isurf == 11) then + do k=1,kdim_soil + write(ifoutput,'(I3,F8.4,F10.4,4E13.4)') & + k, z_soil(k),& + tsoilmn(k),& + phiwmn(k),& + lambdamn(k),& + lambdasmn(k),& + gammasmn(k) + end do + end if close (ifoutput) if (lnetcdf) then vars(:, 1) = tsoilmn @@ -253,7 +290,7 @@ subroutine writelsmstat vars(:, 3) = lambdamn vars(:, 4) = lambdasmn vars(:, 5) = gammasmn - call writestat_nc(ncid_prof,nvar,ncname,vars(1:ksoilmax,:),nrec_prof,ksoilmax) + call writestat_nc(ncid_prof,nvar,ncname,vars(1:kdim_soil,:),nrec_prof,kdim_soil) end if end if ! end if(myid==0) @@ -263,6 +300,8 @@ subroutine writelsmstat lambdasmn = 0.0 gammasmn = 0.0 + deallocate(vars) + end subroutine writelsmstat diff --git a/src/modstat_nc.f90 b/src/modstat_nc.f90 index 3cc885d2..6a90a8e4 100644 --- a/src/modstat_nc.f90 +++ b/src/modstat_nc.f90 @@ -328,6 +328,7 @@ end subroutine exitstat_nc subroutine writestat_dims_nc(ncid, ncoarse) use modglobal, only : dx,dy,zf,zh,jmax,imax use modsurfdata, only : zsoilc,isurf + use modlsm, only : z_soil use modmpi, only : myidx,myidy implicit none integer, intent(in) :: ncid @@ -360,10 +361,15 @@ subroutine writestat_dims_nc(ncid, ncoarse) iret = nf90_inq_varid(ncid, 'zm', VarID) if (iret==0) iret=nf90_inquire_dimension(ncid, zmID, len=length) if (iret==0) iret = nf90_put_var(ncid, varID, zh(1:length),(/1/)) + if (isurf==1) then iret = nf90_inq_varid(ncid, 'zts', VarID) if (iret==0) iret = nf90_inquire_dimension(ncid, ztsID, len=length) if (iret==0) iret = nf90_put_var(ncid, varID, zsoilc(1:length),(/1/)) + else if (isurf==11) then + iret = nf90_inq_varid(ncid, 'zts', VarID) + if (iret==0) iret = nf90_inquire_dimension(ncid, ztsID, len=length) + if (iret==0) iret = nf90_put_var(ncid, varID, z_soil(1:length),(/1/)) end if end subroutine writestat_dims_nc @@ -371,6 +377,7 @@ end subroutine writestat_dims_nc subroutine writestat_dims_q_nc(ncid,k1,k2) use modglobal, only : dx,dy,zf,zh,jmax,imax use modsurfdata, only : zsoilc,isurf + use modlsm, only : z_soil use modmpi, only : myidx,myidy implicit none integer, intent(in) :: ncid,k1,k2 @@ -399,6 +406,10 @@ subroutine writestat_dims_q_nc(ncid,k1,k2) iret = nf90_inq_varid(ncid, 'zts', VarID) if (iret==0) iret = nf90_inquire_dimension(ncid, ztsID, len=length) if (iret==0) iret = nf90_put_var(ncid, varID, zsoilc(1:length),(/1/)) + else if (isurf==11) then + iret = nf90_inq_varid(ncid, 'zts', VarID) + if (iret==0) iret = nf90_inquire_dimension(ncid, ztsID, len=length) + if (iret==0) iret = nf90_put_var(ncid, varID, z_soil(1:length),(/1/)) end if iret = nf90_inq_varid(ncid, 'zq', VarID) From 32614223236160ad6ead3250552aec171ef6bc43 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 30 Jun 2020 11:38:47 +0200 Subject: [PATCH 07/31] Added minimal set of time series statistics for the new LSM --- src/modlsm.f90 | 16 ++++++++++++---- src/modtimestat.f90 | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index b8e8fb95..c5fed89c 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -180,7 +180,8 @@ end subroutine create_soil_grid subroutine allocate_fields use modglobal, only : i2, j2 use modsurfdata, only : & - tsoil, phiw, lambda, lambdas, gammas + tsoil, phiw, lambda, lambdas, gammas, & + H, LE, G0, Qnet implicit none ! Allocate soil variables @@ -188,9 +189,16 @@ subroutine allocate_fields allocate(tsoil (i2, j2, kmax_soil)) allocate(phiw (i2, j2, kmax_soil)) - allocate(lambda (i2, j2, kmax_soil)) - allocate(lambdas (i2, j2, kmax_soil)) - allocate(gammas (i2, j2, kmax_soil)) + ! Soil conductivity and diffusivity + allocate(lambda (i2, j2, kmax_soil)) + allocate(lambdas(i2, j2, kmax_soil)) + allocate(gammas (i2, j2, kmax_soil)) + + ! Tile averaged surface fluxes + allocate(Qnet (i2, j2)) + allocate(H (i2, j2)) + allocate(LE (i2, j2)) + allocate(G0 (i2, j2)) ! Allocate the tiled variables call allocate_tile(tile_lv) diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index 04347d8e..f93a6bcb 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -228,8 +228,10 @@ subroutine inittimestat endif if (lnetcdf) then - if(isurf == 1) then + if (isurf == 1) then nvar = 32 + else if (isurf == 11) then + nvar = 25 else nvar = 21 end if @@ -271,7 +273,13 @@ subroutine inittimestat call ncinfo(ncname(30,:),'Wl','Liquid water reservoir','m','time') call ncinfo(ncname(31,:),'rssoil','Soil evaporation resistance','s/m','time') call ncinfo(ncname(32,:),'rsveg','Vegitation resistance','s/m','time') + else if (isurf==11) then + call ncinfo(ncname(22,:),'Qnet','Net radiation','W/m^2','time') + call ncinfo(ncname(23,:),'H','Sensible heat flux','W/m^2','time') + call ncinfo(ncname(24,:),'LE','Latent heat flux','W/m^2','time') + call ncinfo(ncname(25,:),'G','Ground heat flux','W/m^2','time') end if + call open_nc(fname, ncid,nrec) if(nrec==0) call define_nc( ncid, NVar, ncname) end if @@ -344,7 +352,8 @@ subroutine timestat use modsurfdata,only : wtsurf, wqsurf, isurf,ustar,thlflux,qtflux,z0,oblav,qts,thls,& Qnet, H, LE, G0, rs, ra, tskin, tendskin, & cliq,rsveg,rssoil,Wl, & - lhetero, xpatches, ypatches, qts_patch, wt_patch, wq_patch, thls_patch,obl,z0mav_patch, wco2av, Anav, Respav,gcco2av + lhetero, xpatches, ypatches, qts_patch, wt_patch, wq_patch, & + thls_patch,obl,z0mav_patch, wco2av, Anav, Respav,gcco2av use modsurface, only : patchxnr,patchynr use mpi use modmpi, only : my_real,mpi_sum,mpi_max,mpi_min,comm3d,mpierr,myid @@ -757,6 +766,22 @@ subroutine timestat rssoil_patch = patchsum_1level(rssoil (2:i1, 2:j1)) * (xpatches*ypatches/ijtot) tskin_patch = patchsum_1level(tskin (2:i1, 2:j1)) * (xpatches*ypatches/ijtot) endif + + else if(isurf == 11) then + Qnetavl = sum(Qnet(2:i1,2:j1)) + Havl = sum(H(2:i1,2:j1)) + LEavl = sum(LE(2:i1,2:j1)) + G0avl = sum(G0(2:i1,2:j1)) + + call MPI_ALLREDUCE(Qnetavl, Qnetav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) + call MPI_ALLREDUCE(Havl, Hav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) + call MPI_ALLREDUCE(LEavl, LEav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) + call MPI_ALLREDUCE(G0avl, G0av, 1, MY_REAL,MPI_SUM, comm3d,mpierr) + + Qnetav = Qnetav / ijtot + Hav = Hav / ijtot + LEav = LEav / ijtot + G0av = G0av / ijtot end if ! 9.8 write the results to output file @@ -818,6 +843,7 @@ subroutine timestat gcco2av close(ifoutput) end if + if (lnetcdf) then vars( 1) = rtimee vars( 2) = cc @@ -856,6 +882,11 @@ subroutine timestat vars(30) = wlav vars(31) = rssoilav vars(32) = rsvegav + else if (isurf == 11) then + vars(22) = Qnetav + vars(23) = Hav + vars(24) = LEav + vars(25) = G0av end if call writestat_nc(ncid,nvar,ncname,vars,nrec,.true.) From 61de721e4f10d9b608776c28ca991b1a09dbf647 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 30 Jun 2020 14:39:31 +0200 Subject: [PATCH 08/31] Added calculation derived van Genuchten parameters --- src/modglobal.f90 | 8 ++++ src/modlsm.f90 | 94 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/src/modglobal.f90 b/src/modglobal.f90 index 42b6a0cf..373ba0a8 100644 --- a/src/modglobal.f90 +++ b/src/modglobal.f90 @@ -98,6 +98,14 @@ module modglobal real,parameter :: epscloud = 1.e-5 !< *limit for cloud calculation 0.01 g/kg real,parameter :: boltz = 5.67e-8 !< *Stefan-Boltzmann constant + ! Land-surface + real,parameter :: rho_solid_soil = 2700 !< Density of dry solid soil (kg m-3) + real,parameter :: rho_C_matrix = 1.6e6 !< Volumetric soil heat capacity [J m-3 K-1] + real,parameter :: rho_C_water = 4.18e6 !< Volumetric water heat capacity [J m-3 K-1] + real,parameter :: gamma_T_matrix = 3.4293695508945325 !< Heat conductivity soil [J s-1 m-1 K-1] + real,parameter :: gamma_T_water = 0.57 !< Heat conductivity water [J s-1 m-1 K-1] + + logical :: lcoriol = .true. !< switch for coriolis force logical :: lpressgrad = .true. !< switch for horizontal pressure gradient force diff --git a/src/modlsm.f90 b/src/modlsm.f90 index c5fed89c..02b926e2 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -59,7 +59,15 @@ module modlsm ! Land-surface / van Genuchten parameters from NetCDF input table. real, allocatable :: & theta_res(:), theta_wp(:), theta_fc(:), theta_sat(:), & - gamma_sat(:), vg_a(:), vg_l(:), vg_n(:) + gamma_theta_sat(:), vg_a(:), vg_l(:), vg_n(:) + ! Derived soil parameters + real, allocatable :: & + vg_m(:), & + kappa_theta_min(:), kappa_theta_max(:), & + gamma_theta_min(:), gamma_theta_max(:), & + gamma_t_dry(:), rho_C(:) + + contains @@ -67,9 +75,18 @@ subroutine lsm use modsurfdata, only : thlflux, qtflux implicit none + ! tmp... thlflux(:,:) = 0. qtflux(:,:) = 0. + ! + ! 1. Calculate soil tendencies + ! + ! 1.1 Diffusivity heat + + + + end subroutine lsm ! @@ -122,6 +139,9 @@ subroutine initlsm ! Read the soil parameter table call read_soil_table + + ! Calculate derived soil properties + call calc_soil_properties end if end subroutine initlsm @@ -132,7 +152,7 @@ end subroutine initlsm subroutine exitlsm implicit none - deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_sat, vg_a, vg_l, vg_n ) + deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_theta_sat, vg_a, vg_l, vg_n ) end subroutine exitlsm ! @@ -254,7 +274,7 @@ end subroutine allocate_tile ! subroutine init_homogeneous use modglobal, only : ifnamopt, fname_options, checknamelisterror - use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real + use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer use modsurfdata, only : tsoil, phiw implicit none @@ -267,6 +287,7 @@ subroutine init_homogeneous real :: rs_min_low, rs_min_high real, allocatable :: t_soil_p(:), theta_soil_p(:) + integer, allocatable :: soil_index_p(:) ! Read namelist namelist /NAMLSM_HOMOGENEOUS/ & @@ -276,9 +297,10 @@ subroutine init_homogeneous lambda_us_low, lambda_us_high, lambda_us_bare, & lai_low, lai_high, & rs_min_low, rs_min_high, & - t_soil_p, theta_soil_p + t_soil_p, theta_soil_p, soil_index_p allocate(t_soil_p(kmax_soil), theta_soil_p(kmax_soil)) + allocate(soil_index_p(kmax_soil)) if (myid == 0) then open(ifnamopt, file=fname_options, status='old', iostat=ierr) @@ -313,6 +335,7 @@ subroutine init_homogeneous call MPI_BCAST(t_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) call MPI_BCAST(theta_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) + call MPI_BCAST(soil_index_p, kmax_soil, mpi_integer, 0, comm3d, mpierr) ! Set values tile_lv % z0m(:,:) = z0m_low @@ -340,6 +363,7 @@ subroutine init_homogeneous do k=1, kmax_soil tsoil(:,:,k) = t_soil_p(k) phiw (:,:,k) = theta_soil_p(k) + soil_index(:,:,k) = soil_index_p(k) end do ! Cleanup! @@ -352,18 +376,19 @@ end subroutine init_homogeneous ! subroutine read_soil_table implicit none - integer :: n, ncid, dimid, varid + integer :: table_size, ncid, dimid, varid ! Open the NetCDF file and read the table size print*,'Reading "van_genuchten_parameters.nc"' call check( nf90_open('van_genuchten_parameters.nc', nf90_nowrite, ncid) ) call check( nf90_inq_dimid(ncid, 'index', dimid) ) - call check( nf90_inquire_dimension(ncid, dimid, len=n) ) + call check( nf90_inquire_dimension(ncid, dimid, len=table_size) ) ! Allocate variables allocate( & - theta_res(n), theta_wp(n), theta_fc(n), theta_sat(n), & - gamma_sat(n), vg_a(n), vg_l(n), vg_n(n) ) + theta_res(table_size), theta_wp(table_size), theta_fc(table_size), & + theta_sat(table_size), gamma_theta_sat(table_size), & + vg_a(table_size), vg_l(table_size), vg_n(table_size) ) ! Read variables call check( nf90_inq_varid(ncid, 'theta_res', varid) ) @@ -379,7 +404,7 @@ subroutine read_soil_table call check( nf90_get_var(ncid, varid, theta_sat) ) call check( nf90_inq_varid(ncid, 'gamma_sat', varid) ) - call check( nf90_get_var(ncid, varid, gamma_sat) ) + call check( nf90_get_var(ncid, varid, gamma_theta_sat) ) call check( nf90_inq_varid(ncid, 'alpha', varid) ) call check( nf90_get_var(ncid, varid, vg_a) ) @@ -394,6 +419,52 @@ subroutine read_soil_table end subroutine read_soil_table +! +! Calculate derived (tabulated) soil properties +! +subroutine calc_soil_properties + use modglobal, only : rho_solid_soil, rho_c_matrix, rho_c_water, eps1 + implicit none + + integer :: i, table_size + real :: theta_norm_min, theta_norm_max, rho_dry + + table_size = size(vg_n) + allocate(vg_m(table_size)) + allocate(kappa_theta_min(table_size)) + allocate(kappa_theta_max(table_size)) + allocate(gamma_theta_min(table_size)) + allocate(gamma_theta_max(table_size)) + allocate(gamma_t_dry(table_size)) + allocate(rho_C(table_size)) + + do i=1, table_size + ! van Genuchten parameter `m` + vg_m(i) = (1. - (1. / vg_n(i))) + + ! Min/max values diffusivity soil moisture + theta_norm_min = (1.001 * theta_res(i) - theta_res(i)) / (theta_sat(i) - theta_res(i)) + eps1 + theta_norm_max = (0.999 * theta_sat(i) - theta_res(i)) / (theta_sat(i) - theta_res(i)) + + kappa_theta_min(i) = calc_diffusivity_vg( & + theta_norm_min, vg_a(i), vg_l(i), vg_m(i), gamma_theta_sat(i), & + theta_sat(i), theta_res(i)) + kappa_theta_max(i) = calc_diffusivity_vg( & + theta_norm_max, vg_a(i), vg_l(i), vg_m(i), gamma_theta_sat(i), & + theta_sat(i), theta_res(i)) + + ! Min/max values conductivity soil moisture + gamma_theta_min(i) = 0. + gamma_theta_max(i) = gamma_theta_sat(i) + + ! Conductivity temperature + rho_dry = (1. - theta_sat(i)) * rho_solid_soil ! Density of soil (kg m-3) + gamma_t_dry(i) = (0.135 * rho_dry + 64.7) / (rho_solid_soil - 0.947 * rho_dry) + rho_C(i) = (1. - theta_sat(i)) * rho_C_matrix + theta_fc(i) * rho_C_water + end do + +end subroutine calc_soil_properties + ! ! Convert soil hydraulic head to soil water content, using van Genuchten parameterisation. ! @@ -419,14 +490,14 @@ end function theta_to_psi ! ! Calculate hydraulic diffusivity using van Genuchten parameterisation. ! -pure function calc_diffusivity_vg( & +function calc_diffusivity_vg( & theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res) result(res) implicit none real, intent(in) :: theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res real :: res res = (1.-vg_m)*lambda_sat / (vg_a * vg_m * (theta_sat-theta_res)) * theta_norm**(vg_l-(1./vg_m)) * & - ( (1.-theta_norm**(1./vg_m))**(-vg_m) + (1.-theta_norm**(1/vg_m))**vg_m - 2. ) + ( (1.-theta_norm**(1./vg_m))**(-vg_m) + (1.-theta_norm**(1./vg_m))**vg_m - 2. ) end function calc_diffusivity_vg ! @@ -438,6 +509,7 @@ pure function calc_conductivity_vg(theta_norm, vg_l, vg_m, lambda_sat) result(re real :: res res = lambda_sat * theta_norm**vg_l * ( 1.- (1.-theta_norm**(1./vg_m))**vg_m )**2. + end function calc_conductivity_vg ! From f215e669ca592ad25874cc90a91a0c6546bb4664 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 30 Jun 2020 16:51:10 +0200 Subject: [PATCH 09/31] Added calculation of soil thermal properties, and (explicit) temperature diffusion solver --- src/modlsm.f90 | 124 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 02b926e2..26986c6b 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -72,22 +72,114 @@ module modlsm contains subroutine lsm - use modsurfdata, only : thlflux, qtflux + use modsurfdata, only : thlflux, qtflux, G0 implicit none ! tmp... thlflux(:,:) = 0. qtflux(:,:) = 0. + G0(:,:) = 0. ! ! 1. Calculate soil tendencies ! - ! 1.1 Diffusivity heat + ! 1.1 Calc diffusivity heat, interpolated to half levels: + call calc_thermal_properties + ! 1.2 Solve diffusion equation + call diffusion_t +end subroutine lsm +! +! Calculate temperature diffusivity soil at full and half levels +! +subroutine calc_thermal_properties + use modglobal, only : i1, j1, gamma_t_matrix, gamma_t_water + use modsurfdata, only : lambda, lambdah, phiw + implicit none + + integer :: i, j, k, si + real :: lambda_t_sat, kersten, gamma_t + + ! Calculate diffusivity heat + do k=1,kmax_soil + do j=2,j1 + do i=2,i1 + si = soil_index(i,j,k) + ! Heat conductivity at saturation (from IFS code..) + lambda_T_sat = gamma_T_matrix**(1.-theta_sat(si)) & + * gamma_T_water**phiw(i,j,k) & + * 2.2**(theta_sat(si) - phiw(i,j,k)) + ! Kersten number for fine soils [IFS eq 8.64] (-) + kersten = log10(max(0.1, phiw(i,j,k) / theta_sat(si))) + 1. + ! Heat conductivity soil [IFS eq 8.62] (W m-1 K-1) + gamma_t = kersten * (lambda_T_sat - gamma_t_dry(si)) + gamma_t_dry(si) + ! Heat diffusivity (m2 s-1) + lambda(i,j,k) = gamma_t / rho_C(si) + end do + end do + end do + + ! Interpolate to half levels + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + lambdah(i,j,k) = 0.5*(lambda(i,j,k-1) + lambda(i,j,k)) + end do + end do + end do + +end subroutine calc_thermal_properties + +! +! Solve diffusion equation (explicit) for soil temperature. +! Top flux = G0/rho_C, bottom flux = zero. +! +subroutine diffusion_t + use modglobal, only : rk3step, rdt, i1, j1 + use modsurfdata, only : tsoil, tsoilm, lambdah, G0 + + implicit none + integer :: i, j, k, si + real :: tend, rk3coef, flux_top + + rk3coef = rdt / (4. - dble(rk3step)) + if(rk3step == 1) tsoilm(:,:,:) = tsoil(:,:,:) + + ! Top soil layer + k = kmax_soil + do j=2,j1 + do i=2,i1 + si = soil_index(i,j,k) + flux_top = G0(i,j) / rho_C(si) + tend = (-flux_top - (lambdah(i,j,k) * (tsoil(i,j,k) - tsoil(i,j,k-1)) * dzhi_soil(k)))*dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend + end do + end do + + ! Bottom soil layer + k = 1 + do j=2,j1 + do i=2,i1 + tend = ((lambdah(i,j,k+1) * (tsoil(i,j,k+1) - tsoil(i,j,k)) * dzhi_soil(k+1)))*dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend + end do + end do + + ! Interior + do k=2,kmax_soil-1 + do j=2,j1 + do i=2,i1 + tend = ((lambdah(i,j,k+1) * (tsoil(i,j,k+1) - tsoil(i,j,k )) * dzhi_soil(k+1)) & + - (lambdah(i,j,k ) * (tsoil(i,j,k ) - tsoil(i,j,k-1)) * dzhi_soil(k ))) * dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend + end do + end do + end do + +end subroutine diffusion_t -end subroutine lsm ! ! Initialise the land-surface model @@ -200,19 +292,32 @@ end subroutine create_soil_grid subroutine allocate_fields use modglobal, only : i2, j2 use modsurfdata, only : & - tsoil, phiw, lambda, lambdas, gammas, & + tsoil, tsoilm, phiw, phiwm, & + lambda, lambdah, lambdas, lambdash, gammas, gammash, & H, LE, G0, Qnet implicit none ! Allocate soil variables allocate(soil_index(i2, j2, kmax_soil)) + allocate(tsoil (i2, j2, kmax_soil)) + allocate(tsoilm (i2, j2, kmax_soil)) + allocate(phiw (i2, j2, kmax_soil)) + allocate(phiwm (i2, j2, kmax_soil)) - ! Soil conductivity and diffusivity - allocate(lambda (i2, j2, kmax_soil)) - allocate(lambdas(i2, j2, kmax_soil)) + ! NOTE: names differ from what is described in modsurfdata! + ! Diffusivity temperature: + allocate(lambda (i2, j2, kmax_soil )) + allocate(lambdah(i2, j2, kmax_soil+1)) + + ! Diffusivity theta: + allocate(lambdas (i2, j2, kmax_soil)) + allocate(lambdash(i2, j2, kmax_soil+1)) + + ! Conductivity theta: allocate(gammas (i2, j2, kmax_soil)) + allocate(gammash(i2, j2, kmax_soil+1)) ! Tile averaged surface fluxes allocate(Qnet (i2, j2)) @@ -275,7 +380,7 @@ end subroutine allocate_tile subroutine init_homogeneous use modglobal, only : ifnamopt, fname_options, checknamelisterror use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer - use modsurfdata, only : tsoil, phiw + use modsurfdata, only : tsoil, tsoilm, phiw, phiwm implicit none integer :: ierr, k @@ -366,6 +471,9 @@ subroutine init_homogeneous soil_index(:,:,k) = soil_index_p(k) end do + tsoilm(:,:,:) = tsoil(:,:,:) + phiwm (:,:,:) = phiw (:,:,:) + ! Cleanup! deallocate(t_soil_p, theta_soil_p) From 2b3e8262e99246170484294cb421381e795bf47b Mon Sep 17 00:00:00 2001 From: julietbravo Date: Wed, 1 Jul 2020 10:00:08 +0200 Subject: [PATCH 10/31] Added calculation hydraulic properties (van Genuchten) and diffusion solver for soil moisture --- src/modlsm.f90 | 181 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 164 insertions(+), 17 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 26986c6b..f10a1c13 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -24,6 +24,7 @@ module modlsm public :: initlsm, lsm, exitlsm logical :: sw_lsm ! On/off switch LSM + logical :: sw_free_drainage ! Free drainage bottom BC for soil moisture ! Soil grid integer :: kmax_soil @@ -34,6 +35,10 @@ module modlsm ! Soil properties integer, allocatable :: soil_index(:,:,:) + real, allocatable :: phiw_source(:,:,:) + + ! Precipitation interception et al. + real, allocatable :: throughfall(:,:) ! Data structure for sub-grid tiles type lsm_tile @@ -63,7 +68,7 @@ module modlsm ! Derived soil parameters real, allocatable :: & vg_m(:), & - kappa_theta_min(:), kappa_theta_max(:), & + lambda_theta_min(:), lambda_theta_max(:), & gamma_theta_min(:), gamma_theta_max(:), & gamma_t_dry(:), rho_C(:) @@ -81,13 +86,29 @@ subroutine lsm G0(:,:) = 0. ! - ! 1. Calculate soil tendencies + ! 2. Calculate soil tendencies ! - ! 1.1 Calc diffusivity heat, interpolated to half levels: + ! Calc diffusivity heat call calc_thermal_properties - ! 1.2 Solve diffusion equation + ! Solve diffusion equation call diffusion_t + ! Calc diffusivity and conductivity soil moisture + call calc_hydraulic_properties + + ! TMP TMP + phiw_source(:,:,:) = 0. + throughfall(:,:) = 0. + tile_bs%frac(:,:) = 0. + tile_bs%LE(:,:) = 0. + + ! Solve diffusion equation + call diffusion_theta + + + + + end subroutine lsm @@ -107,14 +128,18 @@ subroutine calc_thermal_properties do j=2,j1 do i=2,i1 si = soil_index(i,j,k) + ! Heat conductivity at saturation (from IFS code..) lambda_T_sat = gamma_T_matrix**(1.-theta_sat(si)) & * gamma_T_water**phiw(i,j,k) & * 2.2**(theta_sat(si) - phiw(i,j,k)) + ! Kersten number for fine soils [IFS eq 8.64] (-) kersten = log10(max(0.1, phiw(i,j,k) / theta_sat(si))) + 1. + ! Heat conductivity soil [IFS eq 8.62] (W m-1 K-1) gamma_t = kersten * (lambda_T_sat - gamma_t_dry(si)) + gamma_t_dry(si) + ! Heat diffusivity (m2 s-1) lambda(i,j,k) = gamma_t / rho_C(si) end do @@ -132,6 +157,65 @@ subroutine calc_thermal_properties end subroutine calc_thermal_properties +! +! Calculate soil moisture diffusivity and conductivity, +! using van Genuchten parameterisation. +! +subroutine calc_hydraulic_properties + use modglobal, only : i1, j1 + use modsurfdata, only : phiw, lambdas, lambdash, gammas, gammash + implicit none + + integer :: i, j, k, si + real :: theta_lim, theta_norm + + ! Calculate diffusivity heat + do k=1,kmax_soil + do j=2,j1 + do i=2,i1 + si = soil_index(i,j,k) + + ! Limit soil moisture just above the residual soil moisture content + theta_lim = max(phiw(i,j,k), 1.001*theta_res(si)) + + ! Dimensionless soil water content + theta_norm = (theta_lim - theta_res(si)) / (theta_sat(si) - theta_res(si)) + + ! Calculate & limit the diffusivity + lambdas(i,j,k) = calc_diffusivity_vg( & + theta_norm, vg_a(si), vg_l(si), vg_m(si), & + gamma_theta_sat(si), theta_sat(si), theta_res(si)) + lambdas(i,j,k) = max(min(lambdas(i,j,k), lambda_theta_max(si)), lambda_theta_min(si)) + + ! Calculate & limit the conductivity + gammas(i,j,k) = calc_conductivity_vg( & + theta_norm, vg_l(si), vg_m(si), gamma_theta_sat(si)) + gammas(i,j,k) = max(min(lambdas(i,j,k), gamma_theta_max(si)), gamma_theta_min(si)) + end do + end do + end do + + ! Interpolate to half levels + ! NOTE: this is the very conservative method used by IFS, + ! using the maximum value of two grid points at the interface. + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + lambdash(i,j,k) = max(lambdas(i,j,k-1), lambdas(i,j,k)) + gammash(i,j,k) = max(gammas (i,j,k-1), gammas (i,j,k)) + end do + end do + end do + + ! Optionally, set free drainage bottom BC + if (sw_free_drainage) then + gammash(:,:,1) = gammash(:,:,2) + else + gammash(:,:,1) = 0. + end if + +end subroutine calc_hydraulic_properties + ! ! Solve diffusion equation (explicit) for soil temperature. ! Top flux = G0/rho_C, bottom flux = zero. @@ -154,6 +238,7 @@ subroutine diffusion_t si = soil_index(i,j,k) flux_top = G0(i,j) / rho_C(si) tend = (-flux_top - (lambdah(i,j,k) * (tsoil(i,j,k) - tsoil(i,j,k-1)) * dzhi_soil(k)))*dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend end do end do @@ -163,6 +248,7 @@ subroutine diffusion_t do j=2,j1 do i=2,i1 tend = ((lambdah(i,j,k+1) * (tsoil(i,j,k+1) - tsoil(i,j,k)) * dzhi_soil(k+1)))*dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend end do end do @@ -173,6 +259,7 @@ subroutine diffusion_t do i=2,i1 tend = ((lambdah(i,j,k+1) * (tsoil(i,j,k+1) - tsoil(i,j,k )) * dzhi_soil(k+1)) & - (lambdah(i,j,k ) * (tsoil(i,j,k ) - tsoil(i,j,k-1)) * dzhi_soil(k ))) * dzi_soil(k) + tsoil(i,j,k) = tsoilm(i,j,k) + rk3coef * tend end do end do @@ -180,6 +267,62 @@ subroutine diffusion_t end subroutine diffusion_t +! +! Solve diffusion equation (explicit) for soil moisture, +! including a source term for root water extraction. +! +subroutine diffusion_theta + use modglobal, only : rk3step, rdt, i1, j1, rhow, rlv + use modsurfdata, only : phiw, phiwm, lambdash, gammash + + implicit none + integer :: i, j, k, si + real :: tend, rk3coef, flux_top, fac + + rk3coef = rdt / (4. - dble(rk3step)) + if(rk3step == 1) phiwm(:,:,:) = phiw(:,:,:) + + fac = 1./(rhow * rlv) + + ! Top soil layer + k = kmax_soil + do j=2,j1 + do i=2,i1 + flux_top = tile_bs%frac(i,j) * tile_bs%LE(i,j) * fac + throughfall(i,j) + tend = (-flux_top - (lambdash(i,j,k) * (phiw(i,j,k) - phiw(i,j,k-1)) * dzhi_soil(k)))*dzi_soil(k) & + - gammash(i,j,k) * dzi_soil(k) + phiw_source(i,j,k) + + phiw(i,j,k) = phiwm(i,j,k) + rk3coef * tend + end do + end do + + ! Bottom soil layer + k = 1 + do j=2,j1 + do i=2,i1 + tend = ((lambdash(i,j,k+1) * (phiw(i,j,k+1) - phiw(i,j,k)) * dzhi_soil(k+1)))*dzi_soil(k) & + + (gammash(i,j,k+1) - gammash(i,j,k)) * dzi_soil(k) + phiw_source(i,j,k) + + phiw(i,j,k) = phiwm(i,j,k) + rk3coef * tend + end do + end do + + ! Interior + do k=2,kmax_soil-1 + do j=2,j1 + do i=2,i1 + tend = ((lambdash(i,j,k+1) * (phiw(i,j,k+1) - phiw(i,j,k )) * dzhi_soil(k+1)) & + - (lambdash(i,j,k ) * (phiw(i,j,k ) - phiw(i,j,k-1)) * dzhi_soil(k ))) * dzi_soil(k) & + + (gammash(i,j,k+1) - gammash(i,j,k)) * dzi_soil(k) + phiw_source(i,j,k) + + phiw(i,j,k) = phiwm(i,j,k) + rk3coef * tend + end do + end do + end do + +end subroutine diffusion_theta + + ! ! Initialise the land-surface model @@ -195,7 +338,7 @@ subroutine initlsm ! Read namelist namelist /NAMLSM/ & - sw_lsm, sw_homogeneous, z_soil, z_size_soil + sw_lsm, sw_homogeneous, sw_free_drainage, z_soil, z_size_soil allocate(z_soil(kmax_soil)) @@ -210,6 +353,7 @@ subroutine initlsm ! Broadcast namelist values to all MPI tasks call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(sw_free_drainage, 1, mpi_logical, 0, comm3d, mpierr) call MPI_BCAST(z_soil, kmax_soil, my_real, 0, comm3d, mpierr) call MPI_BCAST(z_size_soil, 1, my_real, 0, comm3d, mpierr) @@ -300,11 +444,14 @@ subroutine allocate_fields ! Allocate soil variables allocate(soil_index(i2, j2, kmax_soil)) - allocate(tsoil (i2, j2, kmax_soil)) - allocate(tsoilm (i2, j2, kmax_soil)) + allocate(tsoil (i2, j2, kmax_soil)) + allocate(tsoilm(i2, j2, kmax_soil)) + + allocate(phiw (i2, j2, kmax_soil)) + allocate(phiwm (i2, j2, kmax_soil)) + allocate(phiw_source(i2, j2, kmax_soil)) - allocate(phiw (i2, j2, kmax_soil)) - allocate(phiwm (i2, j2, kmax_soil)) + allocate(throughfall(i2, j2)) ! NOTE: names differ from what is described in modsurfdata! ! Diffusivity temperature: @@ -539,8 +686,8 @@ subroutine calc_soil_properties table_size = size(vg_n) allocate(vg_m(table_size)) - allocate(kappa_theta_min(table_size)) - allocate(kappa_theta_max(table_size)) + allocate(lambda_theta_min(table_size)) + allocate(lambda_theta_max(table_size)) allocate(gamma_theta_min(table_size)) allocate(gamma_theta_max(table_size)) allocate(gamma_t_dry(table_size)) @@ -554,10 +701,10 @@ subroutine calc_soil_properties theta_norm_min = (1.001 * theta_res(i) - theta_res(i)) / (theta_sat(i) - theta_res(i)) + eps1 theta_norm_max = (0.999 * theta_sat(i) - theta_res(i)) / (theta_sat(i) - theta_res(i)) - kappa_theta_min(i) = calc_diffusivity_vg( & + lambda_theta_min(i) = calc_diffusivity_vg( & theta_norm_min, vg_a(i), vg_l(i), vg_m(i), gamma_theta_sat(i), & theta_sat(i), theta_res(i)) - kappa_theta_max(i) = calc_diffusivity_vg( & + lambda_theta_max(i) = calc_diffusivity_vg( & theta_norm_max, vg_a(i), vg_l(i), vg_m(i), gamma_theta_sat(i), & theta_sat(i), theta_res(i)) @@ -598,7 +745,7 @@ end function theta_to_psi ! ! Calculate hydraulic diffusivity using van Genuchten parameterisation. ! -function calc_diffusivity_vg( & +pure function calc_diffusivity_vg( & theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res) result(res) implicit none real, intent(in) :: theta_norm, vg_a, vg_l, vg_m, lambda_sat, theta_sat, theta_res @@ -611,12 +758,12 @@ end function calc_diffusivity_vg ! ! Calculate hydraulic conductivity using van Genuchten parameterisation. ! -pure function calc_conductivity_vg(theta_norm, vg_l, vg_m, lambda_sat) result(res) +pure function calc_conductivity_vg(theta_norm, vg_l, vg_m, gamma_sat) result(res) implicit none - real, intent(in) :: theta_norm, vg_l, vg_m, lambda_sat + real, intent(in) :: theta_norm, vg_l, vg_m, gamma_sat real :: res - res = lambda_sat * theta_norm**vg_l * ( 1.- (1.-theta_norm**(1./vg_m))**vg_m )**2. + res = gamma_sat * theta_norm**vg_l * ( 1.- (1.-theta_norm**(1./vg_m))**vg_m )**2. end function calc_conductivity_vg From 8def726de1eae040453995e28caeae4f199f39d2 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Wed, 1 Jul 2020 16:25:10 +0200 Subject: [PATCH 11/31] Start of tiled surface layer solver --- src/modlsm.f90 | 207 +++++++++++++++++++++++++++++++++++++++------ src/modstartup.f90 | 9 +- src/modsurface.f90 | 8 +- 3 files changed, 191 insertions(+), 33 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index f10a1c13..917a09c8 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -21,7 +21,7 @@ module modlsm use netcdf implicit none - public :: initlsm, lsm, exitlsm + public :: initlsm, lsm, exitlsm, init_lsm_tiles logical :: sw_lsm ! On/off switch LSM logical :: sw_free_drainage ! Free drainage bottom BC for soil moisture @@ -52,8 +52,8 @@ module modlsm real, allocatable :: lambda_stable(:,:), lambda_unstable(:,:) ! Surface fluxes: real, allocatable :: H(:,:), LE(:,:), G(:,:), wthl(:,:), wqt(:,:) - ! Surface temperature and humidity: - real, allocatable :: tskin(:,:), qskin(:,:) + ! Surface (potential) temperature and humidity: + real, allocatable :: tskin(:,:), thlskin(:,:), qtskin(:,:) ! Vegetation properties: real, allocatable :: lai(:,:), rs_min(:,:) end type lsm_tile @@ -84,33 +84,46 @@ subroutine lsm thlflux(:,:) = 0. qtflux(:,:) = 0. G0(:,:) = 0. + phiw_source(:,:,:) = 0. + throughfall(:,:) = 0. + tile_bs%frac(:,:) = 0. + tile_bs%LE(:,:) = 0. + + ! + ! 1. Surface layer + ! + call stability + + + ! ! 2. Calculate soil tendencies ! - ! Calc diffusivity heat + ! Calc diffusivity heat: call calc_thermal_properties - ! Solve diffusion equation - call diffusion_t + ! Solve diffusion equation: + call integrate_t_soil - ! Calc diffusivity and conductivity soil moisture + ! Calc diffusivity and conductivity soil moisture: call calc_hydraulic_properties + ! Solve diffusion equation: + call integrate_theta_soil - ! TMP TMP - phiw_source(:,:,:) = 0. - throughfall(:,:) = 0. - tile_bs%frac(:,:) = 0. - tile_bs%LE(:,:) = 0. - ! Solve diffusion equation - call diffusion_theta +end subroutine lsm +! +! Calculate Obukhov length, ustar, .. for all tiles +! +subroutine stability + implicit none +end subroutine stability -end subroutine lsm ! ! Calculate temperature diffusivity soil at full and half levels @@ -169,7 +182,7 @@ subroutine calc_hydraulic_properties integer :: i, j, k, si real :: theta_lim, theta_norm - ! Calculate diffusivity heat + ! Calculate diffusivity and conductivity soil moisture do k=1,kmax_soil do j=2,j1 do i=2,i1 @@ -190,7 +203,7 @@ subroutine calc_hydraulic_properties ! Calculate & limit the conductivity gammas(i,j,k) = calc_conductivity_vg( & theta_norm, vg_l(si), vg_m(si), gamma_theta_sat(si)) - gammas(i,j,k) = max(min(lambdas(i,j,k), gamma_theta_max(si)), gamma_theta_min(si)) + gammas(i,j,k) = max(min(gammas(i,j,k), gamma_theta_max(si)), gamma_theta_min(si)) end do end do end do @@ -220,7 +233,7 @@ end subroutine calc_hydraulic_properties ! Solve diffusion equation (explicit) for soil temperature. ! Top flux = G0/rho_C, bottom flux = zero. ! -subroutine diffusion_t +subroutine integrate_t_soil use modglobal, only : rk3step, rdt, i1, j1 use modsurfdata, only : tsoil, tsoilm, lambdah, G0 @@ -265,13 +278,13 @@ subroutine diffusion_t end do end do -end subroutine diffusion_t +end subroutine integrate_t_soil ! ! Solve diffusion equation (explicit) for soil moisture, ! including a source term for root water extraction. ! -subroutine diffusion_theta +subroutine integrate_theta_soil use modglobal, only : rk3step, rdt, i1, j1, rhow, rlv use modsurfdata, only : phiw, phiwm, lambdash, gammash @@ -320,15 +333,13 @@ subroutine diffusion_theta end do end do -end subroutine diffusion_theta - - +end subroutine integrate_theta_soil ! ! Initialise the land-surface model ! subroutine initlsm - use modglobal, only : ifnamopt, fname_options, checknamelisterror + use modglobal, only : ifnamopt, fname_options, checknamelisterror, lwarmstart use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real use modsurfdata, only : isurf implicit none @@ -387,7 +398,6 @@ end subroutine initlsm ! subroutine exitlsm implicit none - deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_theta_sat, vg_a, vg_l, vg_n ) end subroutine exitlsm @@ -513,7 +523,8 @@ subroutine allocate_tile(tile) ! Surface temperature and humidity: allocate(tile % tskin(i2, j2)) - allocate(tile % qskin(i2, j2)) + allocate(tile % thlskin(i2, j2)) + allocate(tile % qtskin(i2, j2)) ! Vegetation properties: allocate(tile % lai(i2, j2)) @@ -521,6 +532,32 @@ subroutine allocate_tile(tile) end subroutine allocate_tile +! +! Init some of the tiled variables, in case of cold start. +! Called from modstartup -> readinitfiles() +! +subroutine init_lsm_tiles + use modfields, only : thlprof, qtprof + implicit none + + tile_lv % thlskin(:,:) = thlprof(1) + tile_lv % qtskin (:,:) = qtprof(1) + tile_lv % obuk (:,:) = -0.1 + + tile_hv % thlskin(:,:) = thlprof(1) + tile_hv % qtskin (:,:) = qtprof(1) + tile_hv % obuk (:,:) = -0.1 + + tile_bs % thlskin(:,:) = thlprof(1) + tile_bs % qtskin (:,:) = qtprof(1) + tile_bs % obuk (:,:) = -0.1 + + tile_ws % thlskin(:,:) = thlprof(1) + tile_ws % qtskin (:,:) = qtprof(1) + tile_ws % obuk (:,:) = -0.1 + +end subroutine init_lsm_tiles + ! ! Initialise the LSM homogeneous from namelist input ! @@ -720,6 +757,124 @@ subroutine calc_soil_properties end subroutine calc_soil_properties +! +! Iterative Rib -> Obukhov length solver +! +function calc_obuk_dirichlet(L_in, du, db_in, zsl, z0m, z0h) result(res) + use modglobal, only : fkar + implicit none + real, intent(in) :: L_in, du, db_in, zsl, z0m, z0h + + integer :: m, n, nlim + real :: res, L, db, Lmax, Ri, L0, Lstart, Lend, fx0, fxdif + + m = 0 + nlim = 10 + Lmax = 1e10 + L = L_in + db = db_in + + ! The solver does not have a solution for large Ri numbers, + ! i.e. the `fx` equation below has no zero crossing. + ! The limit of 0.13 typically results in a minimum (positive) L of ~1. + ! IFS caps z/L at 5, so for a typical zsl at ~L=2. + Ri = fkar * db * zsl / du**2 + if (Ri > 0.13) then + print*,'WARNING: Ri out of range, returning L=1' + res = 1 + return + end if + + ! Avoid buoyancy difference of zero: + if (db >= 0) then + db = max(db, 1e-9) + else + db = min(db, -1e-9) + end if + + ! Allow for one restart of iterative procedure: + do while (m <= 1) + ! if L and db are of different sign, or the last calculation did not converge, + ! the stability has changed and the procedure needs to be reset + if (L*db <= 0) then + nlim = 200 + if (db >= 0) then + L = 1e-9 + else + L = -1e-9 + end if + end if + + ! Make sure the iteration starts + if (db >= 0) then + L0 = 1e30 + else + L0 = -1e30 + end if + + ! Exit on convergence or on iteration count + n = 0 + fxdif = 1 + do while (abs((L - L0) / L0) > 0.001 .and. n < nlim .and. abs(L) < Lmax) + L0 = L + Lstart = L - 0.001*L + Lend = L + 0.001*L + + fx0 = fx(zsl, L0, du, db, z0h, z0m) + fxdif = (fx(zsl, Lend, du, db, z0h, z0m) - fx(zsl, Lstart, du, db, z0h, z0m)) / (Lend - Lstart) + L = L - fx0/fxdif + n = n+1 + end do + + if (n < nlim .and. abs(L) < Lmax) then + ! Convergence has been reached + res = L + return + else + ! Convergence has not been reached, procedure restarted once + L = 1e-9 + m = m+1 + nlim = 200 + end if + end do + + if (m > 1) then + print*,'WARNING: convergence has not been reached in Obukhov length iteration' + res = 1e-9 + return + end if + +end function calc_obuk_dirichlet + +pure function fx(zsl, L, du, db, z0h, z0m) result(res) + implicit none + real, intent(in) :: zsl, L, du, db, z0h, z0m + real :: res, fkar + fkar = 0.4 + + res = zsl/L - fkar * zsl * db * fh(zsl, z0h, L) / (du * fm(zsl, z0m, L))**2 +end function fx + +pure function fm(zsl, z0m, L) result(res) + use modglobal, only : fkar + use modsurface, only : psim + implicit none + real, intent(in) :: zsl, z0m, L + real :: res + + res = fkar / (log(zsl/z0m) - psim(zsl/L) + psim(z0m/L)) +end function fm + +pure function fh(zsl, z0h, L) result(res) + use modglobal, only : fkar + use modsurface, only : psih + implicit none + real, intent(in) :: zsl, z0h, L + real :: res + + res = fkar / (log(zsl/z0h) - psih(zsl/L) + psih(z0h/L)) +end function fh + ! ! Convert soil hydraulic head to soil water content, using van Genuchten parameterisation. ! diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 5a9a9393..4e64d693 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -391,6 +391,7 @@ subroutine readinitfiles thls,tskin,tskinm,tsoil,tsoilm,phiw,phiwm,Wl,Wlm,thvs,qts,isurf,svs,obl,oblav,& thvs_patch,lhetero,qskin use modsurface, only : surface,qtsurf,dthldz,ps + use modlsm, only : init_lsm_tiles use modboundary, only : boundary use modmpi, only : slabsum,myid,comm3d,mpierr,my_real use modthermodynamics, only : thermodynamics,calc_halflev @@ -588,11 +589,13 @@ subroutine readinitfiles Wlm = Wl case(2) tskin = thls - case(3,4,11) - thls = thlprof(1) - qts = qtprof(1) + case(3,4) + thls = thlprof(1) + qts = qtprof(1) tskin = thls qskin = qts + case(11) + call init_lsm_tiles case(10) call initsurf_user end select diff --git a/src/modsurface.f90 b/src/modsurface.f90 index a4ced76c..cefdb5bd 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -1335,7 +1335,7 @@ subroutine getobl end subroutine getobl - function psim(zeta) + pure function psim(zeta) implicit none real :: psim @@ -1355,7 +1355,7 @@ function psim(zeta) return end function psim - function psih(zeta) + pure function psih(zeta) implicit none @@ -1381,7 +1381,7 @@ end function psih ! Phi and Psi above are related by an integral and should in principle match, ! currently they do not. ! FJ 2018: For very stable situations, zeta > 1 add cap to phi - the linear expression is valid only for zeta < 1 - function phim(zeta) + pure function phim(zeta) implicit none real :: phim real, intent(in) :: zeta @@ -1399,7 +1399,7 @@ function phim(zeta) end function phim ! stability function Phi for heat. - function phih(zeta) + pure function phih(zeta) implicit none real :: phih real, intent(in) :: zeta From c56c4781f83501b0c6bf047d5f7dd8ddf691b072 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Thu, 2 Jul 2020 12:13:05 +0200 Subject: [PATCH 12/31] Working tiled surface layer solver --- src/modlsm.f90 | 96 ++++++++++++++++++++++++++++++++++++++++++--- src/modstartup.f90 | 4 ++ src/modtimestat.f90 | 87 +++++++++++++++++++++++++++++++--------- 3 files changed, 162 insertions(+), 25 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 917a09c8..265262f2 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -40,10 +40,15 @@ module modlsm ! Precipitation interception et al. real, allocatable :: throughfall(:,:) + ! Random + real, allocatable :: du_tot(:,:), thv_1(:,:) + ! Data structure for sub-grid tiles type lsm_tile ! Static properties: real, allocatable :: z0m(:,:), z0h(:,:) + ! Base tile fraction (i.e. without liquid water) + real, allocatable :: base_frac(:,:) ! Dynamic tile fraction: real, allocatable :: frac(:,:) ! Monin-obukhov / surface layer: @@ -81,8 +86,8 @@ subroutine lsm implicit none ! tmp... - thlflux(:,:) = 0. - qtflux(:,:) = 0. + thlflux(:,:) = 0.1 + qtflux(:,:) = 0.1e-3 G0(:,:) = 0. phiw_source(:,:,:) = 0. throughfall(:,:) = 0. @@ -95,8 +100,6 @@ subroutine lsm call stability - - ! ! 2. Calculate soil tendencies ! @@ -116,13 +119,71 @@ subroutine lsm end subroutine lsm ! -! Calculate Obukhov length, ustar, .. for all tiles +! Calculate Obukhov length, ustar, and aerodynamic resistance, for all tiles ! subroutine stability + use modglobal, only : i1, j1, cu, cv, rv, rd + use modfields, only : u0, v0, thl0, qt0 implicit none + real :: du, dv + integer :: i, j + real, parameter :: du_min = 0.1 + + ! Calculate properties shared by all tiles: + ! Absolute wind speed difference, and virtual potential temperature atmosphere + do j=2,j1 + do i=2,i1 + du = 0.5*(u0(i,j,1) + u0(i+1,j,1)) + cu + dv = 0.5*(v0(i,j,1) + v0(i,j+1,1)) + cv + du_tot(i,j) = sqrt(du**2 + dv**2) + + thv_1(i,j) = thl0(i,j,1) * (1.+(rv/rd-1.)*qt0(i,j,1)) + end do + end do + + call calc_obuk_ustar_ra(tile_lv) + call calc_obuk_ustar_ra(tile_hv) + call calc_obuk_ustar_ra(tile_bs) + call calc_obuk_ustar_ra(tile_ws) + end subroutine stability +! +! Calculate Obukhov length and ustar, for single tile +! +subroutine calc_obuk_ustar_ra(tile) + use modglobal, only : i1, j1, rd, rv, grav, zf + implicit none + + type(lsm_tile), intent(inout) :: tile + integer :: i, j + real :: thvs, db + + do j=2,j1 + do i=2,i1 + ! Buoyancy difference surface - atmosphere + thvs = tile%thlskin(i,j) * (1.+(rv/rd-1.)*tile%qtskin(i,j)) + db = grav/thvs * (thvs - thv_1(i,j)) + + ! Iteratively find Obukhov length + tile%obuk(i,j) = calc_obuk_dirichlet( & + tile%obuk(i,j), du_tot(i,j), db, zf(1), tile%z0m(i,j), tile%z0h(i,j)) + end do + end do + + do j=2,j1 + do i=2,i1 + ! Calculate friction velocity and aerodynamic resistance + tile%ustar(i,j) = du_tot(i,j) * fm(zf(1), tile%z0m(i,j), tile%obuk(i,j)) + tile%ra(i,j) = 1./tile%ustar(i,j) * fh(zf(1), tile%z0h(i,j), tile%obuk(i,j)) + end do + end do + +end subroutine calc_obuk_ustar_ra + + + ! @@ -463,6 +524,9 @@ subroutine allocate_fields allocate(throughfall(i2, j2)) + allocate(du_tot(i2, j2)) + allocate(thv_1(i2, j2)) + ! NOTE: names differ from what is described in modsurfdata! ! Diffusivity temperature: allocate(lambda (i2, j2, kmax_soil )) @@ -502,7 +566,8 @@ subroutine allocate_tile(tile) allocate(tile % z0m(i2, j2)) allocate(tile % z0h(i2, j2)) - ! Dynamic tile fraction: + ! Base and dynamic tile fraction: + allocate(tile % base_frac(i2, j2)) allocate(tile % frac(i2, j2)) ! Monin-obukhov / surface layer: @@ -568,6 +633,7 @@ subroutine init_homogeneous implicit none integer :: ierr, k + real :: c_low, c_high, c_bare real :: z0m_low, z0m_high, z0m_bare real :: z0h_low, z0h_high, z0h_bare real :: lambda_s_low, lambda_s_high, lambda_s_bare @@ -580,6 +646,7 @@ subroutine init_homogeneous ! Read namelist namelist /NAMLSM_HOMOGENEOUS/ & + c_low, c_high, & z0m_low, z0m_high, z0m_bare, & z0h_low, z0h_high, z0h_bare, & lambda_s_low, lambda_s_high, lambda_s_bare, & @@ -600,6 +667,9 @@ subroutine init_homogeneous end if ! Broadcast to all MPI tasks + call MPI_BCAST(c_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(c_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_low, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(z0m_high, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(z0m_bare, 1, my_real, 0, comm3d, mpierr) @@ -627,6 +697,12 @@ subroutine init_homogeneous call MPI_BCAST(soil_index_p, kmax_soil, mpi_integer, 0, comm3d, mpierr) ! Set values + c_bare = 1.-c_low-c_high + tile_lv % base_frac(:,:) = c_low + tile_hv % base_frac(:,:) = c_high + tile_bs % base_frac(:,:) = c_bare + tile_ws % base_frac(:,:) = 0. + tile_lv % z0m(:,:) = z0m_low tile_hv % z0m(:,:) = z0m_high tile_bs % z0m(:,:) = z0m_bare @@ -658,6 +734,13 @@ subroutine init_homogeneous tsoilm(:,:,:) = tsoil(:,:,:) phiwm (:,:,:) = phiw (:,:,:) + ! Set properties wet skin tile + tile_ws % z0m(:,:) = c_low*z0m_low + c_high*z0m_high + c_bare*z0m_bare + tile_ws % z0h(:,:) = c_low*z0h_low + c_high*z0h_high + c_bare*z0h_bare + + tile_ws % lambda_stable(:,:) = c_low*lambda_s_low + c_high*lambda_s_high + c_bare*lambda_s_bare + tile_ws % lambda_unstable(:,:) = c_low*lambda_us_low + c_high*lambda_us_high + c_bare*lambda_us_bare + ! Cleanup! deallocate(t_soil_p, theta_soil_p) @@ -840,6 +923,7 @@ function calc_obuk_dirichlet(L_in, du, db_in, zsl, z0m, z0h) result(res) if (m > 1) then print*,'WARNING: convergence has not been reached in Obukhov length iteration' + print*,'Input: ', L_in, du, db_in, zsl, z0m, z0h res = 1e-9 return end if diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 4e64d693..e5b91eb5 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -595,6 +595,10 @@ subroutine readinitfiles tskin = thls qskin = qts case(11) + thls = thlprof(1) + qts = qtprof(1) + tskin = thls + qskin = qts call init_lsm_tiles case(10) call initsurf_user diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index f93a6bcb..b3b717a1 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -63,7 +63,7 @@ module modtimestat real :: qlint logical:: store_zi = .false. - !Variables for heterogeneity + ! Variables for heterogeneity real, allocatable :: u0av_patch (:,:) ! patch averaged um at full level real, allocatable :: v0av_patch (:,:) ! patch averaged vm at full level real, allocatable :: w0av_patch (:,:) ! patch averaged wm at full level @@ -72,11 +72,13 @@ module modtimestat real,allocatable, dimension(:,:) :: cc_patch, qlint_patch, qlintmax_patch, qlintmax_patchl, tke_tot_patch real,allocatable, dimension(:,:) :: wmax_patch, wmax_patchl, qlmax_patch, qlmax_patchl, ztopmax_patch, ztopmax_patchl real,allocatable, dimension(:,:) :: ust_patch, qst_patch, tst_patch, wthls_patch, wqls_patch, wthvs_patch - !In combination with isurf = 1 + + ! In combination with isurf = 1 real,allocatable, dimension(:,:) :: Qnet_patch, H_patch, LE_patch, G0_patch, tendskin_patch,rs_patch,ra_patch real,allocatable, dimension(:,:) :: cliq_patch, wl_patch, rsveg_patch, rssoil_patch, tskin_patch, obl_patch real,allocatable, dimension(:,:) :: zi_patch,ziold_patch,we_patch, zi_field + contains !> Initializing Timestat. Read out the namelist, initializing the variables subroutine inittimestat @@ -185,7 +187,7 @@ subroutine inittimestat ' [m/s] ' close(ifoutput) end if - + if(lhetero) then do i=1,xpatches do j=1,ypatches @@ -197,7 +199,7 @@ subroutine inittimestat '# time cc z_cbase z_ctop_avg z_ctop_max zi we', & ' <> <>_max w_max tke ql_max' close(ifoutput) - + name = 'tmsurfpatchiiixjjj.'//cexpnr write (name(12:14),'(i3.3)') i write (name(16:18),'(i3.3)') j @@ -206,7 +208,7 @@ subroutine inittimestat '# time ust tst qst obukh', & ' thls z0 wthls wthvs wqls ' close(ifoutput) - + if(isurf == 1) then name = 'tmlsmpatchiiixjjj.'//cexpnr write (name(11:13),'(i3.3)') i @@ -231,7 +233,7 @@ subroutine inittimestat if (isurf == 1) then nvar = 32 else if (isurf == 11) then - nvar = 25 + nvar = 34 else nvar = 21 end if @@ -273,11 +275,24 @@ subroutine inittimestat call ncinfo(ncname(30,:),'Wl','Liquid water reservoir','m','time') call ncinfo(ncname(31,:),'rssoil','Soil evaporation resistance','s/m','time') call ncinfo(ncname(32,:),'rsveg','Vegitation resistance','s/m','time') + else if (isurf==11) then call ncinfo(ncname(22,:),'Qnet','Net radiation','W/m^2','time') call ncinfo(ncname(23,:),'H','Sensible heat flux','W/m^2','time') call ncinfo(ncname(24,:),'LE','Latent heat flux','W/m^2','time') call ncinfo(ncname(25,:),'G','Ground heat flux','W/m^2','time') + + call ncinfo(ncname(26,:),'obuk_lv','Obukhov length low veg','m','time') + call ncinfo(ncname(27,:),'obuk_hv','Obukhov length high veg','m','time') + call ncinfo(ncname(28,:),'obuk_bs','Obukhov length bare soil','m','time') + + call ncinfo(ncname(29,:),'ustar_lv','Friction velocity low veg','m s-1','time') + call ncinfo(ncname(30,:),'ustar_hv','Friction velocity high veg','m s-1','time') + call ncinfo(ncname(31,:),'ustar_bs','Friction velocity bare soil','m s-1','time') + + call ncinfo(ncname(32,:),'ra_lv','Aerodynamic resistance low veg','s m-1','time') + call ncinfo(ncname(33,:),'ra_hv','Aerodynamic resistance high veg','s m-1','time') + call ncinfo(ncname(34,:),'ra_bs','Aerodynamic resistance bare soil','s m-1','time') end if call open_nc(fname, ncid,nrec) @@ -355,9 +370,10 @@ subroutine timestat lhetero, xpatches, ypatches, qts_patch, wt_patch, wq_patch, & thls_patch,obl,z0mav_patch, wco2av, Anav, Respav,gcco2av use modsurface, only : patchxnr,patchynr + use modlsm, only : tile_lv, tile_hv, tile_bs, tile_ws use mpi use modmpi, only : my_real,mpi_sum,mpi_max,mpi_min,comm3d,mpierr,myid - use modstat_nc, only : lnetcdf, writestat_nc,nc_fillvalue + use modstat_nc, only : lnetcdf, writestat_nc,nc_fillvalue implicit none real :: zbaseavl, ztopavl, ztopmaxl, ztop,zbaseminl @@ -372,6 +388,12 @@ subroutine timestat ! lsm variables real :: Qnetavl, Havl, LEavl, G0avl, tendskinavl, rsavl, raavl, tskinavl,Wlavl,cliqavl,rsvegavl,rssoilavl real :: Qnetav, Hav, LEav, G0av, tendskinav, rsav, raav, tskinav,Wlav,cliqav,rsvegav,rssoilav + + ! LSM tiled variables + real :: obuk_lv_av, obuk_hv_av, obuk_bs_av + real :: ustar_lv_av, ustar_hv_av, ustar_bs_av + real :: ra_lv_av, ra_hv_av, ra_bs_av + integer:: i, j, k ! heterogeneity variables @@ -768,20 +790,25 @@ subroutine timestat endif else if(isurf == 11) then - Qnetavl = sum(Qnet(2:i1,2:j1)) - Havl = sum(H(2:i1,2:j1)) - LEavl = sum(LE(2:i1,2:j1)) - G0avl = sum(G0(2:i1,2:j1)) - call MPI_ALLREDUCE(Qnetavl, Qnetav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) - call MPI_ALLREDUCE(Havl, Hav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) - call MPI_ALLREDUCE(LEavl, LEav, 1, MY_REAL,MPI_SUM, comm3d,mpierr) - call MPI_ALLREDUCE(G0avl, G0av, 1, MY_REAL,MPI_SUM, comm3d,mpierr) + Qnetav = mean_2d(Qnet) + Hav = mean_2d(H) + LEav = mean_2d(LE) + G0av = mean_2d(G0) + + ! Tiled variables + obuk_lv_av = mean_2d(tile_lv%obuk) + obuk_hv_av = mean_2d(tile_hv%obuk) + obuk_bs_av = mean_2d(tile_bs%obuk) + + ustar_lv_av = mean_2d(tile_lv%ustar) + ustar_hv_av = mean_2d(tile_hv%ustar) + ustar_bs_av = mean_2d(tile_bs%ustar) + + ra_lv_av = mean_2d(tile_lv%ra) + ra_hv_av = mean_2d(tile_hv%ra) + ra_bs_av = mean_2d(tile_bs%ra) - Qnetav = Qnetav / ijtot - Hav = Hav / ijtot - LEav = LEav / ijtot - G0av = G0av / ijtot end if ! 9.8 write the results to output file @@ -887,6 +914,15 @@ subroutine timestat vars(23) = Hav vars(24) = LEav vars(25) = G0av + vars(26) = obuk_lv_av + vars(27) = obuk_hv_av + vars(28) = obuk_bs_av + vars(29) = ustar_lv_av + vars(30) = ustar_hv_av + vars(31) = ustar_bs_av + vars(32) = ra_lv_av + vars(33) = ra_hv_av + vars(34) = ra_bs_av end if call writestat_nc(ncid,nvar,ncname,vars,nrec,.true.) @@ -960,6 +996,19 @@ subroutine timestat end subroutine timestat + function mean_2d(var_2d) result(res) + use modglobal, only : i1, j1, ijtot + use modmpi, only : my_real, mpi_sum, comm3d, mpierr + implicit none + real, intent(in) :: var_2d(:,:) + real :: res, var_sum_l, var_sum + + var_sum_l = sum(var_2d(2:i1,2:j1)) + call mpi_allreduce(var_sum_l, var_sum, 1, my_real, mpi_sum, comm3d, mpierr) + res = var_sum / ijtot + + end function mean_2d + !>Calculate the boundary layer height !! !! There are 3 available ways to calculate the boundary layer height: From 656839f90dfe463bcd0e7bdc4d3db4b99740dcd8 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Thu, 2 Jul 2020 16:30:10 +0200 Subject: [PATCH 13/31] Added calculation canopy and soil resistance --- src/modlsm.f90 | 272 ++++++++++++++++++++++++++++++++++++++------ src/modtimestat.f90 | 41 ++++++- 2 files changed, 275 insertions(+), 38 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 265262f2..f3e407e7 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -23,7 +23,7 @@ module modlsm public :: initlsm, lsm, exitlsm, init_lsm_tiles - logical :: sw_lsm ! On/off switch LSM + logical :: sw_lsm ! On/off switch LSM logical :: sw_free_drainage ! Free drainage bottom BC for soil moisture ! Soil grid @@ -33,13 +33,21 @@ module modlsm real, allocatable :: dz_soil(:), dzh_soil(:) real, allocatable :: dzi_soil(:), dzhi_soil(:) - ! Soil properties + ! Soil index in `van_genuchten_parameters.nc` lookup table: integer, allocatable :: soil_index(:,:,:) + + ! Source term in soil water budget real, allocatable :: phiw_source(:,:,:) - ! Precipitation interception et al. + ! Precipitation, interception et al. real, allocatable :: throughfall(:,:) + ! Dependency factor canopy resistance on VPD (high veg only) + real, allocatable :: gD(:,:) + + ! Reduction functions canopy resistance + real, allocatable :: f1(:,:), f2_lv(:,:), f2_hv(:,:), f2b(:,:), f3(:,:) + ! Random real, allocatable :: du_tot(:,:), thv_1(:,:) @@ -60,7 +68,12 @@ module modlsm ! Surface (potential) temperature and humidity: real, allocatable :: tskin(:,:), thlskin(:,:), qtskin(:,:) ! Vegetation properties: - real, allocatable :: lai(:,:), rs_min(:,:) + real, allocatable :: lai(:,:), rs_min(:,:), rs(:,:) + ! Root fraction in soil + real, allocatable :: a_r(:,:), b_r(:,:) + real, allocatable :: root_frac(:,:,:) + ! Root fraction weighted mean soil water content + real, allocatable :: phiw_mean(:,:) end type lsm_tile ! Tiles for low veg (lv), high veg (hv), bare soil (bs), wet skin (ws): @@ -77,14 +90,14 @@ module modlsm gamma_theta_min(:), gamma_theta_max(:), & gamma_t_dry(:), rho_C(:) - - contains subroutine lsm use modsurfdata, only : thlflux, qtflux, G0 implicit none + if (.not. sw_lsm) return + ! tmp... thlflux(:,:) = 0.1 qtflux(:,:) = 0.1e-3 @@ -94,15 +107,25 @@ subroutine lsm tile_bs%frac(:,:) = 0. tile_bs%LE(:,:) = 0. - ! - ! 1. Surface layer - ! + ! Calculate dynamic tile fractions, + ! based on the amount of liquid water on vegetation. + call calc_tile_fractions + + ! Calculate root fraction weighted mean soil water content + call calc_theta_mean(tile_lv) + call calc_theta_mean(tile_hv) + + ! Calculate canopy/soil resistances + call calc_canopy_resistances + + ! Calculate Obukhov length, ustar, + ! and aerodynamic resistance, for all tiles: call stability - ! - ! 2. Calculate soil tendencies - ! + + + ! Calculate soil tendencies ! Calc diffusivity heat: call calc_thermal_properties ! Solve diffusion equation: @@ -113,10 +136,105 @@ subroutine lsm ! Solve diffusion equation: call integrate_theta_soil +end subroutine lsm +! +! Calculate dynamic tile fractions, based on liquid water on vegetation +! +subroutine calc_tile_fractions + implicit none + ! Not so dynamic right now..... + tile_lv%frac(:,:) = tile_lv%base_frac(:,:) + tile_hv%frac(:,:) = tile_hv%base_frac(:,:) + tile_bs%frac(:,:) = tile_bs%base_frac(:,:) + tile_ws%frac(:,:) = tile_ws%base_frac(:,:) -end subroutine lsm +end subroutine calc_tile_fractions + +! +! Calculate root fraction weighted mean soil water content +! +subroutine calc_theta_mean(tile) + use modglobal, only : i1, j1 + use modsurfdata, only : phiw + implicit none + + type(lsm_tile), intent(inout) :: tile + integer :: i, j, k, si + real :: theta_lim + + tile%phiw_mean(:,:) = 0. + + do k=1, kmax_soil + do j=2,j1 + do i=2,i1 + si = soil_index(i,j,k) + + theta_lim = max(phiw(i,j,k), theta_wp(si)) + tile%phiw_mean(i,j) = tile%phiw_mean(i,j) + tile%root_frac(i,j,k) * & + (theta_lim - theta_wp(si)) / (theta_fc(si) - theta_wp(si)) + end do + end do + end do + +end subroutine calc_theta_mean + +! +! Calculate canopy and soil resistances +! +subroutine calc_canopy_resistances + use modglobal, only : i1, j1 + use modfields, only : thl0, qt0, exnf, presf + use modsurface, only : ps + use modraddata, only : swd + use modsurfdata, only : phiw + implicit none + + integer :: i, j, k, si + real :: swd_pos, T, esat, e, theta_min, theta_rel, c_veg + + ! Constants f1 calculation: + real, parameter :: a_f1 = 0.81 + real, parameter :: b_f1 = 0.004 + real, parameter :: c_f1 = 0.05 + + k = kmax_soil + do j=2,j1 + do i=2,i1 + si = soil_index(i,j,k) + + ! f1: reduction vegetation resistance as f(sw_in): + swd_pos = max(0., -swd(i,j,1)) + f1(i,j) = 1./min(1., (b_f1*swd_pos + c_f1) / (a_f1 * (b_f1*swd_pos + 1.))) + + ! f2: reduction vegetation resistance as f(theta): + f2_lv(i,j) = 1./min(1., max(1.e-9, tile_lv%phiw_mean(i,j))) + f2_hv(i,j) = 1./min(1., max(1.e-9, tile_hv%phiw_mean(i,j))) + + ! f3: reduction vegetation resistance as f(VPD) (high veg only): + T = thl0(i,j,1) * exnf(1) + esat = 0.611e3 * exp(17.2694 * (T - 273.16) / (T - 35.86)) + e = qt0(i,j,1) * presf(1) / 0.622 + + f3(i,j) = 1./exp(-gD(i,j) * (esat-e)) + + ! f2b: reduction soil resistance as f(theta) + c_veg = tile_lv%base_frac(i,j) + tile_hv%base_frac(i,j) + theta_min = c_veg * theta_wp(si) + (1.-c_veg) * theta_res(si); + theta_rel = (phiw(i,j,k) - theta_min) / (theta_fc(si) - theta_min); + f2b(i,j) = 1./min(1., max(1.e-9, theta_rel)) + + ! Calculate canopy and soil resistance + tile_lv%rs(i,j) = tile_lv%rs_min(i,j) / (tile_lv%lai(i,j) * f1(i,j) * f2_lv(i,j)) + tile_hv%rs(i,j) = tile_hv%rs_min(i,j) / (tile_hv%lai(i,j) * f1(i,j) * f2_hv(i,j) * f3(i,j)) + tile_bs%rs(i,j) = tile_hv%rs_min(i,j) / f2b(i,j) + tile_ws%rs(i,j) = 0. + + end do + end do + +end subroutine calc_canopy_resistances ! ! Calculate Obukhov length, ustar, and aerodynamic resistance, for all tiles @@ -126,9 +244,9 @@ subroutine stability use modfields, only : u0, v0, thl0, qt0 implicit none + real, parameter :: du_min = 0.1 real :: du, dv integer :: i, j - real, parameter :: du_min = 0.1 ! Calculate properties shared by all tiles: ! Absolute wind speed difference, and virtual potential temperature atmosphere @@ -408,29 +526,33 @@ subroutine initlsm integer :: ierr logical :: sw_homogeneous - ! Read namelist + ! Namelist definition namelist /NAMLSM/ & - sw_lsm, sw_homogeneous, sw_free_drainage, z_soil, z_size_soil + sw_homogeneous, sw_free_drainage, z_soil, z_size_soil - allocate(z_soil(kmax_soil)) + sw_lsm = (isurf == 11) - if (myid == 0) then - open(ifnamopt, file=fname_options, status='old', iostat=ierr) - read(ifnamopt, NAMLSM, iostat=ierr) - call checknamelisterror(ierr, ifnamopt, 'NAMLSM') - write(6, NAMLSM) - close(ifnamopt) - end if + if (sw_lsm) then - ! Broadcast namelist values to all MPI tasks - call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) - call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) - call MPI_BCAST(sw_free_drainage, 1, mpi_logical, 0, comm3d, mpierr) + allocate(z_soil(kmax_soil)) - call MPI_BCAST(z_soil, kmax_soil, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z_size_soil, 1, my_real, 0, comm3d, mpierr) + ! Read namelist + if (myid == 0) then + open(ifnamopt, file=fname_options, status='old', iostat=ierr) + read(ifnamopt, NAMLSM, iostat=ierr) + call checknamelisterror(ierr, ifnamopt, 'NAMLSM') + write(6, NAMLSM) + close(ifnamopt) + end if + + ! Broadcast namelist values to all MPI tasks + call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(sw_free_drainage, 1, mpi_logical, 0, comm3d, mpierr) + + call MPI_BCAST(z_soil, kmax_soil, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z_size_soil, 1, my_real, 0, comm3d, mpierr) - if (sw_lsm) then ! Create/calculate soil grid properties call create_soil_grid @@ -450,6 +572,9 @@ subroutine initlsm ! Calculate derived soil properties call calc_soil_properties + + ! Calculate root fractions soil (low and high veg) + call calc_root_fractions end if end subroutine initlsm @@ -517,13 +642,20 @@ subroutine allocate_fields allocate(tsoil (i2, j2, kmax_soil)) allocate(tsoilm(i2, j2, kmax_soil)) + allocate(phiw (i2, j2, kmax_soil)) + allocate(phiwm (i2, j2, kmax_soil)) - allocate(phiw (i2, j2, kmax_soil)) - allocate(phiwm (i2, j2, kmax_soil)) allocate(phiw_source(i2, j2, kmax_soil)) allocate(throughfall(i2, j2)) + allocate(gD(i2, j2)) + allocate(f1(i2, j2)) + allocate(f2_lv(i2, j2)) + allocate(f2_hv(i2, j2)) + allocate(f2b(i2, j2)) + allocate(f3(i2, j2)) + allocate(du_tot(i2, j2)) allocate(thv_1(i2, j2)) @@ -594,6 +726,15 @@ subroutine allocate_tile(tile) ! Vegetation properties: allocate(tile % lai(i2, j2)) allocate(tile % rs_min(i2, j2)) + allocate(tile % rs(i2, j2)) + + ! Root fraction in soil: + allocate(tile % a_r(i2, j2)) + allocate(tile % b_r(i2, j2)) + allocate(tile % root_frac(i2, j2, kmax_soil)) + + ! Root fraction weigthed mean soil water content + allocate(tile % phiw_mean(i2, j2)) end subroutine allocate_tile @@ -639,7 +780,9 @@ subroutine init_homogeneous real :: lambda_s_low, lambda_s_high, lambda_s_bare real :: lambda_us_low, lambda_us_high, lambda_us_bare real :: lai_low, lai_high - real :: rs_min_low, rs_min_high + real :: rs_min_low, rs_min_high, rs_min_bare + real :: ar_low, br_low, ar_high, br_high + real :: gD_high real, allocatable :: t_soil_p(:), theta_soil_p(:) integer, allocatable :: soil_index_p(:) @@ -652,8 +795,10 @@ subroutine init_homogeneous lambda_s_low, lambda_s_high, lambda_s_bare, & lambda_us_low, lambda_us_high, lambda_us_bare, & lai_low, lai_high, & - rs_min_low, rs_min_high, & - t_soil_p, theta_soil_p, soil_index_p + rs_min_low, rs_min_high, rs_min_bare, & + t_soil_p, theta_soil_p, soil_index_p, & + ar_low, br_low, ar_high, br_high, & + gD_high allocate(t_soil_p(kmax_soil), theta_soil_p(kmax_soil)) allocate(soil_index_p(kmax_soil)) @@ -691,11 +836,19 @@ subroutine init_homogeneous call MPI_BCAST(rs_min_low, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(rs_min_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(rs_min_bare, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(t_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) call MPI_BCAST(theta_soil_p, kmax_soil, my_real, 0, comm3d, mpierr) call MPI_BCAST(soil_index_p, kmax_soil, mpi_integer, 0, comm3d, mpierr) + call MPI_BCAST(ar_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(br_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(ar_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(br_high, 1, my_real, 0, comm3d, mpierr) + + call MPI_BCAST(gD_high, 1, my_real, 0, comm3d, mpierr) + ! Set values c_bare = 1.-c_low-c_high tile_lv % base_frac(:,:) = c_low @@ -724,6 +877,14 @@ subroutine init_homogeneous tile_lv % rs_min(:,:) = rs_min_low tile_hv % rs_min(:,:) = rs_min_high + tile_bs % rs_min(:,:) = rs_min_bare + + tile_lv % a_r(:,:) = ar_low + tile_lv % b_r(:,:) = br_low + tile_hv % a_r(:,:) = ar_high + tile_hv % b_r(:,:) = br_high + + gD(:,:) = gD_high do k=1, kmax_soil tsoil(:,:,k) = t_soil_p(k) @@ -840,6 +1001,45 @@ subroutine calc_soil_properties end subroutine calc_soil_properties +! +! Calculate root fraction for the low and high vegetation tiles +! +subroutine calc_root_fractions + use modglobal, only : i1, j1 + implicit none + real :: root_sum_lv, root_sum_hv + integer i, j, k + + do j=2,j1 + do i=2,i1 + root_sum_lv = 0 + root_sum_hv = 0 + do k=2, kmax_soil + + tile_lv%root_frac(i,j,k) = 0.5 * (& + exp(tile_lv%a_r(i,j) * zh_soil(k+1)) + & + exp(tile_lv%b_r(i,j) * zh_soil(k+1)) - & + exp(tile_lv%a_r(i,j) * zh_soil(k )) - & + exp(tile_lv%b_r(i,j) * zh_soil(k ))) + + tile_hv%root_frac(i,j,k) = 0.5 * (& + exp(tile_hv%a_r(i,j) * zh_soil(k+1)) + & + exp(tile_hv%b_r(i,j) * zh_soil(k+1)) - & + exp(tile_hv%a_r(i,j) * zh_soil(k )) - & + exp(tile_hv%b_r(i,j) * zh_soil(k ))) + + root_sum_lv = root_sum_lv + tile_lv%root_frac(i,j,k) + root_sum_hv = root_sum_hv + tile_hv%root_frac(i,j,k) + end do + + ! Make sure that the fractions sum to one. + tile_lv%root_frac(i,j,1) = 1. - root_sum_lv + tile_hv%root_frac(i,j,1) = 1. - root_sum_hv + end do + end do + +end subroutine calc_root_fractions + ! ! Iterative Rib -> Obukhov length solver ! diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index b3b717a1..9216b031 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -233,7 +233,7 @@ subroutine inittimestat if (isurf == 1) then nvar = 32 else if (isurf == 11) then - nvar = 34 + nvar = 42 else nvar = 21 end if @@ -293,6 +293,17 @@ subroutine inittimestat call ncinfo(ncname(32,:),'ra_lv','Aerodynamic resistance low veg','s m-1','time') call ncinfo(ncname(33,:),'ra_hv','Aerodynamic resistance high veg','s m-1','time') call ncinfo(ncname(34,:),'ra_bs','Aerodynamic resistance bare soil','s m-1','time') + + call ncinfo(ncname(35,:),'f1','Reduction canopy resistance f(swd)','-','time') + call ncinfo(ncname(36,:),'f2_lv','Reduction canopy resistance low veg f(theta)','-','time') + call ncinfo(ncname(37,:),'f2_hv','Reduction canopy resistance low veg f(theta)','-','time') + call ncinfo(ncname(38,:),'f3','Reduction canopy resistance f(VPD)','-','time') + call ncinfo(ncname(39,:),'f2b','Reduction soil resistance f(theta)','-','time') + + call ncinfo(ncname(40,:),'rs_lv','Canopy resistance low veg','s m-1','time') + call ncinfo(ncname(41,:),'rs_hv','Canopy resistance low veg','s m-1','time') + call ncinfo(ncname(42,:),'rs_bs','Soil resistance','s m-1','time') + end if call open_nc(fname, ncid,nrec) @@ -370,7 +381,7 @@ subroutine timestat lhetero, xpatches, ypatches, qts_patch, wt_patch, wq_patch, & thls_patch,obl,z0mav_patch, wco2av, Anav, Respav,gcco2av use modsurface, only : patchxnr,patchynr - use modlsm, only : tile_lv, tile_hv, tile_bs, tile_ws + use modlsm, only : tile_lv, tile_hv, tile_bs, tile_ws, f1, f2_lv, f2_hv, f3, f2b use mpi use modmpi, only : my_real,mpi_sum,mpi_max,mpi_min,comm3d,mpierr,myid use modstat_nc, only : lnetcdf, writestat_nc,nc_fillvalue @@ -393,6 +404,8 @@ subroutine timestat real :: obuk_lv_av, obuk_hv_av, obuk_bs_av real :: ustar_lv_av, ustar_hv_av, ustar_bs_av real :: ra_lv_av, ra_hv_av, ra_bs_av + real :: f1_av, f2_lv_av, f2_hv_av, f3_av, f2b_av + real :: rs_lv_av, rs_hv_av, rs_bs_av integer:: i, j, k @@ -809,6 +822,16 @@ subroutine timestat ra_hv_av = mean_2d(tile_hv%ra) ra_bs_av = mean_2d(tile_bs%ra) + f1_av = mean_2d(f1) + f2_lv_av = mean_2d(f2_lv) + f2_hv_av = mean_2d(f2_hv) + f3_av = mean_2d(f3) + f2b_av = mean_2d(f2b) + + rs_lv_av = mean_2d(tile_lv%rs) + rs_hv_av = mean_2d(tile_hv%rs) + rs_bs_av = mean_2d(tile_bs%rs) + end if ! 9.8 write the results to output file @@ -914,15 +937,29 @@ subroutine timestat vars(23) = Hav vars(24) = LEav vars(25) = G0av + vars(26) = obuk_lv_av vars(27) = obuk_hv_av vars(28) = obuk_bs_av + vars(29) = ustar_lv_av vars(30) = ustar_hv_av vars(31) = ustar_bs_av + vars(32) = ra_lv_av vars(33) = ra_hv_av vars(34) = ra_bs_av + + vars(35) = f1_av + vars(36) = f2_lv_av + vars(37) = f2_hv_av + vars(38) = f3_av + vars(39) = f2b_av + + vars(40) = rs_lv_av + vars(41) = rs_hv_av + vars(42) = rs_bs_av + end if call writestat_nc(ncid,nvar,ncname,vars,nrec,.true.) From 3184f6b6cdcb1c0b052abfa5ee174b28900c3364 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Thu, 2 Jul 2020 16:32:56 +0200 Subject: [PATCH 14/31] Bugfix calculation canopy resistances --- src/modlsm.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index f3e407e7..efd531fa 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -226,8 +226,8 @@ subroutine calc_canopy_resistances f2b(i,j) = 1./min(1., max(1.e-9, theta_rel)) ! Calculate canopy and soil resistance - tile_lv%rs(i,j) = tile_lv%rs_min(i,j) / (tile_lv%lai(i,j) * f1(i,j) * f2_lv(i,j)) - tile_hv%rs(i,j) = tile_hv%rs_min(i,j) / (tile_hv%lai(i,j) * f1(i,j) * f2_hv(i,j) * f3(i,j)) + tile_lv%rs(i,j) = tile_lv%rs_min(i,j) / tile_lv%lai(i,j) * f1(i,j) * f2_lv(i,j) + tile_hv%rs(i,j) = tile_hv%rs_min(i,j) / tile_hv%lai(i,j) * f1(i,j) * f2_hv(i,j) * f3(i,j) tile_bs%rs(i,j) = tile_hv%rs_min(i,j) / f2b(i,j) tile_ws%rs(i,j) = 0. From a0d4cbfd713c13cde0e6c5ca545bb7daaf46b74d Mon Sep 17 00:00:00 2001 From: julietbravo Date: Fri, 3 Jul 2020 18:02:56 +0200 Subject: [PATCH 15/31] Working surface solver, with fluxes per tile --- src/modlsm.f90 | 114 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 14 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index efd531fa..840a03ee 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -67,6 +67,8 @@ module modlsm real, allocatable :: H(:,:), LE(:,:), G(:,:), wthl(:,:), wqt(:,:) ! Surface (potential) temperature and humidity: real, allocatable :: tskin(:,:), thlskin(:,:), qtskin(:,:) + ! Buoyancy difference surface - atmosphere + real, allocatable :: db(:,:) ! Vegetation properties: real, allocatable :: lai(:,:), rs_min(:,:), rs(:,:) ! Root fraction in soil @@ -111,18 +113,22 @@ subroutine lsm ! based on the amount of liquid water on vegetation. call calc_tile_fractions - ! Calculate root fraction weighted mean soil water content + ! Calculate root fraction weighted mean soil water content. call calc_theta_mean(tile_lv) call calc_theta_mean(tile_hv) - ! Calculate canopy/soil resistances - call calc_canopy_resistances - - ! Calculate Obukhov length, ustar, - ! and aerodynamic resistance, for all tiles: - call stability + ! Calculate canopy/soil resistances. + call calc_canopy_resistance + ! Calculate aerodynamic resistance (and u*, obuk). + call calc_stability + ! Solve the surface energy balance and calculate + ! surface fluxes (H, LE, G0, wthl, wqt) + call calc_fluxes(tile_lv) + call calc_fluxes(tile_hv) + call calc_fluxes(tile_bs) + !call calc_fluxes(tile_ws) ! Calculate soil tendencies @@ -183,7 +189,7 @@ end subroutine calc_theta_mean ! ! Calculate canopy and soil resistances ! -subroutine calc_canopy_resistances +subroutine calc_canopy_resistance use modglobal, only : i1, j1 use modfields, only : thl0, qt0, exnf, presf use modsurface, only : ps @@ -234,12 +240,12 @@ subroutine calc_canopy_resistances end do end do -end subroutine calc_canopy_resistances +end subroutine calc_canopy_resistance ! ! Calculate Obukhov length, ustar, and aerodynamic resistance, for all tiles ! -subroutine stability +subroutine calc_stability use modglobal, only : i1, j1, cu, cv, rv, rd use modfields, only : u0, v0, thl0, qt0 implicit none @@ -265,7 +271,7 @@ subroutine stability call calc_obuk_ustar_ra(tile_bs) call calc_obuk_ustar_ra(tile_ws) -end subroutine stability +end subroutine calc_stability ! ! Calculate Obukhov length and ustar, for single tile @@ -276,17 +282,17 @@ subroutine calc_obuk_ustar_ra(tile) type(lsm_tile), intent(inout) :: tile integer :: i, j - real :: thvs, db + real :: thvs do j=2,j1 do i=2,i1 ! Buoyancy difference surface - atmosphere thvs = tile%thlskin(i,j) * (1.+(rv/rd-1.)*tile%qtskin(i,j)) - db = grav/thvs * (thvs - thv_1(i,j)) + tile%db(i,j) = grav/thvs * (thvs - thv_1(i,j)) ! Iteratively find Obukhov length tile%obuk(i,j) = calc_obuk_dirichlet( & - tile%obuk(i,j), du_tot(i,j), db, zf(1), tile%z0m(i,j), tile%z0h(i,j)) + tile%obuk(i,j), du_tot(i,j), tile%db(i,j), zf(1), tile%z0m(i,j), tile%z0h(i,j)) end do end do @@ -300,8 +306,85 @@ subroutine calc_obuk_ustar_ra(tile) end subroutine calc_obuk_ustar_ra +! +! Solve SEB, calculating the new surface fluxes per tile +! +subroutine calc_fluxes(tile) + use modglobal, only : i1, j1, cp, rlv, boltz + use modfields, only : exnh, exnf, presh, thl0, qt0, rhof + use modraddata, only : swd, swu, lwd, lwu + use modsurfdata, only : ps, tsoil, Qnet + implicit none + + type(lsm_tile), intent(inout) :: tile + integer :: i, j + real :: Ts, thvs, esats, qsats, desatdTs, dqsatdTs, & + rs_lim, fH, fLE, fG, num, denom, Ta, qsat_new + + ! tmp + !real :: HLEG, lwu_new, Qnet_new + + do j=2, j1 + do i=2, i1 + ! Disable canopy resistance in case of dew fall + Ts = tile%thlskin(i,j) * exnh(1) + esats = 0.611e3 * exp(17.2694 * (Ts - 273.16) / (Ts - 35.86)) + qsats = 0.622 * esats / ps + if (qsats < qt0(i,j,1)) then + rs_lim = 0. + else + rs_lim = tile%rs(i,j) + end if + + ! Calculate recuring terms + ! NOTE: this should use the surface density, not rhof(1). + ! Not sure if rhoh is available somewhere... + fH = rhof(1) * cp / tile%ra(i,j) + fLE = rhof(1) * rlv / (tile%rs(i,j) + rs_lim) + + if (tile%db(i,j) > 0) then + fG = tile%lambda_stable(i,j) + else + fG = tile%lambda_unstable(i,j) + end if + + ! Net radiation; negative sign = net input of radiation at surface + Qnet(i,j) = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) + + ! Solve new skin temperature from SEB + desatdTs = esats * (17.2694 / (Ts - 35.86) - 17.2694 * (Ts - 273.16) / (Ts - 35.86)**2.) + dqsatdTs = 0.622 * desatdTs / ps + Ta = thl0(i,j,1) * exnf(1) + + num = -(Qnet(i,j) - lwu(i,j,1) & + - fH * Ta + (qsats - dqsatdTs * Ts - qt0(i,j,1)) * fLE & + - fG * tsoil(i,j,kmax_soil) - 3.*boltz * Ts**4) + denom = (fH + fLE * dqsatdTs + fG + 4.*boltz * Ts**3) + tile%tskin(i,j) = num / denom + + ! Update qsat with linearised relation, to make sure that the SEB closes + qsat_new = qsats + dqsatdTs * (tile%tskin(i,j) - Ts) + + ! Calculate surface fluxes + tile%H (i,j) = fH * (tile%tskin(i,j) - Ta) + tile%LE(i,j) = fLE * (qsat_new - qt0(i,j,1)) + tile%G (i,j) = fG * (tsoil(i,j,kmax_soil) - tile%tskin(i,j)) + + !HLEG = tile%H(i,j) + tile%LE(i,j) - tile%G(i,j) + !lwu_new = 1. * boltz * tile%tskin(i,j)**4. + !Qnet_new = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu_new + + !print*,'---------' + !print*,'tskin_old=', Ts, 'tskin_new=', tile%tskin(i,j) + !print*,'Qnet (old)=', -Qnet(i,j), 'H=', tile%H(i,j), 'LE=', tile%LE(i,j), 'G=', tile%G(i,j) + !print*,'Qnet (new)=', -Qnet_new, 'H+LE+G=', HLEG + + end do + end do + +end subroutine calc_fluxes ! @@ -723,6 +806,9 @@ subroutine allocate_tile(tile) allocate(tile % thlskin(i2, j2)) allocate(tile % qtskin(i2, j2)) + ! Buoyancy difference surface-atmosphere + allocate(tile % db(i2, j2)) + ! Vegetation properties: allocate(tile % lai(i2, j2)) allocate(tile % rs_min(i2, j2)) From 027b03b04beae696910fd87417e6de2900e13da0 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 6 Jul 2020 09:01:42 +0200 Subject: [PATCH 16/31] Added calculation of bulk (grid point mean) scalar BCs --- src/modlsm.f90 | 111 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 83 insertions(+), 28 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 840a03ee..b73ddd16 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -100,14 +100,8 @@ subroutine lsm if (.not. sw_lsm) return - ! tmp... - thlflux(:,:) = 0.1 - qtflux(:,:) = 0.1e-3 - G0(:,:) = 0. phiw_source(:,:,:) = 0. throughfall(:,:) = 0. - tile_bs%frac(:,:) = 0. - tile_bs%LE(:,:) = 0. ! Calculate dynamic tile fractions, ! based on the amount of liquid water on vegetation. @@ -123,13 +117,15 @@ subroutine lsm ! Calculate aerodynamic resistance (and u*, obuk). call calc_stability - ! Solve the surface energy balance and calculate - ! surface fluxes (H, LE, G0, wthl, wqt) - call calc_fluxes(tile_lv) - call calc_fluxes(tile_hv) - call calc_fluxes(tile_bs) - !call calc_fluxes(tile_ws) + ! Calculate surface temperature for each tile, and calculate + ! surface fluxes (H, LE, G0, wthl, wqt) and values (thlskin, qtskin) + call calc_tile_bcs(tile_lv) + call calc_tile_bcs(tile_hv) + call calc_tile_bcs(tile_bs) + call calc_tile_bcs(tile_ws) + ! Set grid point averaged boundary conditions (thls, qts, gradients, ..) + call calc_bulk_bcs ! Calculate soil tendencies ! Calc diffusivity heat: @@ -307,19 +303,24 @@ subroutine calc_obuk_ustar_ra(tile) end subroutine calc_obuk_ustar_ra ! -! Solve SEB, calculating the new surface fluxes per tile +! Calculate surface temperature for each tile, and calculate +! surface fluxes (H, LE, G0, wthl, wqt) and values (thlskin, qtskin) ! -subroutine calc_fluxes(tile) +subroutine calc_tile_bcs(tile) use modglobal, only : i1, j1, cp, rlv, boltz use modfields, only : exnh, exnf, presh, thl0, qt0, rhof use modraddata, only : swd, swu, lwd, lwu - use modsurfdata, only : ps, tsoil, Qnet + use modsurfdata, only : ps, tsoil implicit none type(lsm_tile), intent(inout) :: tile integer :: i, j real :: Ts, thvs, esats, qsats, desatdTs, dqsatdTs, & - rs_lim, fH, fLE, fG, num, denom, Ta, qsat_new + rs_lim, fH, fLE, fG, num, denom, Ta, qsat_new, & + rhocp_i, rholv_i, Qnet + + rhocp_i = 1. / (rhof(1) * cp) + rholv_i = 1. / (rhof(1) * rlv) ! tmp !real :: HLEG, lwu_new, Qnet_new @@ -342,7 +343,7 @@ subroutine calc_fluxes(tile) ! NOTE: this should use the surface density, not rhof(1). ! Not sure if rhoh is available somewhere... fH = rhof(1) * cp / tile%ra(i,j) - fLE = rhof(1) * rlv / (tile%rs(i,j) + rs_lim) + fLE = rhof(1) * rlv / (tile%ra(i,j) + rs_lim) if (tile%db(i,j) > 0) then fG = tile%lambda_stable(i,j) @@ -351,14 +352,14 @@ subroutine calc_fluxes(tile) end if ! Net radiation; negative sign = net input of radiation at surface - Qnet(i,j) = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) + Qnet = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) ! Solve new skin temperature from SEB desatdTs = esats * (17.2694 / (Ts - 35.86) - 17.2694 * (Ts - 273.16) / (Ts - 35.86)**2.) dqsatdTs = 0.622 * desatdTs / ps Ta = thl0(i,j,1) * exnf(1) - num = -(Qnet(i,j) - lwu(i,j,1) & + num = -(Qnet - lwu(i,j,1) & - fH * Ta + (qsats - dqsatdTs * Ts - qt0(i,j,1)) * fLE & - fG * tsoil(i,j,kmax_soil) - 3.*boltz * Ts**4) denom = (fH + fLE * dqsatdTs + fG + 4.*boltz * Ts**3) @@ -367,24 +368,78 @@ subroutine calc_fluxes(tile) ! Update qsat with linearised relation, to make sure that the SEB closes qsat_new = qsats + dqsatdTs * (tile%tskin(i,j) - Ts) - ! Calculate surface fluxes + ! Calculate energetic surface fluxes tile%H (i,j) = fH * (tile%tskin(i,j) - Ta) tile%LE(i,j) = fLE * (qsat_new - qt0(i,j,1)) tile%G (i,j) = fG * (tsoil(i,j,kmax_soil) - tile%tskin(i,j)) - !HLEG = tile%H(i,j) + tile%LE(i,j) - tile%G(i,j) - !lwu_new = 1. * boltz * tile%tskin(i,j)**4. - !Qnet_new = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu_new + ! Calculate kinematic surface fluxes + tile%wthl(i,j) = tile%H (i,j) * rhocp_i + tile%wqt (i,j) = tile%LE(i,j) * rholv_i + + ! Calculate surface values + tile%thlskin(i,j) = thl0(i,j,1) + tile%wthl(i,j) * tile%ra(i,j) + tile%qtskin (i,j) = qt0 (i,j,1) + tile%wqt (i,j) * tile%ra(i,j) + + end do + end do + +end subroutine calc_tile_bcs - !print*,'---------' - !print*,'tskin_old=', Ts, 'tskin_new=', tile%tskin(i,j) - !print*,'Qnet (old)=', -Qnet(i,j), 'H=', tile%H(i,j), 'LE=', tile%LE(i,j), 'G=', tile%G(i,j) - !print*,'Qnet (new)=', -Qnet_new, 'H+LE+G=', HLEG +! +! Set grid point mean boundary conditions, as used by +! the diffusion scheme, thermodynamics, ... +! +subroutine calc_bulk_bcs + use modglobal, only : i1, j1, cp, rlv, zf + use modfields, only : rhof, thl0, qt0 + use modsurfdata, only : H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz + implicit none + + integer :: i, j + real :: rhocp_i, rholv_i, ra + + rhocp_i = 1. / (rhof(1) * cp) + rholv_i = 1. / (rhof(1) * rlv) + + do j=2,j1 + do i=2,i1 + ! Calc grid point averaged quantities + H(i,j) = tile_lv%frac(i,j) * tile_lv%H(i,j) + & + tile_hv%frac(i,j) * tile_hv%H(i,j) + & + tile_bs%frac(i,j) * tile_bs%H(i,j) + & + tile_ws%frac(i,j) * tile_ws%H(i,j) + + LE(i,j) = tile_lv%frac(i,j) * tile_lv%LE(i,j) + & + tile_hv%frac(i,j) * tile_hv%LE(i,j) + & + tile_bs%frac(i,j) * tile_bs%LE(i,j) + & + tile_ws%frac(i,j) * tile_ws%LE(i,j) + + G0(i,j) = tile_lv%frac(i,j) * tile_lv%G(i,j) + & + tile_hv%frac(i,j) * tile_hv%G(i,j) + & + tile_bs%frac(i,j) * tile_bs%G(i,j) + & + tile_ws%frac(i,j) * tile_ws%G(i,j) + + tskin(i,j) = tile_lv%frac(i,j) * tile_lv%thlskin(i,j) + & + tile_hv%frac(i,j) * tile_hv%thlskin(i,j) + & + tile_bs%frac(i,j) * tile_bs%thlskin(i,j) + & + tile_ws%frac(i,j) * tile_ws%thlskin(i,j) + + qskin(i,j) = tile_lv%frac(i,j) * tile_lv%qtskin(i,j) + & + tile_hv%frac(i,j) * tile_hv%qtskin(i,j) + & + tile_bs%frac(i,j) * tile_bs%qtskin(i,j) + & + tile_ws%frac(i,j) * tile_ws%qtskin(i,j) + + thlflux(i,j) = H(i,j) * rhocp_i + qtflux (i,j) = LE(i,j) * rholv_i + + dthldz(i,j) = (thl0(i,j,1) - tskin(i,j)) / zf(1) + dqtdz (i,j) = (qt0 (i,j,1) - qskin(i,j)) / zf(1) end do end do -end subroutine calc_fluxes +end subroutine calc_bulk_bcs ! From 4891bb5c931e5f551d3b102ccefef3963281013c Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 6 Jul 2020 11:23:16 +0200 Subject: [PATCH 17/31] Added bulk (grid point mean) quantities needed for SGS model --- src/modlsm.f90 | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index b73ddd16..c7ced8fb 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -64,7 +64,8 @@ module modlsm ! Conductivity skin layer: real, allocatable :: lambda_stable(:,:), lambda_unstable(:,:) ! Surface fluxes: - real, allocatable :: H(:,:), LE(:,:), G(:,:), wthl(:,:), wqt(:,:) + real, allocatable :: H(:,:), LE(:,:), G(:,:) + real, allocatable :: wthl(:,:), wqt(:,:) ! Surface (potential) temperature and humidity: real, allocatable :: tskin(:,:), thlskin(:,:), qtskin(:,:) ! Buoyancy difference surface - atmosphere @@ -256,7 +257,7 @@ subroutine calc_stability do i=2,i1 du = 0.5*(u0(i,j,1) + u0(i+1,j,1)) + cu dv = 0.5*(v0(i,j,1) + v0(i,j+1,1)) + cv - du_tot(i,j) = sqrt(du**2 + dv**2) + du_tot(i,j) = max(0.1, sqrt(du**2 + dv**2)) thv_1(i,j) = thl0(i,j,1) * (1.+(rv/rd-1.)*qt0(i,j,1)) end do @@ -274,6 +275,7 @@ end subroutine calc_stability ! subroutine calc_obuk_ustar_ra(tile) use modglobal, only : i1, j1, rd, rv, grav, zf + use modfields, only : u0, v0 implicit none type(lsm_tile), intent(inout) :: tile @@ -391,13 +393,15 @@ end subroutine calc_tile_bcs ! the diffusion scheme, thermodynamics, ... ! subroutine calc_bulk_bcs - use modglobal, only : i1, j1, cp, rlv, zf - use modfields, only : rhof, thl0, qt0 - use modsurfdata, only : H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz + use modglobal, only : i1, j1, cp, rlv, fkar, zf, cu, cv + use modfields, only : rhof, thl0, qt0, u0, v0 + use modsurface, only : phim, phih + use modsurfdata, only : & + H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz, dudz, dvdz, ustar, obl implicit none integer :: i, j - real :: rhocp_i, rholv_i, ra + real :: rhocp_i, rholv_i, ra, ucu, vcv rhocp_i = 1. / (rhof(1) * cp) rholv_i = 1. / (rhof(1) * rlv) @@ -420,6 +424,16 @@ subroutine calc_bulk_bcs tile_bs%frac(i,j) * tile_bs%G(i,j) + & tile_ws%frac(i,j) * tile_ws%G(i,j) + ustar(i,j) = tile_lv%frac(i,j) * tile_lv%ustar(i,j) + & + tile_hv%frac(i,j) * tile_hv%ustar(i,j) + & + tile_bs%frac(i,j) * tile_bs%ustar(i,j) + & + tile_ws%frac(i,j) * tile_ws%ustar(i,j) + + obl(i,j) = tile_lv%frac(i,j) * tile_lv%obuk(i,j) + & + tile_hv%frac(i,j) * tile_hv%obuk(i,j) + & + tile_bs%frac(i,j) * tile_bs%obuk(i,j) + & + tile_ws%frac(i,j) * tile_ws%obuk(i,j) + tskin(i,j) = tile_lv%frac(i,j) * tile_lv%thlskin(i,j) + & tile_hv%frac(i,j) * tile_hv%thlskin(i,j) + & tile_bs%frac(i,j) * tile_bs%thlskin(i,j) + & @@ -430,18 +444,25 @@ subroutine calc_bulk_bcs tile_bs%frac(i,j) * tile_bs%qtskin(i,j) + & tile_ws%frac(i,j) * tile_ws%qtskin(i,j) + ! Kinematic surface fluxes thlflux(i,j) = H(i,j) * rhocp_i qtflux (i,j) = LE(i,j) * rholv_i - dthldz(i,j) = (thl0(i,j,1) - tskin(i,j)) / zf(1) - dqtdz (i,j) = (qt0 (i,j,1) - qskin(i,j)) / zf(1) + ! MO gradients + dthldz(i,j) = -thlflux(i,j) / (fkar * zf(1) * ustar(i,j)) * phih(zf(1)/obl(i,j)) + dqtdz (i,j) = -qtflux (i,j) / (fkar * zf(1) * ustar(i,j)) * phih(zf(1)/obl(i,j)) + ! NOTE: dudz, dvdz are at the grid center (full level), not the velocity locations. + ucu = 0.5*(u0(i,j,1) + u0(i+1,j,1))+cu + vcv = 0.5*(v0(i,j,1) + v0(i,j+1,1))+cv + + dudz(i,j) = ustar(i,j) / (fkar * zf(1)) * phim(zf(1)/obl(i,j)) * (ucu/du_tot(i,j)) + dvdz(i,j) = ustar(i,j) / (fkar * zf(1)) * phim(zf(1)/obl(i,j)) * (vcv/du_tot(i,j)) end do end do end subroutine calc_bulk_bcs - ! ! Calculate temperature diffusivity soil at full and half levels ! From 491b3933dea52377d2fcdbb6f0e35e46603d1f5b Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 6 Jul 2020 12:21:21 +0200 Subject: [PATCH 18/31] Added root water extraction to soil water budget --- src/modlsm.f90 | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index c7ced8fb..29bb43e5 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -101,7 +101,6 @@ subroutine lsm if (.not. sw_lsm) return - phiw_source(:,:,:) = 0. throughfall(:,:) = 0. ! Calculate dynamic tile fractions, @@ -136,6 +135,8 @@ subroutine lsm ! Calc diffusivity and conductivity soil moisture: call calc_hydraulic_properties + ! Calculate tendency due to root water extraction + call calc_root_water_extraction ! Solve diffusion equation: call integrate_theta_soil @@ -567,6 +568,43 @@ subroutine calc_hydraulic_properties end subroutine calc_hydraulic_properties +! +! Calculate soil moisture tendency from root water extraction +! +subroutine calc_root_water_extraction + use modglobal, only : i1, j1, rhow, rlv + use modsurfdata, only : phiw + implicit none + + integer :: i, j, k + real :: phiw_rf_lv, phiw_rf_hv, phi_frac_lv, phi_frac_hv, LE_lv, LE_hv + real, parameter :: fac = 1./(rhow * rlv) + + do j=2, j1 + do i=2, i1 + + LE_lv = tile_lv%frac(i,j) * tile_lv%LE(i,j) + LE_hv = tile_hv%frac(i,j) * tile_hv%LE(i,j) + + phiw_rf_lv = 0. + phiw_rf_hv = 0. + do k=1, kmax_soil + phiw_rf_lv = phiw_rf_lv + tile_lv%root_frac(i,j,k) * phiw(i,j,k) + phiw_rf_hv = phiw_rf_hv + tile_hv%root_frac(i,j,k) * phiw(i,j,k) + end do + + do k=1, kmax_soil + phi_frac_lv = tile_lv%root_frac(i,j,k) * phiw(i,j,k) / phiw_rf_lv + phi_frac_hv = tile_hv%root_frac(i,j,k) * phiw(i,j,k) / phiw_rf_hv + + phiw_source(i,j,k) = -max(0., LE_lv) * fac * dzi_soil(k) * phi_frac_lv & + -max(0., LE_hv) * fac * dzi_soil(k) * phi_frac_hv + end do + end do + end do + +end subroutine calc_root_water_extraction + ! ! Solve diffusion equation (explicit) for soil temperature. ! Top flux = G0/rho_C, bottom flux = zero. From a2e51fbee5cf9af33e742db938884f40a6d4310c Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 6 Jul 2020 16:29:59 +0200 Subject: [PATCH 19/31] Added calculation of liquid water reservoir (plus bugfix ra calculation) --- src/modlsm.f90 | 110 ++++++++++++++++++++++++++++++++++++++++---- src/modtimestat.f90 | 104 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 203 insertions(+), 11 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 29bb43e5..4b39b3f9 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -41,6 +41,8 @@ module modlsm ! Precipitation, interception et al. real, allocatable :: throughfall(:,:) + real, allocatable :: interception(:,:) + real, allocatable :: wl_max(:,:) ! Dependency factor canopy resistance on VPD (high veg only) real, allocatable :: gD(:,:) @@ -101,8 +103,6 @@ subroutine lsm if (.not. sw_lsm) return - throughfall(:,:) = 0. - ! Calculate dynamic tile fractions, ! based on the amount of liquid water on vegetation. call calc_tile_fractions @@ -140,22 +140,97 @@ subroutine lsm ! Solve diffusion equation: call integrate_theta_soil + ! Update liquid water reservoir + call calc_liquid_reservoir + end subroutine lsm ! ! Calculate dynamic tile fractions, based on liquid water on vegetation ! subroutine calc_tile_fractions + use modglobal, only : i1, j1 + use modsurfdata, only : wl, wmax implicit none - ! Not so dynamic right now..... - tile_lv%frac(:,:) = tile_lv%base_frac(:,:) - tile_hv%frac(:,:) = tile_hv%base_frac(:,:) - tile_bs%frac(:,:) = tile_bs%base_frac(:,:) - tile_ws%frac(:,:) = tile_ws%base_frac(:,:) + integer :: i, j + + do j=2, j1 + do i=2, i1 + tile_ws%frac(i,j) = min(1., wl(i,j) / wl_max(i,j)) + tile_lv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_lv%base_frac(i,j) + tile_hv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_hv%base_frac(i,j) + tile_bs%frac(i,j) = (1.-tile_ws%frac(i,j)) * (1. - tile_lv%base_frac(i,j) - tile_hv%base_frac(i,j)) + end do + end do end subroutine calc_tile_fractions +! +! Calculate changes in the liquid water reservoir +! +subroutine calc_liquid_reservoir + use modglobal, only : rk3step, rdt, i1, j1, rhow, rlv + use modsurfdata, only : wl, wlm + use modmicrodata, only : imicro, sed_qr + implicit none + + integer :: i, j + real :: tend, rk3coef, rainrate, c_veg, wl_tend_max, wl_tend_min + real :: wl_tend_liq, wl_tend_dew, wl_tend_precip, wl_tend_sum, wl_tend_lim + + real, parameter :: intercept_eff = 0.5 + real, parameter :: to_ms = 1./(rhow*rlv) + + rk3coef = rdt / (4. - dble(rk3step)) + if(rk3step == 1) wlm(:,:) = wl(:,:) + + do j=2, j1 + do i=2, i1 + c_veg = tile_lv%base_frac(i,j)+tile_hv%base_frac(i,j) + + ! Max and min possible tendencies + wl_tend_min = -wlm(i,j) / rk3coef + wl_tend_max = (wl_max(i,j) - wlm(i,j)) / rk3coef + + ! Tendency due to evaporation from liquid water reservoir/tile. + wl_tend_liq = -max(0., tile_ws%frac(i,j) * tile_ws%LE(i,j) * to_ms) + + ! Tendency due to dewfall into vegetation/soil/liquid water tiles + wl_tend_dew = & + -( min(0., tile_lv%frac(i,j) * tile_lv%LE(i,j) * to_ms) + & + min(0., tile_hv%frac(i,j) * tile_hv%LE(i,j) * to_ms) + & + min(0., tile_bs%frac(i,j) * tile_bs%LE(i,j) * to_ms) + & + min(0., tile_ws%frac(i,j) * tile_ws%LE(i,j) * to_ms) ) + + ! Tendency due to interception of precipitation by vegetation + if (imicro == 0) then + rainrate = 0. + else + rainrate = -sed_qr(i,j,1)/rhow + end if + wl_tend_precip = intercept_eff * c_veg * rainrate + + ! Total and limited tendencies + wl_tend_sum = wl_tend_liq + wl_tend_dew + wl_tend_precip; + wl_tend_lim = min(wl_tend_max, max(wl_tend_min, wl_tend_sum)); + + ! Diagnose interception and throughfall + throughfall(i,j) = & + -(1.-c_veg) * rainrate & + -(1.-intercept_eff) * c_veg * rainrate & + + min(0., wl_tend_lim - wl_tend_sum) + interception(i,j) = max(0., wl_tend_lim) + + ! Integrate + wl(i,j) = wlm(i,j) + rk3coef * wl_tend_lim + + end do + end do + +end subroutine calc_liquid_reservoir + + ! ! Calculate root fraction weighted mean soil water content ! @@ -299,7 +374,7 @@ subroutine calc_obuk_ustar_ra(tile) do i=2,i1 ! Calculate friction velocity and aerodynamic resistance tile%ustar(i,j) = du_tot(i,j) * fm(zf(1), tile%z0m(i,j), tile%obuk(i,j)) - tile%ra(i,j) = 1./tile%ustar(i,j) * fh(zf(1), tile%z0h(i,j), tile%obuk(i,j)) + tile%ra(i,j) = 1./(tile%ustar(i,j) * fh(zf(1), tile%z0h(i,j), tile%obuk(i,j))) end do end do @@ -831,7 +906,7 @@ subroutine allocate_fields use modsurfdata, only : & tsoil, tsoilm, phiw, phiwm, & lambda, lambdah, lambdas, lambdash, gammas, gammash, & - H, LE, G0, Qnet + H, LE, G0, Qnet, wl, wlm implicit none ! Allocate soil variables @@ -842,9 +917,15 @@ subroutine allocate_fields allocate(phiw (i2, j2, kmax_soil)) allocate(phiwm (i2, j2, kmax_soil)) + allocate(wl (i2, j2)) + allocate(wlm(i2, j2)) + + allocate(wl_max(i2, j2)) + allocate(phiw_source(i2, j2, kmax_soil)) allocate(throughfall(i2, j2)) + allocate(interception(i2, j2)) allocate(gD(i2, j2)) allocate(f1(i2, j2)) @@ -970,7 +1051,7 @@ end subroutine init_lsm_tiles subroutine init_homogeneous use modglobal, only : ifnamopt, fname_options, checknamelisterror use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer - use modsurfdata, only : tsoil, tsoilm, phiw, phiwm + use modsurfdata, only : tsoil, tsoilm, phiw, phiwm, wl, wlm, wmax implicit none integer :: ierr, k @@ -1096,12 +1177,21 @@ subroutine init_homogeneous phiwm (:,:,:) = phiw (:,:,:) ! Set properties wet skin tile + wl(:,:) = 0.2-3 + wlm(:,:) = 0.2-3 + tile_ws % z0m(:,:) = c_low*z0m_low + c_high*z0m_high + c_bare*z0m_bare tile_ws % z0h(:,:) = c_low*z0h_low + c_high*z0h_high + c_bare*z0h_bare tile_ws % lambda_stable(:,:) = c_low*lambda_s_low + c_high*lambda_s_high + c_bare*lambda_s_bare tile_ws % lambda_unstable(:,:) = c_low*lambda_us_low + c_high*lambda_us_high + c_bare*lambda_us_bare + ! Max liquid water per grid point, accounting for LAI + wl_max(:,:) = wmax * ( & + tile_lv%base_frac(:,:) * tile_lv%lai(:,:) + & + tile_hv%base_frac(:,:) * tile_hv%lai(:,:) + & + tile_bs%base_frac(:,:)) + ! Cleanup! deallocate(t_soil_p, theta_soil_p) diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index 9216b031..7a71b7a9 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -233,7 +233,7 @@ subroutine inittimestat if (isurf == 1) then nvar = 32 else if (isurf == 11) then - nvar = 42 + nvar = 67 else nvar = 21 end if @@ -304,6 +304,38 @@ subroutine inittimestat call ncinfo(ncname(41,:),'rs_hv','Canopy resistance low veg','s m-1','time') call ncinfo(ncname(42,:),'rs_bs','Soil resistance','s m-1','time') + call ncinfo(ncname(43,:),'c_lv','Tile fraction low vegetation','-','time') + call ncinfo(ncname(44,:),'c_hv','Tile fraction high vegetation','-','time') + call ncinfo(ncname(45,:),'c_bs','Tile fraction bare soil','-','time') + call ncinfo(ncname(46,:),'c_ws','Tile fraction wet skin','-','time') + + call ncinfo(ncname(47,:),'wl','Liquid water reservoir','m','time') + + call ncinfo(ncname(48,:),'H_lv','Sensible heat flux low vegetation','W m-2','time') + call ncinfo(ncname(49,:),'H_hv','Sensible heat flux high vegetation','W m-2','time') + call ncinfo(ncname(50,:),'H_bs','Sensible heat flux bare soil','W m-2','time') + call ncinfo(ncname(51,:),'H_ws','Sensible heat flux wet skin','W m-2','time') + + call ncinfo(ncname(52,:),'LE_lv','Latent heat flux low vegetation','W m-2','time') + call ncinfo(ncname(53,:),'LE_hv','Latent heat flux high vegetation','W m-2','time') + call ncinfo(ncname(54,:),'LE_bs','Latent heat flux bare soil','W m-2','time') + call ncinfo(ncname(55,:),'LE_ws','Latent heat flux wet skin','W m-2','time') + + call ncinfo(ncname(56,:),'G_lv','Soil heat flux low vegetation','W m-2','time') + call ncinfo(ncname(57,:),'G_hv','Soil heat flux high vegetation','W m-2','time') + call ncinfo(ncname(58,:),'G_bs','Soil heat flux bare soil','W m-2','time') + call ncinfo(ncname(59,:),'G_ws','Soil heat flux wet skin','W m-2','time') + + call ncinfo(ncname(60,:),'thlskin_lv','Skin potential temperature low vegetation','K','time') + call ncinfo(ncname(61,:),'thlskin_hv','Skin potential temperature high vegetation','K','time') + call ncinfo(ncname(62,:),'thlskin_bs','Skin potential temperature bare soil','K','time') + call ncinfo(ncname(63,:),'thlskin_ws','Skin potential temperature wet skin','K','time') + + call ncinfo(ncname(64,:),'qtskin_lv','Skin specific humidity low vegetation','kg kg-1','time') + call ncinfo(ncname(65,:),'qtskin_hv','Skin specific humidity high vegetation','kg kg-1','time') + call ncinfo(ncname(66,:),'qtskin_bs','Skin specific humidity bare soil','kg kg-1','time') + call ncinfo(ncname(67,:),'qtskin_ws','Skin specific humidity wet skin','kg kg-1','time') + end if call open_nc(fname, ncid,nrec) @@ -406,6 +438,12 @@ subroutine timestat real :: ra_lv_av, ra_hv_av, ra_bs_av real :: f1_av, f2_lv_av, f2_hv_av, f3_av, f2b_av real :: rs_lv_av, rs_hv_av, rs_bs_av + real :: c_lv_av, c_hv_av, c_bs_av, c_ws_av + real :: H_lv_av, H_hv_av, H_bs_av, H_ws_av + real :: LE_lv_av, LE_hv_av, LE_bs_av, LE_ws_av + real :: G_lv_av, G_hv_av, G_bs_av, G_ws_av + real :: thlskin_lv_av, thlskin_hv_av, thlskin_bs_av, thlskin_ws_av + real :: qtskin_lv_av, qtskin_hv_av, qtskin_bs_av, qtskin_ws_av integer:: i, j, k @@ -832,6 +870,38 @@ subroutine timestat rs_hv_av = mean_2d(tile_hv%rs) rs_bs_av = mean_2d(tile_bs%rs) + c_lv_av = mean_2d(tile_lv%frac) + c_hv_av = mean_2d(tile_hv%frac) + c_bs_av = mean_2d(tile_bs%frac) + c_ws_av = mean_2d(tile_ws%frac) + + wlav = mean_2d(wl) + + H_lv_av = mean_2d(tile_lv%H) + H_hv_av = mean_2d(tile_hv%H) + H_bs_av = mean_2d(tile_bs%H) + H_ws_av = mean_2d(tile_ws%H) + + LE_lv_av = mean_2d(tile_lv%LE) + LE_hv_av = mean_2d(tile_hv%LE) + LE_bs_av = mean_2d(tile_bs%LE) + LE_ws_av = mean_2d(tile_ws%LE) + + G_lv_av = mean_2d(tile_lv%G) + G_hv_av = mean_2d(tile_hv%G) + G_bs_av = mean_2d(tile_bs%G) + G_ws_av = mean_2d(tile_ws%G) + + thlskin_lv_av = mean_2d(tile_lv%thlskin) + thlskin_hv_av = mean_2d(tile_hv%thlskin) + thlskin_bs_av = mean_2d(tile_bs%thlskin) + thlskin_ws_av = mean_2d(tile_ws%thlskin) + + qtskin_lv_av = mean_2d(tile_lv%qtskin) + qtskin_hv_av = mean_2d(tile_hv%qtskin) + qtskin_bs_av = mean_2d(tile_bs%qtskin) + qtskin_ws_av = mean_2d(tile_ws%qtskin) + end if ! 9.8 write the results to output file @@ -960,6 +1030,38 @@ subroutine timestat vars(41) = rs_hv_av vars(42) = rs_bs_av + vars(43) = c_lv_av + vars(44) = c_hv_av + vars(45) = c_bs_av + vars(46) = c_ws_av + + vars(47) = wlav + + vars(48) = H_lv_av + vars(49) = H_hv_av + vars(50) = H_bs_av + vars(51) = H_ws_av + + vars(52) = LE_lv_av + vars(53) = LE_hv_av + vars(54) = LE_bs_av + vars(55) = LE_ws_av + + vars(56) = G_lv_av + vars(57) = G_hv_av + vars(58) = G_bs_av + vars(59) = G_ws_av + + vars(60) = thlskin_lv_av + vars(61) = thlskin_hv_av + vars(62) = thlskin_bs_av + vars(63) = thlskin_ws_av + + vars(64) = qtskin_lv_av + vars(65) = qtskin_hv_av + vars(66) = qtskin_bs_av + vars(67) = qtskin_ws_av + end if call writestat_nc(ncid,nvar,ncname,vars,nrec,.true.) From cfc205ed523a6231acf95ecd90f99d7770f76f33 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 7 Jul 2020 11:00:32 +0200 Subject: [PATCH 20/31] Added old soil grid definition, different soil interpolation types, plus fixed bug in buoyancy calculation --- src/modlsm.f90 | 183 ++++++++++++++++++++++++++++++++------------ src/modsurface.f90 | 1 + src/modtimestat.f90 | 3 + 3 files changed, 136 insertions(+), 51 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 4b39b3f9..9f6dd228 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -23,8 +23,15 @@ module modlsm public :: initlsm, lsm, exitlsm, init_lsm_tiles - logical :: sw_lsm ! On/off switch LSM - logical :: sw_free_drainage ! Free drainage bottom BC for soil moisture + logical :: llsm ! On/off switch LSM + logical :: lfreedrainage ! Free drainage bottom BC for soil moisture + + ! Interpolation types soil from full to half level + integer :: iinterp_t, iinterp_theta + integer, parameter :: iinterp_amean = 1 ! val = 0.5*(v1+v2) + integer, parameter :: iinterp_gmean = 2 ! val = sqrt(v1*v2) + integer, parameter :: iinterp_hmean = 3 ! val = ((dz1+dz2)*v1*v2)/(dz1*v1+dz2*v2) + integer, parameter :: iinterp_max = 4 ! val = max(a,b) ! Soil grid integer :: kmax_soil @@ -101,7 +108,7 @@ subroutine lsm use modsurfdata, only : thlflux, qtflux, G0 implicit none - if (.not. sw_lsm) return + if (.not. llsm) return ! Calculate dynamic tile fractions, ! based on the amount of liquid water on vegetation. @@ -362,7 +369,8 @@ subroutine calc_obuk_ustar_ra(tile) do i=2,i1 ! Buoyancy difference surface - atmosphere thvs = tile%thlskin(i,j) * (1.+(rv/rd-1.)*tile%qtskin(i,j)) - tile%db(i,j) = grav/thvs * (thvs - thv_1(i,j)) + !tile%db(i,j) = grav/thvs * (thvs - thv_1(i,j)) + tile%db(i,j) = grav/thvs * (thv_1(i,j) - thvs) ! Iteratively find Obukhov length tile%obuk(i,j) = calc_obuk_dirichlet( & @@ -539,6 +547,58 @@ subroutine calc_bulk_bcs end subroutine calc_bulk_bcs +! +! Interpolation soil from full to half levels, +! using various interpolation methods +! +subroutine interpolate_soil(fieldh, field, iinterp) + use modglobal, only : i1, j1 + implicit none + real, intent(inout) :: fieldh(:,:,:) + real, intent(in) :: field(:,:,:) + integer, intent(in) :: iinterp + integer i, j, k + + if (iinterp == iinterp_amean) then + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + fieldh(i,j,k) = 0.5*(field(i,j,k-1) + field(i,j,k)) + end do + end do + end do + else if (iinterp == iinterp_gmean) then + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + fieldh(i,j,k) = sqrt(field(i,j,k-1) * field(i,j,k)) + end do + end do + end do + else if (iinterp == iinterp_hmean) then + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + fieldh(i,j,k) = ((dz_soil(k-1)+dz_soil(k))*field(i,j,k-1)*field(i,j,k)) / & + (dz_soil(k)*field(i,j,k) + dz_soil(k-1)*field(i,j,k-1)) + end do + end do + end do + else if (iinterp == iinterp_max) then + do k=2,kmax_soil + do j=2,j1 + do i=2,i1 + fieldh(i,j,k) = max(field(i,j,k-1), field(i,j,k)) + end do + end do + end do + else + print*,'ERROR: invalid soil interpolation type iinterp=',iinterp + stop + end if + +end subroutine interpolate_soil + ! ! Calculate temperature diffusivity soil at full and half levels ! @@ -574,13 +634,7 @@ subroutine calc_thermal_properties end do ! Interpolate to half levels - do k=2,kmax_soil - do j=2,j1 - do i=2,i1 - lambdah(i,j,k) = 0.5*(lambda(i,j,k-1) + lambda(i,j,k)) - end do - end do - end do + call interpolate_soil(lambdah, lambda, iinterp_t) end subroutine calc_thermal_properties @@ -623,19 +677,11 @@ subroutine calc_hydraulic_properties end do ! Interpolate to half levels - ! NOTE: this is the very conservative method used by IFS, - ! using the maximum value of two grid points at the interface. - do k=2,kmax_soil - do j=2,j1 - do i=2,i1 - lambdash(i,j,k) = max(lambdas(i,j,k-1), lambdas(i,j,k)) - gammash(i,j,k) = max(gammas (i,j,k-1), gammas (i,j,k)) - end do - end do - end do + call interpolate_soil(lambdash, lambdas, iinterp_theta) + call interpolate_soil(gammash, gammas, iinterp_theta) ! Optionally, set free drainage bottom BC - if (sw_free_drainage) then + if (lfreedrainage) then gammash(:,:,1) = gammash(:,:,2) else gammash(:,:,1) = 0. @@ -791,22 +837,22 @@ end subroutine integrate_theta_soil ! subroutine initlsm use modglobal, only : ifnamopt, fname_options, checknamelisterror, lwarmstart - use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real + use modmpi, only : myid, comm3d, mpierr, mpi_logical, mpi_integer, my_real use modsurfdata, only : isurf implicit none integer :: ierr - logical :: sw_homogeneous + logical :: lheterogeneous ! Namelist definition namelist /NAMLSM/ & - sw_homogeneous, sw_free_drainage, z_soil, z_size_soil + lheterogeneous, lfreedrainage, dz_soil, iinterp_t, iinterp_theta - sw_lsm = (isurf == 11) + llsm = (isurf == 11) - if (sw_lsm) then + if (llsm) then - allocate(z_soil(kmax_soil)) + allocate(dz_soil(kmax_soil)) ! Read namelist if (myid == 0) then @@ -818,12 +864,13 @@ subroutine initlsm end if ! Broadcast namelist values to all MPI tasks - call MPI_BCAST(sw_lsm, 1, mpi_logical, 0, comm3d, mpierr) - call MPI_BCAST(sw_homogeneous, 1, mpi_logical, 0, comm3d, mpierr) - call MPI_BCAST(sw_free_drainage, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(lheterogeneous, 1, mpi_logical, 0, comm3d, mpierr) + call MPI_BCAST(lfreedrainage, 1, mpi_logical, 0, comm3d, mpierr) + + call MPI_BCAST(iinterp_t, 1, mpi_integer, 0, comm3d, mpierr) + call MPI_BCAST(iinterp_theta, 1, mpi_integer, 0, comm3d, mpierr) - call MPI_BCAST(z_soil, kmax_soil, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z_size_soil, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(dz_soil, kmax_soil, my_real, 0, comm3d, mpierr) ! Create/calculate soil grid properties call create_soil_grid @@ -831,12 +878,12 @@ subroutine initlsm ! Allocate required fields in modsurfacedata, and arrays / tiles from this module: call allocate_fields - if (sw_homogeneous) then - ! Initialise homogeneous LSM from namelist input - call init_homogeneous - else + if (lheterogeneous) then ! Initialise heterogeneous LSM from external input stop 'Heterogeneous LSM not (yet) implemented!' + else + ! Initialise homogeneous LSM from namelist input + call init_homogeneous end if ! Read the soil parameter table @@ -856,6 +903,9 @@ end subroutine initlsm ! subroutine exitlsm implicit none + + if (.not. llsm) return + deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_theta_sat, vg_a, vg_l, vg_n ) end subroutine exitlsm @@ -866,23 +916,27 @@ subroutine create_soil_grid implicit none integer :: k - allocate(dz_soil (kmax_soil )) + allocate(z_soil (kmax_soil )) allocate(dzi_soil (kmax_soil )) allocate(zh_soil (kmax_soil+1)) allocate(dzh_soil (kmax_soil+1)) allocate(dzhi_soil(kmax_soil+1)) - ! Half level heights + ! + ! Full level is in the middle of two half levels + ! + z_size_soil = -sum(dz_soil) + + ! Half level height zh_soil(1) = z_size_soil zh_soil(kmax_soil+1) = 0. - do k=2, kmax_soil - zh_soil(k) = 0.5*(z_soil(k) + z_soil(k-1)) + zh_soil(k) = zh_soil(k-1) + dz_soil(k-1) end do - ! Grid spacing full and half levels + ! Full level height do k=1, kmax_soil - dz_soil(k) = zh_soil(k+1) - zh_soil(k) + z_soil(k) = 0.5*(zh_soil(k) + zh_soil(k+1)) end do do k=2, kmax_soil @@ -896,6 +950,33 @@ subroutine create_soil_grid dzi_soil(:) = 1./dz_soil(:) dzhi_soil(:) = 1./dzh_soil(:) + ! + ! Half level is in the middle of two full levels + ! + !! Half level heights + !zh_soil(1) = z_size_soil + !zh_soil(kmax_soil+1) = 0. + + !do k=2, kmax_soil + ! zh_soil(k) = 0.5*(z_soil(k) + z_soil(k-1)) + !end do + + !! Grid spacing full and half levels + !do k=1, kmax_soil + ! dz_soil(k) = zh_soil(k+1) - zh_soil(k) + !end do + + !do k=2, kmax_soil + ! dzh_soil(k) = z_soil(k) - z_soil(k-1) + !end do + + !dzh_soil(1) = 2*(z_soil(1) - zh_soil(1)) + !dzh_soil(kmax_soil+1) = 2*(-z_soil(kmax_soil)) + + !! Inverse grid spacings + !dzi_soil(:) = 1./dz_soil(:) + !dzhi_soil(:) = 1./dzh_soil(:) + end subroutine create_soil_grid ! @@ -1177,8 +1258,8 @@ subroutine init_homogeneous phiwm (:,:,:) = phiw (:,:,:) ! Set properties wet skin tile - wl(:,:) = 0.2-3 - wlm(:,:) = 0.2-3 + wl(:,:) = 0 + wlm(:,:) = 0 tile_ws % z0m(:,:) = c_low*z0m_low + c_high*z0m_high + c_bare*z0m_bare tile_ws % z0h(:,:) = c_low*z0h_low + c_high*z0h_high + c_bare*z0h_bare @@ -1351,12 +1432,12 @@ function calc_obuk_dirichlet(L_in, du, db_in, zsl, z0m, z0h) result(res) ! i.e. the `fx` equation below has no zero crossing. ! The limit of 0.13 typically results in a minimum (positive) L of ~1. ! IFS caps z/L at 5, so for a typical zsl at ~L=2. - Ri = fkar * db * zsl / du**2 - if (Ri > 0.13) then - print*,'WARNING: Ri out of range, returning L=1' - res = 1 - return - end if + !Ri = fkar * db * zsl / du**2 + !if (Ri > 0.13) then + ! print*,'WARNING: Ri out of range, returning L=1' + ! res = 1 + ! return + !end if ! Avoid buoyancy difference of zero: if (db >= 0) then diff --git a/src/modsurface.f90 b/src/modsurface.f90 index cefdb5bd..40746f07 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -873,6 +873,7 @@ subroutine surface dvdz (i,j) = ustar(i,j) * phimzf / (fkar*zf(1))*(vpcv/horv) dthldz(i,j) = - thlflux(i,j) / ustar(i,j) * phihzf / (fkar*zf(1)) dqtdz (i,j) = - qtflux(i,j) / ustar(i,j) * phihzf / (fkar*zf(1)) + end do end do diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index 7a71b7a9..293fefde 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -417,6 +417,7 @@ subroutine timestat use mpi use modmpi, only : my_real,mpi_sum,mpi_max,mpi_min,comm3d,mpierr,myid use modstat_nc, only : lnetcdf, writestat_nc,nc_fillvalue + use modraddata, only : swd, swu, lwd, lwu implicit none real :: zbaseavl, ztopavl, ztopmaxl, ztop,zbaseminl @@ -842,6 +843,8 @@ subroutine timestat else if(isurf == 11) then + Qnet(:,:) = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) + Qnetav = mean_2d(Qnet) Hav = mean_2d(H) LEav = mean_2d(LE) From b6249221bb89e298237f8e46e8fc6db1712b1512 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 7 Jul 2020 11:53:21 +0200 Subject: [PATCH 21/31] Added restart functionality to new LSM --- src/modlsm.f90 | 29 ++++++++++++++-------- src/modstartup.f90 | 62 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 9f6dd228..9b1cbd06 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -105,7 +105,6 @@ module modlsm contains subroutine lsm - use modsurfdata, only : thlflux, qtflux, G0 implicit none if (.not. llsm) return @@ -1130,7 +1129,7 @@ end subroutine init_lsm_tiles ! Initialise the LSM homogeneous from namelist input ! subroutine init_homogeneous - use modglobal, only : ifnamopt, fname_options, checknamelisterror + use modglobal, only : ifnamopt, fname_options, checknamelisterror, lwarmstart use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer use modsurfdata, only : tsoil, tsoilm, phiw, phiwm, wl, wlm, wmax implicit none @@ -1248,23 +1247,31 @@ subroutine init_homogeneous gD(:,:) = gD_high + if (.not. lwarmstart) then + ! Init prognostic variables in case of cold start. + ! For a warm start, these are read from restartfiles + ! in modstartup. + do k=1, kmax_soil + tsoil(:,:,k) = t_soil_p(k) + phiw (:,:,k) = theta_soil_p(k) + end do + + tsoilm(:,:,:) = tsoil(:,:,:) + phiwm (:,:,:) = phiw (:,:,:) + + wl(:,:) = 0. + wlm(:,:) = 0. + end if + do k=1, kmax_soil - tsoil(:,:,k) = t_soil_p(k) - phiw (:,:,k) = theta_soil_p(k) soil_index(:,:,k) = soil_index_p(k) end do - tsoilm(:,:,:) = tsoil(:,:,:) - phiwm (:,:,:) = phiw (:,:,:) - ! Set properties wet skin tile - wl(:,:) = 0 - wlm(:,:) = 0 - tile_ws % z0m(:,:) = c_low*z0m_low + c_high*z0m_high + c_bare*z0m_bare tile_ws % z0h(:,:) = c_low*z0h_low + c_high*z0h_high + c_bare*z0h_bare - tile_ws % lambda_stable(:,:) = c_low*lambda_s_low + c_high*lambda_s_high + c_bare*lambda_s_bare + tile_ws % lambda_stable(:,:) = c_low*lambda_s_low + c_high*lambda_s_high + c_bare*lambda_s_bare tile_ws % lambda_unstable(:,:) = c_low*lambda_us_low + c_high*lambda_us_high + c_bare*lambda_us_bare ! Max liquid water per grid point, accounting for LAI diff --git a/src/modstartup.f90 b/src/modstartup.f90 index e5b91eb5..beb2549d 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -848,6 +848,7 @@ subroutine readrestartfiles tres,ifinput,nsv,dt use modmpi, only : cmyid use modsubgriddata, only : ekm,ekh + use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws character(50) :: name @@ -956,7 +957,35 @@ subroutine readrestartfiles end if read(ifinput) timee close(ifinput) + + else if (isurf == 11) then + name(5:5) = 'l' + write(6,*) 'loading ',name + open(unit=ifinput,file=name,form='unformatted') + read(ifinput) (((tsoil(i,j,k), i=1,i2), j=1,j2), k=1,kmax_soil) + read(ifinput) (((phiw (i,j,k), i=1,i2), j=1,j2), k=1,kmax_soil) + read(ifinput) ((tskin (i,j), i=1,i2), j=1,j2) + read(ifinput) ((Wl (i,j), i=1,i2), j=1,j2) + + read(ifinput) ((tile_lv%thlskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_hv%thlskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_bs%thlskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_ws%thlskin(i,j), i=1,i2), j=1,j2) + + read(ifinput) ((tile_lv%qtskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_hv%qtskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_bs%qtskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_ws%qtskin(i,j), i=1,i2), j=1,j2) + + read(ifinput) ((tile_lv%obuk(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_hv%obuk(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_bs%obuk(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_ws%obuk(i,j), i=1,i2), j=1,j2) + + read(ifinput) timee + close(ifinput) end if + end subroutine readrestartfiles !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -996,13 +1025,13 @@ subroutine do_writerestartfiles use modglobal, only : i1,i2,ih,j1,j2,jh,k1,dsv,cexpnr,ifoutput,timee,rtimee,tres,nsv,dtheta,dqt,dt use modmpi, only : cmyid,myid use modsubgriddata, only : ekm,ekh + use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws implicit none integer imin,ihour integer i,j,k,n character(50) name,linkname - ihour = floor(rtimee/3600) imin = floor((rtimee-ihour * 3600) /3600. * 60.) name = 'initdXXXhXXmXXXXXXXX.XXX' @@ -1114,8 +1143,37 @@ subroutine do_writerestartfiles linkname = name linkname(6:11) = "latest" call system("cp "//name //" "//linkname) - end if + else if (isurf == 11) then + name(5:5)='l' + open (ifoutput,file=name,form='unformatted') + write(ifoutput) (((tsoil(i,j,k), i=1,i2), j=1,j2), k=1,kmax_soil) + write(ifoutput) (((phiw (i,j,k), i=1,i2), j=1,j2), k=1,kmax_soil) + write(ifoutput) ((tskin (i,j), i=1,i2), j=1,j2) + write(ifoutput) ((Wl (i,j), i=1,i2), j=1,j2) + + ! Sub-grid tiles + write(ifoutput) ((tile_lv%thlskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_hv%thlskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_bs%thlskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_ws%thlskin(i,j), i=1,i2), j=1,j2) + + write(ifoutput) ((tile_lv%qtskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_hv%qtskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_bs%qtskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_ws%qtskin(i,j), i=1,i2), j=1,j2) + + write(ifoutput) ((tile_lv%obuk(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_hv%obuk(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_bs%obuk(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_ws%obuk(i,j), i=1,i2), j=1,j2) + + write(ifoutput) timee + close (ifoutput) + linkname = name + linkname(6:11) = "latest" + call system("cp "//name //" "//linkname) + end if if (myid==0) then write(*,'(A,F15.7,A,I4)') 'dump at time = ',rtimee,' unit = ',ifoutput From a6ca5f1cf560df782fac24acbd7b5a3120c8f8af Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 7 Jul 2020 17:28:34 +0200 Subject: [PATCH 22/31] Added option to read the spatial data for heterogeneous land surfaces --- src/modlsm.f90 | 103 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 9b1cbd06..6525c903 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -479,6 +479,7 @@ subroutine calc_bulk_bcs use modglobal, only : i1, j1, cp, rlv, fkar, zf, cu, cv use modfields, only : rhof, thl0, qt0, u0, v0 use modsurface, only : phim, phih + use modmpi, only : excjs use modsurfdata, only : & H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz, dudz, dvdz, ustar, obl implicit none @@ -541,6 +542,10 @@ subroutine calc_bulk_bcs dudz(i,j) = ustar(i,j) / (fkar * zf(1)) * phim(zf(1)/obl(i,j)) * (ucu/du_tot(i,j)) dvdz(i,j) = ustar(i,j) / (fkar * zf(1)) * phim(zf(1)/obl(i,j)) * (vcv/du_tot(i,j)) + + ! Cyclic BCs where needed. + call excjs(ustar,2,i1,2,j1,1,1,1,1) + end do end do @@ -879,7 +884,7 @@ subroutine initlsm if (lheterogeneous) then ! Initialise heterogeneous LSM from external input - stop 'Heterogeneous LSM not (yet) implemented!' + call init_heterogeneous else ! Initialise homogeneous LSM from namelist input call init_homogeneous @@ -1285,6 +1290,102 @@ subroutine init_homogeneous end subroutine init_homogeneous +! +! Init LSM properties from external input +! +subroutine init_heterogeneous + use modglobal, only : i1, j1, lwarmstart, iexpnr + use modmpi, only : myidx, myidy + use modsurfdata, only : tsoil, phiw, wl, wlm, wmax + implicit none + + character(20) :: input_file = 'lsm.inp.x___y___.___' + write(input_file(10:12), '(i3.3)') myidx + write(input_file(14:16), '(i3.3)') myidy + write(input_file(18:20), '(i3.3)') iexpnr + + open(666, file=input_file, form='unformatted', status='unknown', action='read', access='stream') + + ! 2D surface fields + read(666) tile_lv%base_frac(2:i1, 2:j1) + read(666) tile_hv%base_frac(2:i1, 2:j1) + + read(666) tile_lv%z0m(2:i1, 2:j1) + read(666) tile_hv%z0m(2:i1, 2:j1) + read(666) tile_bs%z0m(2:i1, 2:j1) + + read(666) tile_lv%z0h(2:i1, 2:j1) + read(666) tile_hv%z0h(2:i1, 2:j1) + read(666) tile_bs%z0h(2:i1, 2:j1) + + read(666) tile_lv%lambda_stable(2:i1, 2:j1) + read(666) tile_hv%lambda_stable(2:i1, 2:j1) + read(666) tile_bs%lambda_stable(2:i1, 2:j1) + + read(666) tile_lv%lambda_unstable(2:i1, 2:j1) + read(666) tile_hv%lambda_unstable(2:i1, 2:j1) + read(666) tile_bs%lambda_unstable(2:i1, 2:j1) + + read(666) tile_lv%lai(2:i1, 2:j1) + read(666) tile_hv%lai(2:i1, 2:j1) + + read(666) tile_lv%rs_min(2:i1, 2:j1) + read(666) tile_hv%rs_min(2:i1, 2:j1) + read(666) tile_bs%rs_min(2:i1, 2:j1) + + read(666) tile_lv%a_r(2:i1, 2:j1) + read(666) tile_lv%b_r(2:i1, 2:j1) + read(666) tile_hv%a_r(2:i1, 2:j1) + read(666) tile_hv%b_r(2:i1, 2:j1) + + read(666) gD(2:i1, 2:j1) + + ! 3D soil fields + read(666) soil_index(2:i1, 2:j1, 1:kmax_soil) + + if (.not. lwarmstart) then + read(666) tsoil (2:i1, 2:j1, 1:kmax_soil) + read(666) phiw (2:i1, 2:j1, 1:kmax_soil) + + wl(:,:) = 0. + wlm(:,:) = 0. + end if + + close(666) + + ! Derived quantities + tile_bs%base_frac(:,:) = 1.-tile_lv%base_frac(:,:)-tile_hv%base_frac(:,:) + tile_ws%base_frac(:,:) = 0. + + ! Set properties wet skin tile + tile_ws%z0m(:,:) = & + tile_lv%base_frac(:,:)*tile_lv%z0m(:,:) + & + tile_hv%base_frac(:,:)*tile_lv%z0m(:,:) + & + tile_bs%base_frac(:,:)*tile_bs%z0m(:,:) + + tile_ws%z0h(:,:) = & + tile_lv%base_frac(:,:)*tile_lv%z0h(:,:) + & + tile_hv%base_frac(:,:)*tile_lv%z0h(:,:) + & + tile_bs%base_frac(:,:)*tile_bs%z0h(:,:) + + tile_ws%lambda_stable(:,:) = & + tile_lv%base_frac(:,:)*tile_lv%lambda_stable(:,:) + & + tile_hv%base_frac(:,:)*tile_lv%lambda_stable(:,:) + & + tile_bs%base_frac(:,:)*tile_bs%lambda_stable(:,:) + + tile_ws%lambda_unstable(:,:) = & + tile_lv%base_frac(:,:)*tile_lv%lambda_unstable(:,:) + & + tile_hv%base_frac(:,:)*tile_lv%lambda_unstable(:,:) + & + tile_bs%base_frac(:,:)*tile_bs%lambda_unstable(:,:) + + ! Max liquid water per grid point, accounting for LAI + wl_max(:,:) = wmax * ( & + tile_lv%base_frac(:,:) * tile_lv%lai(:,:) + & + tile_hv%base_frac(:,:) * tile_hv%lai(:,:) + & + tile_bs%base_frac(:,:)) + +end subroutine init_heterogeneous + ! ! Read the input table with the (van Genuchten) soil parameters ! From 3f8c6acd40aa18e299445adf92443f0452faecd6 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 20 Jul 2020 11:55:18 +0200 Subject: [PATCH 23/31] First ~working version with water tile (still has some issues...) --- src/modlsm.f90 | 293 +++++++++++++++++++++++++++++---------------- src/modsurface.f90 | 2 +- 2 files changed, 190 insertions(+), 105 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 6525c903..14820756 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -88,8 +88,8 @@ module modlsm real, allocatable :: phiw_mean(:,:) end type lsm_tile - ! Tiles for low veg (lv), high veg (hv), bare soil (bs), wet skin (ws): - type(lsm_tile) tile_lv, tile_hv, tile_bs, tile_ws + ! Tiles for low veg (lv), high veg (hv), bare soil (bs), wet skin (ws), water (aq): + type(lsm_tile) tile_lv, tile_hv, tile_bs, tile_ws, tile_aq ! Land-surface / van Genuchten parameters from NetCDF input table. real, allocatable :: & @@ -123,13 +123,6 @@ subroutine lsm ! Calculate aerodynamic resistance (and u*, obuk). call calc_stability - ! Calculate surface temperature for each tile, and calculate - ! surface fluxes (H, LE, G0, wthl, wqt) and values (thlskin, qtskin) - call calc_tile_bcs(tile_lv) - call calc_tile_bcs(tile_hv) - call calc_tile_bcs(tile_bs) - call calc_tile_bcs(tile_ws) - ! Set grid point averaged boundary conditions (thls, qts, gradients, ..) call calc_bulk_bcs @@ -147,7 +140,7 @@ subroutine lsm call integrate_theta_soil ! Update liquid water reservoir - call calc_liquid_reservoir + !call calc_liquid_reservoir end subroutine lsm @@ -161,14 +154,21 @@ subroutine calc_tile_fractions integer :: i, j - do j=2, j1 - do i=2, i1 - tile_ws%frac(i,j) = min(1., wl(i,j) / wl_max(i,j)) - tile_lv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_lv%base_frac(i,j) - tile_hv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_hv%base_frac(i,j) - tile_bs%frac(i,j) = (1.-tile_ws%frac(i,j)) * (1. - tile_lv%base_frac(i,j) - tile_hv%base_frac(i,j)) - end do - end do + ! BvS: tmp hack... + tile_ws%frac(:,:) = 0. + tile_lv%frac(:,:) = tile_lv%base_frac(:,:) + tile_hv%frac(:,:) = tile_hv%base_frac(:,:) + tile_bs%frac(:,:) = tile_bs%base_frac(:,:) + tile_aq%frac(:,:) = tile_aq%base_frac(:,:) + + !do j=2, j1 + ! do i=2, i1 + ! tile_ws%frac(i,j) = min(1., wl(i,j) / wl_max(i,j)) + ! tile_lv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_lv%base_frac(i,j) + ! tile_hv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_hv%base_frac(i,j) + ! tile_bs%frac(i,j) = (1.-tile_ws%frac(i,j)) * (1. - tile_lv%base_frac(i,j) - tile_hv%base_frac(i,j)) + ! end do + !end do end subroutine calc_tile_fractions @@ -349,6 +349,7 @@ subroutine calc_stability call calc_obuk_ustar_ra(tile_hv) call calc_obuk_ustar_ra(tile_bs) call calc_obuk_ustar_ra(tile_ws) + call calc_obuk_ustar_ra(tile_aq) end subroutine calc_stability @@ -366,22 +367,27 @@ subroutine calc_obuk_ustar_ra(tile) do j=2,j1 do i=2,i1 - ! Buoyancy difference surface - atmosphere - thvs = tile%thlskin(i,j) * (1.+(rv/rd-1.)*tile%qtskin(i,j)) - !tile%db(i,j) = grav/thvs * (thvs - thv_1(i,j)) - tile%db(i,j) = grav/thvs * (thv_1(i,j) - thvs) - - ! Iteratively find Obukhov length - tile%obuk(i,j) = calc_obuk_dirichlet( & - tile%obuk(i,j), du_tot(i,j), tile%db(i,j), zf(1), tile%z0m(i,j), tile%z0h(i,j)) + if (tile%frac(i,j) > 0) then + ! Buoyancy difference surface - atmosphere + thvs = tile%thlskin(i,j) * (1.+(rv/rd-1.)*tile%qtskin(i,j)) + !tile%db(i,j) = grav/thvs * (thvs - thv_1(i,j)) + tile%db(i,j) = grav/thvs * (thv_1(i,j) - thvs) + + ! Iteratively find Obukhov length + tile%obuk(i,j) = calc_obuk_dirichlet( & + tile%obuk(i,j), du_tot(i,j), tile%db(i,j), zf(1), tile%z0m(i,j), tile%z0h(i,j)) + + end if end do end do do j=2,j1 do i=2,i1 - ! Calculate friction velocity and aerodynamic resistance - tile%ustar(i,j) = du_tot(i,j) * fm(zf(1), tile%z0m(i,j), tile%obuk(i,j)) - tile%ra(i,j) = 1./(tile%ustar(i,j) * fh(zf(1), tile%z0h(i,j), tile%obuk(i,j))) + if (tile%frac(i,j) > 0) then + ! Calculate friction velocity and aerodynamic resistance + tile%ustar(i,j) = du_tot(i,j) * fm(zf(1), tile%z0m(i,j), tile%obuk(i,j)) + tile%ra(i,j) = 1./(tile%ustar(i,j) * fh(zf(1), tile%z0h(i,j), tile%obuk(i,j))) + end if end do end do @@ -412,64 +418,99 @@ subroutine calc_tile_bcs(tile) do j=2, j1 do i=2, i1 - - ! Disable canopy resistance in case of dew fall - Ts = tile%thlskin(i,j) * exnh(1) - esats = 0.611e3 * exp(17.2694 * (Ts - 273.16) / (Ts - 35.86)) - qsats = 0.622 * esats / ps - - if (qsats < qt0(i,j,1)) then - rs_lim = 0. - else - rs_lim = tile%rs(i,j) + if (tile%frac(i,j) > 0) then + + ! Disable canopy resistance in case of dew fall + Ts = tile%thlskin(i,j) * exnh(1) + esats = 0.611e3 * exp(17.2694 * (Ts - 273.16) / (Ts - 35.86)) + qsats = 0.622 * esats / ps + + if (qsats < qt0(i,j,1)) then + rs_lim = 0. + else + rs_lim = tile%rs(i,j) + end if + + ! Calculate recuring terms + ! NOTE: this should use the surface density, not rhof(1). + ! Not sure if rhoh is available somewhere... + fH = rhof(1) * cp / tile%ra(i,j) + fLE = rhof(1) * rlv / (tile%ra(i,j) + rs_lim) + + if (tile%db(i,j) > 0) then + fG = tile%lambda_stable(i,j) + else + fG = tile%lambda_unstable(i,j) + end if + + ! Net radiation; negative sign = net input of radiation at surface + Qnet = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) + + ! Solve new skin temperature from SEB + desatdTs = esats * (17.2694 / (Ts - 35.86) - 17.2694 * (Ts - 273.16) / (Ts - 35.86)**2.) + dqsatdTs = 0.622 * desatdTs / ps + Ta = thl0(i,j,1) * exnf(1) + + num = -(Qnet - lwu(i,j,1) & + - fH * Ta + (qsats - dqsatdTs * Ts - qt0(i,j,1)) * fLE & + - fG * tsoil(i,j,kmax_soil) - 3.*boltz * Ts**4) + denom = (fH + fLE * dqsatdTs + fG + 4.*boltz * Ts**3) + tile%tskin(i,j) = num / denom + + ! Update qsat with linearised relation, to make sure that the SEB closes + qsat_new = qsats + dqsatdTs * (tile%tskin(i,j) - Ts) + + ! Calculate energetic surface fluxes + tile%H (i,j) = fH * (tile%tskin(i,j) - Ta) + tile%LE(i,j) = fLE * (qsat_new - qt0(i,j,1)) + tile%G (i,j) = fG * (tsoil(i,j,kmax_soil) - tile%tskin(i,j)) + + ! Calculate kinematic surface fluxes + tile%wthl(i,j) = tile%H (i,j) * rhocp_i + tile%wqt (i,j) = tile%LE(i,j) * rholv_i + + ! Calculate surface values + tile%thlskin(i,j) = thl0(i,j,1) + tile%wthl(i,j) * tile%ra(i,j) + tile%qtskin (i,j) = qt0 (i,j,1) + tile%wqt (i,j) * tile%ra(i,j) end if + end do + end do - ! Calculate recuring terms - ! NOTE: this should use the surface density, not rhof(1). - ! Not sure if rhoh is available somewhere... - fH = rhof(1) * cp / tile%ra(i,j) - fLE = rhof(1) * rlv / (tile%ra(i,j) + rs_lim) - - if (tile%db(i,j) > 0) then - fG = tile%lambda_stable(i,j) - else - fG = tile%lambda_unstable(i,j) - end if - - ! Net radiation; negative sign = net input of radiation at surface - Qnet = swd(i,j,1) + swu(i,j,1) + lwd(i,j,1) + lwu(i,j,1) - - ! Solve new skin temperature from SEB - desatdTs = esats * (17.2694 / (Ts - 35.86) - 17.2694 * (Ts - 273.16) / (Ts - 35.86)**2.) - dqsatdTs = 0.622 * desatdTs / ps - Ta = thl0(i,j,1) * exnf(1) - - num = -(Qnet - lwu(i,j,1) & - - fH * Ta + (qsats - dqsatdTs * Ts - qt0(i,j,1)) * fLE & - - fG * tsoil(i,j,kmax_soil) - 3.*boltz * Ts**4) - denom = (fH + fLE * dqsatdTs + fG + 4.*boltz * Ts**3) - tile%tskin(i,j) = num / denom - - ! Update qsat with linearised relation, to make sure that the SEB closes - qsat_new = qsats + dqsatdTs * (tile%tskin(i,j) - Ts) - - ! Calculate energetic surface fluxes - tile%H (i,j) = fH * (tile%tskin(i,j) - Ta) - tile%LE(i,j) = fLE * (qsat_new - qt0(i,j,1)) - tile%G (i,j) = fG * (tsoil(i,j,kmax_soil) - tile%tskin(i,j)) +end subroutine calc_tile_bcs - ! Calculate kinematic surface fluxes - tile%wthl(i,j) = tile%H (i,j) * rhocp_i - tile%wqt (i,j) = tile%LE(i,j) * rholv_i +! +! Calculate BCs and fluxes for the water tile +! +subroutine calc_water_bcs + use modglobal, only : i1, j1, cp, rlv + use modfields, only : exnh, thl0, qt0, rhof + use modsurfdata, only : ps + implicit none - ! Calculate surface values - tile%thlskin(i,j) = thl0(i,j,1) + tile%wthl(i,j) * tile%ra(i,j) - tile%qtskin (i,j) = qt0 (i,j,1) + tile%wqt (i,j) * tile%ra(i,j) + integer :: i, j + real :: esats + do j=2, j1 + do i=2, i1 + if (tile_aq%frac(i,j) > 0) then + ! Calculate BCs. `tile_aq%tskin` is fixed in time (for now...) + tile_aq%thlskin(i,j) = tile_aq%tskin(i,j) / exnh(1) + esats = 0.611e3 * exp(17.2694 * (tile_aq%tskin(i,j) - 273.16) / (tile_aq%tskin(i,j) - 35.86)) + tile_aq%qtskin(i,j) = 0.622 * esats / ps + + ! Calculate kinematic fluxes + tile_aq%wthl(i,j) = 1./tile_aq%ra(i,j) * (tile_aq%thlskin(i,j) - thl0(i,j,1)) + tile_aq%wqt (i,j) = 1./tile_aq%ra(i,j) * (tile_aq%qtskin (i,j) - qt0 (i,j,1)) + + ! Calculate energetic fluxes + tile_aq%H (i,j) = tile_aq%wthl(i,j) * rhof(1) * cp + tile_aq%LE(i,j) = tile_aq%wqt (i,j) * rhof(1) * rlv + tile_aq%G (i,j) = 0. + end if end do end do -end subroutine calc_tile_bcs +end subroutine calc_water_bcs ! ! Set grid point mean boundary conditions, as used by @@ -490,43 +531,58 @@ subroutine calc_bulk_bcs rhocp_i = 1. / (rhof(1) * cp) rholv_i = 1. / (rhof(1) * rlv) + ! Calculate surface temperature for each tile, and calculate + ! surface fluxes (H, LE, G0, wthl, wqt) and values (thlskin, qtskin) + call calc_tile_bcs(tile_lv) + call calc_tile_bcs(tile_hv) + call calc_tile_bcs(tile_bs) + call calc_tile_bcs(tile_ws) + call calc_water_bcs + do j=2,j1 do i=2,i1 ! Calc grid point averaged quantities H(i,j) = tile_lv%frac(i,j) * tile_lv%H(i,j) + & tile_hv%frac(i,j) * tile_hv%H(i,j) + & tile_bs%frac(i,j) * tile_bs%H(i,j) + & - tile_ws%frac(i,j) * tile_ws%H(i,j) + tile_ws%frac(i,j) * tile_ws%H(i,j) + & + tile_aq%frac(i,j) * tile_aq%H(i,j) LE(i,j) = tile_lv%frac(i,j) * tile_lv%LE(i,j) + & tile_hv%frac(i,j) * tile_hv%LE(i,j) + & tile_bs%frac(i,j) * tile_bs%LE(i,j) + & - tile_ws%frac(i,j) * tile_ws%LE(i,j) + tile_ws%frac(i,j) * tile_ws%LE(i,j) + & + tile_aq%frac(i,j) * tile_aq%LE(i,j) G0(i,j) = tile_lv%frac(i,j) * tile_lv%G(i,j) + & tile_hv%frac(i,j) * tile_hv%G(i,j) + & tile_bs%frac(i,j) * tile_bs%G(i,j) + & - tile_ws%frac(i,j) * tile_ws%G(i,j) + tile_ws%frac(i,j) * tile_ws%G(i,j) + & + tile_aq%frac(i,j) * tile_aq%G(i,j) ustar(i,j) = tile_lv%frac(i,j) * tile_lv%ustar(i,j) + & tile_hv%frac(i,j) * tile_hv%ustar(i,j) + & tile_bs%frac(i,j) * tile_bs%ustar(i,j) + & - tile_ws%frac(i,j) * tile_ws%ustar(i,j) + tile_ws%frac(i,j) * tile_ws%ustar(i,j) + & + tile_aq%frac(i,j) * tile_aq%ustar(i,j) obl(i,j) = tile_lv%frac(i,j) * tile_lv%obuk(i,j) + & tile_hv%frac(i,j) * tile_hv%obuk(i,j) + & tile_bs%frac(i,j) * tile_bs%obuk(i,j) + & - tile_ws%frac(i,j) * tile_ws%obuk(i,j) + tile_ws%frac(i,j) * tile_ws%obuk(i,j) + & + tile_aq%frac(i,j) * tile_aq%obuk(i,j) tskin(i,j) = tile_lv%frac(i,j) * tile_lv%thlskin(i,j) + & tile_hv%frac(i,j) * tile_hv%thlskin(i,j) + & tile_bs%frac(i,j) * tile_bs%thlskin(i,j) + & - tile_ws%frac(i,j) * tile_ws%thlskin(i,j) + tile_ws%frac(i,j) * tile_ws%thlskin(i,j) + & + tile_aq%frac(i,j) * tile_aq%thlskin(i,j) qskin(i,j) = tile_lv%frac(i,j) * tile_lv%qtskin(i,j) + & tile_hv%frac(i,j) * tile_hv%qtskin(i,j) + & tile_bs%frac(i,j) * tile_bs%qtskin(i,j) + & - tile_ws%frac(i,j) * tile_ws%qtskin(i,j) + tile_ws%frac(i,j) * tile_ws%qtskin(i,j) + & + tile_aq%frac(i,j) * tile_aq%qtskin(i,j) ! Kinematic surface fluxes thlflux(i,j) = H(i,j) * rhocp_i @@ -991,7 +1047,8 @@ subroutine allocate_fields use modsurfdata, only : & tsoil, tsoilm, phiw, phiwm, & lambda, lambdah, lambdas, lambdash, gammas, gammash, & - H, LE, G0, Qnet, wl, wlm + H, LE, G0, Qnet, wl, wlm, & + tendskin, cliq, rsveg, rssoil implicit none ! Allocate soil variables @@ -1041,11 +1098,18 @@ subroutine allocate_fields allocate(LE (i2, j2)) allocate(G0 (i2, j2)) + ! TMP, to prevent segfaults in modlsmcrosssection.f90 + allocate(tendskin(i2, j2)) + allocate(cliq(i2, j2)) + allocate(rsveg(i2, j2)) + allocate(rssoil(i2, j2)) + ! Allocate the tiled variables call allocate_tile(tile_lv) call allocate_tile(tile_hv) call allocate_tile(tile_bs) call allocate_tile(tile_ws) + call allocate_tile(tile_aq) end subroutine allocate_fields @@ -1128,6 +1192,10 @@ subroutine init_lsm_tiles tile_ws % qtskin (:,:) = qtprof(1) tile_ws % obuk (:,:) = -0.1 + tile_aq % thlskin(:,:) = thlprof(1) + tile_aq % qtskin (:,:) = qtprof(1) + tile_aq % obuk (:,:) = -0.1 + end subroutine init_lsm_tiles ! @@ -1140,31 +1208,32 @@ subroutine init_homogeneous implicit none integer :: ierr, k - real :: c_low, c_high, c_bare - real :: z0m_low, z0m_high, z0m_bare - real :: z0h_low, z0h_high, z0h_bare + real :: c_low, c_high, c_bare, c_water + real :: z0m_low, z0m_high, z0m_bare, z0m_water + real :: z0h_low, z0h_high, z0h_bare, z0h_water real :: lambda_s_low, lambda_s_high, lambda_s_bare real :: lambda_us_low, lambda_us_high, lambda_us_bare real :: lai_low, lai_high real :: rs_min_low, rs_min_high, rs_min_bare real :: ar_low, br_low, ar_high, br_high real :: gD_high + real :: tskin_water real, allocatable :: t_soil_p(:), theta_soil_p(:) integer, allocatable :: soil_index_p(:) ! Read namelist namelist /NAMLSM_HOMOGENEOUS/ & - c_low, c_high, & - z0m_low, z0m_high, z0m_bare, & - z0h_low, z0h_high, z0h_bare, & + c_low, c_high, c_bare, c_water, & + z0m_low, z0m_high, z0m_bare, z0m_water, & + z0h_low, z0h_high, z0h_bare, z0h_water, & lambda_s_low, lambda_s_high, lambda_s_bare, & lambda_us_low, lambda_us_high, lambda_us_bare, & lai_low, lai_high, & rs_min_low, rs_min_high, rs_min_bare, & t_soil_p, theta_soil_p, soil_index_p, & ar_low, br_low, ar_high, br_high, & - gD_high + gD_high, tskin_water allocate(t_soil_p(kmax_soil), theta_soil_p(kmax_soil)) allocate(soil_index_p(kmax_soil)) @@ -1178,16 +1247,20 @@ subroutine init_homogeneous end if ! Broadcast to all MPI tasks - call MPI_BCAST(c_low, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(c_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(c_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(c_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(c_bare, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(c_water, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0m_low, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0m_high, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0m_bare, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_bare, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0m_water, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0h_low, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0h_high, 1, my_real, 0, comm3d, mpierr) - call MPI_BCAST(z0h_bare, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_low, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_bare, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(z0h_water, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(lambda_s_low, 1, my_real, 0, comm3d, mpierr) call MPI_BCAST(lambda_s_high, 1, my_real, 0, comm3d, mpierr) @@ -1215,20 +1288,24 @@ subroutine init_homogeneous call MPI_BCAST(gD_high, 1, my_real, 0, comm3d, mpierr) + call MPI_BCAST(tskin_water, 1, my_real, 0, comm3d, mpierr) + ! Set values - c_bare = 1.-c_low-c_high tile_lv % base_frac(:,:) = c_low tile_hv % base_frac(:,:) = c_high tile_bs % base_frac(:,:) = c_bare + tile_aq % base_frac(:,:) = c_water tile_ws % base_frac(:,:) = 0. tile_lv % z0m(:,:) = z0m_low tile_hv % z0m(:,:) = z0m_high tile_bs % z0m(:,:) = z0m_bare + tile_aq % z0m(:,:) = z0m_water tile_lv % z0h(:,:) = z0h_low tile_hv % z0h(:,:) = z0h_high tile_bs % z0h(:,:) = z0h_bare + tile_aq % z0h(:,:) = z0h_water tile_lv % lambda_stable(:,:) = lambda_s_low tile_hv % lambda_stable(:,:) = lambda_s_high @@ -1252,6 +1329,8 @@ subroutine init_homogeneous gD(:,:) = gD_high + tile_aq % tskin(:,:) = tskin_water + if (.not. lwarmstart) then ! Init prognostic variables in case of cold start. ! For a warm start, these are read from restartfiles @@ -1309,14 +1388,18 @@ subroutine init_heterogeneous ! 2D surface fields read(666) tile_lv%base_frac(2:i1, 2:j1) read(666) tile_hv%base_frac(2:i1, 2:j1) + read(666) tile_bs%base_frac(2:i1, 2:j1) + read(666) tile_aq%base_frac(2:i1, 2:j1) read(666) tile_lv%z0m(2:i1, 2:j1) read(666) tile_hv%z0m(2:i1, 2:j1) read(666) tile_bs%z0m(2:i1, 2:j1) + read(666) tile_aq%z0m(2:i1, 2:j1) read(666) tile_lv%z0h(2:i1, 2:j1) read(666) tile_hv%z0h(2:i1, 2:j1) read(666) tile_bs%z0h(2:i1, 2:j1) + read(666) tile_aq%z0h(2:i1, 2:j1) read(666) tile_lv%lambda_stable(2:i1, 2:j1) read(666) tile_hv%lambda_stable(2:i1, 2:j1) @@ -1340,6 +1423,8 @@ subroutine init_heterogeneous read(666) gD(2:i1, 2:j1) + read(666) tile_aq%tskin(2:i1, 2:j1) + ! 3D soil fields read(666) soil_index(2:i1, 2:j1, 1:kmax_soil) @@ -1354,7 +1439,6 @@ subroutine init_heterogeneous close(666) ! Derived quantities - tile_bs%base_frac(:,:) = 1.-tile_lv%base_frac(:,:)-tile_hv%base_frac(:,:) tile_ws%base_frac(:,:) = 0. ! Set properties wet skin tile @@ -1603,6 +1687,7 @@ function calc_obuk_dirichlet(L_in, du, db_in, zsl, z0m, z0h) result(res) if (m > 1) then print*,'WARNING: convergence has not been reached in Obukhov length iteration' print*,'Input: ', L_in, du, db_in, zsl, z0m, z0h + stop res = 1e-9 return end if diff --git a/src/modsurface.f90 b/src/modsurface.f90 index 40746f07..2608a443 100644 --- a/src/modsurface.f90 +++ b/src/modsurface.f90 @@ -608,8 +608,8 @@ subroutine initsurface end if allocate(rs(i2,j2)) + allocate(ra(i2,j2)) if(isurf <= 2) then - allocate(ra(i2,j2)) ! CvH set initial values for rs and ra to be able to compute qskin ra = 50. From 5925ee216ad89ba44f24da3598df7071416b8de6 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 21 Jul 2020 09:19:12 +0200 Subject: [PATCH 24/31] Fixed some MPI issues in new LSM --- src/modlsm.f90 | 63 +++++++++++++++++++++++++++++----------------- src/modstartup.f90 | 1 + 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 14820756..8284b2e8 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -1474,14 +1474,19 @@ end subroutine init_heterogeneous ! Read the input table with the (van Genuchten) soil parameters ! subroutine read_soil_table + use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer implicit none integer :: table_size, ncid, dimid, varid - ! Open the NetCDF file and read the table size - print*,'Reading "van_genuchten_parameters.nc"' - call check( nf90_open('van_genuchten_parameters.nc', nf90_nowrite, ncid) ) - call check( nf90_inq_dimid(ncid, 'index', dimid) ) - call check( nf90_inquire_dimension(ncid, dimid, len=table_size) ) + if (myid == 0) then + ! Open the NetCDF file and read the table size + print*,'Reading "van_genuchten_parameters.nc"' + call check( nf90_open('van_genuchten_parameters.nc', nf90_nowrite, ncid) ) + call check( nf90_inq_dimid(ncid, 'index', dimid) ) + call check( nf90_inquire_dimension(ncid, dimid, len=table_size) ) + end if + + call MPI_BCAST(table_size, 1, mpi_integer, 0, comm3d, mpierr) ! Allocate variables allocate( & @@ -1489,32 +1494,44 @@ subroutine read_soil_table theta_sat(table_size), gamma_theta_sat(table_size), & vg_a(table_size), vg_l(table_size), vg_n(table_size) ) - ! Read variables - call check( nf90_inq_varid(ncid, 'theta_res', varid) ) - call check( nf90_get_var(ncid, varid, theta_res) ) + if (myid == 0) then + ! Read variables + call check( nf90_inq_varid(ncid, 'theta_res', varid) ) + call check( nf90_get_var(ncid, varid, theta_res) ) + + call check( nf90_inq_varid(ncid, 'theta_wp', varid) ) + call check( nf90_get_var(ncid, varid, theta_wp) ) - call check( nf90_inq_varid(ncid, 'theta_wp', varid) ) - call check( nf90_get_var(ncid, varid, theta_wp) ) + call check( nf90_inq_varid(ncid, 'theta_fc', varid) ) + call check( nf90_get_var(ncid, varid, theta_fc) ) - call check( nf90_inq_varid(ncid, 'theta_fc', varid) ) - call check( nf90_get_var(ncid, varid, theta_fc) ) + call check( nf90_inq_varid(ncid, 'theta_sat', varid) ) + call check( nf90_get_var(ncid, varid, theta_sat) ) - call check( nf90_inq_varid(ncid, 'theta_sat', varid) ) - call check( nf90_get_var(ncid, varid, theta_sat) ) + call check( nf90_inq_varid(ncid, 'gamma_sat', varid) ) + call check( nf90_get_var(ncid, varid, gamma_theta_sat) ) - call check( nf90_inq_varid(ncid, 'gamma_sat', varid) ) - call check( nf90_get_var(ncid, varid, gamma_theta_sat) ) + call check( nf90_inq_varid(ncid, 'alpha', varid) ) + call check( nf90_get_var(ncid, varid, vg_a) ) - call check( nf90_inq_varid(ncid, 'alpha', varid) ) - call check( nf90_get_var(ncid, varid, vg_a) ) + call check( nf90_inq_varid(ncid, 'l', varid) ) + call check( nf90_get_var(ncid, varid, vg_l) ) - call check( nf90_inq_varid(ncid, 'l', varid) ) - call check( nf90_get_var(ncid, varid, vg_l) ) + call check( nf90_inq_varid(ncid, 'n', varid) ) + call check( nf90_get_var(ncid, varid, vg_n) ) - call check( nf90_inq_varid(ncid, 'n', varid) ) - call check( nf90_get_var(ncid, varid, vg_n) ) + call check( nf90_close(ncid) ) + end if - call check( nf90_close(ncid) ) + ! Broadcast to other MPI tasks + call MPI_BCAST(theta_res, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(theta_wp, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(theta_fc, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(theta_sat, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(gamma_theta_sat, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(vg_a, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(vg_l, table_size, my_real, 0, comm3d, mpierr) + call MPI_BCAST(vg_n, table_size, my_real, 0, comm3d, mpierr) end subroutine read_soil_table diff --git a/src/modstartup.f90 b/src/modstartup.f90 index beb2549d..967b4abb 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -175,6 +175,7 @@ subroutine startup(path) call MPI_BCAST(itot ,1,MPI_INTEGER,0,commwrld,mpierr) call MPI_BCAST(jtot ,1,MPI_INTEGER,0,commwrld,mpierr) call MPI_BCAST(kmax ,1,MPI_INTEGER,0,commwrld,mpierr) + call MPI_BCAST(kmax_soil ,1,MPI_INTEGER,0,commwrld,mpierr) call MPI_BCAST(xsize ,1,MY_REAL ,0,commwrld,mpierr) call MPI_BCAST(ysize ,1,MY_REAL ,0,commwrld,mpierr) call MPI_BCAST(xlat ,1,MY_REAL ,0,commwrld,mpierr) From 45c6b5efa2f84aa51b567930fe3216ffeae5ebdb Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 21 Jul 2020 12:45:14 +0200 Subject: [PATCH 25/31] Store/load the water tiles for restarts (results = bitwise identical now) --- src/modstartup.f90 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modstartup.f90 b/src/modstartup.f90 index 967b4abb..38b41556 100644 --- a/src/modstartup.f90 +++ b/src/modstartup.f90 @@ -849,7 +849,7 @@ subroutine readrestartfiles tres,ifinput,nsv,dt use modmpi, only : cmyid use modsubgriddata, only : ekm,ekh - use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws + use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws, tile_aq character(50) :: name @@ -972,16 +972,19 @@ subroutine readrestartfiles read(ifinput) ((tile_hv%thlskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_bs%thlskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_ws%thlskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_aq%thlskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_lv%qtskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_hv%qtskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_bs%qtskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_ws%qtskin(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_aq%qtskin(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_lv%obuk(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_hv%obuk(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_bs%obuk(i,j), i=1,i2), j=1,j2) read(ifinput) ((tile_ws%obuk(i,j), i=1,i2), j=1,j2) + read(ifinput) ((tile_aq%obuk(i,j), i=1,i2), j=1,j2) read(ifinput) timee close(ifinput) @@ -1026,7 +1029,7 @@ subroutine do_writerestartfiles use modglobal, only : i1,i2,ih,j1,j2,jh,k1,dsv,cexpnr,ifoutput,timee,rtimee,tres,nsv,dtheta,dqt,dt use modmpi, only : cmyid,myid use modsubgriddata, only : ekm,ekh - use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws + use modlsm, only : kmax_soil, tile_lv, tile_hv, tile_bs, tile_ws, tile_aq implicit none integer imin,ihour @@ -1158,16 +1161,19 @@ subroutine do_writerestartfiles write(ifoutput) ((tile_hv%thlskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_bs%thlskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_ws%thlskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_aq%thlskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_lv%qtskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_hv%qtskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_bs%qtskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_ws%qtskin(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_aq%qtskin(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_lv%obuk(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_hv%obuk(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_bs%obuk(i,j), i=1,i2), j=1,j2) write(ifoutput) ((tile_ws%obuk(i,j), i=1,i2), j=1,j2) + write(ifoutput) ((tile_aq%obuk(i,j), i=1,i2), j=1,j2) write(ifoutput) timee close (ifoutput) From 663791693c98267c075f49ee4d51d06b6e089dae Mon Sep 17 00:00:00 2001 From: julietbravo Date: Tue, 21 Jul 2020 14:12:40 +0200 Subject: [PATCH 26/31] Modified modlsmcrosssection for new LSM --- src/modlsm.f90 | 28 ++++++- src/modlsmcrosssection.f90 | 166 +++++++++++++++++++++++-------------- 2 files changed, 128 insertions(+), 66 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 8284b2e8..86f0248a 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -522,11 +522,12 @@ subroutine calc_bulk_bcs use modsurface, only : phim, phih use modmpi, only : excjs use modsurfdata, only : & - H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz, dudz, dvdz, ustar, obl + H, LE, G0, tskin, qskin, thlflux, qtflux, dthldz, dqtdz, & + dudz, dvdz, ustar, obl, cliq, ra, rsveg, rssoil implicit none integer :: i, j - real :: rhocp_i, rholv_i, ra, ucu, vcv + real :: rhocp_i, rholv_i, ucu, vcv, cveg rhocp_i = 1. / (rhof(1) * cp) rholv_i = 1. / (rhof(1) * rlv) @@ -602,6 +603,29 @@ subroutine calc_bulk_bcs ! Cyclic BCs where needed. call excjs(ustar,2,i1,2,j1,1,1,1,1) + ! Just for diagnostics (modlsmcrosssection) + cliq(i,j) = tile_ws%frac(i,j) + + ra(i,j) = tile_lv%frac(i,j) * tile_lv%ra(i,j) + & + tile_hv%frac(i,j) * tile_hv%ra(i,j) + & + tile_bs%frac(i,j) * tile_bs%ra(i,j) + & + tile_ws%frac(i,j) * tile_ws%ra(i,j) + & + tile_aq%frac(i,j) * tile_aq%ra(i,j) + + cveg = tile_lv%frac(i,j) + tile_hv%frac(i,j) + if (cveg > 0) then + rsveg(i,j) = (tile_lv%frac(i,j) * tile_lv%rs(i,j) + & + tile_hv%frac(i,j) * tile_hv%rs(i,j)) / cveg + else + rsveg(i,j) = 0. + end if + + if (tile_bs%frac(i,j) > 0) then + rssoil(i,j) = tile_bs%rs(i,j) + else + rssoil(i,j) = 0. + end if + end do end do diff --git a/src/modlsmcrosssection.f90 b/src/modlsmcrosssection.f90 index 95ac6ff7..42298378 100644 --- a/src/modlsmcrosssection.f90 +++ b/src/modlsmcrosssection.f90 @@ -37,7 +37,8 @@ module modlsmcrosssection PUBLIC :: initlsmcrosssection, lsmcrosssection,exitlsmcrosssection save !NetCDF variables - integer,parameter :: nvar = 2,nvar3=12 + integer,parameter :: nvar=2 + integer :: nvar3 integer :: ncid1 = 0 integer :: ncid2 = 0 integer :: ncid3 = 0 @@ -45,20 +46,16 @@ module modlsmcrosssection integer :: nrec2 = 0 integer :: nrec3 = 0 integer :: crossheight -! integer :: nxy = 0 -! integer :: cross -! integer :: nrc character(4) :: cheight character(80) :: fname1 = 'lsmcrossxz.xxxxyxxx.xxx.nc' character(80) :: fname2 = 'lsmcrossxy.xxxx.xxxxyxxx.xxx.nc' character(80) :: fname3 = 'surfcross.xxxxyxxx.xxx.nc' - character(80),dimension(nvar,4) :: ncname1 - character(80),dimension(1,4) :: tncname1 - character(80),dimension(nvar,4) :: ncname2 - character(80),dimension(1,4) :: tncname2 - character(80),dimension(nvar3,4) :: ncname3 - character(80),dimension(1,4) :: tncname3 - + character(80), dimension(nvar,4) :: ncname1 + character(80), dimension(1,4) :: tncname1 + character(80), dimension(nvar,4) :: ncname2 + character(80), dimension(1,4) :: tncname2 + character(80), allocatable, dimension(:,:) :: ncname3 + character(80), dimension(1,4) :: tncname3 real :: dtav integer(kind=longint) :: idtav,tnext @@ -69,10 +66,11 @@ module modlsmcrosssection !> Initializing lsmcrosssection. Read out the namelist, initializing the variables subroutine initlsmcrosssection use mpi - use modmpi, only :myid,my_real,mpierr,comm3d,mpi_logical,mpi_integer,cmyid - use modglobal,only :imax,jmax,ifnamopt,fname_options,dtmax,dtav_glob,ladaptive,j1,dt_lim,cexpnr,tres,btime,checknamelisterror - use modstat_nc,only : lnetcdf,open_nc, define_nc,ncinfo,nctiminfo,writestat_dims_nc - implicit none + use modmpi, only : myid,my_real,mpierr,comm3d,mpi_logical,mpi_integer,cmyid + use modglobal, only : imax,jmax,ifnamopt,fname_options,dtmax,dtav_glob,ladaptive,j1,dt_lim,cexpnr,tres,btime,checknamelisterror + use modstat_nc, only : lnetcdf,open_nc, define_nc,ncinfo,nctiminfo,writestat_dims_nc + use modsurfdata, only : isurf + implicit none integer :: ierr @@ -97,7 +95,6 @@ subroutine initlsmcrosssection call MPI_BCAST(crossheight,1,MPI_INTEGER,0,comm3d,mpierr) call MPI_BCAST(crossplane ,1,MPI_INTEGER,0,comm3d,mpierr) - idtav = dtav/tres tnext = idtav+btime if(.not.(lcross)) return @@ -137,28 +134,58 @@ subroutine initlsmcrosssection call define_nc( ncid2, NVar, ncname2) end if ! -! !Surface values +! ! Surface values fname3(11:18) = cmyid fname3(20:22) = cexpnr - call nctiminfo(tncname3(1,:)) - call ncinfo(ncname3( 1,:),'Qnet','Net radiation','W/m^2','tt0t') - call ncinfo(ncname3( 2,:),'H','Sensible heat flux','W/m^2','tt0t') - call ncinfo(ncname3( 3,:),'LE','Latent heat flux','W/m^2','tt0t') - call ncinfo(ncname3( 4,:),'G0','Ground heat flux','W/m^2','tt0t') - call ncinfo(ncname3( 5,:),'tskin','Skin temperature','K','tt0t') - call ncinfo(ncname3( 6,:),'tendskin','Skin tendency','W/m^2','tt0t') - call ncinfo(ncname3( 7,:),'rs','Surface resistance','s/m','tt0t') - call ncinfo(ncname3( 8,:),'ra','Aerodynamic resistance','s/m','tt0t') - call ncinfo(ncname3( 9,:),'cliq','Fraction of vegetated surface covered with liquid water','-','tt0t') - call ncinfo(ncname3(10,:),'Wl','Liquid water reservoir','m','tt0t') - call ncinfo(ncname3(11,:),'rssoil','Soil evaporation resistance','s/m','tt0t') - call ncinfo(ncname3(12,:),'rsveg','Vegitation resistance','s/m','tt0t') - call open_nc(fname3, ncid3,nrec3,n1=imax,n2=jmax) - if (nrec3==0) then - call define_nc( ncid3, 1, tncname3) - call writestat_dims_nc(ncid3) + + if (isurf == 1) then + nvar3 = 12 + else if (isurf == 11) then + nvar3 = 11 + end if + + allocate(ncname3(nvar3,4)) + + if (isurf == 1) then + call nctiminfo(tncname3(1,:)) + call ncinfo(ncname3( 1,:),'Qnet','Net radiation','W/m^2','tt0t') + call ncinfo(ncname3( 2,:),'H','Sensible heat flux','W/m^2','tt0t') + call ncinfo(ncname3( 3,:),'LE','Latent heat flux','W/m^2','tt0t') + call ncinfo(ncname3( 4,:),'G0','Ground heat flux','W/m^2','tt0t') + call ncinfo(ncname3( 5,:),'tskin','Skin temperature','K','tt0t') + call ncinfo(ncname3( 6,:),'tendskin','Skin tendency','W/m^2','tt0t') + call ncinfo(ncname3( 7,:),'rs','Surface resistance','s/m','tt0t') + call ncinfo(ncname3( 8,:),'ra','Aerodynamic resistance','s/m','tt0t') + call ncinfo(ncname3( 9,:),'cliq','Fraction of vegetated surface covered with liquid water','-','tt0t') + call ncinfo(ncname3(10,:),'Wl','Liquid water reservoir','m','tt0t') + call ncinfo(ncname3(11,:),'rssoil','Soil evaporation resistance','s/m','tt0t') + call ncinfo(ncname3(12,:),'rsveg','Vegitation resistance','s/m','tt0t') + call open_nc(fname3, ncid3, nrec3, n1=imax, n2=jmax) + if (nrec3==0) then + call define_nc(ncid3, 1, tncname3) + call writestat_dims_nc(ncid3) + end if + call define_nc(ncid3, nvar3, ncname3) + else if (isurf == 11) then + call nctiminfo(tncname3(1,:)) + call ncinfo(ncname3( 1,:),'H', 'Sensible heat flux', 'W/m^2', 'tt0t') + call ncinfo(ncname3( 2,:),'LE', 'Latent heat flux', 'W/m^2', 'tt0t') + call ncinfo(ncname3( 3,:),'G0', 'Ground heat flux', 'W/m^2', 'tt0t') + call ncinfo(ncname3( 4,:),'tskin', 'Skin temperature', 'K', 'tt0t') + call ncinfo(ncname3( 5,:),'obuk', 'Obukhov length', 'm', 'tt0t') + call ncinfo(ncname3( 6,:),'ustar', 'Friction velocity', 'm/s^-1', 'tt0t') + call ncinfo(ncname3( 7,:),'cliq', 'Fraction of vegetated surface covered with liquid water', '-', 'tt0t') + call ncinfo(ncname3( 8,:),'ra', 'Aerodynamic resistance', 's/m', 'tt0t') + call ncinfo(ncname3( 9,:),'rssoil', 'Soil evaporation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(10,:),'rsveg', 'Vegitation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(11,:),'wl', 'Liquid water reservoir', 'm', 'tt0t') + call open_nc(fname3, ncid3, nrec3, n1=imax, n2=jmax) + if (nrec3==0) then + call define_nc(ncid3, 1, tncname3) + call writestat_dims_nc(ncid3) + end if + call define_nc( ncid3, nvar3, ncname3) end if - call define_nc( ncid3, NVar3, ncname3) end if @@ -224,14 +251,10 @@ subroutine wrthorz use modstat_nc, only : lnetcdf, writestat_nc implicit none - ! LOCAL integer i,j real, allocatable :: vars(:,:,:) - - - write(cheight,'(i4.4)') crossheight open(ifoutput,file='movh_tsoil.'//cexpnr,position='append',action='write') write(ifoutput,'(es12.5)') ((tsoil(i,j,crossheight),i=2,i1),j=2,j1) @@ -251,24 +274,20 @@ subroutine wrthorz deallocate(vars) end if - end subroutine wrthorz + !> Do the xy lsmcrosssections and dump them to file subroutine wrtsurf use modglobal, only : imax,jmax,i1,j1,cexpnr,ifoutput,rtimee use modsurfdata, only : Qnet, H, LE, G0, rs, ra, tskin, tendskin, & - cliq,rsveg,rssoil,Wl + cliq,rsveg,rssoil,Wl, isurf, obl, ustar use modstat_nc, only : lnetcdf, writestat_nc implicit none - ! LOCAL integer i,j real, allocatable :: vars(:,:,:) - - - open(ifoutput,file='movh_qnet.'//cexpnr,position='append',action='write') write(ifoutput,'(es12.5)') ((qnet(i,j),i=2,i1),j=2,j1) close(ifoutput) @@ -317,23 +336,40 @@ subroutine wrtsurf write(ifoutput,'(es12.5)') ((wl(i,j),i=2,i1),j=2,j1) close(ifoutput) - if (lnetcdf) then allocate(vars(1:imax,1:jmax,nvar3)) - vars(:,:,1) = qnet(2:i1,2:j1) - vars(:,:,2) = h(2:i1,2:j1) - vars(:,:,3) = le(2:i1,2:j1) - vars(:,:,4) = g0(2:i1,2:j1) - vars(:,:,5) = tskin(2:i1,2:j1) - vars(:,:,6) = tendskin(2:i1,2:j1) - vars(:,:,7) = rs(2:i1,2:j1) - vars(:,:,8) = ra(2:i1,2:j1) - vars(:,:,9) = cliq(2:i1,2:j1) - vars(:,:,10) = Wl(2:i1,2:j1) - vars(:,:,11) = rssoil(2:i1,2:j1) - vars(:,:,12) = rsveg(2:i1,2:j1) - call writestat_nc(ncid3,1,tncname3,(/rtimee/),nrec3,.true.) - call writestat_nc(ncid3,nvar3,ncname3(1:nvar3,:),vars,nrec3,imax,jmax) + + if (isurf == 1) then + vars(:,:,1) = qnet(2:i1,2:j1) + vars(:,:,2) = h(2:i1,2:j1) + vars(:,:,3) = le(2:i1,2:j1) + vars(:,:,4) = g0(2:i1,2:j1) + vars(:,:,5) = tskin(2:i1,2:j1) + vars(:,:,6) = tendskin(2:i1,2:j1) + vars(:,:,7) = rs(2:i1,2:j1) + vars(:,:,8) = ra(2:i1,2:j1) + vars(:,:,9) = cliq(2:i1,2:j1) + vars(:,:,10) = Wl(2:i1,2:j1) + vars(:,:,11) = rssoil(2:i1,2:j1) + vars(:,:,12) = rsveg(2:i1,2:j1) + call writestat_nc(ncid3, 1, tncname3, (/rtimee/), nrec3, .true.) + call writestat_nc(ncid3, nvar3, ncname3(1:nvar3,:), vars, nrec3, imax, jmax) + else if (isurf == 11) then + vars(:,:, 1) = H(2:i1,2:j1) + vars(:,:, 2) = LE(2:i1,2:j1) + vars(:,:, 3) = G0(2:i1,2:j1) + vars(:,:, 4) = tskin(2:i1,2:j1) + vars(:,:, 5) = obl(2:i1,2:j1) + vars(:,:, 6) = ustar(2:i1,2:j1) + vars(:,:, 7) = cliq(2:i1,2:j1) + vars(:,:, 8) = ra(2:i1,2:j1) + vars(:,:, 9) = rssoil(2:i1,2:j1) + vars(:,:,10) = rsveg(2:i1,2:j1) + vars(:,:,11) = Wl(2:i1,2:j1) + call writestat_nc(ncid3, 1, tncname3, (/rtimee/), nrec3, .true.) + call writestat_nc(ncid3, nvar3, ncname3(1:nvar3,:), vars, nrec3, imax, jmax) + end if + deallocate(vars) end if @@ -346,10 +382,12 @@ subroutine exitlsmcrosssection implicit none if(lcross .and. lnetcdf) then - if (myid==0) then - call exitstat_nc(ncid1) - end if - call exitstat_nc(ncid2) + if (myid==0) then + call exitstat_nc(ncid1) + end if + call exitstat_nc(ncid2) + call exitstat_nc(ncid3) + deallocate(ncname3) end if end subroutine exitlsmcrosssection From 0069bf457f3f25530bae30931caad3f6994e53d9 Mon Sep 17 00:00:00 2001 From: julietbravo Date: Thu, 30 Jul 2020 15:43:29 +0200 Subject: [PATCH 27/31] Added additional variables to surface cross-sections --- src/modlsmcrosssection.f90 | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/modlsmcrosssection.f90 b/src/modlsmcrosssection.f90 index 42298378..077d0810 100644 --- a/src/modlsmcrosssection.f90 +++ b/src/modlsmcrosssection.f90 @@ -58,7 +58,7 @@ module modlsmcrosssection character(80), dimension(1,4) :: tncname3 real :: dtav - integer(kind=longint) :: idtav,tnext + integer(kind=longint) :: idtav, tnext logical :: lcross = .false. !< switch for doing the lsmcrosssection (on/off) integer :: crossplane = 2 !< Location of the xz lsmcrosssection @@ -72,7 +72,7 @@ subroutine initlsmcrosssection use modsurfdata, only : isurf implicit none - integer :: ierr + integer :: ierr, ii namelist/NAMLSMCROSSSECTION/ & lcross, dtav, crossheight, crossplane @@ -141,7 +141,7 @@ subroutine initlsmcrosssection if (isurf == 1) then nvar3 = 12 else if (isurf == 11) then - nvar3 = 11 + nvar3 = 16 end if allocate(ncname3(nvar3,4)) @@ -159,7 +159,7 @@ subroutine initlsmcrosssection call ncinfo(ncname3( 9,:),'cliq','Fraction of vegetated surface covered with liquid water','-','tt0t') call ncinfo(ncname3(10,:),'Wl','Liquid water reservoir','m','tt0t') call ncinfo(ncname3(11,:),'rssoil','Soil evaporation resistance','s/m','tt0t') - call ncinfo(ncname3(12,:),'rsveg','Vegitation resistance','s/m','tt0t') + call ncinfo(ncname3(12,:),'rsveg','Vegetation resistance','s/m','tt0t') call open_nc(fname3, ncid3, nrec3, n1=imax, n2=jmax) if (nrec3==0) then call define_nc(ncid3, 1, tncname3) @@ -175,10 +175,15 @@ subroutine initlsmcrosssection call ncinfo(ncname3( 5,:),'obuk', 'Obukhov length', 'm', 'tt0t') call ncinfo(ncname3( 6,:),'ustar', 'Friction velocity', 'm/s^-1', 'tt0t') call ncinfo(ncname3( 7,:),'cliq', 'Fraction of vegetated surface covered with liquid water', '-', 'tt0t') - call ncinfo(ncname3( 8,:),'ra', 'Aerodynamic resistance', 's/m', 'tt0t') - call ncinfo(ncname3( 9,:),'rssoil', 'Soil evaporation resistance', 's/m', 'tt0t') - call ncinfo(ncname3(10,:),'rsveg', 'Vegitation resistance', 's/m', 'tt0t') - call ncinfo(ncname3(11,:),'wl', 'Liquid water reservoir', 'm', 'tt0t') + call ncinfo(ncname3( 8,:),'wl', 'Liquid water reservoir', 'm', 'tt0t') + call ncinfo(ncname3( 9,:),'ra', 'Aerodynamic resistance', 's/m', 'tt0t') + call ncinfo(ncname3(10,:),'rssoil', 'Soil evaporation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(11,:),'rsveg', 'Vegetation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(12,:),'f1', 'f1(SWD) function vegetation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(13,:),'f2_lv', 'f2(theta) function low vegetation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(14,:),'f2_hv', 'f2(theta) function high vegetation resistance', 's/m', 'tt0t') + call ncinfo(ncname3(15,:),'f2_b', 'f2(theta) function soil resistance', 's/m', 'tt0t') + call ncinfo(ncname3(16,:),'f3', 'f3(VPD) function vegetation resistance', 's/m', 'tt0t') call open_nc(fname3, ncid3, nrec3, n1=imax, n2=jmax) if (nrec3==0) then call define_nc(ncid3, 1, tncname3) @@ -264,7 +269,6 @@ subroutine wrthorz write(ifoutput,'(es12.5)') ((phiw(i,j,crossheight),i=2,i1),j=2,j1) close(ifoutput) - if (lnetcdf) then allocate(vars(1:imax,1:jmax,2)) vars(:,:,1) = tsoil(2:i1,2:j1,crossheight) @@ -280,7 +284,8 @@ end subroutine wrthorz subroutine wrtsurf use modglobal, only : imax,jmax,i1,j1,cexpnr,ifoutput,rtimee use modsurfdata, only : Qnet, H, LE, G0, rs, ra, tskin, tendskin, & - cliq,rsveg,rssoil,Wl, isurf, obl, ustar + cliq, rsveg, rssoil, Wl, isurf, obl, ustar + use modlsm, only : f1, f2_lv, f2_hv, f2b, f3 use modstat_nc, only : lnetcdf, writestat_nc implicit none @@ -352,8 +357,6 @@ subroutine wrtsurf vars(:,:,10) = Wl(2:i1,2:j1) vars(:,:,11) = rssoil(2:i1,2:j1) vars(:,:,12) = rsveg(2:i1,2:j1) - call writestat_nc(ncid3, 1, tncname3, (/rtimee/), nrec3, .true.) - call writestat_nc(ncid3, nvar3, ncname3(1:nvar3,:), vars, nrec3, imax, jmax) else if (isurf == 11) then vars(:,:, 1) = H(2:i1,2:j1) vars(:,:, 2) = LE(2:i1,2:j1) @@ -362,14 +365,20 @@ subroutine wrtsurf vars(:,:, 5) = obl(2:i1,2:j1) vars(:,:, 6) = ustar(2:i1,2:j1) vars(:,:, 7) = cliq(2:i1,2:j1) - vars(:,:, 8) = ra(2:i1,2:j1) - vars(:,:, 9) = rssoil(2:i1,2:j1) - vars(:,:,10) = rsveg(2:i1,2:j1) - vars(:,:,11) = Wl(2:i1,2:j1) - call writestat_nc(ncid3, 1, tncname3, (/rtimee/), nrec3, .true.) - call writestat_nc(ncid3, nvar3, ncname3(1:nvar3,:), vars, nrec3, imax, jmax) + vars(:,:, 8) = Wl(2:i1,2:j1) + vars(:,:, 9) = ra(2:i1,2:j1) + vars(:,:,10) = rssoil(2:i1,2:j1) + vars(:,:,11) = rsveg(2:i1,2:j1) + vars(:,:,12) = f1(2:i1,2:j1) + vars(:,:,13) = f2_lv(2:i1,2:j1) + vars(:,:,14) = f2_hv(2:i1,2:j1) + vars(:,:,15) = f2b(2:i1,2:j1) + vars(:,:,16) = f3(2:i1,2:j1) end if + call writestat_nc(ncid3, 1, tncname3, (/rtimee/), nrec3, .true.) + call writestat_nc(ncid3, nvar3, ncname3(1:nvar3,:), vars, nrec3, imax, jmax) + deallocate(vars) end if From 4aec697b06afb4ac4892aa722620eea14d5924ca Mon Sep 17 00:00:00 2001 From: julietbravo Date: Fri, 4 Sep 2020 11:33:18 +0200 Subject: [PATCH 28/31] Added water tile to time series statistics --- src/modtimestat.f90 | 336 +++++++++++++++++++++++++++----------------- 1 file changed, 208 insertions(+), 128 deletions(-) diff --git a/src/modtimestat.f90 b/src/modtimestat.f90 index 293fefde..1f99ff03 100644 --- a/src/modtimestat.f90 +++ b/src/modtimestat.f90 @@ -93,7 +93,7 @@ subroutine inittimestat integer :: ierr,k,location = 1 real :: gradient = 0.0 real, allocatable,dimension(:) :: profile - integer :: i,j + integer :: i,j,vi character(len=1000) :: line namelist/NAMTIMESTAT/ & !< namelist @@ -233,7 +233,7 @@ subroutine inittimestat if (isurf == 1) then nvar = 32 else if (isurf == 11) then - nvar = 67 + nvar = 75 else nvar = 21 end if @@ -277,64 +277,127 @@ subroutine inittimestat call ncinfo(ncname(32,:),'rsveg','Vegitation resistance','s/m','time') else if (isurf==11) then - call ncinfo(ncname(22,:),'Qnet','Net radiation','W/m^2','time') - call ncinfo(ncname(23,:),'H','Sensible heat flux','W/m^2','time') - call ncinfo(ncname(24,:),'LE','Latent heat flux','W/m^2','time') - call ncinfo(ncname(25,:),'G','Ground heat flux','W/m^2','time') - - call ncinfo(ncname(26,:),'obuk_lv','Obukhov length low veg','m','time') - call ncinfo(ncname(27,:),'obuk_hv','Obukhov length high veg','m','time') - call ncinfo(ncname(28,:),'obuk_bs','Obukhov length bare soil','m','time') - - call ncinfo(ncname(29,:),'ustar_lv','Friction velocity low veg','m s-1','time') - call ncinfo(ncname(30,:),'ustar_hv','Friction velocity high veg','m s-1','time') - call ncinfo(ncname(31,:),'ustar_bs','Friction velocity bare soil','m s-1','time') - - call ncinfo(ncname(32,:),'ra_lv','Aerodynamic resistance low veg','s m-1','time') - call ncinfo(ncname(33,:),'ra_hv','Aerodynamic resistance high veg','s m-1','time') - call ncinfo(ncname(34,:),'ra_bs','Aerodynamic resistance bare soil','s m-1','time') - - call ncinfo(ncname(35,:),'f1','Reduction canopy resistance f(swd)','-','time') - call ncinfo(ncname(36,:),'f2_lv','Reduction canopy resistance low veg f(theta)','-','time') - call ncinfo(ncname(37,:),'f2_hv','Reduction canopy resistance low veg f(theta)','-','time') - call ncinfo(ncname(38,:),'f3','Reduction canopy resistance f(VPD)','-','time') - call ncinfo(ncname(39,:),'f2b','Reduction soil resistance f(theta)','-','time') - - call ncinfo(ncname(40,:),'rs_lv','Canopy resistance low veg','s m-1','time') - call ncinfo(ncname(41,:),'rs_hv','Canopy resistance low veg','s m-1','time') - call ncinfo(ncname(42,:),'rs_bs','Soil resistance','s m-1','time') - - call ncinfo(ncname(43,:),'c_lv','Tile fraction low vegetation','-','time') - call ncinfo(ncname(44,:),'c_hv','Tile fraction high vegetation','-','time') - call ncinfo(ncname(45,:),'c_bs','Tile fraction bare soil','-','time') - call ncinfo(ncname(46,:),'c_ws','Tile fraction wet skin','-','time') - - call ncinfo(ncname(47,:),'wl','Liquid water reservoir','m','time') - - call ncinfo(ncname(48,:),'H_lv','Sensible heat flux low vegetation','W m-2','time') - call ncinfo(ncname(49,:),'H_hv','Sensible heat flux high vegetation','W m-2','time') - call ncinfo(ncname(50,:),'H_bs','Sensible heat flux bare soil','W m-2','time') - call ncinfo(ncname(51,:),'H_ws','Sensible heat flux wet skin','W m-2','time') - - call ncinfo(ncname(52,:),'LE_lv','Latent heat flux low vegetation','W m-2','time') - call ncinfo(ncname(53,:),'LE_hv','Latent heat flux high vegetation','W m-2','time') - call ncinfo(ncname(54,:),'LE_bs','Latent heat flux bare soil','W m-2','time') - call ncinfo(ncname(55,:),'LE_ws','Latent heat flux wet skin','W m-2','time') - - call ncinfo(ncname(56,:),'G_lv','Soil heat flux low vegetation','W m-2','time') - call ncinfo(ncname(57,:),'G_hv','Soil heat flux high vegetation','W m-2','time') - call ncinfo(ncname(58,:),'G_bs','Soil heat flux bare soil','W m-2','time') - call ncinfo(ncname(59,:),'G_ws','Soil heat flux wet skin','W m-2','time') - - call ncinfo(ncname(60,:),'thlskin_lv','Skin potential temperature low vegetation','K','time') - call ncinfo(ncname(61,:),'thlskin_hv','Skin potential temperature high vegetation','K','time') - call ncinfo(ncname(62,:),'thlskin_bs','Skin potential temperature bare soil','K','time') - call ncinfo(ncname(63,:),'thlskin_ws','Skin potential temperature wet skin','K','time') - - call ncinfo(ncname(64,:),'qtskin_lv','Skin specific humidity low vegetation','kg kg-1','time') - call ncinfo(ncname(65,:),'qtskin_hv','Skin specific humidity high vegetation','kg kg-1','time') - call ncinfo(ncname(66,:),'qtskin_bs','Skin specific humidity bare soil','kg kg-1','time') - call ncinfo(ncname(67,:),'qtskin_ws','Skin specific humidity wet skin','kg kg-1','time') + vi = 22 + call ncinfo(ncname(vi,:),'Qnet','Net radiation','W/m^2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'H','Sensible heat flux','W/m^2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'LE','Latent heat flux','W/m^2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'G','Ground heat flux','W/m^2','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'obuk_lv','Obukhov length low veg','m','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'obuk_hv','Obukhov length high veg','m','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'obuk_bs','Obukhov length bare soil','m','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'obuk_aq','Obukhov length water','m','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'ustar_lv','Friction velocity low veg','m s-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ustar_hv','Friction velocity high veg','m s-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ustar_bs','Friction velocity bare soil','m s-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ustar_aq','Friction velocity water','m s-1','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'ra_lv','Aerodynamic resistance low veg','s m-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ra_hv','Aerodynamic resistance high veg','s m-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ra_bs','Aerodynamic resistance bare soil','s m-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'ra_aq','Aerodynamic resistance water','s m-1','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'f1','Reduction canopy resistance f(swd)','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'f2_lv','Reduction canopy resistance low veg f(theta)','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'f2_hv','Reduction canopy resistance low veg f(theta)','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'f3','Reduction canopy resistance f(VPD)','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'f2b','Reduction soil resistance f(theta)','-','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'rs_lv','Canopy resistance low veg','s m-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'rs_hv','Canopy resistance low veg','s m-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'rs_bs','Soil resistance','s m-1','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'c_lv','Tile fraction low vegetation','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'c_hv','Tile fraction high vegetation','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'c_bs','Tile fraction bare soil','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'c_ws','Tile fraction wet skin','-','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'c_aq','Tile fraction water','-','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'wl','Liquid water reservoir','m','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'H_lv','Sensible heat flux low vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'H_hv','Sensible heat flux high vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'H_bs','Sensible heat flux bare soil','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'H_ws','Sensible heat flux wet skin','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'H_aq','Sensible heat flux water','W m-2','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'LE_lv','Latent heat flux low vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'LE_hv','Latent heat flux high vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'LE_bs','Latent heat flux bare soil','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'LE_ws','Latent heat flux wet skin','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'LE_aq','Latent heat flux water','W m-2','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'G_lv','Soil heat flux low vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'G_hv','Soil heat flux high vegetation','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'G_bs','Soil heat flux bare soil','W m-2','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'G_ws','Soil heat flux wet skin','W m-2','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'thlskin_lv','Skin potential temperature low vegetation','K','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'thlskin_hv','Skin potential temperature high vegetation','K','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'thlskin_bs','Skin potential temperature bare soil','K','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'thlskin_ws','Skin potential temperature wet skin','K','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'thlskin_aq','Skin potential temperature water','K','time') + vi = vi+1 + + call ncinfo(ncname(vi,:),'qtskin_lv','Skin specific humidity low vegetation','kg kg-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'qtskin_hv','Skin specific humidity high vegetation','kg kg-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'qtskin_bs','Skin specific humidity bare soil','kg kg-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'qtskin_ws','Skin specific humidity wet skin','kg kg-1','time') + vi = vi+1 + call ncinfo(ncname(vi,:),'qtskin_ws','Skin specific humidity water','kg kg-1','time') + vi = vi+1 end if @@ -413,7 +476,7 @@ subroutine timestat lhetero, xpatches, ypatches, qts_patch, wt_patch, wq_patch, & thls_patch,obl,z0mav_patch, wco2av, Anav, Respav,gcco2av use modsurface, only : patchxnr,patchynr - use modlsm, only : tile_lv, tile_hv, tile_bs, tile_ws, f1, f2_lv, f2_hv, f3, f2b + use modlsm, only : tile_lv, tile_hv, tile_bs, tile_ws, tile_aq, f1, f2_lv, f2_hv, f3, f2b use mpi use modmpi, only : my_real,mpi_sum,mpi_max,mpi_min,comm3d,mpierr,myid use modstat_nc, only : lnetcdf, writestat_nc,nc_fillvalue @@ -434,19 +497,19 @@ subroutine timestat real :: Qnetav, Hav, LEav, G0av, tendskinav, rsav, raav, tskinav,Wlav,cliqav,rsvegav,rssoilav ! LSM tiled variables - real :: obuk_lv_av, obuk_hv_av, obuk_bs_av - real :: ustar_lv_av, ustar_hv_av, ustar_bs_av - real :: ra_lv_av, ra_hv_av, ra_bs_av + real :: obuk_lv_av, obuk_hv_av, obuk_bs_av, obuk_aq_av + real :: ustar_lv_av, ustar_hv_av, ustar_bs_av, ustar_aq_av + real :: ra_lv_av, ra_hv_av, ra_bs_av, ra_aq_av real :: f1_av, f2_lv_av, f2_hv_av, f3_av, f2b_av real :: rs_lv_av, rs_hv_av, rs_bs_av - real :: c_lv_av, c_hv_av, c_bs_av, c_ws_av - real :: H_lv_av, H_hv_av, H_bs_av, H_ws_av - real :: LE_lv_av, LE_hv_av, LE_bs_av, LE_ws_av + real :: c_lv_av, c_hv_av, c_bs_av, c_ws_av, c_aq_av + real :: H_lv_av, H_hv_av, H_bs_av, H_ws_av, H_aq_av + real :: LE_lv_av, LE_hv_av, LE_bs_av, LE_ws_av, LE_aq_av real :: G_lv_av, G_hv_av, G_bs_av, G_ws_av - real :: thlskin_lv_av, thlskin_hv_av, thlskin_bs_av, thlskin_ws_av - real :: qtskin_lv_av, qtskin_hv_av, qtskin_bs_av, qtskin_ws_av + real :: thlskin_lv_av, thlskin_hv_av, thlskin_bs_av, thlskin_ws_av, thlskin_aq_av + real :: qtskin_lv_av, qtskin_hv_av, qtskin_bs_av, qtskin_ws_av, qtskin_aq_av - integer:: i, j, k + integer:: i, j, k, vi ! heterogeneity variables integer:: patchx, patchy @@ -854,14 +917,17 @@ subroutine timestat obuk_lv_av = mean_2d(tile_lv%obuk) obuk_hv_av = mean_2d(tile_hv%obuk) obuk_bs_av = mean_2d(tile_bs%obuk) + obuk_aq_av = mean_2d(tile_aq%obuk) ustar_lv_av = mean_2d(tile_lv%ustar) ustar_hv_av = mean_2d(tile_hv%ustar) ustar_bs_av = mean_2d(tile_bs%ustar) + ustar_aq_av = mean_2d(tile_aq%ustar) ra_lv_av = mean_2d(tile_lv%ra) ra_hv_av = mean_2d(tile_hv%ra) ra_bs_av = mean_2d(tile_bs%ra) + ra_aq_av = mean_2d(tile_aq%ra) f1_av = mean_2d(f1) f2_lv_av = mean_2d(f2_lv) @@ -877,6 +943,7 @@ subroutine timestat c_hv_av = mean_2d(tile_hv%frac) c_bs_av = mean_2d(tile_bs%frac) c_ws_av = mean_2d(tile_ws%frac) + c_aq_av = mean_2d(tile_aq%frac) wlav = mean_2d(wl) @@ -884,11 +951,13 @@ subroutine timestat H_hv_av = mean_2d(tile_hv%H) H_bs_av = mean_2d(tile_bs%H) H_ws_av = mean_2d(tile_ws%H) + H_aq_av = mean_2d(tile_aq%H) LE_lv_av = mean_2d(tile_lv%LE) LE_hv_av = mean_2d(tile_hv%LE) LE_bs_av = mean_2d(tile_bs%LE) LE_ws_av = mean_2d(tile_ws%LE) + LE_aq_av = mean_2d(tile_aq%LE) G_lv_av = mean_2d(tile_lv%G) G_hv_av = mean_2d(tile_hv%G) @@ -899,11 +968,13 @@ subroutine timestat thlskin_hv_av = mean_2d(tile_hv%thlskin) thlskin_bs_av = mean_2d(tile_bs%thlskin) thlskin_ws_av = mean_2d(tile_ws%thlskin) + thlskin_aq_av = mean_2d(tile_aq%thlskin) qtskin_lv_av = mean_2d(tile_lv%qtskin) qtskin_hv_av = mean_2d(tile_hv%qtskin) qtskin_bs_av = mean_2d(tile_bs%qtskin) qtskin_ws_av = mean_2d(tile_ws%qtskin) + qtskin_aq_av = mean_2d(tile_aq%qtskin) end if @@ -1006,64 +1077,73 @@ subroutine timestat vars(31) = rssoilav vars(32) = rsvegav else if (isurf == 11) then - vars(22) = Qnetav - vars(23) = Hav - vars(24) = LEav - vars(25) = G0av - - vars(26) = obuk_lv_av - vars(27) = obuk_hv_av - vars(28) = obuk_bs_av - - vars(29) = ustar_lv_av - vars(30) = ustar_hv_av - vars(31) = ustar_bs_av - - vars(32) = ra_lv_av - vars(33) = ra_hv_av - vars(34) = ra_bs_av - - vars(35) = f1_av - vars(36) = f2_lv_av - vars(37) = f2_hv_av - vars(38) = f3_av - vars(39) = f2b_av - - vars(40) = rs_lv_av - vars(41) = rs_hv_av - vars(42) = rs_bs_av - - vars(43) = c_lv_av - vars(44) = c_hv_av - vars(45) = c_bs_av - vars(46) = c_ws_av - - vars(47) = wlav - - vars(48) = H_lv_av - vars(49) = H_hv_av - vars(50) = H_bs_av - vars(51) = H_ws_av - - vars(52) = LE_lv_av - vars(53) = LE_hv_av - vars(54) = LE_bs_av - vars(55) = LE_ws_av - - vars(56) = G_lv_av - vars(57) = G_hv_av - vars(58) = G_bs_av - vars(59) = G_ws_av - - vars(60) = thlskin_lv_av - vars(61) = thlskin_hv_av - vars(62) = thlskin_bs_av - vars(63) = thlskin_ws_av - - vars(64) = qtskin_lv_av - vars(65) = qtskin_hv_av - vars(66) = qtskin_bs_av - vars(67) = qtskin_ws_av + vi = 22 + vars(vi) = Qnetav; vi = vi+1 + vars(vi) = Hav; vi = vi+1 + vars(vi) = LEav; vi = vi+1 + vars(vi) = G0av; vi = vi+1 + + vars(vi) = obuk_lv_av; vi = vi+1 + vars(vi) = obuk_hv_av; vi = vi+1 + vars(vi) = obuk_bs_av; vi = vi+1 + vars(vi) = obuk_aq_av; vi = vi+1 + + vars(vi) = ustar_lv_av; vi = vi+1 + vars(vi) = ustar_hv_av; vi = vi+1 + vars(vi) = ustar_bs_av; vi = vi+1 + vars(vi) = ustar_aq_av; vi = vi+1 + + vars(vi) = ra_lv_av; vi = vi+1 + vars(vi) = ra_hv_av; vi = vi+1 + vars(vi) = ra_bs_av; vi = vi+1 + vars(vi) = ra_aq_av; vi = vi+1 + + vars(vi) = f1_av; vi = vi+1 + vars(vi) = f2_lv_av; vi = vi+1 + vars(vi) = f2_hv_av; vi = vi+1 + vars(vi) = f3_av; vi = vi+1 + vars(vi) = f2b_av; vi = vi+1 + + vars(vi) = rs_lv_av; vi = vi+1 + vars(vi) = rs_hv_av; vi = vi+1 + vars(vi) = rs_bs_av; vi = vi+1 + + vars(vi) = c_lv_av; vi = vi+1 + vars(vi) = c_hv_av; vi = vi+1 + vars(vi) = c_bs_av; vi = vi+1 + vars(vi) = c_ws_av; vi = vi+1 + vars(vi) = c_aq_av; vi = vi+1 + + vars(vi) = wlav; vi = vi+1 + + vars(vi) = H_lv_av; vi = vi+1 + vars(vi) = H_hv_av; vi = vi+1 + vars(vi) = H_bs_av; vi = vi+1 + vars(vi) = H_ws_av; vi = vi+1 + vars(vi) = H_aq_av; vi = vi+1 + + vars(vi) = LE_lv_av; vi = vi+1 + vars(vi) = LE_hv_av; vi = vi+1 + vars(vi) = LE_bs_av; vi = vi+1 + vars(vi) = LE_ws_av; vi = vi+1 + vars(vi) = LE_aq_av; vi = vi+1 + + vars(vi) = G_lv_av; vi = vi+1 + vars(vi) = G_hv_av; vi = vi+1 + vars(vi) = G_bs_av; vi = vi+1 + vars(vi) = G_ws_av; vi = vi+1 + + vars(vi) = thlskin_lv_av; vi = vi+1 + vars(vi) = thlskin_hv_av; vi = vi+1 + vars(vi) = thlskin_bs_av; vi = vi+1 + vars(vi) = thlskin_ws_av; vi = vi+1 + vars(vi) = thlskin_aq_av; vi = vi+1 + + vars(vi) = qtskin_lv_av; vi = vi+1 + vars(vi) = qtskin_hv_av; vi = vi+1 + vars(vi) = qtskin_bs_av; vi = vi+1 + vars(vi) = qtskin_ws_av; vi = vi+1 + vars(vi) = qtskin_aq_av; vi = vi+1 end if From c1eb5a43dd4c8bfe1135c68bcaa4675fe8c319ea Mon Sep 17 00:00:00 2001 From: julietbravo Date: Fri, 4 Sep 2020 15:02:36 +0200 Subject: [PATCH 29/31] Fixed dynamic tile fraction for cases with open water --- src/modlsm.f90 | 55 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 86f0248a..3b3aaa38 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -23,7 +23,7 @@ module modlsm public :: initlsm, lsm, exitlsm, init_lsm_tiles - logical :: llsm ! On/off switch LSM + logical :: llsm ! On/off switch LSM logical :: lfreedrainage ! Free drainage bottom BC for soil moisture ! Interpolation types soil from full to half level @@ -58,7 +58,7 @@ module modlsm real, allocatable :: f1(:,:), f2_lv(:,:), f2_hv(:,:), f2b(:,:), f3(:,:) ! Random - real, allocatable :: du_tot(:,:), thv_1(:,:) + real, allocatable :: du_tot(:,:), thv_1(:,:), land_frac(:,:) ! Data structure for sub-grid tiles type lsm_tile @@ -140,7 +140,7 @@ subroutine lsm call integrate_theta_soil ! Update liquid water reservoir - !call calc_liquid_reservoir + call calc_liquid_reservoir end subroutine lsm @@ -153,22 +153,20 @@ subroutine calc_tile_fractions implicit none integer :: i, j + real :: c_liq - ! BvS: tmp hack... - tile_ws%frac(:,:) = 0. - tile_lv%frac(:,:) = tile_lv%base_frac(:,:) - tile_hv%frac(:,:) = tile_hv%base_frac(:,:) - tile_bs%frac(:,:) = tile_bs%base_frac(:,:) + ! Water tile fraction is not variable tile_aq%frac(:,:) = tile_aq%base_frac(:,:) - !do j=2, j1 - ! do i=2, i1 - ! tile_ws%frac(i,j) = min(1., wl(i,j) / wl_max(i,j)) - ! tile_lv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_lv%base_frac(i,j) - ! tile_hv%frac(i,j) = (1.-tile_ws%frac(i,j)) * tile_hv%base_frac(i,j) - ! tile_bs%frac(i,j) = (1.-tile_ws%frac(i,j)) * (1. - tile_lv%base_frac(i,j) - tile_hv%base_frac(i,j)) - ! end do - !end do + do j=2, j1 + do i=2, i1 + c_liq = min(1., wl(i,j)/wl_max(i,j)) + tile_ws%frac(i,j) = land_frac(i,j) * c_liq + tile_lv%frac(i,j) = (1.-c_liq) * tile_lv%base_frac(i,j) + tile_hv%frac(i,j) = (1.-c_liq) * tile_hv%base_frac(i,j) + tile_bs%frac(i,j) = (1.-c_liq) * (1.-tile_lv%base_frac(i,j)-tile_hv%base_frac(i,j)-tile_aq%base_frac(i,j)) + end do + end do end subroutine calc_tile_fractions @@ -215,6 +213,7 @@ subroutine calc_liquid_reservoir else rainrate = -sed_qr(i,j,1)/rhow end if + wl_tend_precip = intercept_eff * c_veg * rainrate ! Total and limited tendencies @@ -1102,6 +1101,7 @@ subroutine allocate_fields allocate(du_tot(i2, j2)) allocate(thv_1(i2, j2)) + allocate(land_frac(i2, j2)) ! NOTE: names differ from what is described in modsurfdata! ! Diffusivity temperature: @@ -1226,7 +1226,7 @@ end subroutine init_lsm_tiles ! Initialise the LSM homogeneous from namelist input ! subroutine init_homogeneous - use modglobal, only : ifnamopt, fname_options, checknamelisterror, lwarmstart + use modglobal, only : ifnamopt, fname_options, checknamelisterror, lwarmstart, eps1 use modmpi, only : myid, comm3d, mpierr, mpi_logical, my_real, mpi_integer use modsurfdata, only : tsoil, tsoilm, phiw, phiwm, wl, wlm, wmax implicit none @@ -1355,6 +1355,7 @@ subroutine init_homogeneous tile_aq % tskin(:,:) = tskin_water + if (.not. lwarmstart) then ! Init prognostic variables in case of cold start. ! For a warm start, these are read from restartfiles @@ -1382,11 +1383,16 @@ subroutine init_homogeneous tile_ws % lambda_stable(:,:) = c_low*lambda_s_low + c_high*lambda_s_high + c_bare*lambda_s_bare tile_ws % lambda_unstable(:,:) = c_low*lambda_us_low + c_high*lambda_us_high + c_bare*lambda_us_bare + ! Calculate land fraction, and limit to prevent div/0's + land_frac(:,:) = 1.-c_water + where (land_frac == 0) land_frac = eps1 + ! Max liquid water per grid point, accounting for LAI wl_max(:,:) = wmax * ( & tile_lv%base_frac(:,:) * tile_lv%lai(:,:) + & tile_hv%base_frac(:,:) * tile_hv%lai(:,:) + & tile_bs%base_frac(:,:)) + where (wl_max == 0) wl_max = eps1 ! Cleanup! deallocate(t_soil_p, theta_soil_p) @@ -1397,7 +1403,7 @@ end subroutine init_homogeneous ! Init LSM properties from external input ! subroutine init_heterogeneous - use modglobal, only : i1, j1, lwarmstart, iexpnr + use modglobal, only : i1, j1, lwarmstart, iexpnr, eps1 use modmpi, only : myidx, myidy use modsurfdata, only : tsoil, phiw, wl, wlm, wmax implicit none @@ -1453,8 +1459,8 @@ subroutine init_heterogeneous read(666) soil_index(2:i1, 2:j1, 1:kmax_soil) if (.not. lwarmstart) then - read(666) tsoil (2:i1, 2:j1, 1:kmax_soil) - read(666) phiw (2:i1, 2:j1, 1:kmax_soil) + read(666) tsoil(2:i1, 2:j1, 1:kmax_soil) + read(666) phiw (2:i1, 2:j1, 1:kmax_soil) wl(:,:) = 0. wlm(:,:) = 0. @@ -1486,11 +1492,16 @@ subroutine init_heterogeneous tile_hv%base_frac(:,:)*tile_lv%lambda_unstable(:,:) + & tile_bs%base_frac(:,:)*tile_bs%lambda_unstable(:,:) + ! Calculate land fraction, and limit to prevent div/0's + land_frac(:,:) = 1.-tile_aq%base_frac(:,:) + where (land_frac == 0) land_frac = eps1 + ! Max liquid water per grid point, accounting for LAI wl_max(:,:) = wmax * ( & tile_lv%base_frac(:,:) * tile_lv%lai(:,:) + & tile_hv%base_frac(:,:) * tile_hv%lai(:,:) + & - tile_bs%base_frac(:,:)) + tile_bs%base_frac(:,:)) / land_frac(:,:) + where (wl_max == 0) wl_max = eps1 end subroutine init_heterogeneous @@ -1728,7 +1739,7 @@ function calc_obuk_dirichlet(L_in, du, db_in, zsl, z0m, z0h) result(res) if (m > 1) then print*,'WARNING: convergence has not been reached in Obukhov length iteration' print*,'Input: ', L_in, du, db_in, zsl, z0m, z0h - stop + !stop res = 1e-9 return end if From 704d145ee245c3153e36a21fc0916bdc99ed79db Mon Sep 17 00:00:00 2001 From: julietbravo Date: Mon, 7 Sep 2020 10:31:11 +0200 Subject: [PATCH 30/31] Small fix in calculation liq. water fraction, plus added cleanup of arrays --- src/modlsm.f90 | 57 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/src/modlsm.f90 b/src/modlsm.f90 index 3b3aaa38..fd42618f 100644 --- a/src/modlsm.f90 +++ b/src/modlsm.f90 @@ -603,7 +603,7 @@ subroutine calc_bulk_bcs call excjs(ustar,2,i1,2,j1,1,1,1,1) ! Just for diagnostics (modlsmcrosssection) - cliq(i,j) = tile_ws%frac(i,j) + cliq(i,j) = tile_ws%frac(i,j) / land_frac(i,j) ra(i,j) = tile_lv%frac(i,j) * tile_lv%ra(i,j) + & tile_hv%frac(i,j) * tile_hv%ra(i,j) + & @@ -985,11 +985,38 @@ end subroutine initlsm ! Cleanup (deallocate) the land-surface model ! subroutine exitlsm + use modsurfdata, only : & + tsoil, tsoilm, phiw, phiwm, & + lambda, lambdah, lambdas, lambdash, gammas, gammash, & + H, LE, G0, Qnet, wl, wlm, & + tendskin, cliq, rsveg, rssoil implicit none if (.not. llsm) return + ! Allocated from `read_soil_table`: deallocate( theta_res, theta_wp, theta_fc, theta_sat, gamma_theta_sat, vg_a, vg_l, vg_n ) + + ! Allocated from `allocate_fields`: + deallocate( soil_index, tsoil, tsoilm, phiw, phiwm, phiw_source ) + deallocate( wl, wlm, wl_max ) + deallocate( throughfall, interception ) + deallocate( gD, f1, f2_lv, f2_hv, f2b, f3 ) + deallocate( du_tot, thv_1, land_frac ) + deallocate( lambda, lambdah, lambdas, lambdash, gammas, gammash ) + deallocate( Qnet, H, LE, G0 ) + deallocate( tendskin, cliq, rsveg, rssoil ) + + ! Allocated from `create_soil_grid`: + deallocate( z_soil, dz_soil, dzi_soil, zh_soil, dzh_soil, dzhi_soil ) + + ! Tiles, allocated from `allocate_tile`: + call deallocate_tile(tile_lv) + call deallocate_tile(tile_hv) + call deallocate_tile(tile_bs) + call deallocate_tile(tile_ws) + call deallocate_tile(tile_aq) + end subroutine exitlsm ! @@ -1192,6 +1219,23 @@ subroutine allocate_tile(tile) end subroutine allocate_tile +! +! Deallocate all fields of a LSM tile +! +subroutine deallocate_tile(tile) + implicit none + type(lsm_tile), intent(inout) :: tile + + deallocate( tile%z0m, tile%z0h, tile%base_frac, tile%frac ) + deallocate( tile%obuk, tile%ustar, tile%ra ) + deallocate( tile%lambda_stable, tile%lambda_unstable ) + deallocate( tile%H, tile%LE, tile%G, tile%wthl, tile%wqt) + deallocate( tile%tskin, tile%thlskin, tile%qtskin) + deallocate( tile%db, tile%lai, tile%rs_min, tile%rs ) + deallocate( tile%a_r, tile%b_r, tile%root_frac, tile%phiw_mean ) + +end subroutine deallocate_tile + ! ! Init some of the tiled variables, in case of cold start. ! Called from modstartup -> readinitfiles() @@ -1391,11 +1435,11 @@ subroutine init_homogeneous wl_max(:,:) = wmax * ( & tile_lv%base_frac(:,:) * tile_lv%lai(:,:) + & tile_hv%base_frac(:,:) * tile_hv%lai(:,:) + & - tile_bs%base_frac(:,:)) + tile_bs%base_frac(:,:)) / land_frac(:,:) where (wl_max == 0) wl_max = eps1 ! Cleanup! - deallocate(t_soil_p, theta_soil_p) + deallocate(t_soil_p, theta_soil_p, soil_index_p) end subroutine init_homogeneous @@ -1524,10 +1568,9 @@ subroutine read_soil_table call MPI_BCAST(table_size, 1, mpi_integer, 0, comm3d, mpierr) ! Allocate variables - allocate( & - theta_res(table_size), theta_wp(table_size), theta_fc(table_size), & - theta_sat(table_size), gamma_theta_sat(table_size), & - vg_a(table_size), vg_l(table_size), vg_n(table_size) ) + allocate( theta_res(table_size), theta_wp(table_size), theta_fc(table_size) ) + allocate( theta_sat(table_size), gamma_theta_sat(table_size) ) + allocate( vg_a(table_size), vg_l(table_size), vg_n(table_size) ) if (myid == 0) then ! Read variables From 8230865e7e998597a393d203c53b6158dbca4fd7 Mon Sep 17 00:00:00 2001 From: Bart van Stratum Date: Fri, 16 Oct 2020 10:18:03 +0200 Subject: [PATCH 31/31] Added NetCDF file with van Genuchten parameters --- data/van_genuchten_parameters.nc | Bin 0 -> 16564 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/van_genuchten_parameters.nc diff --git a/data/van_genuchten_parameters.nc b/data/van_genuchten_parameters.nc new file mode 100644 index 0000000000000000000000000000000000000000..bd450be8ffde121cdf47482d6de926d71eca1de9 GIT binary patch literal 16564 zcmeHO3tZI2_McrAMMK0y!$-9#z6IaCkl!rGQxOmpv8%2dtgr?P?JlC$KP&a7X{Dx_ zsf1q5G&LVROsmD@ntv-Fh0oORt*NPqpi*+@%$!-)#l`OZ`2YL=-^=*H^E-3q`8{W5 z=gebTba-T=hAkTs2wq-9LwxzC{I3ebCpr-$c_|+yKitsx=U zj|24uO-9;KO;ix$%Uf}@w~#jFaqPPu@S+Lc{oz|he25qOr;qEGV9CxgT5KkxmBlw@ ze^8~&IM!&fnzGFyJ-eEXw$!xIA-#hh59-x5BW-k_kRCxjx(D_2W-U^&e-H5TaxB?n zjHx!gDUB7v0+i>){$zQHxh89x!I(+ZASGnN+^v0iR~rpcsr(^Er4ng71H*!(9^O!h zFDnenBSeRN9i-tb@xv-KJRPLLDqi+_5K?%k3R;Sk37E23Yo;|O@IhpximEB}4ccCh z_pM)SXi^g6p-%ua7DK{g21djs#l*+yllq0mMiB4zAX5=fo+~bNU<6CdbAcLUE|WRU zIF5KTO@Zosh&M~EkK-Ws4mV|S0HL>{ds-;Mkr%$b9|M_eTdvt;v$9xd8`L0c2|WH< zyG9iP;dk$bupeY=!SILPRrk<~c>04xtyZfbmIt8(UXO<7yOTsBPq=0YDL|D-!uyN3Y2Ay-mnwmDsIHe?yGcK~Ap_9tOX$~I+cLGGQv42DHFX88C! zX+Ab4X)wc%WdR%>Vt)XTv}IY$Z4?n4%F;Cs59k!%FM5EdFVqM)gHYP9cTd7>Nk!Z@ z6=oq>Hg767E|?2Eh~JY;$U>w{utf0xTmSoP8aV!6sjIEYKT6mwgR%CRf!kjbE#;UK zq9eT@Aib+Rm@c?czKL@rpZHi)s%Hq%uKXxzrC^>D_ z95~L?R#q=?8yp?ZMlQ4b#w>qY$D_>jAvAO#a`4+^n~0PDPaEE5el<9k+lu%xDE?=9HNA7@ zI^F_LXaW4>vKDj~1y#4;hs(pZ5E9idK9=LoO{}`FSgjnqXp)2sz~L8&38aRHW?LB= z+OQsv3qg=O`4d9W&7B;FIX0KiUXT>ak7v^z50b*{20_XZXdKo7g|CHJ8rSlrUgV_a zf`6FQPZXA4Lyr21b|**u#JH2Ae)_wUqkaaslcRoOQSRD5Iqn9!k;`$%CRQF?^UHA; z??x`iU4k3A9Cw3Ij;m%e+GOU7oKOKiW@qcj>Kip0H z2siN~-NftN#A7tsEfp&x>S!GTbqLfUP=`Pr0(A(~Ay9|F{|f~8dKMR{u$Fb^Va?0e z!zhO}GGA{a4{LC~3`8E*-h5q+Jgl{y<=|5|^WZ}`^WZZ$^WY;m^WYOW^Wft-^Wf7t z^Weid^Wd{_e+%0OKAJNRKAAHg?ZU^n@Ze)Prw5D?#?{; zTFyN9R?a;5QqDa1PR=~|O3pm^zRo=Oy3RcKw$AMXU)Gt2Jq2eT_7I$T*fVhEVUNI> zhdlvj9`*p7d7TR{eS8+}*n?vmT0F?zLFxZ_V!ZUvq<@F17X4&tczujYKhGQUHA3ox zv@ud&q)m`Mh_oqEKcvl&Hb?ppQh%f^kmA`5!5vz{H#?c^VU$0D^iiZ*q>mwOgA}(1 zNdWTgkhVwK0cl61oshmap9lJa-0x5x13&*)d~S?jMEpX5`DCdlo!gOq=SKmwfW=YWhJ$L3H0CU(>++;Q2FeY^JaD|FF}fGexxhjKJ1Q zD~o9vsR+GgE2gUxlAqf1%Vauu%e21vm2>H$oZklj`07mB_3VQSc9k!pJxzta%coDK ztE1XIo*loAF6m$L>B@kev@&AT4xiKw^wHh{o1SaCmU`_C$$b9&78<^1^2DR7x6zB= zeYIfKirutX*s{E)!TV_Uq_!)(b{5dF_LL(ROE%N0nATxyZxz!0>u1hedu|84^j?ee z%~W5~rJ5rzo9pkQakutcJ_^`J4|v6`dujXus#^KY3Dfe!^sSOukBHb~bo`_V9pby5 zqSmd!DX+}mK}qa{N4)ZO(LkTp%RI}9>1a!zZ+n#Pr6-N*Q(1F9rvs+j6Y8mdqPv6N zNt@KFn2w#g<>rXR`{_3q1`ppl{{W3m8}sxVnFr~XN9J6ceWrw}3)-b6{&AG9ZQXOu zXK(GJ&wr5EY}<|lbi|?)%f7BWKm%r-?J#Qp30mk?zHLyOlQeAe#AO$KPtl$W=Y(7e zveUTA(gF6F+o&NtK{uq;4q9q?I(@S5F8XYrp&RC2T}n06FOHn_>Tat2dDfzkgd=pt zypINYt=~`MmJh$N*oP%_ebB&BTjoqMFPfrYv`(T#RCu>f(sHWSM{9EILl^$OyMbp<5{H^!L$~*e9 ziBrGWrUYEm4aDa?UF4?ahAc zov>NiJ7(7UmWi{K(dvC~dYybtnbToUlc=wAm4)_s-L!KTD{F%4=UqO!RIyiR@*4K~ zLfM_2GVomaIOWM>(4^I;%KXTzqBjFJC|?|lP1+N=UAg$w(iN}f?N)wEPk(LA%^#Jo z4o}&rUa}6$o23lz5@>Al{7l6%wfN1VZJU(E3+k7hS~XE=@~pY&m74{MzJ)QW?8VoV z!r!!KD>p7wO5Q&;b-~h?l@8|Tif-87QK|+uctZWfZbjIkk51^XfsPpiQ}C}8tfJ%a zM!H@+c`1i3L)VFa{Eqp5N4uz^!j5D;Dr|2hrn^qXaPiZLbT_aaSE*>ILWZy8^O?feP*e;V6=*-@Urqg)=V$e*hkw3c&Iwj72l9PW?jVQqrPe!|C$4#nYA~(y+qLM9E&J|V3cx3IhG*W)t*Twmuy&Mr_%#ey^}?C=Xp=EB zO`B@SF{GMo1^}a2W~ni8 zq~)gCOk>$x0kcQ76`a-d53Qwnp?9kj9BWx;vcas4GMaN!+0C2`ZH~dhZo1iw7H#H@ znQxyRQ+jVK;xxL_cb{3hKPHUkep&=S4|cJLxasp&)&e;j#&@;|4=1Rzm_0>2*{BcLu8zF(~bHgo;S8^Pa9B`>hgR-{`ukELWiPcZqjmOl~L*?)*sd^F`8 zVRIJCw)M&b*U2dM*@{>jMSspd>cam41i1d$#E(I!&0#e{&G4(ngU}>1D-lL9(u!SJ z#~=(rx-Mufa#f19;UyCb3*%=i5GAb4Vc769*)Us0@d!tGw46PA}ivW z#HU1Tkpv4{cu3?$GT8m@Y|D851O-mCr1kW(7!5XK8oTUm%g~M*YcT86VW|nr8NJQ$ zY^G6f%hsDygL1}?(wfZLQSizpCZb!G$(lM!KDU6!FFE=GeTHACBR=#a!t-fW!#PiGq4R;m#nEkUWyD>jy6 z`nLu%i~*;h!}XJl3Rb;+I5IjsjK~*SAb9-y<)>5{Ic_}ps|UO#G3x0N+XA*Ctqv&AOb3l z9G|L8iGoTa2dT>3GDu?bpGINScs~ht1rI`O?58j>A~GT|BCcPAUOW(L&a@0CqiF`4 z!D_VK6(Beb1udkRvW#YUh=)0(X51A}}PH+F|6rd;{h}Cnff~p_o;oQS6QNW`pF@^z8Jv=Niq;gPU40mFG za)^Z)EH*8hdbCM4wuh3Nr47_R%c6oU~3;AEF3kxsg_w8DI+&<{J_wp7lsj`b+YIO4KSN2+* z!CkhCv)}J99U-Gm?QZk)jeYhlI~GTLcD2$TK0WhH<%)C2s|x>%_8GY;MpgJrJRCwd zF8pNs*stGz@1eq<20rVX@4%y4?k)K`tqj=m6JJde6-&Onk&i-?Y8%hA6|ZiW{#V{l3b-vObca@{Z7L;a22#26f^xg