Skip to content

Conversation

@braghiere
Copy link
Member

@braghiere braghiere commented Oct 22, 2025

Purpose

Add support for spatially-varying canopy height in ClimaLand vegetation model, replacing the fixed 1m default with realistic height data from CLM artifacts (allowing for a cap).

Content

Core Implementation

  • Canopy height parameterization: Added clm_canopy_height() function to read spatially-varying canopy height from CLM artifact data (src/standalone/Vegetation/spatially_varying_parameters.jl), optionally capping it
  • Type system updates: Extended PrescribedBiomassModel to support spatially-varying height through type parameter HTH (canopy height type)

Testing and Validation

  • Unit tests: Comprehensive test suite covering spatially-varying height functionality, including Field operations, capping behavior, and integration with canopy model

Impact Assessment

image

Figure: Sensible heat flux comparison showing Run 1 (default 1m height), Run 2 (spatially-varying height), absolute difference, and percent change.

The spatially-varying canopy height primarily affects:

  • Turbulent fluxes: Changes in aerodynamic resistance modify sensible and latent heat exchange with atmosphere
  • Regional patterns: Strongest impacts in forested regions (tropical/temperate) where height exceeds 1m default
  • Minimal impact: Grasslands and croplands show small changes (CLM heights ~1m already)

To-do

  • Add NEWS.md entry after reviewer feedback

  • I have read and checked the items on the review checklist.

@braghiere braghiere added the enhancement New feature or request label Oct 22, 2025
@braghiere braghiere changed the title Rb/spatially varying canopy height spatially varying canopy height Oct 22, 2025
@braghiere braghiere changed the title spatially varying canopy height Spatially varying canopy height Oct 22, 2025
Copy link
Member

@kmdeck kmdeck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's try running this and see how the SHF, LHF, etc change compared to observations

Moving forwards, I think there are two ways this can be extended:

  • make a canopy roughness model type. right now we are using physical quantities (height) to compute roughness and displacement height (using physical equations someone has come up with). Something more flexible would be to predict roughness, displacement height, from inputs like LAI, canopy height, etc using a data driven model. Having a type for "canopy roughness" would let us switch between the two easily. (I am separately adding this already in another PR)
  • in Biomass - create a type of "height model": this would be prescribed (constant or spatially varying) for now, PrescribedCanopyHeight. Then in the future we can also easily make different parameterizations for canopy height (e.g. prognostic, predicting it from input/time varying data). but this seems very far in the future compared to the first point

Comment on lines +356 to +350
canopy_height = SpaceVaryingInput(
joinpath(clm_artifact_path, "vegetation_properties_map.nc"),
"z_top",
surface_space;
regridder_type,
regridder_kwargs = (; extrapolation_bc, interpolation_method),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could provide the effective_canopy_height as a preprocessing function instead, so we don't have to think about it wherever we access the canopy height data. This would be safer because we'll avoid users forgetting to apply the clipping.

        file_reader_kwargs = (; preprocess_func = (data) -> effective_canopy_height(data, z_atm; buffer),),

And then we take z_atm and buffer as inputs to this function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

effective_canopy_height would also have to change to take in a float canopy_height instead of the whole field, since here it'll be broadcasted over the field. We wouldn't be able to log the information you have in the warning, but I think that's okay. Usually when we add warnings like this we regret it later because it clutters the simulation output

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this would be a good change - then we won't have to call effective_canopy_height every time we read in canopy height from a map

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I combined both of these into a single function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I can change it to be the preprocessing function instead

@braghiere
Copy link
Member Author

let's try running this and see how the SHF, LHF, etc change compared to observations

Moving forwards, I think there are two ways this can be extended:

  • make a canopy roughness model type. right now we are using physical quantities (height) to compute roughness and displacement height (using physical equations someone has come up with). Something more flexible would be to predict roughness, displacement height, from inputs like LAI, canopy height, etc using a data driven model. Having a type for "canopy roughness" would let us switch between the two easily. (I am separately adding this already in another PR)
  • in Biomass - create a type of "height model": this would be prescribed (constant or spatially varying) for now, PrescribedCanopyHeight. Then in the future we can also easily make different parameterizations for canopy height (e.g. prognostic, predicting it from input/time varying data). but this seems very far in the future compared to the first point

Largest changes are observed:

Sensible Heat (ERA5): Bias ↓ (8.27 → 7.89 W m⁻²) ~5% improvement, RMSE same.
here

Evaporative Fraction (ERA5): Bias slightly closer to 0 (−0.0626 → −0.0601) ~4% improvement, Phase same. (No RMSE.)
here

Surface Upward LW (CERES): Bias closer to 0 (−5.85 → −5.52 W m⁻²) ~6% improvement, RMSE ~ +0.1, Phase ≈ same.
here

All the other variables have minimal changes.

Copy link
Member

@kmdeck kmdeck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this looks good, but I dont love that we now have multiple definitions of height, and have some pause since we arent sure we want to predict canopy height in the future.

I wonder, since these additions affect the turbulent boundary fluxes only, and because the height only affects the roughness lengths and displacment height but is not used directly itself, if we should move height out of the biomass model and only compute displacement and roughness length fields using the height once, and store those, and not the height.

This isnt really possible in this PR, but after PR #1505 merges, I think we can do something like this.

Copilot AI review requested due to automatic review settings December 10, 2025 18:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@kmdeck kmdeck force-pushed the rb/spatially-varying-canopy-height branch from 68eef0a to bd41510 Compare December 10, 2025 18:50
@kmdeck kmdeck force-pushed the rb/spatially-varying-canopy-height branch from 6204d4e to 5adaf5e Compare December 11, 2025 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants