feat: adding LandSurfaceTemperature module for LST modeling - #633
feat: adding LandSurfaceTemperature module for LST modeling#633michaelohanrahan wants to merge 84 commits into
Conversation
The module sets up the first keyword parameter. serves as foundation to implement the methods from the Devi Purnamasari et al. 2025 doi: https://doi.org/10.5194/hess-29-1483-2025
-add read_lst_inputs function handling global data ie radiation, albedo and crop type -probably add emissivity later
- read inputs from read lst inputs - initialize and empty grid
…read in init func
-requires {model:{lst__flag:true}}
-following SnowModel if do_lst will initialize LSTModel or not
Add energy balance calculations with radiation and heat fluxes Integrate atmospheric forcing and SBM soil model coupling Implement detailed LST calculations following Purnamasari et al. 2025
vers-w
left a comment
There was a problem hiding this comment.
Nice start with this new feature @michaelohanrahan !
I added some comments, main suggestion is to include this new feature as part of LandHydrologySBM, make use of variables and parameter fields in the model struct, to move some parameters or forcing to other structs (e.g. for shared land parameters) and to check/discuss what hydromt_wflow should compute.
Add albedo, shortwave radiation (RS_in), and 2m wind speed (u2m) to forcing Support land surface temperature model requirements
Add LST, radiation fluxes, and aerodynamic resistance with proper units Maintain Celsius temperature scale for land surface temperature
…d surface temperature - Use explicit LandSurfaceTemperature* names for model, parameters, and variables - Clean up struct definitions for clarity and maintainability - Update to handle expanded atmospheric forcing structure
Add LST model update call in main timestep loop after soil model updates
Include land_surface_temperature flag in model configuration for SBM GWF model
Include land_surface_temperature flag in model configuration for SBM model
vers-w
left a comment
There was a problem hiding this comment.
Thanks for the changes @michaelohanrahan ! Some additional comments from my side.
- Remove LandSurfaceTemperatureParameters struct entirely - Update LandSurfaceTemperatureModel to only contain variables (no parameters) - Modify update_timestep_land_surface_temperature function to accept network::NetworkLand and vegetation_parameters::VegetationParameters as parameters - Update function to use network.latitude[i] instead of land_surface_temperature_model.parameters.latitude[i] - Update function to use vegetation_parameters.canopy_height[i] instead of land_surface_temperature_model.parameters.crop_height[i] - Add aerodynamic resistance calculation within the update function - Fix bug in calculate_land_surface_temperature function to use correct constant name cp - Update both update! function signatures to include new parameters
- Update all land surface temperature variable mappings to use new variable names: * LST → land_surface_temperature * RSN → net_shortwave_radiation * RLN → net_longwave_radiation * Rnet → net_radiation * LE → latent_heat_flux * H → sensible_heat_flux * ra → aerodynamic_resistance
…on params. Removing clock and dt from model update
…into landsurfacetemp
Also related to recent code changes.
Also renamed `wind_altitude` to a more descriptive standard name.
| wind_speed_measured::Float64, | ||
| z_measured::Float64, | ||
| canopy_height::Float64; | ||
| zm_ref::Float64 = 2.0, # reference height for wind speed (m) |
There was a problem hiding this comment.
I understand that the reference height zm_ref of 2 m is based on the FAO approach, related to the Penman-Monteith equation for a reference surface.
Based on the work of Zink et al. (2018) and other literature I think we can deviate from this reference height of 2 m. My recommendation would be to use a reference height of ~2 m above the canopy (add 2 m to the average canopy height and round to nearest meter). Additionally, I would recommend to stick to the aerodynamic resistance equation based on Thom’s equation (Thom, 1975) and to remove the alternative aerodynamic conductance calculation from AWRA05. And for now just use the default values:
dh_ratio = 2.0 / 3.0
z0m_ratio = 0.123
z0h_ratio = 0.1
There was a problem hiding this comment.
The reference height is now set to skin_layer_height + ~2.0 m. Additionally, the resistance equation is now only based on Thom’s equation (Thom, 1975), including the part for heat transfer.
| ) | ||
| # Handle measurement height below canopy | ||
| if z_measured < canopy_height | ||
| z_measured = canopy_height |
There was a problem hiding this comment.
Maybe better to check if z_measured is smaller than the zero plane displacement height, and than move it to canopy_height?
There was a problem hiding this comment.
Removed the check for z_measured. The expectation is that the provided wind speed (and measurement height) is consistent with d0 and z0m. Need to decide if validation of this input is needed (e.g. measurement height should be higher than d0 + z0m).
|
|
||
| # Wind speed conversion to reference height (2m) for consistency with FAO-56 | ||
| wind_speed_ref = | ||
| max(wind_speed_measured * (log(zm_ref / z0m) / log(z_measured / z0m)), 0.5) |
There was a problem hiding this comment.
I noticed that a minimum wind speed of 0.5 is mentioned in Purnamasari et al. (2025) based on Allen et al., (1998) but cannot find it. Do you know where this is mentioned in Allen et al., (1998)?
There was a problem hiding this comment.
Need to decide if we want to stick with this minimum wind speed value of 0.5 m/s.
| if canopy_height < 1.0 | ||
| # Aerodynamic resistance using reference height | ||
| # ra = (log((zm_ref - d) / z0m)) * (log((zm_ref - d) / z0h)) / (k^2 * wind_speed_ref) | ||
| ra = log((zm_ref - d) / z0m) / (k^2 * wind_speed_ref) |
There was a problem hiding this comment.
Is this not missing the inclusion of z0h in the equation?
There was a problem hiding this comment.
Now using Thom’s equation.
From `LandSurfaceTemperatureVariables`.
Aerodynamic surface roughness lengths and zero-place displacement height.
Computation now only based on Thom’s equation.
Explanation
Working towards implementation of the methods from the Devi Purnamasari et al. 2025 paper: "Identifying irrigated areas using land surface temperature and hydrological modelling: application to the Rhine basin" doi
Implementation details:
The land surface temperature (LST) module is an optional component of the SBM land hydrology model, controlled by the land_surface_temperature__flag in the model config. When enabled, a LandSurfaceTemperatureModel is created and stored as a field in LandHydrologySBM, alongside interception, snow, soil, and other land processes. The LST model uses atmospheric forcing (temperature, wind speed, net radiation), soil actual evapotranspiration, and vegetation parameters (e.g. canopy height) to compute aerodynamic resistance, sensible heat flux, and land surface temperature per cell. It is updated each timestep in the land model update! sequence, after soil and before interception is added to actual evaporation. When the flag is off, a NoLandSurfaceTemperatureModel is used instead, and the LST update is skipped.
Checklist
master