|
| 1 | +# # Global run of land model at low resolution |
| 2 | + |
| 3 | +# The code sets up and runs ClimaLand v1, which |
| 4 | +# includes soil, canopy, and snow, on a spherical domain, |
| 5 | +# using low resolution ERA5 data as forcing. |
| 6 | + |
| 7 | +# Simulation Setup |
| 8 | +# Number of spatial elements: 30 in horizontal, 15 in vertical |
| 9 | +# Soil depth: 50 m |
| 10 | +# Simulation duration: 730 d |
| 11 | +# Timestep: 450 s |
| 12 | +# Timestepper: ARS111 |
| 13 | +# Fixed number of iterations: 3 |
| 14 | +# Jacobian update: every new Newton iteration |
| 15 | +# Atmos forcing update: every 3 hours |
| 16 | + |
| 17 | +import ClimaComms |
| 18 | +ClimaComms.@import_required_backends |
| 19 | +using ClimaUtilities.ClimaArtifacts |
| 20 | +import ClimaUtilities.TimeManager: ITime, date |
| 21 | + |
| 22 | +import ClimaDiagnostics |
| 23 | +import ClimaUtilities |
| 24 | + |
| 25 | +import ClimaUtilities.TimeVaryingInputs: |
| 26 | + TimeVaryingInput, LinearInterpolation, PeriodicCalendar |
| 27 | +import ClimaUtilities.ClimaArtifacts: @clima_artifact |
| 28 | +import ClimaParams as CP |
| 29 | +using ClimaCore |
| 30 | +using ClimaLand |
| 31 | +using ClimaLand.Snow |
| 32 | +using ClimaLand.Soil |
| 33 | +using ClimaLand.Canopy |
| 34 | +import ClimaLand |
| 35 | +import ClimaLand.Parameters as LP |
| 36 | +import ClimaLand.Simulations: LandSimulation, solve! |
| 37 | + |
| 38 | +using Dates |
| 39 | + |
| 40 | +using CairoMakie, GeoMakie, Poppler_jll, ClimaAnalysis |
| 41 | +LandSimVis = |
| 42 | + Base.get_extension( |
| 43 | + ClimaLand, |
| 44 | + :LandSimulationVisualizationExt, |
| 45 | + ).LandSimulationVisualizationExt; |
| 46 | + |
| 47 | +const FT = Float64; |
| 48 | +context = ClimaComms.context() |
| 49 | +ClimaComms.init(context) |
| 50 | +device = ClimaComms.device() |
| 51 | +device_suffix = device isa ClimaComms.CPUSingleThreaded ? "cpu" : "gpu" |
| 52 | +root_path = "lowres_snowy_land_longrun_$(device_suffix)" |
| 53 | +diagnostics_outdir = joinpath(root_path, "global_diagnostics") |
| 54 | +outdir = |
| 55 | + ClimaUtilities.OutputPathGenerator.generate_output_path(diagnostics_outdir) |
| 56 | + |
| 57 | +function setup_model(FT, start_date, stop_date, Δt, domain, earth_param_set) |
| 58 | + surface_space = domain.space.surface |
| 59 | + subsurface_space = domain.space.subsurface |
| 60 | + # Forcing data |
| 61 | + era5_ncdata_path = ClimaLand.Artifacts.era5_land_forcing_data2008_path(; |
| 62 | + context, |
| 63 | + lowres = true, |
| 64 | + ) |
| 65 | + atmos, radiation = ClimaLand.prescribed_forcing_era5( |
| 66 | + era5_ncdata_path, |
| 67 | + surface_space, |
| 68 | + start_date, |
| 69 | + earth_param_set, |
| 70 | + FT; |
| 71 | + max_wind_speed = 25.0, |
| 72 | + time_interpolation_method = LinearInterpolation(PeriodicCalendar()), |
| 73 | + ) |
| 74 | + |
| 75 | + (; ν_ss_om, ν_ss_quartz, ν_ss_gravel) = |
| 76 | + ClimaLand.Soil.soil_composition_parameters(subsurface_space, FT) |
| 77 | + (; ν, hydrology_cm, K_sat, θ_r) = |
| 78 | + ClimaLand.Soil.soil_vangenuchten_parameters(subsurface_space, FT) |
| 79 | + soil_albedo = Soil.CLMTwoBandSoilAlbedo{FT}(; |
| 80 | + ClimaLand.Soil.clm_soil_albedo_parameters(surface_space)..., |
| 81 | + ) |
| 82 | + S_s = ClimaCore.Fields.zeros(subsurface_space) .+ FT(1e-3) |
| 83 | + soil_params = Soil.EnergyHydrologyParameters( |
| 84 | + FT; |
| 85 | + ν, |
| 86 | + ν_ss_om, |
| 87 | + ν_ss_quartz, |
| 88 | + ν_ss_gravel, |
| 89 | + hydrology_cm, |
| 90 | + K_sat, |
| 91 | + S_s, |
| 92 | + θ_r, |
| 93 | + albedo = soil_albedo, |
| 94 | + ) |
| 95 | + f_over = FT(3.28) # 1/m |
| 96 | + R_sb = FT(1.484e-4 / 1000) # m/s |
| 97 | + runoff_model = ClimaLand.Soil.Runoff.TOPMODELRunoff{FT}(; |
| 98 | + f_over = f_over, |
| 99 | + f_max = ClimaLand.Soil.topmodel_fmax(surface_space, FT), |
| 100 | + R_sb = R_sb, |
| 101 | + ) |
| 102 | + |
| 103 | + # Spatially varying canopy parameters from CLM |
| 104 | + g1 = ClimaLand.Canopy.clm_medlyn_g1(surface_space) |
| 105 | + rooting_depth = ClimaLand.Canopy.clm_rooting_depth(surface_space) |
| 106 | + (; is_c3, Vcmax25) = |
| 107 | + ClimaLand.Canopy.clm_photosynthesis_parameters(surface_space) |
| 108 | + (; Ω, G_Function, α_PAR_leaf, τ_PAR_leaf, α_NIR_leaf, τ_NIR_leaf) = |
| 109 | + ClimaLand.Canopy.clm_canopy_radiation_parameters(surface_space) |
| 110 | + |
| 111 | + # Energy Balance model |
| 112 | + ac_canopy = FT(2.5e3) |
| 113 | + # Plant Hydraulics and general plant parameters |
| 114 | + SAI = FT(0.0) # m2/m2 |
| 115 | + f_root_to_shoot = FT(3.5) |
| 116 | + RAI = FT(1.0) |
| 117 | + K_sat_plant = FT(7e-8) # m/s |
| 118 | + ψ63 = FT(-4 / 0.0098) # / MPa to m |
| 119 | + Weibull_param = FT(4) # unitless |
| 120 | + a = FT(0.2 * 0.0098) # 1/m |
| 121 | + conductivity_model = |
| 122 | + Canopy.PlantHydraulics.Weibull{FT}(K_sat_plant, ψ63, Weibull_param) |
| 123 | + retention_model = Canopy.PlantHydraulics.LinearRetentionCurve{FT}(a) |
| 124 | + plant_ν = FT(1.44e-4) |
| 125 | + plant_S_s = FT(1e-2 * 0.0098) # m3/m3/MPa to m3/m3/m |
| 126 | + n_stem = 0 |
| 127 | + n_leaf = 1 |
| 128 | + h_stem = FT(0.0) |
| 129 | + h_leaf = FT(1.0) |
| 130 | + zmax = FT(0.0) |
| 131 | + h_canopy = h_stem + h_leaf |
| 132 | + compartment_midpoints = |
| 133 | + n_stem > 0 ? [h_stem / 2, h_stem + h_leaf / 2] : [h_leaf / 2] |
| 134 | + compartment_surfaces = |
| 135 | + n_stem > 0 ? [zmax, h_stem, h_canopy] : [zmax, h_leaf] |
| 136 | + |
| 137 | + z0_m = FT(0.13) * h_canopy |
| 138 | + z0_b = FT(0.1) * z0_m |
| 139 | + |
| 140 | + soil_args = (domain = domain, parameters = soil_params) |
| 141 | + soil_model_type = Soil.EnergyHydrology{FT} |
| 142 | + |
| 143 | + # Soil microbes model |
| 144 | + soilco2_type = Soil.Biogeochemistry.SoilCO2Model{FT} |
| 145 | + soilco2_ps = Soil.Biogeochemistry.SoilCO2ModelParameters(FT) |
| 146 | + Csom = ClimaLand.PrescribedSoilOrganicCarbon{FT}(TimeVaryingInput((t) -> 5)) |
| 147 | + |
| 148 | + soilco2_args = (; domain = domain, parameters = soilco2_ps) |
| 149 | + |
| 150 | + # Now we set up the canopy model, which we set up by component: |
| 151 | + # Component Types |
| 152 | + canopy_component_types = (; |
| 153 | + autotrophic_respiration = Canopy.AutotrophicRespirationModel{FT}, |
| 154 | + radiative_transfer = Canopy.TwoStreamModel{FT}, |
| 155 | + photosynthesis = Canopy.FarquharModel{FT}, |
| 156 | + conductance = Canopy.MedlynConductanceModel{FT}, |
| 157 | + hydraulics = Canopy.PlantHydraulicsModel{FT}, |
| 158 | + energy = Canopy.BigLeafEnergyModel{FT}, |
| 159 | + ) |
| 160 | + # Individual Component arguments |
| 161 | + # Set up autotrophic respiration |
| 162 | + autotrophic_respiration_args = |
| 163 | + (; parameters = Canopy.AutotrophicRespirationParameters(FT)) |
| 164 | + # Set up radiative transfer |
| 165 | + radiative_transfer_args = (; |
| 166 | + parameters = Canopy.TwoStreamParameters( |
| 167 | + FT; |
| 168 | + Ω, |
| 169 | + α_PAR_leaf, |
| 170 | + τ_PAR_leaf, |
| 171 | + α_NIR_leaf, |
| 172 | + τ_NIR_leaf, |
| 173 | + G_Function, |
| 174 | + ) |
| 175 | + ) |
| 176 | + # Set up conductance |
| 177 | + conductance_args = |
| 178 | + (; parameters = Canopy.MedlynConductanceParameters(FT; g1)) |
| 179 | + # Set up photosynthesis |
| 180 | + photosynthesis_args = |
| 181 | + (; parameters = Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) |
| 182 | + # Set up plant hydraulics |
| 183 | + modis_lai_ncdata_path = ClimaLand.Artifacts.modis_lai_multiyear_paths(; |
| 184 | + context = nothing, |
| 185 | + start_date, |
| 186 | + end_date = stop_date, |
| 187 | + ) |
| 188 | + LAIfunction = ClimaLand.prescribed_lai_modis( |
| 189 | + modis_lai_ncdata_path, |
| 190 | + surface_space, |
| 191 | + start_date; |
| 192 | + time_interpolation_method = LinearInterpolation(), |
| 193 | + ) |
| 194 | + ai_parameterization = |
| 195 | + Canopy.PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI) |
| 196 | + |
| 197 | + plant_hydraulics_ps = Canopy.PlantHydraulics.PlantHydraulicsParameters(; |
| 198 | + ai_parameterization = ai_parameterization, |
| 199 | + ν = plant_ν, |
| 200 | + S_s = plant_S_s, |
| 201 | + rooting_depth = rooting_depth, |
| 202 | + conductivity_model = conductivity_model, |
| 203 | + retention_model = retention_model, |
| 204 | + ) |
| 205 | + plant_hydraulics_args = ( |
| 206 | + parameters = plant_hydraulics_ps, |
| 207 | + n_stem = n_stem, |
| 208 | + n_leaf = n_leaf, |
| 209 | + compartment_midpoints = compartment_midpoints, |
| 210 | + compartment_surfaces = compartment_surfaces, |
| 211 | + ) |
| 212 | + |
| 213 | + energy_args = (parameters = Canopy.BigLeafEnergyParameters{FT}(ac_canopy),) |
| 214 | + |
| 215 | + # Canopy component args |
| 216 | + canopy_component_args = (; |
| 217 | + autotrophic_respiration = autotrophic_respiration_args, |
| 218 | + radiative_transfer = radiative_transfer_args, |
| 219 | + photosynthesis = photosynthesis_args, |
| 220 | + conductance = conductance_args, |
| 221 | + hydraulics = plant_hydraulics_args, |
| 222 | + energy = energy_args, |
| 223 | + ) |
| 224 | + |
| 225 | + # Other info needed |
| 226 | + shared_params = Canopy.SharedCanopyParameters{FT, typeof(earth_param_set)}( |
| 227 | + z0_m, |
| 228 | + z0_b, |
| 229 | + earth_param_set, |
| 230 | + ) |
| 231 | + |
| 232 | + canopy_model_args = (; |
| 233 | + parameters = shared_params, |
| 234 | + domain = ClimaLand.obtain_surface_domain(domain), |
| 235 | + ) |
| 236 | + |
| 237 | + # Snow model |
| 238 | + # α_snow = Snow.ConstantAlbedoModel(FT(0.7)) |
| 239 | + # Set β = 0 in order to regain model without density dependence |
| 240 | + α_snow = Snow.ZenithAngleAlbedoModel( |
| 241 | + FT(0.64), |
| 242 | + FT(0.06), |
| 243 | + FT(2); |
| 244 | + β = FT(0.4), |
| 245 | + x0 = FT(0.2), |
| 246 | + ) |
| 247 | + horz_degree_res = |
| 248 | + sum(ClimaLand.Domains.average_horizontal_resolution_degrees(domain)) / 2 # mean of resolution in latitude and longitude, in degrees |
| 249 | + scf = Snow.WuWuSnowCoverFractionModel( |
| 250 | + FT(0.08), |
| 251 | + FT(1.77), |
| 252 | + FT(1.0), |
| 253 | + horz_degree_res, |
| 254 | + ) |
| 255 | + snow_parameters = SnowParameters{FT}( |
| 256 | + Δt; |
| 257 | + earth_param_set = earth_param_set, |
| 258 | + α_snow = α_snow, |
| 259 | + scf = scf, |
| 260 | + ) |
| 261 | + snow_args = (; |
| 262 | + parameters = snow_parameters, |
| 263 | + domain = ClimaLand.obtain_surface_domain(domain), |
| 264 | + ) |
| 265 | + snow_model_type = Snow.SnowModel |
| 266 | + |
| 267 | + land_input = ( |
| 268 | + atmos = atmos, |
| 269 | + radiation = radiation, |
| 270 | + runoff = runoff_model, |
| 271 | + soil_organic_carbon = Csom, |
| 272 | + ) |
| 273 | + land = LandModel{FT}(; |
| 274 | + soilco2_type = soilco2_type, |
| 275 | + soilco2_args = soilco2_args, |
| 276 | + land_args = land_input, |
| 277 | + soil_model_type = soil_model_type, |
| 278 | + soil_args = soil_args, |
| 279 | + canopy_component_types = canopy_component_types, |
| 280 | + canopy_component_args = canopy_component_args, |
| 281 | + canopy_model_args = canopy_model_args, |
| 282 | + snow_args = snow_args, |
| 283 | + snow_model_type = snow_model_type, |
| 284 | + ) |
| 285 | + return land |
| 286 | +end |
| 287 | +# Note that since the Northern hemisphere's winter season is defined as DJF, |
| 288 | +# we simulate from and until the beginning of |
| 289 | +# March so that a full season is included in seasonal metrics. |
| 290 | +start_date = DateTime("2008-03-01") |
| 291 | +stop_date = DateTime("2010-03-02") |
| 292 | +Δt = 450.0 |
| 293 | +nelements = (30, 15) |
| 294 | +domain = ClimaLand.Domains.global_domain( |
| 295 | + FT; |
| 296 | + context, |
| 297 | + nelements, |
| 298 | + mask_threshold = FT(0.99), |
| 299 | +) |
| 300 | +params = LP.LandParameters(FT) |
| 301 | +model = setup_model(FT, start_date, stop_date, Δt, domain, params) |
| 302 | +user_callbacks = ( |
| 303 | + ClimaLand.NaNCheckCallback( |
| 304 | + Dates.Month(6), |
| 305 | + start_date, |
| 306 | + ITime(Δt, epoch = start_date), |
| 307 | + mask = ClimaLand.Domains.landsea_mask(ClimaLand.get_domain(model)), |
| 308 | + ), |
| 309 | + ClimaLand.ReportCallback(10000), |
| 310 | +) |
| 311 | +simulation = |
| 312 | + LandSimulation(FT, start_date, stop_date, Δt, model; user_callbacks, outdir) |
| 313 | +@info "Run: Global Soil-Canopy-Snow Model" |
| 314 | +@info "Resolution: $nelements" |
| 315 | +@info "Timestep: $Δt s" |
| 316 | +@info "Start Date: $start_date" |
| 317 | +@info "Stop Date: $stop_date" |
| 318 | +ClimaLand.Simulations.solve!(simulation) |
| 319 | + |
| 320 | +LandSimVis.make_annual_timeseries(simulation; savedir = root_path) |
| 321 | +LandSimVis.make_heatmaps(simulation; savedir = root_path, date = stop_date) |
| 322 | +LandSimVis.make_leaderboard_plots(simulation; savedir = root_path) |
0 commit comments