Skip to content

Commit 30ab762

Browse files
committed
# Update to v0.1.2:
1. Remove TODO list 2. Upload `OptModel` 3. Refine documentation of existed function
1 parent 6fdf999 commit 30ab762

File tree

15 files changed

+1501
-158
lines changed

15 files changed

+1501
-158
lines changed

.gitignore

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
docs/build
2-
3-
41
.vscode
5-
62
desktop.ini
73

4+
docs/build
5+
doc/*
6+
87
Manifest.toml
98
Prompts.md
9+
TODO.md

Project.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
name = "Junimo"
22
uuid = "3d54b6d4-9b4d-4421-9513-a6007c061c41"
33
authors = ["cug-xyx <xieyuxuan0430@igsnrr.ac.cn>"]
4-
version = "0.1.1"
4+
version = "0.1.2"
55

66
[deps]
7+
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
78
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
9+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
810
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
911
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1012

1113
[compat]
14+
CSV = "0.10.15"
1215
CairoMakie = "0.15.8"
16+
DataFrames = "1.8.1"
1317
Random = "1.11.0"
1418
Statistics = "1.11.1"
1519

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
- [Documentation](#documentation)
1919
- [Contributing](#contributing)
2020
- [License](#license)
21-
- [TODO](#todo)
2221

2322
## Overview
2423

@@ -34,6 +33,9 @@ YuxuanXie
3433
- Utils: colored console output helpers (`cprint`, `red`, `green`, `blue`, `purple` and macros)
3534
- Statistics: goodness-of-fit metrics (`gof`, colorized `GofResult` output)
3635
- Visualize: GoF annotation helpers for Makie (`show_gof!`)
36+
- Model
37+
- Plant Physiology
38+
- **Opt model** (`OptModel`, Hu et al., 2025): an eco-evolutionary optimality-based model that simulates vegetation GPP, gs, Vcmax from LAI and climate drivers.
3739

3840
## Installation
3941

@@ -64,7 +66,3 @@ Contributions are welcome. Please open an issue to discuss ideas before submitti
6466
## License
6567

6668
GPL-3.0-only
67-
68-
## TODO
69-
70-
- [x] colorful `gof` output (positive in red, negative in blue; `colorize=true`)

data/BE-Vie_8day

Lines changed: 831 additions & 0 deletions
Large diffs are not rendered by default.

data/OptModel_param_b.tif

444 KB
Binary file not shown.

docs/make.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ makedocs(
66
sitename="Junimo.jl",
77
pages = [
88
"index.md",
9+
"Model" => [
10+
"Plant Physiology" => [
11+
"Model/PlantPhysiology/OptModel.md"
12+
]
13+
],
914
"Statistics" => [
10-
"Statistics/Gof.md"
15+
"Statistics/Gof.md"
1116
],
1217
"Utils" => [
1318
"Utils/Print.md"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Opt model
2+
3+
```@meta
4+
CurrentModule = Junimo
5+
```
6+
7+
## Description
8+
9+
`OptModel` is built on eco-evolutionary optimality principles to simulate how vegetation gross primary productivity (GPP) responds to climate and atmospheric CO₂. It integrates optimal stomatal conductance theory, optimal Ci/Ca theory, and the coordination theory, treating stomatal conductance (gs), intercellular CO₂ ratio, and maximum rate of Ribulose-1,5-bisphosphate carboxylase/ oxygenase (Rubisco) carboxylation (Vcmax) as outcomes of a carbon gain versus water cost trade-off, which enables analytical solutions for key variables. Driven by LAI and climate forcings (temperature, precipitation, potential evapotranspiration, VPD, radiation, elevation, and CO₂), the model requires only a small number of parameters (i.e., b). It can compute GPP, gs, Vcmax, and marginal water-use efficiency, among others. Compared with empirical formulations, `OptModel` is parsimonious and physically interpretable, better capturing responses to climate variability and elevated CO₂, and it scales well across sites and regions (Hu et al., 2025).
10+
11+
## Examples
12+
13+
```@example
14+
using Junimo
15+
using CairoMakie
16+
using CSV, DataFrames
17+
18+
root_dir = joinpath(@__DIR__, "..", "..", "..", "..") # hide
19+
# root_dir = "."
20+
data_path = joinpath(root_dir, "data", "BE-Vie_8day")
21+
df = CSV.read(data_path, DataFrame)
22+
23+
begin
24+
Ta = df.Tavg # [°C]
25+
Precip = df.Prcp # [mm]
26+
PET = df.PET # [mm]
27+
PAR = (df.Rs * 0.4) * 4.6 # [W m-2] -> [umol m-2 s-1]
28+
LAI = df.LAI # [m2 m-2]
29+
VPD = df.VPD # [kPa]
30+
elv = df.elv # [m]
31+
Ca = df.co2 # [ppm]
32+
b = df.b # [-]
33+
34+
res = OptModel.(Ta, Precip, PET, PAR, LAI, VPD, elv, Ca, b)
35+
GPP_Opt = getindex.(res, 1)
36+
end
37+
38+
begin
39+
yobs, ysim = df.GPP_obs, GPP_Opt
40+
fig = Figure(; size=(300, 300))
41+
ax = Axis(
42+
fig[1, 1], titlefont = :regular,
43+
xlabel = "Observed GPP (gC/m2/day)", ylabel = "OptModel GPP (gC/m2/day)",
44+
rightspinevisible = false, topspinevisible = false,
45+
xgridvisible = false, ygridvisible = false
46+
)
47+
scatter!(ax, yobs, ysim; markersize=10, color=:lightblue)
48+
lines!(ax, [0, 15], [0, 15]; color=:red, linestyle=:dash, linewidth=2)
49+
show_gof!(
50+
ax, 0.1, 14.9, yobs, ysim; metrics=["R2", "RMSE", "PBIAS"],
51+
n=3, align=(:left, :top), color=:black
52+
)
53+
xlims!(ax, 0, 15)
54+
ylims!(ax, 0, 15)
55+
fig
56+
end
57+
```
58+
59+
## APIs
60+
61+
```@docs
62+
OptModel
63+
fraction_of_photosynthetically_active_radiation
64+
soil_moisture_constraint_factor
65+
surface_pressure
66+
photorespiratory_compensation_point
67+
effective_michaelis_menten_coefficient_of_rubisco
68+
marginal_water_use_efficiency
69+
```

docs/src/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ YuxuanXie
3131
## Function Categories
3232

3333
- Utils: colored console output helpers (`cprint`, `red`, `green`, `blue`, `purple` and macros)
34-
- Statistics: goodness-of-fit metrics (`gof`, colorized `GofResult` output)
34+
- Statistics: goodness-of-fit metrics (`gof`, colorized `GofResult` output)
3535
- Visualize: GoF annotation helpers for Makie (`show_gof!`)
36+
- Model
37+
- Plant Physiology
38+
- **Opt model** (`OptModel`, Hu et al., 2025): an eco-evolutionary optimality-based model that simulates vegetation GPP, gs, Vcmax from LAI and climate drivers.
3639

3740
## Installation
3841

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using Junimo
2+
using CairoMakie
3+
using MAT
4+
using Rasters, ArchGDAL
5+
using CSV, DataFrames
6+
using HydroTools
7+
8+
9+
# gridded parameter b
10+
m_param_b = matopen("doc/Model/Plant_physiology/OptModel/B05degree.mat")
11+
var_name = keys(m_param_b)
12+
arr_param_b = read(m_param_b, "B05degree")
13+
14+
ras_param_b = Raster(arr_param_b, (Y(89.75:-0.5:-89.75), X(-179.75:0.5:179.75)))
15+
# plot(ras_param_b)
16+
# write("data/OptModel_param_b.tif", ras_param_b)
17+
18+
# FLUXNET
19+
df_all = CSV.read("E:/Eddy_covariance_flux_towers/PML-V2.2-cali-final/input_output_qc_8d_pml_v22.csv", DataFrame)
20+
df_all = df_all[.!ismissing.(df_all.GPP_obs), :]
21+
22+
df_nGPPobs = DataFrames.combine(groupby(df_all, "ID"), nrow => :Count)
23+
24+
site_most_GPPobs = sort!(df_nGPPobs, "Count", rev = true)[1, "ID"]
25+
26+
df_res = df_all[df_all.ID .== site_most_GPPobs, :]
27+
28+
# extract b
29+
df_loc = CSV.read("E:/Eddy_covariance_flux_towers/PML-V2.2-cali-final/sites_qc_pml_v22.csv", DataFrame)
30+
df_loc[df_loc.ID .== site_most_GPPobs, ["long", "lat"]]
31+
32+
param_b = ras_param_b[X(Near(5.9981)), Y(Near(50.305))]
33+
df_res.b .= param_b
34+
df_res.elv .= 152.9
35+
36+
# calculate PET
37+
df_res.PET .= ET0_FAO98.(df_res.Rn, df_res.Tavg, df_res.VPD, df_res.U2, df_res.Pa; z_wind=2, tall_crop=false)
38+
39+
# write
40+
cols = ["ID", "IGBP1", "Date", "elv", "GPP_obs", "GPP", "Prcp", "Rs", "Tavg", "VPD", "Pa", "co2", "LAI", "PET", "b"]
41+
df_res = df_res[:, cols]
42+
CSV.write("data/$(site_most_GPPobs)_8day", df_res)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Junimo
2+
using CairoMakie
3+
using CSV, DataFrames
4+
5+
df = CSV.read("data/BE-Vie_8day", DataFrame)
6+
7+
begin
8+
Ta = df.Tavg # [°C]
9+
Precip = df.Prcp # [mm]
10+
PET = df.PET # [mm]
11+
PAR = (df.Rs * 0.4) * 4.6 # [W m-2] -> [umol m-2 s-1]
12+
LAI = df.LAI # [m2 m-2]
13+
VPD = df.VPD # [kPa]
14+
elv = df.elv # [m]
15+
Ca = df.co2 # [ppm]
16+
b = df.b # [-]
17+
18+
res = OptModel.(Ta, Precip, PET, PAR, LAI, VPD, elv, Ca, b)
19+
GPP_Opt = getindex.(res, 1)
20+
end
21+
22+
begin
23+
yobs, ysim = df.GPP_obs, GPP_Opt
24+
fig = Figure(; size=(400, 400))
25+
ax = Axis(
26+
fig[1, 1], titlefont = :regular,
27+
xlabel = "Observed GPP (gC/m2/day)", ylabel = "OptModel GPP (gC/m2/day)",
28+
rightspinevisible = false, topspinevisible = false,
29+
xgridvisible = false, ygridvisible = false
30+
)
31+
scatter!(ax, yobs, ysim; markersize=10, color=:lightblue)
32+
lines!(ax, [0, 15], [0, 15]; color=:red, linestyle=:dash, linewidth=2)
33+
show_gof!(
34+
ax, 0.1, 14.9, yobs, ysim; metrics=["R2", "RMSE", "PBIAS"],
35+
n=3, align=(:left, :top), color=:black
36+
)
37+
xlims!(ax, 0, 15)
38+
ylims!(ax, 0, 15)
39+
display(fig)
40+
end

0 commit comments

Comments
 (0)