From 3e9a73adfe0793287e034c20d38047e800f0db2a Mon Sep 17 00:00:00 2001 From: imreddyTeja Date: Mon, 16 Sep 2024 17:40:36 -0700 Subject: [PATCH 1/2] Extend photosynthesis mechanism to support fields This change is applied to both OptimalityFarquharParameters and FarquharParameters. Previously, the mechanism parameter was held as an empty struct of type ::C3 or ::C4 to allow for dispatch. Because it is not possible to create a ClimaCore field of different structs, the mechanism param is changed to a is_c3 param that holds 1s and 0s. This choice was made because reading a NetCDF file requires regridding, which must be performed over Numbers. If the input data file must store the mechanism as a float, then it is easiest to also internally represent the mechanism as a float. The C3/C4 structs are now removed. The functions that previously used C3/C4 structs for dispatching are converted to two seperate functions, which are called based on the is_c3 value. The constructor checks that the mechanism value of field is between 0 and 1, and throws an error otherwise. Regridding a field of 0s and 1s may result in values between 0 and 1, so the constuctor rounds them to the nearest integer. This implementation allows for future support of mixed mechanisms in an area represented by proportion c3 without many changes. All calls to the FarquharParameters constructor are changed to work with the changes to mechanism representation. Tests are also changed to work with new constructor and representation of mechanism. The tests in canopy_model are changed to run twice using a for loop. The first run has all the parameters as floats, and the second run has all canopy parameters that can be fields as fields. In this case, the photosynthesis mechanism is tested as a field that alternates between 1.0 and 0.0, which represents alternating between C3 and C4. The tests were previously all run over a Point domain. The tests that use FarquharParameters are changed to run on a spherical surface. Additionally, a test for vcmax as a field is added. Land.jl and land_region.jl are changed to use the mechanism map from the clm data. --- NEWS.md | 2 + docs/src/standalone/apps/leaf_an.jl | 7 +- docs/src/standalone/apps/leaf_an_ci.jl | 7 +- .../integrated/soil_canopy_tutorial.jl | 3 +- .../standalone/Canopy/canopy_tutorial.jl | 6 +- experiments/benchmarks/land.jl | 17 +-- experiments/integrated/fluxnet/ozark_pft.jl | 3 +- experiments/integrated/fluxnet/run_fluxnet.jl | 3 +- .../integrated/global/global_soil_canopy.jl | 6 +- .../conservation/ozark_conservation_setup.jl | 3 +- .../performance/profile_allocations.jl | 3 +- experiments/long_runs/land.jl | 18 +-- experiments/long_runs/land_region.jl | 30 ++--- .../standalone/Vegetation/no_vegetation.jl | 4 +- .../standalone/Vegetation/varying_lai.jl | 4 +- .../Vegetation/varying_lai_with_stem.jl | 4 +- ext/CreateParametersExt.jl | 37 +++++-- .../fluxnet_sites/US-Ha1/US-Ha1_parameters.jl | 4 +- .../fluxnet_sites/US-MOz/US-MOz_parameters.jl | 4 +- .../fluxnet_sites/US-NR1/US-NR1_parameters.jl | 4 +- .../fluxnet_sites/US-Var/US-Var_parameters.jl | 4 +- .../src/Fluxnet/run_fluxnet.jl | 4 +- .../Vegetation/canopy_parameterizations.jl | 45 ++++++-- .../Vegetation/optimality_farquhar.jl | 15 ++- src/standalone/Vegetation/photosynthesis.jl | 37 ++----- test/standalone/Vegetation/canopy_model.jl | 103 +++++++++++++----- .../Vegetation/plant_hydraulics_test.jl | 6 +- .../test_bigleaf_parameterizations.jl | 20 ++-- 28 files changed, 244 insertions(+), 159 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2e40365078..a2c29ef5e6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,8 @@ ClimaLand.jl Release Notes main -------- - Add regional simulation example PR [#757](https://github.com/CliMA/ClimaLand.jl/pull/757) + - ![][badge-💥breaking] Extend photosynthesis mechanism parameter to support fields. + PR[#774](https://github.com/CliMA/ClimaLand.jl/pull/774) v0.14.3 -------- diff --git a/docs/src/standalone/apps/leaf_an.jl b/docs/src/standalone/apps/leaf_an.jl index 5960996a6c..618a24284a 100644 --- a/docs/src/standalone/apps/leaf_an.jl +++ b/docs/src/standalone/apps/leaf_an.jl @@ -38,10 +38,11 @@ function ParamViz.parameterisation( Γstar = co2_compensation(Γstar25, ΔHΓstar, T, To, R) medlynt = 1 + g1 / sqrt(VPD) ci = intercellular_co2(ca, Γstar, medlynt) - Aj = light_assimilation(Canopy.C3(), J, ci, Γstar) + is_c3 = 1.0 + Aj = light_assimilation(is_c3, J, ci, Γstar) Kc = MM_Kc(Kc25, ΔHkc, T, To, R) Ko = MM_Ko(Ko25, ΔHko, T, To, R) - Ac = rubisco_assimilation(Canopy.C3(), Vcmax, ci, Γstar, Kc, Ko, oi) + Ac = rubisco_assimilation(is_c3, Vcmax, ci, Γstar, Kc, Ko, oi) Rd = dark_respiration(Vcmax25, β, f, ΔHRd, T, To, R) An = net_photosynthesis(Ac, Aj, Rd, β) return An @@ -83,7 +84,7 @@ function An_app_f() "Ω", # Clumping index "Γstar25", # co2 compensation at 25c "ΔHΓstar", # a constant energy of activation - "To", # a standard temperature + "To", # a standard temperature "R", # the universal gas constant "Vcmax25", # the maximum rate of carboxylation of Rubisco "ΔHJmax", # a constant diff --git a/docs/src/standalone/apps/leaf_an_ci.jl b/docs/src/standalone/apps/leaf_an_ci.jl index 351338a131..417d658e4c 100644 --- a/docs/src/standalone/apps/leaf_an_ci.jl +++ b/docs/src/standalone/apps/leaf_an_ci.jl @@ -36,8 +36,9 @@ function ParamViz.parameterisation( Γstar = co2_compensation(Γstar25, ΔHΓstar, T, To, R) Kc = MM_Kc(Kc25, ΔHkc, T, To, R) Ko = MM_Ko(Ko25, ΔHko, T, To, R) - Ac = rubisco_assimilation(Canopy.C3(), Vcmax, ci, Γstar, Kc, Ko, oi) - Aj = light_assimilation(Canopy.C3(), J, ci, Γstar) + is_c3 = 1.0 + Ac = rubisco_assimilation(is_c3, Vcmax, ci, Γstar, Kc, Ko, oi) + Aj = light_assimilation(is_c3, J, ci, Γstar) Rd = dark_respiration(Vcmax25, β, f, ΔHRd, T, To, R) An = net_photosynthesis(Ac, Aj, Rd, β) return An @@ -79,7 +80,7 @@ function An_ci_app_f() "Ω", # Clumping index "Γstar25", # co2 compensation at 25c "ΔHΓstar", # a constant energy of activation - "To", # a standard temperature + "To", # a standard temperature "R", # the universal gas constant "ΔHJmax", # a constant "θj", # an empirical "curvature parameter" diff --git a/docs/tutorials/integrated/soil_canopy_tutorial.jl b/docs/tutorials/integrated/soil_canopy_tutorial.jl index ff4ece3849..ce6c4acc83 100644 --- a/docs/tutorials/integrated/soil_canopy_tutorial.jl +++ b/docs/tutorials/integrated/soil_canopy_tutorial.jl @@ -231,8 +231,9 @@ radiative_transfer_args = (; conductance_args = (; parameters = MedlynConductanceParameters(FT; g1 = 141)) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 photosynthesis_args = - (; parameters = FarquharParameters(FT, Canopy.C3(); Vcmax25 = FT(5e-5))); + (; parameters = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5))); K_sat_plant = FT(1.8e-8) RAI = (SAI + maxLAI) * f_root_to_shoot; diff --git a/docs/tutorials/standalone/Canopy/canopy_tutorial.jl b/docs/tutorials/standalone/Canopy/canopy_tutorial.jl index f6efdcf314..ac29d233fe 100644 --- a/docs/tutorials/standalone/Canopy/canopy_tutorial.jl +++ b/docs/tutorials/standalone/Canopy/canopy_tutorial.jl @@ -98,7 +98,7 @@ land_domain = Point(; z_sfc = FT(0.0)) # the simulation with atmospheric and radiative flux models. We also # read in the observed LAI and let that vary in time in a prescribed manner. -# Use the data tools for reading FLUXNET data sets +# Use the data tools for reading FLUXNET data sets include( joinpath(pkgdir(ClimaLand), "experiments/integrated/fluxnet/data_tools.jl"), ); @@ -174,8 +174,8 @@ cond_params = MedlynConductanceParameters(FT; g1 = FT(141.0)) stomatal_model = MedlynConductanceModel{FT}(cond_params); # Arguments for photosynthesis model: - -photo_params = FarquharParameters(FT, Canopy.C3(); Vcmax25 = FT(5e-5)) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 +photo_params = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5)) photosynthesis_model = FarquharModel{FT}(photo_params); diff --git a/experiments/benchmarks/land.jl b/experiments/benchmarks/land.jl index 6024e0af00..cc809ef0c4 100644 --- a/experiments/benchmarks/land.jl +++ b/experiments/benchmarks/land.jl @@ -380,6 +380,14 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) regridder_kwargs = (; extrapolation_bc,), file_reader_kwargs = (; preprocess_func = (data) -> data / 1_000_000,), ) + # photosynthesis mechanism is read as a float, where 1.0 indicates c3 and 0.0 c4 + is_c3 = SpaceVaryingInput( + joinpath(clm_artifact_path, "mechanism_map.nc"), + "c3_dominant", + surface_space; + regridder_type, + regridder_kwargs = (; extrapolation_bc,), + ) # Plant Hydraulics and general plant parameters SAI = FT(0.0) # m2/m2 @@ -465,13 +473,8 @@ function setup_prob(t0, tf, Δt; nelements = (101, 15)) conductance_args = (; parameters = Canopy.MedlynConductanceParameters(FT; g1)) # Set up photosynthesis - photosynthesis_args = (; - parameters = Canopy.FarquharParameters( - FT, - Canopy.C3(); - Vcmax25 = Vcmax25, - ) - ) + photosynthesis_args = + (; parameters = Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics # Note that we clip all values of LAI below 0.05 to zero. # This is because we currently run into issues when LAI is diff --git a/experiments/integrated/fluxnet/ozark_pft.jl b/experiments/integrated/fluxnet/ozark_pft.jl index 4a3cd83d89..4d7be37565 100644 --- a/experiments/integrated/fluxnet/ozark_pft.jl +++ b/experiments/integrated/fluxnet/ozark_pft.jl @@ -184,8 +184,9 @@ radiative_transfer_args = (; conductance_args = (; parameters = MedlynConductanceParameters(FT; g1)) # Set up photosynthesis # Set up photosynthesis +is_c3 = FT(1) # set the photosynthesis mechanism to C3 photosynthesis_args = - (; parameters = FarquharParameters(FT, Canopy.C3(); Vcmax25 = Vcmax25)) + (; parameters = FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics ai_parameterization = PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI) diff --git a/experiments/integrated/fluxnet/run_fluxnet.jl b/experiments/integrated/fluxnet/run_fluxnet.jl index 4ceed3ca3d..e41095c316 100644 --- a/experiments/integrated/fluxnet/run_fluxnet.jl +++ b/experiments/integrated/fluxnet/run_fluxnet.jl @@ -141,8 +141,9 @@ radiative_transfer_args = (; # Set up conductance conductance_args = (; parameters = MedlynConductanceParameters(FT; g1)) # Set up photosynthesis +is_c3 = FT(1) # set the photosynthesis mechanism to C3 photosynthesis_args = - (; parameters = FarquharParameters(FT, Canopy.C3(); Vcmax25 = Vcmax25)) + (; parameters = FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics ai_parameterization = PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI) diff --git a/experiments/integrated/global/global_soil_canopy.jl b/experiments/integrated/global/global_soil_canopy.jl index 010c7c663d..977656bbfd 100644 --- a/experiments/integrated/global/global_soil_canopy.jl +++ b/experiments/integrated/global/global_soil_canopy.jl @@ -222,9 +222,9 @@ radiative_transfer_args = (; # Set up conductance conductance_args = (; parameters = Canopy.MedlynConductanceParameters(FT; g1)) # Set up photosynthesis -photosynthesis_args = (; - parameters = Canopy.FarquharParameters(FT, Canopy.C3(); Vcmax25 = Vcmax25) -) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 +photosynthesis_args = + (; parameters = Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics # Not ideal LAIfunction = TimeVaryingInput( diff --git a/experiments/integrated/performance/conservation/ozark_conservation_setup.jl b/experiments/integrated/performance/conservation/ozark_conservation_setup.jl index 39c7e87a32..c195862b7b 100644 --- a/experiments/integrated/performance/conservation/ozark_conservation_setup.jl +++ b/experiments/integrated/performance/conservation/ozark_conservation_setup.jl @@ -129,8 +129,9 @@ radiative_transfer_args = (; # Set up conductance conductance_args = (; parameters = MedlynConductanceParameters(FT; g1)) # Set up photosynthesis +is_c3 = FT(1) # set the photosynthesis mechanism to C3 photosynthesis_args = - (; parameters = FarquharParameters(FT, Canopy.C3(); Vcmax25 = Vcmax25)) + (; parameters = FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics ai_parameterization = PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI) diff --git a/experiments/integrated/performance/profile_allocations.jl b/experiments/integrated/performance/profile_allocations.jl index 22c41dfea1..2ec19f9192 100644 --- a/experiments/integrated/performance/profile_allocations.jl +++ b/experiments/integrated/performance/profile_allocations.jl @@ -266,8 +266,9 @@ radiative_transfer_args = (; parameters = TwoStreamParameters(FT)) # Set up conductance conductance_args = (; parameters = MedlynConductanceParameters(FT; g1)) # Set up photosynthesis +is_c3 = FT(1) # set the photosynthesis mechanism to C3 photosynthesis_args = - (; parameters = FarquharParameters(FT, Canopy.C3(); Vcmax25 = Vcmax25)) + (; parameters = FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics ai_parameterization = PrescribedSiteAreaIndex{FT}(LAIfunction, SAI, RAI) function root_distribution(z::T) where {T} diff --git a/experiments/long_runs/land.jl b/experiments/long_runs/land.jl index d044ff25dc..676b13136b 100644 --- a/experiments/long_runs/land.jl +++ b/experiments/long_runs/land.jl @@ -389,7 +389,14 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (101, 15)) regridder_kwargs = (; extrapolation_bc,), file_reader_kwargs = (; preprocess_func = (data) -> data / 1_000_000,), ) - + # photosynthesis mechanism is read as a float, where 1.0 indicates c3 and 0.0 c4 + is_c3 = SpaceVaryingInput( + joinpath(clm_artifact_path, "mechanism_map.nc"), + "c3_dominant", + surface_space; + regridder_type, + regridder_kwargs = (; extrapolation_bc,), + ) # Plant Hydraulics and general plant parameters SAI = FT(0.0) # m2/m2 f_root_to_shoot = FT(3.5) @@ -474,13 +481,8 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (101, 15)) conductance_args = (; parameters = Canopy.MedlynConductanceParameters(FT; g1)) # Set up photosynthesis - photosynthesis_args = (; - parameters = Canopy.FarquharParameters( - FT, - Canopy.C3(); - Vcmax25 = Vcmax25, - ) - ) + photosynthesis_args = + (; parameters = Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics # Note that we clip all values of LAI below 0.05 to zero. diff --git a/experiments/long_runs/land_region.jl b/experiments/long_runs/land_region.jl index 92fa8afa87..f9ac565f4a 100644 --- a/experiments/long_runs/land_region.jl +++ b/experiments/long_runs/land_region.jl @@ -368,8 +368,18 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (10, 10, 15)) # Energy Balance model ac_canopy = FT(2.5e4) # this will likely be 10x smaller! - clm_artifact_path = ClimaLand.Artifacts.clm_data_folder_path(; context) + # Conductance Model + # g1 is read in units of sqrt(kPa) and then converted to sqrt(Pa) + g1 = SpaceVaryingInput( + joinpath(clm_artifact_path, "vegetation_properties_map.nc"), + "medlynslope", + surface_space; + regridder_type, + regridder_kwargs = (; extrapolation_bc,), + file_reader_kwargs = (; preprocess_func = (data) -> data * 10^(3 / 2),), + ) + # vcmax is read in units of umol CO2/m^2/s and then converted to mol CO2/m^2/s Vcmax25 = SpaceVaryingInput( joinpath(clm_artifact_path, "vegetation_properties_map.nc"), @@ -380,14 +390,13 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (10, 10, 15)) file_reader_kwargs = (; preprocess_func = (data) -> data / 1_000_000,), ) - # g1 is read in units of sqrt(kPa) and then converted to sqrt(Pa) - g1 = SpaceVaryingInput( - joinpath(clm_artifact_path, "vegetation_properties_map.nc"), - "medlynslope", + # c3 is read in as a float + is_c3 = SpaceVaryingInput( + joinpath(clm_artifact_path, "mechanism_map.nc"), + "c3_dominant", surface_space; regridder_type, regridder_kwargs = (; extrapolation_bc,), - file_reader_kwargs = (; preprocess_func = (data) -> data * 10^(3 / 2),), ) # Plant Hydraulics and general plant parameters @@ -474,13 +483,8 @@ function setup_prob(t0, tf, Δt; outdir = outdir, nelements = (10, 10, 15)) conductance_args = (; parameters = Canopy.MedlynConductanceParameters(FT; g1)) # Set up photosynthesis - photosynthesis_args = (; - parameters = Canopy.FarquharParameters( - FT, - Canopy.C3(); - Vcmax25 = Vcmax25, - ) - ) + photosynthesis_args = + (; parameters = Canopy.FarquharParameters(FT, is_c3; Vcmax25 = Vcmax25)) # Set up plant hydraulics # Note that we clip all values of LAI below 0.05 to zero. diff --git a/experiments/standalone/Vegetation/no_vegetation.jl b/experiments/standalone/Vegetation/no_vegetation.jl index 570e456825..b71240098e 100644 --- a/experiments/standalone/Vegetation/no_vegetation.jl +++ b/experiments/standalone/Vegetation/no_vegetation.jl @@ -82,8 +82,8 @@ cond_params = MedlynConductanceParameters(FT; g1 = FT(141.0)) stomatal_model = MedlynConductanceModel{FT}(cond_params); - -photo_params = FarquharParameters(FT, Canopy.C3(); Vcmax25 = FT(5e-5)) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 +photo_params = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5)) photosynthesis_model = FarquharModel{FT}(photo_params); diff --git a/experiments/standalone/Vegetation/varying_lai.jl b/experiments/standalone/Vegetation/varying_lai.jl index 883b424af3..ecf9dbec3a 100644 --- a/experiments/standalone/Vegetation/varying_lai.jl +++ b/experiments/standalone/Vegetation/varying_lai.jl @@ -82,8 +82,8 @@ cond_params = MedlynConductanceParameters(FT; g1 = FT(141.0)) stomatal_model = MedlynConductanceModel{FT}(cond_params); - -photo_params = FarquharParameters(FT, Canopy.C3(); Vcmax25 = FT(5e-5)) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 +photo_params = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5)) photosynthesis_model = FarquharModel{FT}(photo_params); diff --git a/experiments/standalone/Vegetation/varying_lai_with_stem.jl b/experiments/standalone/Vegetation/varying_lai_with_stem.jl index ac403f8fe1..d1068db1ec 100644 --- a/experiments/standalone/Vegetation/varying_lai_with_stem.jl +++ b/experiments/standalone/Vegetation/varying_lai_with_stem.jl @@ -82,8 +82,8 @@ cond_params = MedlynConductanceParameters(FT; g1 = FT(141.0)) stomatal_model = MedlynConductanceModel{FT}(cond_params); - -photo_params = FarquharParameters(FT, Canopy.C3(); Vcmax25 = FT(5e-5)) +is_c3 = FT(1) # set the photosynthesis mechanism to C3 +photo_params = FarquharParameters(FT, is_c3; Vcmax25 = FT(5e-5)) photosynthesis_model = FarquharModel{FT}(photo_params); diff --git a/ext/CreateParametersExt.jl b/ext/CreateParametersExt.jl index f9abc5359b..6fcda3cad1 100644 --- a/ext/CreateParametersExt.jl +++ b/ext/CreateParametersExt.jl @@ -107,14 +107,14 @@ end """ function FarquharParameters( FT, - mechanism::AbstractPhotosynthesisMechanism; + is_c3::Union{FT, ClimaCore.Fields.Field}; Vcmax25 = FT(5e-5), kwargs... # For individual parameter overrides ) function FarquharParameters( toml_dict::CP.AbstractTOMLDict, - mechanism::AbstractPhotosynthesisMechanism; + is_c3::Union{AbstractFloat, ClimaCore.Fields.Field}; Vcmax25 = FT(5e-5), kwargs... # For individual parameter overrides ) @@ -123,25 +123,25 @@ Constructors for the FarquharParameters struct. Two variants: 1. Pass in the float-type and retrieve parameter values from the default TOML dict. 2. Pass in a TOML dictionary to retrieve parameter values.Possible calls: ```julia -ClimaLand.Canopy.FarquharParameters(Float64, ClimaLand.Canopy.C3()) +ClimaLand.Canopy.FarquharParameters(Float64, 1.0) # Kwarg overrides -ClimaLand.Canopy.FarquharParameters(Float64, ClimaLand.Canopy.C3(); Vcmax25 = 99999999, pc = 444444444) +ClimaLand.Canopy.FarquharParameters(Float64, 1.0; Vcmax25 = 99999999, pc = 444444444) # TOML Dictionary: import ClimaParams as CP toml_dict = CP.create_toml_dict(Float32); -ClimaLand.Canopy.FarquharParameters(toml_dict, ClimaLand.Canopy.C3(); Vcmax25 = 99999999, pc = 444444444) +ClimaLand.Canopy.FarquharParameters(toml_dict, 1.0f0; Vcmax25 = 99999999, pc = 444444444) ``` """ FarquharParameters( ::Type{FT}, - mechanism; + is_c3::Union{FT, ClimaCore.Fields.Field}; kwargs..., ) where {FT <: AbstractFloat} = - FarquharParameters(CP.create_toml_dict(FT), mechanism; kwargs...) + FarquharParameters(CP.create_toml_dict(FT), is_c3; kwargs...) function FarquharParameters( toml_dict::CP.AbstractTOMLDict, - mechanism; + is_c3::Union{AbstractFloat, ClimaCore.Fields.Field}; Vcmax25 = 5e-5, kwargs..., ) @@ -165,11 +165,24 @@ function FarquharParameters( ) parameters = CP.get_parameter_values(toml_dict, name_map, "Land") FT = CP.float_type(toml_dict) - MECH = typeof(mechanism) + if maximum(is_c3) > 1 + error( + "is_c3 has maximum of $(maximum(is_c3)). is_c3 should be between 0 and 1", + ) + end + if minimum(is_c3) < 0 + error( + "is_c3 has minimum of $(minimum(is_c3)). is_c3 should be between 0 and 1", + ) + end + # if is_c3 is a field, is_c3 may contain values between 0.0 and 1.0 after regridding + # this deals with that possibility by rounding to the closest int + is_c3 = round.(is_c3) + MECH = typeof(is_c3) Vcmax25 = FT.(Vcmax25) VC = typeof(Vcmax25) return FarquharParameters{FT, MECH, VC}(; - mechanism, + is_c3, Vcmax25, parameters..., kwargs..., @@ -229,8 +242,8 @@ function OptimalityFarquharParameters(toml_dict; kwargs...) params = CP.get_parameter_values(toml_dict, name_map, "Land") FT = CP.float_type(toml_dict) - mechanism = ClimaLand.Canopy.C3() - return OptimalityFarquharParameters{FT}(; params..., kwargs..., mechanism) + is_c3 = FT(1) + return OptimalityFarquharParameters{FT, FT}(; params..., kwargs..., is_c3) end diff --git a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Ha1/US-Ha1_parameters.jl b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Ha1/US-Ha1_parameters.jl index fde43e2b8d..cc6b100104 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Ha1/US-Ha1_parameters.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Ha1/US-Ha1_parameters.jl @@ -139,7 +139,7 @@ function conductance_harvard(; end function photosynthesis_harvard(; - mechanism = Canopy.C3(), + is_c3 = FT(1), oi = FT(0.209), ϕ = FT(0.6), θj = FT(0.9), @@ -176,7 +176,7 @@ function photosynthesis_harvard(; f, sc, pc, - mechanism, + is_c3, ) end diff --git a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-MOz/US-MOz_parameters.jl b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-MOz/US-MOz_parameters.jl index c51d2dbd80..6326334d43 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-MOz/US-MOz_parameters.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-MOz/US-MOz_parameters.jl @@ -139,7 +139,7 @@ function conductance_ozark(; end function photosynthesis_ozark(; - mechanism = Canopy.C3(), + is_c3 = FT(1), oi = FT(0.209), ϕ = FT(0.6), θj = FT(0.9), @@ -176,7 +176,7 @@ function photosynthesis_ozark(; f, sc, pc, - mechanism, + is_c3, ) end diff --git a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-NR1/US-NR1_parameters.jl b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-NR1/US-NR1_parameters.jl index 2b30e3eb11..bafdb21c53 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-NR1/US-NR1_parameters.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-NR1/US-NR1_parameters.jl @@ -139,7 +139,7 @@ function conductance_niwotridge(; end function photosynthesis_niwotridge(; - mechanism = Canopy.C3(), + is_c3 = FT(1), oi = FT(0.209), ϕ = FT(0.6), θj = FT(0.9), @@ -176,7 +176,7 @@ function photosynthesis_niwotridge(; f, sc, pc, - mechanism, + is_c3, ) end diff --git a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Var/US-Var_parameters.jl b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Var/US-Var_parameters.jl index 4e8947bf8b..6fac7afa48 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Var/US-Var_parameters.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/fluxnet_sites/US-Var/US-Var_parameters.jl @@ -139,7 +139,7 @@ function conductance_vairaranch(; end function photosynthesis_vairaranch(; - mechanism = Canopy.C3(), + is_c3 = FT(1), oi = FT(0.209), ϕ = FT(0.6), θj = FT(0.9), @@ -176,7 +176,7 @@ function photosynthesis_vairaranch(; f, sc, pc, - mechanism, + is_c3, ) end diff --git a/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl b/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl index 1efe21c2be..e584770469 100644 --- a/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl +++ b/lib/ClimaLandSimulations/src/Fluxnet/run_fluxnet.jl @@ -127,7 +127,7 @@ function run_fluxnet( photosynthesis_args = (; parameters = FarquharParameters( FT, - Canopy.C3(); + FT(1); oi = params.photosynthesis.oi, ϕ = params.photosynthesis.ϕ, θj = params.photosynthesis.θj, @@ -253,7 +253,7 @@ function run_fluxnet( land.soil.parameters.earth_param_set, ) Y.soilco2.C .= FT(0.000412) # set to atmospheric co2, mol co2 per mol air - ψ_stem_0 = FT(-1e5 / 9800) # pressure in the leaf divided by rho_liquid*gravitational acceleration [m] + ψ_stem_0 = FT(-1e5 / 9800) # pressure in the leaf divided by rho_liquid*gravitational acceleration [m] ψ_leaf_0 = FT(-2e5 / 9800) ψ_comps = setup.n_stem > 0 ? [ψ_stem_0, ψ_leaf_0] : ψ_leaf_0 diff --git a/src/standalone/Vegetation/canopy_parameterizations.jl b/src/standalone/Vegetation/canopy_parameterizations.jl index b90ceaabdd..51389c28b5 100644 --- a/src/standalone/Vegetation/canopy_parameterizations.jl +++ b/src/standalone/Vegetation/canopy_parameterizations.jl @@ -516,8 +516,21 @@ function co2_compensation( end """ - rubisco_assimilation(::C3, - Vcmax::FT, + rubisco_assimilation(is_c3::AbstractFloat, args...) + +Calls the correct rubisco assimilation function based on the `is_c3`. + +A `is_c3` value of 1.0 corresponds to C3 photosynthesis and calls +`c3_rubisco_assimilation`, while 0.0 corresponds to C4 photsynthesis and calls +`c4_rubisco_assimilation`. +""" +function rubisco_assimilation(is_c3::AbstractFloat, args...) + is_c3 > 0.5 ? c3_rubisco_assimilation(args...) : + c4_rubisco_assimilation(args...) +end + +""" + c3_rubisco_assimilation(Vcmax::FT, ci::FT, Γstar::FT, Kc::FT, @@ -534,8 +547,7 @@ for CO2 and O2, respectively, (`Kc`) and (`Ko`). The empirical parameter oi is equal to 0.209 (mol/mol). See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ -function rubisco_assimilation( - ::C3, +function c3_rubisco_assimilation( Vcmax::FT, ci::FT, Γstar::FT, @@ -548,19 +560,32 @@ function rubisco_assimilation( end """ - rubisco_assimilation(::C4, Vcmax::FT,_...) where {FT} + c4_rubisco_assimilation(Vcmax::FT,_...) where {FT} Computes the Rubisco limiting rate of photosynthesis for C4 plants (`Ac`) in units of moles CO2/m^2/s, as equal to the maximum rate of carboxylation of Rubisco (`Vcmax`). """ -function rubisco_assimilation(::C4, Vcmax::FT, _...) where {FT} +function c4_rubisco_assimilation(Vcmax::FT, _...) where {FT} Ac = Vcmax return Ac end """ - light_assimilation(::C3, + light_assimilation(is_c3::AbstractFloat, args...) + +Calls the correct light assimilation function based on the `is_c3`. + +A `is_c3` value of 1.0 corresponds to C3 photosynthesis and calls +`c3_light_assimilation`, while 0.0 corresponds to C4 photsynthesis and calls +`c4_light_assimilation`. +""" +function light_assimilation(is_c3::AbstractFloat, args...) + is_c3 > 0.5 ? c3_light_assimilation(args...) : + c4_light_assimilation(args...) +end +""" + c3_light_assimilation( J::FT, ci::FT, Γstar::FT) where {FT} @@ -572,19 +597,19 @@ and the CO2 compensation point (`Γstar`). See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ -function light_assimilation(::C3, J::FT, ci::FT, Γstar::FT) where {FT} +function c3_light_assimilation(J::FT, ci::FT, Γstar::FT) where {FT} Aj = J * (ci - Γstar) / (4 * (ci + 2 * Γstar)) return Aj end """ - light_assimilation(::C4, J::FT, _...) where {FT} + light_assimilation(J::FT, _...) where {FT} Computes the electron transport limiting rate (`Aj`), in units of moles CO2/m^2/s, for C4 plants, as equal to the rate of electron transport (`J`). """ -function light_assimilation(::C4, J::FT, _...) where {FT} +function c4_light_assimilation(J::FT, _...) where {FT} Aj = J return Aj end diff --git a/src/standalone/Vegetation/optimality_farquhar.jl b/src/standalone/Vegetation/optimality_farquhar.jl index 1422b20271..ee8228b8fa 100644 --- a/src/standalone/Vegetation/optimality_farquhar.jl +++ b/src/standalone/Vegetation/optimality_farquhar.jl @@ -8,9 +8,12 @@ The required parameters for the optimality Farquhar photosynthesis model. Currently, only C3 photosynthesis is supported. $(DocStringExtensions.FIELDS) """ -Base.@kwdef struct OptimalityFarquharParameters{FT <: AbstractFloat} +Base.@kwdef struct OptimalityFarquharParameters{ + FT <: AbstractFloat, + MECH <: Union{FT, ClimaCore.Fields.Field}, +} "Photosynthesis mechanism: C3 only" - mechanism::C3 + is_c3::MECH "Γstar at 25 °C (mol/mol)" Γstar25::FT "Michaelis-Menten parameter for CO2 at 25 °C (mol/mol)" @@ -91,7 +94,7 @@ ClimaLand.auxiliary_domain_names(::OptimalityFarquharModel) = Computes the net photosynthesis rate `An` for the Optimality Farquhar model, along with the dark respiration `Rd`, and the value of `Vcmax25`, and updates them in place. - + To do so, we require the canopy leaf temperature `T`, Medlyn factor, `APAR` in photons per m^2 per second, CO2 concentration in the atmosphere, moisture stress factor `β` (unitless), and the universal gas constant @@ -118,7 +121,7 @@ function update_photosynthesis!( To, θj, ϕ, - mechanism, + is_c3, oi, Kc25, Ko25, @@ -146,8 +149,8 @@ function update_photosynthesis!( Jmax = rates.:1 Vcmax = rates.:2 J = electron_transport.(APAR, Jmax, θj, ϕ) - Aj = light_assimilation.(mechanism, J, ci, Γstar) - Ac = rubisco_assimilation.(mechanism, Vcmax, ci, Γstar, Kc, Ko, oi) + Aj = light_assimilation.(is_c3, J, ci, Γstar) + Ac = rubisco_assimilation.(is_c3, Vcmax, ci, Γstar, Kc, Ko, oi) @. Vcmax25 = Vcmax / arrhenius_function(T, To, R, ΔHVcmax) @. Rd = dark_respiration(Vcmax25, β, f, ΔHRd, T, To, R) diff --git a/src/standalone/Vegetation/photosynthesis.jl b/src/standalone/Vegetation/photosynthesis.jl index 3257fd894f..8d220f3c12 100644 --- a/src/standalone/Vegetation/photosynthesis.jl +++ b/src/standalone/Vegetation/photosynthesis.jl @@ -1,25 +1,11 @@ -export FarquharParameters, FarquharModel, C3, C4 - -abstract type AbstractPhotosynthesisMechanism end -""" - C3 <: AbstractPhotosynthesisMechanism - -Helper struct for dispatching between C3 and C4 photosynthesis. -""" -struct C3 <: AbstractPhotosynthesisMechanism end - -""" - C4 <: AbstractPhotosynthesisMechanism - -Helper struct for dispatching between C3 and C4 photosynthesis. -""" -struct C4 <: AbstractPhotosynthesisMechanism end +export FarquharParameters, FarquharModel abstract type AbstractPhotosynthesisModel{FT} <: AbstractCanopyComponent{FT} end + """ FarquharParameters{ FT<:AbstractFloat, - MECH <: AbstractPhotosynthesisMechanism, + MECH <: Union{FT, ClimaCore.Fields.Field}, VC <: Union{FT, ClimaCore.Fields.Field}, } @@ -28,7 +14,7 @@ $(DocStringExtensions.FIELDS) """ Base.@kwdef struct FarquharParameters{ FT <: AbstractFloat, - MECH <: AbstractPhotosynthesisMechanism, + MECH <: Union{FT, ClimaCore.Fields.Field}, VC <: Union{FT, ClimaCore.Fields.Field}, } "Vcmax at 25 °C (mol CO2/m^2/s)" @@ -65,8 +51,8 @@ Base.@kwdef struct FarquharParameters{ sc::FT "Reference water pressure for the moisture stress factor (Pa) [Tuzet et al. (2003)]" pc::FT - "Photosynthesis mechanism: C3 or C4" - mechanism::MECH + "Photosynthesis mechanism: 1.0 indicates C3, 0.0 indicates C4" + is_c3::MECH end Base.eltype(::FarquharParameters{FT}) where {FT} = FT @@ -108,7 +94,7 @@ function photosynthesis_at_a_point_Farquhar( To, θj, ϕ, - mechanism, + is_c3, oi, Kc25, Ko25, @@ -120,10 +106,10 @@ function photosynthesis_at_a_point_Farquhar( Vcmax = compute_Vcmax(Vcmax25, T, To, R, ΔHVcmax) Γstar = co2_compensation(Γstar25, ΔHΓstar, T, To, R) ci = intercellular_co2(c_co2, Γstar, medlyn_factor) - Aj = light_assimilation(mechanism, J, ci, Γstar) + Aj = light_assimilation(is_c3, J, ci, Γstar) Kc = MM_Kc(Kc25, ΔHkc, T, To, R) Ko = MM_Ko(Ko25, ΔHko, T, To, R) - Ac = rubisco_assimilation(mechanism, Vcmax, ci, Γstar, Kc, Ko, oi) + Ac = rubisco_assimilation(is_c3, Vcmax, ci, Γstar, Kc, Ko, oi) return net_photosynthesis(Ac, Aj, Rd, β) end @@ -172,7 +158,7 @@ function update_photosynthesis!( To, θj, ϕ, - mechanism, + is_c3, oi, Kc25, Ko25, @@ -198,7 +184,7 @@ function update_photosynthesis!( To, θj, ϕ, - mechanism, + is_c3, oi, Kc25, Ko25, @@ -207,7 +193,6 @@ function update_photosynthesis!( ) Vcmax25field .= Vcmax25 end -Base.broadcastable(m::AbstractPhotosynthesisMechanism) = tuple(m) Base.broadcastable(m::FarquharParameters) = tuple(m) include("./optimality_farquhar.jl") diff --git a/test/standalone/Vegetation/canopy_model.jl b/test/standalone/Vegetation/canopy_model.jl index 9f52df0fb2..0446693a06 100644 --- a/test/standalone/Vegetation/canopy_model.jl +++ b/test/standalone/Vegetation/canopy_model.jl @@ -21,13 +21,26 @@ import ClimaParams @testset "Canopy software pipes" begin for FT in (Float32, Float64) - domain = Point(; z_sfc = FT(0.0)) - for g1 in (FT(790), fill(FT(790), domain.space.surface)) - # create new field with constant value everywhere - Vcmax25 = fill(FT(9e-5), domain.space.surface) + domain = ClimaLand.Domains.SphericalSurface(; + radius = FT(100.0), + nelements = 10, + npolynomial = 1, + ) + # create a field with both 1.0s and 0.0s + mechanism_field = ClimaCore.Fields.Field(FT, domain.space.surface) + ClimaCore.Fields.set!( + x -> x.coordinates.lat > 0 ? 0.0 : 1.0, + mechanism_field, + ) + # create one case where parameters are spatially varying and one where not + g1_cases = (FT(790), fill(FT(790), domain.space.surface)) + Vcmax25_cases = (FT(9e-5), fill(FT(9e-5), domain.space.surface)) + mechanism_cases = (FT(1), mechanism_field) + zipped = zip(g1_cases, Vcmax25_cases, mechanism_cases) + for (g1, Vcmax25, is_c3) in zipped AR_params = AutotrophicRespirationParameters(FT) RTparams = BeerLambertParameters(FT) - photosynthesis_params = FarquharParameters(FT, C3(); Vcmax25) + photosynthesis_params = FarquharParameters(FT, is_c3; Vcmax25) stomatal_g_params = MedlynConductanceParameters(FT; g1) AR_model = AutotrophicRespirationModel{FT}(AR_params) @@ -211,7 +224,7 @@ import ClimaParams # Check that structure of Y is value (will error if not) @test !isnothing(zero(Y)) @test typeof(canopy.energy) == PrescribedCanopyTempModel{FT} - @test propertynames(p) == (:canopy, :drivers) + @test propertynames(p) == (:canopy, :dss_buffer_2d, :drivers) for component in ClimaLand.Canopy.canopy_components(canopy) # Only hydraulics has a prognostic variable if component == :hydraulics @@ -348,22 +361,26 @@ import ClimaParams @test ClimaLand.surface_evaporative_scaling(canopy, Y, p) == FT(1.0) @test ClimaLand.surface_height(canopy, Y, p) == compartment_faces[1] T_sfc = FT.(T_atmos(t0)) - @test Array( - parent(ClimaLand.surface_temperature(canopy, Y, p, t0)), - ) == [T_sfc] + @test all( + Array( + parent(ClimaLand.surface_temperature(canopy, Y, p, t0)), + ) .== [T_sfc], + ) @test ClimaLand.surface_temperature(canopy, Y, p, t0) isa ClimaCore.Fields.Field - @test Array( - parent( - ClimaLand.Canopy.canopy_temperature( - canopy.energy, - canopy, - Y, - p, - t0, + @test all( + Array( + parent( + ClimaLand.Canopy.canopy_temperature( + canopy.energy, + canopy, + Y, + p, + t0, + ), ), - ), - ) == [T_sfc] + ) .== [T_sfc], + ) @test ClimaLand.Canopy.canopy_temperature( canopy.energy, canopy, @@ -533,12 +550,25 @@ end @testset "Canopy software pipes with energy model" begin for FT in (Float32, Float64) - domain = Point(; z_sfc = FT(0.0)) - for g1 in (FT(790), fill(FT(790), domain.space.surface)) - # create new field with constant value everywhere - Vcmax25 = fill(FT(9e-5), domain.space.surface) + domain = ClimaLand.Domains.SphericalSurface(; + radius = FT(100.0), + nelements = 10, + npolynomial = 1, + ) + # create a field with both 1.0s and 0.0s + mechanism_field = ClimaCore.Fields.Field(FT, domain.space.surface) + ClimaCore.Fields.set!( + x -> x.coordinates.lat > 0 ? 0.0 : 1.0, + mechanism_field, + ) + # create one case where parameters are spatially varying and one where not + g1_cases = (FT(790), fill(FT(790), domain.space.surface)) + Vcmax25_cases = (FT(9e-5), fill(FT(9e-5), domain.space.surface)) + mechanism_cases = (FT(1), mechanism_field) + zipped = zip(g1_cases, Vcmax25_cases, mechanism_cases) + for (g1, Vcmax25, is_c3) in zipped RTparams = BeerLambertParameters(FT) - photosynthesis_params = FarquharParameters(FT, C3(); Vcmax25) + photosynthesis_params = FarquharParameters(FT, is_c3; Vcmax25) stomatal_g_params = MedlynConductanceParameters(FT; g1) stomatal_model = MedlynConductanceModel{FT}(stomatal_g_params) @@ -723,7 +753,7 @@ end # Check that structure of Y is value (will error if not) @test !isnothing(zero(Y)) - @test propertynames(p) == (:canopy, :drivers) + @test propertynames(p) == (:canopy, :dss_buffer_2d, :drivers) for component in ClimaLand.Canopy.canopy_components(canopy) # Only hydraulics has a prognostic variable if component == :hydraulics @@ -786,10 +816,23 @@ end @testset "Zero LAI;" begin for FT in (Float32, Float64) - domain = Point(; z_sfc = FT(0.0)) - for g1 in (FT(790), fill(FT(790), domain.space.surface)) - # create new field with constant value everywhere - Vcmax25 = fill(FT(9e-5), domain.space.surface) + domain = ClimaLand.Domains.SphericalSurface(; + radius = FT(100.0), + nelements = 10, + npolynomial = 1, + ) + # create a field with both 1.0s and 0.0s + mechanism_field = ClimaCore.Fields.Field(FT, domain.space.surface) + ClimaCore.Fields.set!( + x -> x.coordinates.lat > 0 ? 0.0 : 1.0, + mechanism_field, + ) + # create one case where parameters are spatially varying and one where not + g1_cases = (FT(790), fill(FT(790), domain.space.surface)) + Vcmax25_cases = (FT(9e-5), fill(FT(9e-5), domain.space.surface)) + mechanism_cases = (FT(1), mechanism_field) + zipped = zip(g1_cases, Vcmax25_cases, mechanism_cases) + for (g1, Vcmax25, is_c3) in zipped BeerLambertparams = BeerLambertParameters(FT) # TwoStreamModel parameters Ω = FT(0.69) @@ -811,7 +854,7 @@ end τ_NIR_leaf, G_Function, ) - photosynthesis_params = FarquharParameters(FT, C3(); Vcmax25) + photosynthesis_params = FarquharParameters(FT, is_c3; Vcmax25) stomatal_g_params = MedlynConductanceParameters(FT; g1) stomatal_model = MedlynConductanceModel{FT}(stomatal_g_params) diff --git a/test/standalone/Vegetation/plant_hydraulics_test.jl b/test/standalone/Vegetation/plant_hydraulics_test.jl index afbc7457c1..481b78770f 100644 --- a/test/standalone/Vegetation/plant_hydraulics_test.jl +++ b/test/standalone/Vegetation/plant_hydraulics_test.jl @@ -115,7 +115,8 @@ for FT in (Float32, Float64) AR_params = AutotrophicRespirationParameters(FT) RTparams = BeerLambertParameters(FT) - photosynthesis_params = FarquharParameters(FT, C3()) + is_c3 = FT(1) # set the photosynthesis mechanism to C3 + photosynthesis_params = FarquharParameters(FT, is_c3) stomatal_g_params = MedlynConductanceParameters(FT) AR_model = AutotrophicRespirationModel{FT}(AR_params) @@ -403,7 +404,8 @@ for FT in (Float32, Float64) AR_params = AutotrophicRespirationParameters(FT) RTparams = BeerLambertParameters(FT) - photosynthesis_params = FarquharParameters(FT, C3()) + is_c3 = FT(1) # set the photosynthesis mechanism to C3 + photosynthesis_params = FarquharParameters(FT, is_c3) stomatal_g_params = MedlynConductanceParameters(FT) AR_model = AutotrophicRespirationModel{FT}(AR_params) diff --git a/test/standalone/Vegetation/test_bigleaf_parameterizations.jl b/test/standalone/Vegetation/test_bigleaf_parameterizations.jl index a5e5e69a23..45893cf504 100644 --- a/test/standalone/Vegetation/test_bigleaf_parameterizations.jl +++ b/test/standalone/Vegetation/test_bigleaf_parameterizations.jl @@ -39,7 +39,7 @@ for FT in (Float32, Float64) @test typeof(Jmax) == FT @test typeof(Vcmax) == FT params = OptimalityFarquharParameters(FT) - @test params.mechanism == C3() + @test params.is_c3 ≈ 1.0 model = OptimalityFarquharModel(params) @test ClimaLand.auxiliary_vars(model) == (:An, :GPP, :Rd, :Vcmax25) @test ClimaLand.auxiliary_types(model) == (FT, FT, FT, FT) @@ -76,7 +76,8 @@ for FT in (Float32, Float64) ARparams = AutotrophicRespirationParameters(FT) RTparams = BeerLambertParameters(FT) RT = BeerLambertModel{FT}(RTparams) - photosynthesisparams = FarquharParameters(FT, C3()) + is_c3 = FT(1) # set the photosynthesis mechanism to C3 + photosynthesisparams = FarquharParameters(FT, is_c3) stomatal_g_params = MedlynConductanceParameters(FT) LAI = FT(5.0) # m2 (leaf) m-2 (ground) @@ -165,7 +166,7 @@ for FT in (Float32, Float64) @test intercellular_co2(ca, FT(1), m_t) == FT(1) Ac = rubisco_assimilation( - photosynthesisparams.mechanism, + photosynthesisparams.is_c3, Vcmax, ci, Γstar, @@ -202,13 +203,7 @@ for FT in (Float32, Float64) ) ) - Aj = - light_assimilation.( - Ref(photosynthesisparams.mechanism), - J, - ci, - Γstar, - ) + Aj = light_assimilation.(Ref(photosynthesisparams.is_c3), J, ci, Γstar) @test all(@.(Aj == J * (ci - Γstar) / (4 * (ci + 2 * Γstar)))) β = moisture_stress( p_l, @@ -220,8 +215,9 @@ for FT in (Float32, Float64) 1 + exp(photosynthesisparams.sc * (p_l - photosynthesisparams.pc)) ) # C4 tests + is_c3 = 0.0 @test rubisco_assimilation( - C4(), + is_c3, Vcmax, ci, Γstar, @@ -229,7 +225,7 @@ for FT in (Float32, Float64) Ko, photosynthesisparams.oi, ) == Vcmax - @test light_assimilation(C4(), J, ci, Γstar) == J + @test light_assimilation(is_c3, J, ci, Γstar) == J Rd = dark_respiration( photosynthesisparams.Vcmax25, From 361b333d92ca89e886494bd964cab0350cbf1d83 Mon Sep 17 00:00:00 2001 From: imreddyTeja Date: Mon, 16 Sep 2024 17:41:59 -0700 Subject: [PATCH 2/2] Remove trailing whitespace --- .../Vegetation/canopy_parameterizations.jl | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/standalone/Vegetation/canopy_parameterizations.jl b/src/standalone/Vegetation/canopy_parameterizations.jl index 51389c28b5..9926496089 100644 --- a/src/standalone/Vegetation/canopy_parameterizations.jl +++ b/src/standalone/Vegetation/canopy_parameterizations.jl @@ -45,8 +45,8 @@ end θs::FT, ) -Returns the leaf angle distribution value for CLM G function as a function of the -solar zenith angle and the leaf orientation index. See section 3.1 of +Returns the leaf angle distribution value for CLM G function as a function of the +solar zenith angle and the leaf orientation index. See section 3.1 of https://www2.cesm.ucar.edu/models/cesm2/land/CLM50_Tech_Note.pdf """ function compute_G(G::CLMGFunction{FT}, θs::FT) where {FT} @@ -72,8 +72,8 @@ end ) Computes the PAR and NIR absorbances, reflectances, and tranmittances -for a canopy in the case of the -Beer-Lambert model. The absorbances are a function of the radiative transfer +for a canopy in the case of the +Beer-Lambert model. The absorbances are a function of the radiative transfer model, as well as the magnitude of incident PAR and NIR radiation in W/m^2, the leaf area index, the extinction coefficient, and the soil albedo in the PAR and NIR bands. Returns a @@ -130,11 +130,11 @@ end ) Computes the PAR and NIR absorbances, reflectances, and tranmittances -for a canopy in the case of the -Beer-Lambert model. The absorbances are a function of the radiative transfer -model, as well as the magnitude of incident PAR and NIR radiation in W/m^2, +for a canopy in the case of the +Beer-Lambert model. The absorbances are a function of the radiative transfer +model, as well as the magnitude of incident PAR and NIR radiation in W/m^2, the leaf area index, the extinction coefficient, and the -soil albedo in the PAR and NIR bands. +soil albedo in the PAR and NIR bands. This model also depends on the diffuse fraction and the zenith angle. Returns a @@ -191,14 +191,14 @@ end α_soil::FT ) -Computes the absorbed, reflected, and transmitted photon flux density -in terms of mol photons per m^2 per +Computes the absorbed, reflected, and transmitted photon flux density +in terms of mol photons per m^2 per second for a radiation band. -This applies the Beer-Lambert law, which is a function of incident +This applies the Beer-Lambert law, which is a function of incident radiation (`SW_IN`; moles of photons/m^2/), leaf reflectance (`α_leaf`), the extinction coefficient (`K`), leaf area index (`LAI`), -and the albedo of the soil (`α_soil`). +and the albedo of the soil (`α_soil`). Returns a tuple of reflected, absorbed, and transmitted radiation in mol photons/m^2/s. @@ -230,13 +230,13 @@ end α_soil::FT, ) -Computes the absorbed, transmitted, and reflected photon flux density -in terms of mol photons per m^2 per second for a radiation band. +Computes the absorbed, transmitted, and reflected photon flux density +in terms of mol photons per m^2 per second for a radiation band. This applies the two-stream radiative transfer solution which takes into account -the impacts of scattering within the canopy. The function takes in all -parameters from the parameter struct of a TwoStreamModel, along with the -incident radiation, LAI, extinction coefficient K, soil albedo from the +the impacts of scattering within the canopy. The function takes in all +parameters from the parameter struct of a TwoStreamModel, along with the +incident radiation, LAI, extinction coefficient K, soil albedo from the canopy soil_driver, solar zenith angle, and τ. Returns a tuple of reflected, absorbed, and transmitted radiation in @@ -276,7 +276,7 @@ function plant_absorbed_pfd( c²θ̄ = pi * G / 4 β = 0.5 * (ω + diff * c²θ̄) / ω - # Compute coefficients for two-stream solution + # Compute coefficients for two-stream solution b = 1 - ω + ω * β c = ω * β d = ω * β₀ * μ̄ * K @@ -312,7 +312,7 @@ function plant_absorbed_pfd( p₁ * s₂ * (d - c - h₁ / σ * (u₁ + μ̄ * K)) ) - # h coefficients for direct downward flux + # h coefficients for direct downward flux h₄ = -f * p₃ - c * d h₅ = -1 / d₂ * @@ -351,7 +351,7 @@ function plant_absorbed_pfd( # Compute cumulative LAI at this layer L = i * Lₗ - # Compute the direct fluxes into/out of the layer + # Compute the direct fluxes into/out of the layer I_dir_up = h₁ * exp(-K * L * Ω) / σ + h₂ * exp(-h * L * Ω) + @@ -368,7 +368,7 @@ function plant_absorbed_pfd( I_dif_up = h₇ * exp(-h * L * Ω) + h₈ * exp(h * L * Ω) I_dif_dn = h₉ * exp(-h * L * Ω) + h₁₀ * exp(h * L * Ω) - # Energy balance giving radiation absorbed in the layer + # Energy balance giving radiation absorbed in the layer if i == 0 I_dir_abs = 0 I_dif_abs = 0 @@ -385,7 +385,7 @@ function plant_absorbed_pfd( # Add radiation absorbed in the layer to total absorbed radiation F_abs += (1 - frac_diff) * I_dir_abs + (frac_diff) * I_dif_abs - # Save input/output values to compute energy balance of next layer + # Save input/output values to compute energy balance of next layer I_dir_up_prev = I_dir_up I_dir_dn_prev = I_dir_dn I_dif_up_prev = I_dif_up @@ -411,7 +411,7 @@ end Computes the fraction of diffuse radiation (`diff_frac`) as a function of the solar zenith angle (`θs`), the total surface incident shortwave radiation (`SW_IN`), the air temperature (`T`), air pressure (`P`), specific humidity (`q`), and the day of the year -(`td`). +(`td`). See Appendix A of Braghiere, "Evaluation of turbulent fluxes of CO2, sensible heat, and latent heat as a function of aerosol optical depth over the course of deforestation @@ -419,10 +419,10 @@ in the Brazilian Amazon" 2013. Note that cos(θs) is equal to zero when θs = π/2, and this is a coefficient of k₀, which we divide by in this expression. This can amplify small errors -when θs is near π/2. +when θs is near π/2. -This formula is empirical and can yied negative numbers depending on the -input, which, when dividing by something very near zero, +This formula is empirical and can yied negative numbers depending on the +input, which, when dividing by something very near zero, can become large negative numbers. Because of that, we cap the returned value to lie within [0,1]. @@ -481,7 +481,7 @@ end intercellular_co2(ca::FT, Γstar::FT, medlyn_factor::FT) where{FT} Computes the intercellular CO2 concentration (mol/mol) given the -atmospheric concentration (`ca`, mol/mol), the CO2 compensation (`Γstar`, +atmospheric concentration (`ca`, mol/mol), the CO2 compensation (`Γstar`, mol/mol), and the Medlyn factor (unitless). """ function intercellular_co2(ca::FT, Γstar::FT, medlyn_term::FT) where {FT} @@ -539,8 +539,8 @@ end Computes the Rubisco limiting rate of photosynthesis for C3 plants (`Ac`), in units of moles CO2/m^2/s, -as a function of the maximum rate of carboxylation of Rubisco (`Vcmax`), -the leaf internal carbon dioxide partial pressure (`ci`), +as a function of the maximum rate of carboxylation of Rubisco (`Vcmax`), +the leaf internal carbon dioxide partial pressure (`ci`), the CO2 compensation point (`Γstar`), and Michaelis-Menten parameters for CO2 and O2, respectively, (`Kc`) and (`Ko`). @@ -618,12 +618,12 @@ end max_electron_transport(Vcmax::FT) where {FT} Computes the maximum potential rate of electron transport (`Jmax`), -in units of mol/m^2/s, +in units of mol/m^2/s, as a function of Vcmax at 25 °C (`Vcmax25`), a constant (`ΔHJmax`), a standard temperature (`To`), the unversal gas constant (`R`), and the temperature (`T`). -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function max_electron_transport( @@ -649,7 +649,7 @@ in units of mol/m^2/s, as a function of the maximum potential rate of electron transport (`Jmax`), absorbed photosynthetically active radiation (`APAR`), an empirical "curvature parameter" (`θj`; Bonan Eqn 11.21) -and the quantum yield of photosystem II (`ϕ`). +and the quantum yield of photosystem II (`ϕ`). See Ch 11, G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ @@ -726,9 +726,9 @@ end β::FT) where {FT} Computes the total net carbon assimilation (`An`), -in units of mol CO2/m^2/s, as a function of +in units of mol CO2/m^2/s, as a function of the Rubisco limiting factor (`Ac`), the electron transport limiting rate (`Aj`), -dark respiration (`Rd`), and the moisture stress factor (`β`). +dark respiration (`Rd`), and the moisture stress factor (`β`). See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ @@ -744,10 +744,10 @@ end Computes the moisture stress factor (`β`), which is unitless, as a function of -a constant (`sc`, 1/Pa), a reference pressure (`pc`, Pa), and -the leaf water pressure (`pl`, Pa) . +a constant (`sc`, 1/Pa), a reference pressure (`pc`, Pa), and +the leaf water pressure (`pl`, Pa) . -See Eqn 12.57 of G. Bonan's textbook, +See Eqn 12.57 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function moisture_stress(pl::FT, sc::FT, pc::FT) where {FT} @@ -770,7 +770,7 @@ and the moisture stress factor (`β`), an empirical factor `f` is equal to 0.015 a constant (`ΔHRd`), a standard temperature (`To`), the unversal gas constant (`R`), and the temperature (`T`). -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function dark_respiration( @@ -792,7 +792,7 @@ end LAI::FT, Ω::FT) where {FT} -Computes the total canopy photosynthesis (`GPP`) as a function of +Computes the total canopy photosynthesis (`GPP`) as a function of the total net carbon assimilation (`An`), the extinction coefficient (`K`), leaf area index (`LAI`) and the clumping index (`Ω`). """ @@ -810,7 +810,7 @@ and (1) converts it to m/s, (2) upscales to the entire canopy, by assuming the leaves in the canopy are in parallel and hence multiplying by LAI. -TODO: Check what CLM does, and check if we can use the same function +TODO: Check what CLM does, and check if we can use the same function for GPP from An, and make more general. """ function upscale_leaf_conductance( @@ -828,10 +828,10 @@ end arrhenius_function(T::FT, To::FT, R::FT, ΔH::FT) Computes the Arrhenius function at temperature `T` given -the reference temperature `To=298.15K`, the universal +the reference temperature `To=298.15K`, the universal gas constant `R`, and the energy activation `ΔH`. -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function arrhenius_function(T::FT, To::FT, R::FT, ΔH::FT) where {FT} @@ -851,7 +851,7 @@ as a function of its value at 25 °C (`Kc25`), a constant (`ΔHkc`), a standard temperature (`To`), the unversal gas constant (`R`), and the temperature (`T`). -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function MM_Kc(Kc25::FT, ΔHkc::FT, T::FT, To::FT, R::FT) where {FT} @@ -872,7 +872,7 @@ as a function of its value at 25 °C (`Ko25`), a constant (`ΔHko`), a standard temperature (`To`), the universal gas constant (`R`), and the temperature (`T`). -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function MM_Ko(Ko25::FT, ΔHko::FT, T::FT, To::FT, R::FT) where {FT} @@ -888,11 +888,11 @@ end ep5::FT) where {FT} Computes the maximum rate of carboxylation of Rubisco (`Vcmax`), -in units of mol/m^2/s, +in units of mol/m^2/s, as a function of temperature (`T`), Vcmax at the reference temperature 25 °C (`Vcmax25`), the universal gas constant (`R`), and the reference temperature (`To`). -See Table 11.5 of G. Bonan's textbook, +See Table 11.5 of G. Bonan's textbook, Climate Change and Terrestrial Ecosystem Modeling (2019). """ function compute_Vcmax( @@ -936,13 +936,13 @@ end An::FT, ca::FT) where {FT} -Computes the stomatal conductance according to Medlyn, as a function of -the minimum stomatal conductance (`g0`), +Computes the stomatal conductance according to Medlyn, as a function of +the minimum stomatal conductance (`g0`), the relative diffusivity of water vapor with respect to CO2 (`Drel`), the Medlyn term (unitless), the biochemical demand for CO2 (`An`), and the atmospheric concentration of CO2 (`ca`). -This returns the conductance in units of mol/m^2/s. It must be converted to +This returns the conductance in units of mol/m^2/s. It must be converted to m/s using the molar density of water prior to use in SurfaceFluxes.jl. """ function medlyn_conductance( @@ -958,11 +958,11 @@ end """ penman_monteith( - Δ::FT, # Rate of change of saturation vapor pressure with air temperature. (Pa K−1) + Δ::FT, # Rate of change of saturation vapor pressure with air temperature. (Pa K−1) Rn::FT, # Net irradiance (W m−2) G::FT, # Ground heat flux (W m−2) ρa::FT, # Dry air density (kg m−3) - cp::FT, # Specific heat capacity of air (J kg−1 K−1) + cp::FT, # Specific heat capacity of air (J kg−1 K−1) VPD::FT, # vapor pressure deficit (Pa) ga::FT, # atmospheric conductance (m s−1) γ::FT, # Psychrometric constant (γ ≈ 66 Pa K−1) @@ -970,7 +970,7 @@ end Lv::FT, # Volumetric latent heat of vaporization (J m-3) ) where {FT} -Computes the evapotranspiration in m/s using the Penman-Monteith equation. +Computes the evapotranspiration in m/s using the Penman-Monteith equation. """ function penman_monteith( Δ::FT, @@ -995,11 +995,11 @@ end LAI::FT, # Leaf area index SAI::FT, RAI::FT, - ηsl::FT, # live stem wood coefficient (kg C m-3) + ηsl::FT, # live stem wood coefficient (kg C m-3) h::FT, # canopy height (m) σl::FT # Specific leaf density (kg C m-2 [leaf]) μr::FT, # Ratio root nitrogen to top leaf nitrogen (-), typical value 1.0 - μs::FT, # Ratio stem nitrogen to top leaf nitrogen (-), typical value 0.1 + μs::FT, # Ratio stem nitrogen to top leaf nitrogen (-), typical value 0.1 ) where {FT} Computes the nitrogen content of leafs (Nl), roots (Nr) and stems (Ns). @@ -1010,11 +1010,11 @@ function nitrogen_content( LAI::FT, # Leaf area index SAI::FT, RAI::FT, - ηsl::FT, # live stem wood coefficient (kg C m-3) + ηsl::FT, # live stem wood coefficient (kg C m-3) h::FT, # canopy height (m) σl::FT, # Specific leaf density (kg C m-2 [leaf]) μr::FT, # Ratio root nitrogen to top leaf nitrogen (-), typical value 1.0 - μs::FT, # Ratio stem nitrogen to top leaf nitrogen (-), typical value 0.1 + μs::FT, # Ratio stem nitrogen to top leaf nitrogen (-), typical value 0.1 ) where {FT} Sc = ηsl * h * LAI * ClimaLand.heaviside(SAI) Rc = σl * RAI @@ -1035,7 +1035,7 @@ end ) where {FT} Computes plant maintenance respiration as a function of dark respiration (Rd), -the nitrogen content of leafs (Nl), roots (Nr) and stems (Ns), +the nitrogen content of leafs (Nl), roots (Nr) and stems (Ns), and the soil moisture factor (β). """ function plant_respiration_maintenance(