Skip to content

Commit 18f63f5

Browse files
committed
documentation for fluxnet sites [skip ci]
1 parent 54a9000 commit 18f63f5

22 files changed

+978
-991
lines changed

docs/Manifest-v1.11.toml

Lines changed: 177 additions & 44 deletions
Large diffs are not rendered by default.

docs/Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
33
BSON = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0"
44
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
55
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
6+
ClimaAnalysis = "29b5916a-a76c-4e73-9657-3c8fd22e65e6"
67
ClimaCore = "d414da3d-4745-48bb-8d80-42e94e092884"
8+
ClimaDiagnostics = "1ecacbb8-0713-4841-9a07-eb5aa8a2d53f"
79
ClimaLand = "08f4d4ce-cf43-44bb-ad95-9d2d5f413532"
810
ClimaLandSimulations = "348a0bd3-1299-4261-8002-d2cd97df6055"
911
ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c"
@@ -17,6 +19,7 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
1719
EnsembleKalmanProcesses = "aa8a2aa5-91d8-4396-bcef-d4f2ec43552d"
1820
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
1921
Format = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8"
22+
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
2023
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
2124
Insolation = "e98cc03f-d57e-4e3c-b70c-8d51efe9e0d8"
2225
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
@@ -25,6 +28,7 @@ JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
2528
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
2629
MethodAnalysis = "85b6ec6f-f7df-4429-9514-a64bcd9ee824"
2730
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
31+
Poppler_jll = "9c32591e-4766-534b-9725-b71a8799265b"
2832
PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d"
2933
Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665"
3034
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"

docs/list_tutorials.jl

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
tutorials = [
2-
"Running land simulations" => [
3-
"Bucket LSM" => [
2+
"Running Fluxnet simulations" => [
3+
"Canopy and soil" => "integrated/soil_canopy_fluxnet_tutorial.jl",
4+
"Canopy, soil, and snow" => "integrated/snowy_land_fluxnet_tutorial.jl",
5+
"Data processing" =>
6+
"Fluxnet forcing and comparison data" => "integrated/fluxnet_data.jl",
7+
"Visualization" =>
8+
"Fluxnet simulation visualization" => "integrated/fluxnet_vis.jl",
9+
],
10+
"Running global simulations" => [
11+
"Bucket land model" => [
412
"standalone/Bucket/bucket_tutorial.jl",
513
"standalone/Bucket/coupled_bucket.jl",
614
],
7-
"Integrated soil+canopy modeling" => [
8-
"Coupled Canopy and Soil" => "integrated/soil_canopy_tutorial.jl",
9-
],
10-
"Handling interactions between model components" => [
11-
"Adjusting boundary conditions for the soil" => "integrated/handling_soil_fluxes.jl",
12-
"Adjusting boundary conditions for the snow" => "integrated/handling_snow_fluxes.jl",
13-
],
1415
],
1516
"Running standalone component simulations" => [
16-
"Soil modeling" => [
17+
"Soil" => [
1718
"Boundary conditions" => "standalone/Soil/boundary_conditions.jl",
1819
"Richards Equation" => "standalone/Soil/richards_equation.jl",
1920
"Energy and Hydrology" => "standalone/Soil/soil_energy_hydrology.jl",
@@ -25,22 +26,28 @@ tutorials = [
2526
"Coarse Sand Evaporation" => "standalone/Soil/evaporation.jl",
2627
"Gilat Loess Evaporation" => "standalone/Soil/evaporation_gilat_loess.jl",
2728
"Bare soil site" => "standalone/Soil/sublimation.jl",
29+
"Changing soil parameterizations" => "standalone/Soil/changing_soil_parameterizations.jl",
2830
],
29-
"Canopy modeling" => [
31+
"Canopy" => [
3032
"Standalone Canopy" => "standalone/Canopy/canopy_tutorial.jl",
33+
"Changing canopy parameterizations" => "standalone/Canopy/changing_canopy_parameterizations.jl",
3134
],
32-
"Snow Modeling" => [
35+
"Snow" => [
3336
"standalone/Snow/base_tutorial.jl",
3437
"standalone/Snow/data_tutorial.jl",
3538
],
3639
],
37-
"Calibrating your ClimaLand model" => [
40+
"Calibrating a ClimaLand model" => [
3841
"Single site perfect model" => "calibration/minimal_working_example.jl",
3942
"Single site observations" => "calibration/minimal_working_example_obs.jl",
4043
],
4144
"For model developers" => [
4245
"Intro to standalone models" => "standalone/Usage/model_tutorial.jl",
43-
"Intro to multi-component models" => "standalone/Usage/LSM_single_column_tutorial.jl",
46+
"Intro to multi-component models" => [
47+
"Single column tutorial" => "standalone/Usage/LSM_single_column_tutorial.jl",
48+
"Adjusting boundary conditions for the soil" => "integrated/handling_soil_fluxes.jl",
49+
"Adjusting boundary conditions for the snow" => "integrated/handling_snow_fluxes.jl",
50+
],
4451
"Intro to ClimaLand Domains" => "standalone/Usage/domain_tutorial.jl",
4552
"Intro to forced site-level runs" => "shared_utilities/driver_tutorial.jl",
4653
"Intro to implicit/explicit timestepping" => "shared_utilities/timestepping.jl",

docs/src/APIs/ClimaLand.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
```@meta
44
CurrentModule = ClimaLand
55
```
6-
## LSM Model Types and methods
6+
## Integrated Land Model Types and methods
77

88
```@docs
9+
ClimaLand.LandModel
10+
ClimaLand.SoilCanopyModel
11+
ClimaLand.LandHydrology
912
ClimaLand.LandSoilBiogeochemistry
1013
ClimaLand.LandHydrology
1114
ClimaLand.land_components
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# # Fluxnet forcing data: LAI, radiation, and atmospheric variables
2+
3+
# To access the forcing data (LAI from MODIS, SW_d, LW_d, T_air, q_air,
4+
# P_air, and precipitation from fluxtower data), you first need the
5+
# the fluxtower site ID.
6+
# Currently, ClimaLand provides an interface for working with four
7+
# fluxtower sites; adding support for a much larger set of sites is
8+
# in progress. The sites we support are Vaira Ranch (US_Var),
9+
# Missouri Ozark (US-MOz), Niwot Ridge (US-NR1), and Harvard Forest
10+
# (US-Ha1).
11+
12+
using Dates
13+
import ClimaParams as CP
14+
using ClimaLand
15+
using ClimaLand.Domains: Column
16+
import ClimaLand.Parameters as LP
17+
using DelimitedFiles
18+
using CairoMakie
19+
import ClimaLand.FluxnetSimulations as FluxnetSimulations
20+
21+
# Pick a site ID; convert the dash to an underscore:
22+
site_ID = "US-NR1";
23+
site_ID_val = FluxnetSimulations.replace_hyphen(site_ID)
24+
# The functions we use below use multiple dispatch, treating the
25+
# site_ID_val as a Julia type.
26+
27+
# First, we need the latitude and longitude of the site. These are used
28+
# to get the zenith angle as a function of time, and to look up
29+
# default parameters using the global ClimaLand parameter maps. We also
30+
# need the offset of the local time of the site in hours from UTC. This is
31+
# because ClimaLand simulations are carried out in UTC, while the fluxtower
32+
# data is reported in local time.
33+
(; time_offset, lat, long) =
34+
FluxnetSimulations.get_location(FT, Val(site_ID_var));
35+
36+
# ClimaLand also needs to know the height at which the atmospheric data
37+
# was recorded.
38+
(; atmos_h) = FluxnetSimulations.get_fluxtower_height(FT, Val(site_ID));
39+
40+
# It is also useful to know the bounds of the data,
41+
# in UTC, to use as the start and stop date of the simulation.
42+
(start_date, stop_date) =
43+
FluxnetSimulations.get_data_dates(site_ID, time_offset);
44+
45+
# Define the floating point precision desired (64 or 32 bit), and get the
46+
# parameter set holding constants used across CliMA Models.
47+
const FT = Float32;
48+
earth_param_set = LP.LandParameters(FT);
49+
50+
# Now we can construct the forcing objects. Under the hood, this
51+
# function finds the local path to the fluxtower data (and downloads it
52+
# if it is not present) and reads the data in. It then creates
53+
# two objects, one called `atmos`, of type `PrescibedAtmosphere`, and
54+
# one called `radiation`, of type `PrescribedRadiativeFluxes`. These
55+
# encode the data in interpolating functions which allow us to
56+
# estimate the forcing at any time during the simulation using linear
57+
# interpolation across gaps in the data.
58+
(; atmos, radiation) = FluxnetSimulations.prescribed_forcing_fluxnet(
59+
site_ID,
60+
lat,
61+
long,
62+
time_offset,
63+
atmos_h,
64+
start_date,
65+
earth_param_set,
66+
FT,
67+
);
68+
# The atmosphere object holds the air temperature, pressure, specific
69+
# humidity, wind speed, and liquid and solid precipitation fluxes.
70+
# Since many fluxtower sites do not measure the snow and rain fluxes
71+
# separately, we estimate them internally. This is optional, and by providing
72+
# the kwarg `split_precip = false`, you can change the behavior to
73+
# return all measured precip as a liquid water flux.
74+
# The radiation object holds the downwelling short and long wave
75+
# radiative fluxes. The diffuse fraction is estimated internally
76+
# using an empirical function, and the zenith angle is computed
77+
# analytically.
78+
79+
# The simulation time is measured in seconds since the `start_date`. We
80+
# can get the atmospheric temperature at the `start_date` as follows:
81+
sim_time = 0.0
82+
T = [NaN]
83+
evaluate!(T, atmos.T, sim_time);
84+
@show T
85+
# Note that `evaluate!` updates `T` in place. This is important: in our
86+
# simulations, we allocate memory for the forcing, and then update the values
87+
# at each step. If we did not do this, the dynamic memory allocation
88+
# would cause the simulation to be incredibly slow.
89+
# We can also plot the interpolated air temperature for the first day:
90+
sim_times = 0.0:1800.0:86400.0 # one day in seconds
91+
air_temps = [atmos.T(t) for t in sim_times] # allocates
92+
fig = CairoMakie.Figure()
93+
ax = CairoMakie.Axis(
94+
fig[1, 1],
95+
ylabel = "Temperature (K)",
96+
xlabel = "Date",
97+
title = "Near-surface air temperature at Niwot Ridge",
98+
)
99+
lines!(ax, sim_times .+ start_date, air_temps)
100+
CairoMakie.save("air_temp.png", fig);
101+
# ![](air_temp.png)
102+
103+
# We do something very similar with LAI, but now the data is coming
104+
# from MODIS. In this case, we linearly interpolate from the gridded
105+
# MODIS data to the latitude and longitude of the site. To do spatial
106+
# interpolation, we need to create a ClimaLand domain.
107+
domain = Column(; zlim = (-3.0, 0.0), nelements = 10, longlat = (long, lat))
108+
surface_space = domain.space.surface
109+
# Get the paths to each year of MODIS data within the start and stop dates.
110+
modis_lai_ncdata_path = ClimaLand.Artifacts.modis_lai_multiyear_paths(;
111+
context = ClimaComms.context(surface_space),
112+
start_date,
113+
end_date = stop_date,
114+
)
115+
LAI = ClimaLand.prescribed_lai_modis(
116+
modis_lai_ncdata_path,
117+
surface_space,
118+
start_date,
119+
);
120+
121+
# Just like with the air temperature, the LAI is an object that we can use
122+
# to linearly interpolate observed LAI to any simulation time.
123+
124+
# It can also be useful to know the maximum LAI at a site. To do so, we
125+
# can call, for the first year of data (first element of `modis_lai_ncdata_path`):
126+
maxLAI =
127+
FluxnetSimulations.get_maxLAI_at_site(modis_lai_ncdata_path[1], lat, long)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# # Visualizing the output of a Fluxnet simulation
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# # Fluxnet simulations with the full land model: snow, soil, canopy
2+
3+
# In the
4+
# [canopy and soil tutorial](docs/src/tutorials/integrated/soil_canopy_fluxnet_tutorial.md),
5+
# we demonstrated how to run the an integrated model with a soil and
6+
# canopy component at the US-MOz fluxnet site.
7+
# Here we add in a snow component, and run the site at the Niwot Ridge site instead.
8+
9+
# The focus of this tutorial is to learn the steps towards setting up and
10+
# running an integrated simulation, and less on the parameterization
11+
# choices. As such, the default parameters are implicitly set.
12+
# To experiment with modularity in the parameters and parameterizations, please see the [canopy parameterizations tutorial](docs/src/tutorials/standalone/Canopy/changing_canopy_parameterizations.md) or the [soiil parameterizations tutorial](docs/src/tutorials/standalone/Soil/changing_soil_parameterizations.md).
13+
14+
# # Preliminary Setup
15+
using Dates
16+
import ClimaParams as CP
17+
using ClimaDiagnostics
18+
using ClimaLand
19+
using ClimaLand.Domains: Column
20+
using ClimaLand.Simulations
21+
import ClimaLand.Parameters as LP
22+
using DelimitedFiles
23+
import ClimaLand.FluxnetSimulations as FluxnetSimulations
24+
using CairoMakie, ClimaAnalysis, GeoMakie, Poppler_jll, Printf, StatsBase
25+
import ClimaLand.LandSimVis as LandSimVis;
26+
27+
# Define the floating point precision desired (64 or 32 bit), and get the
28+
# parameter set holding constants used across CliMA Models.
29+
30+
const FT = Float32;
31+
earth_param_set = LP.LandParameters(FT);
32+
33+
# We will use prescribed atmospheric and radiative forcing from the
34+
# US-NR1 tower. We also
35+
# read in the MODIS LAI and let that vary in time in a prescribed manner.
36+
site_ID = "US-NR1";
37+
site_ID_val = FluxnetSimulations.replace_hyphen(site_ID)
38+
# Get the latitude and longitude in degrees, as well as the
39+
# time offset in hours of local time from UTC
40+
(; time_offset, lat, long) =
41+
FluxnetSimulations.get_location(FT, Val(site_ID_val));
42+
# Get the height of the sensors in m
43+
(; atmos_h) = FluxnetSimulations.get_fluxtower_height(FT, Val(site_ID_val));
44+
# Set a start and stop date of the simulation in UTC, as well as
45+
# a timestep in seconds
46+
(start_date, stop_date) =
47+
FluxnetSimulations.get_data_dates(site_ID, time_offset)
48+
Δt = 450.0;
49+
50+
# Forcing data for the site - this uses our interface for working with Fluxnet data
51+
forcing = FluxnetSimulations.prescribed_forcing_fluxnet(
52+
site_ID,
53+
lat,
54+
long,
55+
time_offset,
56+
atmos_h,
57+
start_date,
58+
earth_param_set,
59+
FT,
60+
);
61+
# LAI for the site - this uses our interface for working with MODIS data.
62+
modis_lai_ncdata_path = ClimaLand.Artifacts.modis_lai_multiyear_paths(;
63+
start_date,
64+
end_date = stop_date,
65+
)
66+
LAI = ClimaLand.prescribed_lai_modis(
67+
modis_lai_ncdata_path,
68+
domain.space.surface,
69+
start_date,
70+
);
71+
# Setup the domain for the model. This corresponds to
72+
# a column of 2m in depth, with 10 equally spaced layers.
73+
# The lat and long are provided so that we can look up default parameters
74+
# for this location using the default ClimaLand parameter maps.
75+
zmin = FT(-2) # in m
76+
zmax = FT(0) # in m
77+
domain = Column(; zlim = (zmin, zmax), nelements = 10, longlat = (long, lat))
78+
79+
# # Setup the integrated model
80+
81+
# We want to simulate the canopy-soil-snow system together, so the model type
82+
# [`LandModel`](https://clima.github.io/ClimaLand.jl/dev/APIs/ClimaLand/#Integrated-Land-Model-Types-and-methods)
83+
# is chosen. Here we use the highest level model constructor, which uses default parameters,
84+
# and parameterizations, for the soil, snow, and canopy models.
85+
86+
land_model = LandModel{FT}(forcing, LAI, earth_param_set, domain, Δt);
87+
set_ic! = FluxnetSimulations.make_set_fluxnet_initial_conditions(
88+
site_ID,
89+
start_date,
90+
time_offset,
91+
land_model,
92+
);
93+
output_vars = ["swu", "lwu", "shf", "lhf", "swe", "swc", "si"]
94+
diagnostics = ClimaLand.default_diagnostics(
95+
land_model,
96+
start_date;
97+
output_writer = ClimaDiagnostics.Writers.DictWriter(),
98+
output_vars,
99+
average_period = :hourly,
100+
);
101+
102+
simulation = Simulations.LandSimulation(
103+
FT,
104+
start_date,
105+
stop_date,
106+
Δt, # seconds
107+
land_model;
108+
set_ic!,
109+
user_callbacks = (),
110+
diagnostics,
111+
);
112+
@time solve!(simulation);
113+
114+
# # Plotting results
115+
LandSimulationVisualizationExt.make_diurnal_timeseries(
116+
simulation;
117+
short_names = ["shf", "lhf", "swu", "lwu"],
118+
spinup_date = start_date + Day(20),
119+
);
120+
# ![](diurnal_timeseries.png)
121+
LandSimulationVisualizationExt.make_timeseries(
122+
simulation;
123+
short_names = ["swc", "si", "swe"],
124+
spinup_date = start_date + Day(20),
125+
);
126+
# ![](variable_timeseries.png)

0 commit comments

Comments
 (0)