Hello,
Here, I would like to describe several issues I revealed in the LSM module modlsm.f90 and my tentative suggestions to solve them. Note that each of them results in a segmentation fault:
Probably, some of these problems are happen in some grid points only, so they are domain-specific. Also, some of these issues may be interconnected, so resolving one of them could potentially address several others. In my case, I faced with them when simulating a fine domain:
# fine domain 156.25m resolution
x0 = 874468 # 3.25 deg E
y0 = 926741 # 51.5 deg N t
itot = 1536
jtot = 768
dx = 156.25
dy = 156.25
nprocx = 16 # 256 cores = 2 nodes
nprocy = 16
- Prognostic phiw(i,j,k) (soil water content) that is read from restart files when the model runs from warmstart for some grid points may turn into highly negative values causing overflow and segmentation fault in subroutine calc_thermal_properties
! 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))
My temporal solution was just to set the soil moisture content to 0 in the first place where it is used, if it turns into negative value, i.e., in subroutine calc_theta_mean(tile):
do k=1, kmax_soil
do j=2,j1
do i=2,i1
if((phiw(i,j,k) <= 1e-5)) phiw(i,j,k)=0. !if warmstart,
!prognostic phiw(i,j,k) might turns into negative values for some grid points
!causing overflow and seg. fault, so
!I set soil water content equals 0 if it is negative !Arseni
si = soil_index(i,j,k)
However, it needs to be checked more precisely.
theta_norm** (1. / vg_m) becomes greater than 1, resulting in a rising of negative value to a fraction power in pure function calc_diffusivity_vg and pure function calc_conductivity_vg: (1. - theta_norm ** (1. / vg_m)).
Here, I replace theta_norm** (1. / vg_m) with 0.9 everytime when theta_norm** (1. / vg_m)>1. However, it should be fixed in a more prompt way, I guess:
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_old
real :: part1, part2, part3, part4, part5
part1 = (1. - vg_m)
part2 = lambda_sat / (vg_a * vg_m * (theta_sat - theta_res))
part3 = theta_norm ** (vg_l - (1. / vg_m))
if((theta_norm ** (1. / vg_m))>=1.0) then !some values above 1 are appeared and the part4 and part5 turns into
!a complex number (due to rising a negative value to a fraction power) that couses segmentation fault:
!here I put 0.9 to avoid negative values !Arseni
part4 = (1. - 0.9) ** (-vg_m)
part5 = (1. - 0.9) ** vg_m - 2.
else
part4 = (1. - theta_norm ** (1. / vg_m)) ** (-vg_m)
part5 = (1. - theta_norm ** (1. / vg_m)) ** vg_m - 2.
endif
res = part1 * part2 * part3 * (part4 + part5)
end function calc_diffusivity_vg
!
! Calculate hydraulic conductivity using van Genuchten parameterisation.
!
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, gamma_sat
real :: res
if((theta_norm ** (1. / vg_m))>=1.0) then !some values above 1 are appeared and it turns into
!a complex number (due to rising a negative value to a fraction power) that couses segmentation fault:
!here I put 0.9 to avoid negative values !Arseni
res = gamma_sat * theta_norm**vg_l * ( 1.- (1.-0.9)**vg_m )**2.
else
res = gamma_sat * theta_norm**vg_l * ( 1.- (1.-theta_norm**(1./vg_m))**vg_m )**2.
endif
end function calc_conductivity_vg
- I also changed the subroutine Calc_canopy_resistance_ags so as not to calculate anything above water, I put an if statement (because over the water, tskin is NaN here and the model crashes); set output variables to 0 in case when Am < 0.01; Also, the units in co2_abs calculation have been corrected, co2_abs seems uses mg/m3? units here (Ronda et al., 2001), because the CO2 scalar tracers in DALES are expressed in units of µg/g, to correctly calculate co2_abs you simply need to multiply the value of co2 in µg/g by density at bottom layer ( rhof(1), kg/m3):
to_abs = rhof(1) !from ug/g to mg/m3
from_abs = 1/to_abs !from mg/m3 to ug/g
do j=2,j1
do i=2,i1
! Check if tskin(i, j) is NaN
if (isnan(tskin(i, j))) then
! Surface resistances for moisture and carbon dioxide:
tile_lv%rs(i,j) = 0.
tile_hv%rs(i,j) = 0.
tile_bs%rs(i,j) = 0.
tile_ws%rs(i,j) = 0.
!respiration and vegetation:
resp_co2(i,j) = 0.
an_co2(i,j) = 0.
else
! Check if tskin(i, j) less than 200 or more than 350, and replace with 287.
!if ((tskin(i, j).lt.200.).OR.(tskin(i, j).gt.350.)) then
!tskin(i, j) = 287.
!endif
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
si = soil_index(i, j, kmax_soil)
Ts = tskin(i,j) * exnh(1)
! Calculate the CO2 compensation concentration (IFS eq. 8.92)
! "The compensation point Γ is defined as the CO2 concentration at which the net CO2 assimilation of a fully lit leaf becomes zero."
! NOTE: The old DALES LSM used the atmospheric `thl`, IFS uses the surface temperature.
co2_comp = rhof(1) * co2_comp298 * Q10lambda**(0.1 * (Ts - 298.0)) ![g m-3 or ppm??]
! Calculate the mesophyll conductance (IFS eq. 8.93)
! "The mesophyll conductance gm describes the transport of CO2 from the substomatal cavities to the mesophyll cells where the carbon is fixed."
! NOTE: The old DALES LSM used the atmospheric `thl`, IFS uses the surface temperature.
gm = gm298 * Q10gm**(0.1 * (Ts - 298.0)) / ((1. + exp(0.3 * (T1gm - Ts))) * (1. + exp(0.3 * (Ts - T2gm)))) / 1000.
! Calculate CO2 concentration inside the leaf (ci)
! NOTE: Differs from IFS
fmin0 = gmin/nuco2q - (1./9.)*gm
fmin = (-fmin0 + (fmin0**2 + 4*gmin/nuco2q*gm)**0.5) / (2.*gm)
! Calculate atmospheric moisture deficit
! NOTE: "Therefore Ci/Cs is specified as a function of atmospheric moisture deficit Ds at the leaf surface"
! In DALES, this uses a mix between esat(surface) and e(atmosphere)
! In IFS, this uses (in kg kg-1): qsat(Ts)-qs instead of qsat(Ts)-qa!
! NOTE: Old DALES LSM used the surface pressure in the calculation of `e`, not sure why...
esatsurf = 0.611e3 * exp(17.2694 * (Ts - 273.16) / (Ts - 35.86))
e = qt0(i,j,1) * presf(1) / 0.622
Ds = (esatsurf - e) / 1000.
! This seems to differ from IFS?
Dmax = (f0 - fmin) / ad
! Coupling factor (IFS eq. 8.101)
!cfrac = f0 * (1.0 - Ds/Dmax) + fmin * (Ds/Dmax)
cfrac = max(0.01, f0 * (1.0 - Ds/Dmax) + fmin * (Ds/Dmax))
! Absolute CO2 concentration [mg/m3?]:
if (l_emission) then
co2_abs = svm(i,j,1,svco2sum)*to_abs !from ug/g to mg/m3 (svm scalar tasers for gases are in ug/g now)
else
co2_abs = svm(i,j,1,co2_index)*to_abs !from ug/g to mg/m3 (svm scalar tasers for gases are in ug/g now)
endif
!if (lrelaxci) then
! if (ci_old_set) then
! ci_inf = cfrac * (co2_abs - co2_comp) + co2_comp
! ci = ci_old(i,j) + min(kci*rk3coef, 1.0) * (ci_inf - ci_old(i,j))
! if (rk3step == 3) then
! ci_old(i,j) = ci
! endif
! else
! ci = cfrac * (co2_abs - co2_comp) + co2_comp
! ci_old(i,j) = ci
! endif
!else
! ci = cfrac * (co2_abs - co2_comp) + co2_comp
!endif
! CO2 concentration in leaf (IFS eq. ???): [mg m−3 or ppm]
ci = cfrac * (co2_abs - co2_comp) + co2_comp
! Max gross primary production in high light conditions (Ag) (IFS eq. 8.94)
! NOTE: The old DALES LSM used the atmospheric `thl`, IFS uses the surface temperature.
Ammax = Ammax298 * Q10am**(0.1 * (Ts - 298.0)) / ((1.0 + exp(0.3 * (T1Am - Ts))) * (1. + exp(0.3 * (Ts - T2Am))))
! Effect of soil moisture stress on gross assimilation rate.
! NOTE: this seems to be different in IFS...
! NOTE: for now, this uses the relative soil moisture content from the low vegetation only!
fstr = max(1.0e-3, min(1.0, tile_lv%phiw_mean(i,j)))
! Gross assimilation rate (Am, IFS eq. 8.97)
Am = Ammax * (1 - exp(-(gm * (ci - co2_comp) / Ammax)))
if((Am.lt.0.01).OR.(isnan(Am))) then !I fix Am.lt.0.01 as threshold
!since Am below might cause problems later in the code !Arseni
! Surface resistances for moisture and carbon dioxide:
tile_lv%rs(i,j) = 0.
tile_hv%rs(i,j) = 0.
tile_bs%rs(i,j) = 0.
tile_ws%rs(i,j) = 0.
!respiration and vegetation:
resp_co2(i,j) = 0.
an_co2(i,j) = 0.
else
! Autotrophic dark respiration (IFS eq. 8.99)
Rdark = Am/9.
.................
! NOTE:Combined assimilation for low and high veg, using only low veg as vegetation type:
resp_co2(i,j) = R10 * (1.-fw) * exp(Eact0 / (283.15 * 8.314) * (1.0 - 283.15 / t_mean))
! Scale with vegetation fraction, and translate to `ug/g m s-1`.
resp_co2(i,j) = cveg * resp_co2(i,j) * from_abs
! Calculate net assimilation
! NOTE: this uses the aerodynamic resistance from low vegetation...
an_co2(i,j) = -(co2_abs - ci) / (tile_lv%ra(i,j) + rs_co2)
! Scale with vegetation + bare soil fraction, and translate to `ug/g m s-1`.
an_co2(i,j) = cland * an_co2(i,j) * from_abs
!Replace unreasonable values with 0:
if (an_co2(i,j)>0.) an_co2(i,j)=0.
if (resp_co2(i,j)<0.) resp_co2(i,j)=0.
endif
endif
! Set CO2 fluxes:
if (l_emission) then
! Total flux into the CO2 field holding the sum of all CO2 concentrations:
svflux(i,j,svco2sum) = resp_co2(i,j) + an_co2(i,j) !Kinematic scalar flux [ug/g m/s]
! Respiration flux into the respiration specific field:
svflux(i,j,svco2ags) = resp_co2(i,j) !Kinematic scalar flux [ug/g m/s]
! Net update into the vegetation specific field:
!svflux(i,j,svco2veg) = an_co2(i,j) !Kinematic scalar flux [ug/g m/s]
else
! Respiration flux into the respiration specific field:
svflux(i,j,svco2ags) = resp_co2(i,j) !Kinematic scalar flux [ug/g m/s]
svflux(i,j,co2_index) = resp_co2(i,j) + an_co2(i,j) !Kinematic scalar flux [ug/g m/s]
endif
end do
end do
Let me know, if I was not right somewhere and if you have a more smart way to solve issues I mentioned.
Cheers,
Arseni
Edits by Fredrik: added code tags
Hello,
Here, I would like to describe several issues I revealed in the LSM module modlsm.f90 and my tentative suggestions to solve them. Note that each of them results in a segmentation fault:
Probably, some of these problems are happen in some grid points only, so they are domain-specific. Also, some of these issues may be interconnected, so resolving one of them could potentially address several others. In my case, I faced with them when simulating a fine domain:
My temporal solution was just to set the soil moisture content to 0 in the first place where it is used, if it turns into negative value, i.e., in subroutine calc_theta_mean(tile):
However, it needs to be checked more precisely.
theta_norm** (1. / vg_m)becomes greater than 1, resulting in a rising of negative value to a fraction power in pure functioncalc_diffusivity_vgand pure functioncalc_conductivity_vg: (1. - theta_norm ** (1. / vg_m)).Here, I replace
theta_norm** (1. / vg_m)with 0.9 everytime whentheta_norm** (1. / vg_m)>1. However, it should be fixed in a more prompt way, I guess:Let me know, if I was not right somewhere and if you have a more smart way to solve issues I mentioned.
Cheers,
Arseni
Edits by Fredrik: added code tags