Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
75b77df
add output file from a previous aboveground stock modelling; rename s…
hrlai Aug 16, 2025
4e812e0
mark older analysis as superseded
hrlai Aug 23, 2025
7bf77c1
deadwood stock models
hrlai Aug 23, 2025
8a88bf3
root stock models and estimation (wip)
hrlai Aug 23, 2025
127a1d1
rename section
hrlai Aug 23, 2025
03ba81e
add ref as a metadata reminder
hrlai Aug 23, 2025
e97fe02
separate woody litter decomposition model from leaf
hrlai Aug 23, 2025
82d1129
revise leaf litter decay model to use another dataset with more compl…
hrlai Aug 24, 2025
bfa7f2c
custom lognormal distribution
hrlai Aug 24, 2025
1c8d1a7
remove model saving
hrlai Aug 24, 2025
36e29e3
rename model to avoid conflicts
hrlai Aug 24, 2025
a14cbaf
notes on wood model
hrlai Aug 26, 2025
65d2c61
add modelling script and data for lignin-decay rate relationship
hrlai Aug 26, 2025
127308e
add script to combine model parameters and its output table
hrlai Aug 27, 2025
2766aa3
Merge branch '102-rethinking-litter-decay-models' into 101-estimate-e…
hrlai Aug 27, 2025
52f822b
explore month and decided not to use it
hrlai Sep 17, 2025
9c71381
correct lignin's unit
hrlai Sep 17, 2025
9060d20
remove old comment
hrlai Sep 17, 2025
b10a45f
update aboveground litter analysis
hrlai Sep 17, 2025
9eb0c83
polish metadata
hrlai Sep 17, 2025
0d23abf
add missing extension
hrlai Sep 17, 2025
93c773b
combine litter stock values
hrlai Sep 17, 2025
b675848
Merge branch 'main' into 101-estimate-equilbrium-litter-stocks
hrlai Sep 18, 2025
391ce80
add missing input file metadata and note by J Cook
hrlai Sep 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions analysis/litter/chemistry_and_turnover/combine_parameters.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#| ---
#| title: Combine the estimated litter decay parameters into an output table
#|
#| description: |
#| This R script combines the parameters estimated by model_leaf.R,
#| model_wood.R and model_rate_lignin.R into a single table.
#|
#| VE_module: Litter
#|
#| author:
#| - name: Hao Ran Lai
#|
#| status: final
#|
#| input_files:
#| - name: plant_stoichiometry.csv
#| path: data/derived/plant/traits_data
#| description: |
#| Plant stoichiometry trait data compiled by Arne; I am using it to
#| obtain mean leaf and stem lignin content for parameter recovery here.
#|
#| output_files:
#| - name: decay_parameters.csv
#| path: data/derived/litter/turnover/
#| description: |
#| Parameters for the litter decay model. Values are reported as
#| posterior median and the lower and upper bounds of the 95% credible
#| intervals (for .interval == "qi") OR point estimate and 95% confidence
#| intervals (for .interval == "ci").
#|
#| package_dependencies:
#| - tidybayes
#| - readxl
#| - brms
#| - bayesplot
#| - modelr
#| - glmmTMB
#|
#| usage_notes: |
#| The leaf litter model is Bayessian inference so it will take a few
#| minutes to run.
#| ---


library(tidybayes)



# Source modelling codes --------------------------------------------------

# leaf litter model (takes a while to fit Bayesian inference)
source("analysis/litter/chemistry_and_turnover/model_leaf.R")

# wood litter model
source("analysis/litter/chemistry_and_turnover/model_wood.R")

# lignin model
source("analysis/litter/chemistry_and_turnover/model_rate_lignin.R")



# Combine parameter estimates ---------------------------------------------

# leaf and wood lignin (constant across species for now) to recover the original
# ks and kw
# for consistency and simplicity, I will simply use the values that Arne took
# for the plant stoichiometry script as of 24/8/2025
# see analysis/plant/plant_stoichiometry/plant_stoichiometry.R
plant_stoich <- read_csv("data/derived/plant/traits_data/plant_stoichiometry.csv")
lignin_leaf <- mean(plant_stoich$leaf_lignin)
lignin_wood <- mean(plant_stoich$stem_lignin)

# summarise the posterior
# Values are reported as median and the lower and upper bounds of the 90%
# credible intervals
param_summary <-
# leaf model parameter ==================================================
mod_leaf %>%
spread_draws(
b_logsN_Intercept,
b_logsP_Intercept,
b_logitfM_Intercept,
b_logks_Intercept,
b_logkmdiff_Intercept
) %>%
# back-transform parameters and back-scale when required
# note that at this stage r (lignin effect) is "absorbed" into kw and ks
# we will convert kw and ks to our units later
mutate(
sN = exp(b_logsN_Intercept) / sd(litter$CN) / lignin_leaf,
sP = exp(b_logsP_Intercept) / sd(litter$CP) / lignin_leaf,
logitfM = b_logitfM_Intercept +
exp(b_logsN_Intercept) / sd(litter$CN) * mean(litter$CN) +
exp(b_logsP_Intercept) / sd(litter$CP) * mean(litter$CP),
ks = exp(b_logks_Intercept) / 365.25,
km = (ks + exp(b_logkmdiff_Intercept)) / 365.25,
.keep = "unused"
) %>%
pivot_longer(
cols = sN:km,
names_to = "Parameter",
values_to = "value"
) %>%
# summarise posterior
group_by(Parameter) %>%
median_qi(value,
.width = 0.95
) %>%
# join wood model parameter =============================================
bind_rows(
data.frame(
Parameter = "kw",
value = -param_wood["weeks", "Estimate"] / 7,
.lower = -param_wood["weeks", "2.5 %"] / 7,
.upper = -param_wood["weeks", "97.5 %"] / 7,
.width = 0.95,
.point = "mean",
.interval = "ci"
)
) %>%
# join the lignin-rate model ============================================
bind_rows(
data.frame(
Parameter = "r",
value = param_k_lignin["lignin", "Estimate"],
.lower = param_k_lignin["lignin", "2.5 %"],
.upper = param_k_lignin["lignin", "97.5 %"],
.width = 0.95,
.point = "mean",
.interval = "ci"
)
)

# now that we have r, we will convert kw and ks to the right units
# the crude assumption here is that all species have the same leaf and wood
# lignin content; also this is a crude conversion because ideally we would
# incorporate the parameter uncertainty of r, but I'm only using its mean
# estimate for simplicity here
# nolint start
param_summary[param_summary$Parameter == "kw", c("value", ".lower", ".upper")] <-
param_summary[param_summary$Parameter == "kw", c("value", ".lower", ".upper")] /
exp(param_summary$value[param_summary$Parameter == "r"] * lignin_wood)
param_summary[param_summary$Parameter == "ks", c("value", ".lower", ".upper")] <-
param_summary[param_summary$Parameter == "ks", c("value", ".lower", ".upper")] /
exp(param_summary$value[param_summary$Parameter == "r"] * lignin_leaf)
# nolint end

# save output table
write_csv(
param_summary,
"data/derived/litter/turnover/decay_parameters.csv"
)
62 changes: 62 additions & 0 deletions analysis/litter/chemistry_and_turnover/lognormal_natural.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# nolint start
# From https://github.com/paul-buerkner/custom-brms-families/blob/master/families/lognormal_natural.R

# helper functions for post-processing of the family
log_lik_lognormal_natural <- function(i, prep) {
mu <- prep$dpars$mu[, i]
if (NCOL(prep$dpars$sigma) == 1) {
sigma <- prep$dpars$sigma
} else
## [, i] if sigma is modelled, without otherwise
{
sigma <- prep$dpars$sigma[, i]
}
y <- prep$data$Y[i]
common_term <- log(1 + sigma^2 / mu^2)
Vectorize(dlnorm)(y, log(mu) - common_term / 2, sqrt(common_term), log = TRUE)
}


posterior_predict_lognormal_natural <- function(i, prep, ...) {
mu <- prep$dpars$mu[, i]
if (NCOL(prep$dpars$sigma) == 1) {
sigma <- prep$dpars$sigma
} else
## [, i] if sigma is modelled, without otherwise
{
sigma <- prep$dpars$sigma[, i]
}
common_term <- log(1 + sigma^2 / mu^2)
rlnorm(n, log(mu) - common_term / 2, sqrt(common_term))
}

posterior_epred_lognormal_natural <- function(prep) {
mu <- prep$dpars$mu
return(mu)
}

# definition of the custom family
lognormal_natural <-
custom_family(
name = "lognormal_natural",
dpars = c("mu", "sigma"),
links = c("log", "log"),
lb = c(0, 0),
type = "real"
)

# additionally required Stan code
stan_lognormal_natural <- "
real lognormal_natural_lpdf(real y, real mu, real sigma) {
real common_term = log(1+sigma^2/mu^2);
return lognormal_lpdf(y | log(mu)-common_term/2,
sqrt(common_term));
}
real lognormal_natural_rng(real mu, real sigma) {
real common_term = log(1+sigma^2/mu^2);
return lognormal_rng(log(mu)-common_term/2,
sqrt(common_term));
}
"

# nolint end
Loading