-
Notifications
You must be signed in to change notification settings - Fork 313
add ca fertilization harmonization and ncc compost support #4002
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 36 commits
5b609f9
af7171a
9079a65
4ba0b09
4da526e
00a4ce8
b738e65
70f56c8
ef7d28c
1b96343
36ed147
3598750
adc5190
9a15db8
e850cee
c5cb2ad
5e2fff3
e41539f
de0bb71
9b22635
4dfed63
56a95f6
ee1ef5e
d858456
c5d72f0
c353ef5
1d24977
a71ef65
5f5172c
f52c1c3
955e7f1
30e620d
d0c79ef
0974b36
d0844c9
0bd71d9
246fb5b
f503d37
3fbfcec
81864e8
0b71a7e
daf20d8
bb138b0
93c7456
eb461d3
5d4070a
aec34bf
d4567f0
34a4068
9a9dcf0
7ee162b
6d943fb
3da67e0
6e025b4
9433f59
0a715a6
2467216
1e5dbe5
aa60cf2
6d3850c
b5c658b
1bc7c15
740cc8c
4150ede
7a59c49
229b2ac
ca51a68
39ce033
50c2ffa
0cc6cdf
7b3dd3b
76455c1
335031c
fc67a4e
b7d07e3
e721e3f
aadf430
3f7592e
37a7245
3fcaab0
702cf07
6ac9e51
b5aa883
c56a21c
e8752f6
db4b6b1
d7b4391
2044ea3
1a4a05a
95165aa
2ad6403
d58ca09
6b95f43
7d7ae2f
5d60c92
24f9212
b7ee992
84cda6f
40d6d5b
a9e4e10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #' Convert N application rate from oz N per tree to lb N per acre | ||
| #' | ||
| #' California extension guidelines for young orchards (CDFA FREP, UC ANR, | ||
| #' Almond Board) publish N rates per tree. This helper multiplies by | ||
| #' orchard density to give lb N per acre for pipelines that need per acre | ||
| #' inputs. | ||
| #' | ||
| #' @param oz_per_tree numeric vector. N rate in ounces N per tree. | ||
| #' @param tpa numeric scalar or vector. Orchard density in trees per acre. | ||
| #' Recycled to length of oz_per_tree if scalar. | ||
| #' | ||
| #' @return numeric vector. N rate in lb N per acre. NA inputs propagate. | ||
| #' | ||
| #' @examples | ||
| #' oz_per_tree_to_lb_per_acre(c(1, 3), tpa = 145) | ||
| #' | ||
| #' @export | ||
| oz_per_tree_to_lb_per_acre <- function(oz_per_tree, tpa) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤯 plz use Also, I would suggest quarantining imperial units and not supporting them in PEcAn; i.e. the conversion can be done within the data ingest functions.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good catch, |
||
| oz_per_tree * tpa / 16 | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,187 @@ | ||||||||
| #' Sample California compost amendment %C | ||||||||
| #' | ||||||||
| #' Draws n values of compost carbon content (%C, dry weight basis) from | ||||||||
| #' the bundled truncated normal distribution at | ||||||||
| #' ca_compost_pct_c_distribution. | ||||||||
| #' | ||||||||
| #' The default range (15 to 30%, mean 20%, sd 5) reflects CA finished | ||||||||
| #' compost characterizations: Bernard et al. 2023 (central coast | ||||||||
| #' vineyard, 14 to 15% C); Sullivan, Bary, Miller and Brewer 2018 (OSU | ||||||||
| #' EM9217 proficiency program, median 15% TOC); CalRecycle generic | ||||||||
| #' compost roughly 20 to 23% C. | ||||||||
| #' | ||||||||
| #' @param n integer. Number of draws. | ||||||||
| #' @param params optional list overriding the bundled distribution. Must | ||||||||
| #' contain numeric a, b, mean, sd. | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be easier on the reader to name the bounds
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||
| #' | ||||||||
| #' @return numeric vector of length n, each value in [a, b]. | ||||||||
| #' | ||||||||
| #' @examples | ||||||||
| #' sample_ca_compost_pct_c(5) | ||||||||
| #' | ||||||||
| #' @export | ||||||||
| sample_ca_compost_pct_c <- function(n, params = NULL) { | ||||||||
| p <- params %||% as.list(PEcAn.data.land::ca_compost_pct_c_distribution) | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not set this as the default directly?
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make sense, |
||||||||
| truncnorm::rtruncnorm(n, a = p$a, b = p$b, mean = p$mean, sd = p$sd) | ||||||||
| } | ||||||||
|
|
||||||||
| #' Sample California compost C:N ratio | ||||||||
| #' | ||||||||
| #' Draws n values of compost C:N from the bundled truncated normal | ||||||||
| #' distribution at ca_compost_cn_distribution. | ||||||||
| #' | ||||||||
| #' Default range (8 to 25, mean 12, sd 4) reflects the bimodal CA compost | ||||||||
| #' distribution documented in the CDFA Healthy Soils Program white paper | ||||||||
| #' (Geisseler et al.): manure dominant compost clusters at C:N at or | ||||||||
| #' below 11 while plant dominant compost sits above 11. HSP 2026 Practice | ||||||||
| #' Guidelines require applied C:N at or below 25. | ||||||||
| #' | ||||||||
| #' @param n integer. Number of draws. | ||||||||
| #' @param params optional list overriding the bundled distribution. | ||||||||
| #' | ||||||||
| #' @return numeric vector of length n. | ||||||||
| #' | ||||||||
| #' @examples | ||||||||
| #' sample_ca_compost_cn(5) | ||||||||
| #' | ||||||||
| #' @export | ||||||||
| sample_ca_compost_cn <- function(n, params = NULL) { | ||||||||
| p <- params %||% as.list(PEcAn.data.land::ca_compost_cn_distribution) | ||||||||
| truncnorm::rtruncnorm(n, a = p$a, b = p$b, mean = p$mean, sd = p$sd) | ||||||||
| } | ||||||||
|
|
||||||||
| #' Sample California compost application rate by PFT family | ||||||||
| #' | ||||||||
| #' Draws n application rates (dry weight tons per acre) uniformly from | ||||||||
| #' the per family envelope at ca_compost_app_rate_envelope. | ||||||||
| #' | ||||||||
| #' Envelopes follow CDFA Healthy Soils Program white paper Table 2 plus | ||||||||
| #' HSP 2026 Practice Guidelines plus USDA NRCS Conservation Practice | ||||||||
| #' Standard 336 Soil Carbon Amendment (2022). Defaults: annual crops 3 | ||||||||
| #' to 8 t/ac dry, perennial crops 2 to 6 t/ac dry. | ||||||||
| #' | ||||||||
| #' @param pft_family character vector. PFT family for each draw. Must be | ||||||||
| #' one of "annual", "perennial". Length 1 broadcasts to length n. | ||||||||
| #' @param n integer. Number of draws. If pft_family has length > 1 then | ||||||||
| #' n must equal length(pft_family). | ||||||||
| #' | ||||||||
| #' @return numeric vector of length n in tons per acre dry weight. | ||||||||
| #' | ||||||||
| #' @examples | ||||||||
| #' sample_ca_compost_app_rate("annual", 5) | ||||||||
| #' sample_ca_compost_app_rate(c("annual", "perennial", "annual"), 3) | ||||||||
| #' | ||||||||
| #' @export | ||||||||
| sample_ca_compost_app_rate <- function(pft_family, n = length(pft_family)) { | ||||||||
| if (length(pft_family) == 1 && n > 1) { | ||||||||
| pft_family <- rep(pft_family, n) | ||||||||
| } | ||||||||
| if (length(pft_family) != n) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "length(pft_family) must equal n; got ", length(pft_family), " vs ", n) | ||||||||
| } | ||||||||
| env <- PEcAn.data.land::ca_compost_app_rate_envelope | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confusing name -- in R "env" almost always means an environment. Consider |
||||||||
| out <- numeric(n) | ||||||||
| for (fam in unique(pft_family)) { | ||||||||
| if (!fam %in% env$pft_family) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "Unknown pft_family '", fam, "'. Supported: ", | ||||||||
| paste(env$pft_family, collapse = ", ")) | ||||||||
| } | ||||||||
| row <- env[env$pft_family == fam, , drop = FALSE] | ||||||||
| idx <- which(pft_family == fam) | ||||||||
| out[idx] <- stats::runif(length(idx), min = row$min_t_ac, max = row$max_t_ac) | ||||||||
| } | ||||||||
| out | ||||||||
| } | ||||||||
|
|
||||||||
| #' Sample California compost application date offset by PFT family | ||||||||
| #' | ||||||||
| #' Returns n integer day offsets (subtract from the cycle anchor, | ||||||||
| #' typically MSLSP mslsp_OGI) drawn uniformly from the per family | ||||||||
| #' calendar window at ca_compost_calendar_window. | ||||||||
| #' | ||||||||
| #' Defaults: annual crops 7 to 28 days before the anchor (CDFA HSP Box 1, | ||||||||
| #' applied and incorporated before planting); perennial crops 60 to 180 | ||||||||
| #' days before the anchor (dormant season application, UC ANR cost | ||||||||
| #' studies plus Bernard et al. 2023 vineyard timing of Nov 9 and Jan 10). | ||||||||
| #' | ||||||||
| #' @param pft_family character vector. PFT family for each draw. | ||||||||
| #' @param n integer. Number of draws. | ||||||||
| #' | ||||||||
| #' @return integer vector of day offsets. | ||||||||
| #' | ||||||||
| #' @examples | ||||||||
| #' sample_ca_compost_date_offset("annual", 5) | ||||||||
| #' | ||||||||
| #' @export | ||||||||
| sample_ca_compost_date_offset <- function(pft_family, n = length(pft_family)) { | ||||||||
| if (length(pft_family) == 1 && n > 1) { | ||||||||
| pft_family <- rep(pft_family, n) | ||||||||
| } | ||||||||
| if (length(pft_family) != n) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "length(pft_family) must equal n; got ", length(pft_family), " vs ", n) | ||||||||
| } | ||||||||
| win <- PEcAn.data.land::ca_compost_calendar_window | ||||||||
| out <- integer(n) | ||||||||
| for (fam in unique(pft_family)) { | ||||||||
| if (!fam %in% win$pft_family) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "Unknown pft_family '", fam, "'. Supported: ", | ||||||||
| paste(win$pft_family, collapse = ", ")) | ||||||||
| } | ||||||||
| row <- win[win$pft_family == fam, , drop = FALSE] | ||||||||
| idx <- which(pft_family == fam) | ||||||||
| out[idx] <- sample(row$offset_days_min:row$offset_days_max, | ||||||||
| length(idx), replace = TRUE) | ||||||||
| } | ||||||||
| out | ||||||||
| } | ||||||||
|
|
||||||||
| #' Sample California compost material class by PFT family | ||||||||
| #' | ||||||||
| #' Returns n material classes drawn from the per family whitelist at | ||||||||
| #' ca_compost_material_whitelist. Uniform within the whitelist. | ||||||||
| #' | ||||||||
| #' Whitelist follows the CalRecycle taxonomy (14 CCR section 17852 plus | ||||||||
| #' SB 1383 dominant feedstocks: green, food, wood, yard, ag, biosolids). | ||||||||
| #' Defaults: annual crops can receive green / food / ag; perennial crops | ||||||||
| #' can receive green / food / yard. Biosolids are excluded from food | ||||||||
| #' crop parcels for regulatory compliance; wood waste is deferred since | ||||||||
| #' it immobilizes N. | ||||||||
| #' | ||||||||
| #' @param pft_family character vector. PFT family for each draw. | ||||||||
| #' @param n integer. Number of draws. | ||||||||
| #' | ||||||||
| #' @return character vector of material classes. | ||||||||
| #' | ||||||||
| #' @examples | ||||||||
| #' sample_ca_compost_material("annual", 5) | ||||||||
| #' | ||||||||
| #' @export | ||||||||
| sample_ca_compost_material <- function(pft_family, n = length(pft_family)) { | ||||||||
| if (length(pft_family) == 1 && n > 1) { | ||||||||
| pft_family <- rep(pft_family, n) | ||||||||
| } | ||||||||
| if (length(pft_family) != n) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "length(pft_family) must equal n; got ", length(pft_family), " vs ", n) | ||||||||
| } | ||||||||
| wl <- PEcAn.data.land::ca_compost_material_whitelist | ||||||||
| out <- character(n) | ||||||||
| for (fam in unique(pft_family)) { | ||||||||
| allowed <- wl$material_class[wl$pft_family == fam] | ||||||||
| if (length(allowed) == 0) { | ||||||||
| PEcAn.logger::logger.severe( | ||||||||
| "No material whitelist for pft_family '", fam, "'. Supported: ", | ||||||||
| paste(unique(wl$pft_family), collapse = ", ")) | ||||||||
| } | ||||||||
| idx <- which(pft_family == fam) | ||||||||
| out[idx] <- sample(allowed, length(idx), replace = TRUE) | ||||||||
| } | ||||||||
| out | ||||||||
| } | ||||||||
|
|
||||||||
| # small null fallback helper. | ||||||||
| `%||%` <- function(x, y) if (is.null(x)) y else x | ||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #' Look up California orchard density (trees per acre) by crop | ||
| #' | ||
| #' Returns the median trees per acre value for a California orchard crop, | ||
| #' derived from the DEMETER cropland biomass dataset. DEMETER photo dates | ||
| #' span 2010 to 2016, so modern high density plantings may be under | ||
| #' represented. | ||
| #' | ||
| #' Supported crops: Almonds, Walnuts, Oranges, Pistachios. Other crops | ||
| #' return NA with a warning. | ||
| #' | ||
| #' @param crop character. Crop name (case insensitive). One of "Almonds", | ||
| #' "Walnuts", "Oranges", "Pistachios". | ||
| #' | ||
| #' @return integer scalar. Trees per acre for the requested crop. Returns | ||
| #' NA_integer_ with a warning if the crop is not supported. | ||
| #' | ||
| #' @source Kroodsma, D. A., & Field, C. B. (2006). Carbon sequestration | ||
| #' in California agriculture, 1980-2000. Ecological Applications, | ||
| #' 16(5), 1975-1985. | ||
| #' | ||
| #' @examples | ||
| #' tpa_lookup("Almonds") | ||
| #' tpa_lookup("walnuts") | ||
| #' | ||
| #' @export | ||
| tpa_lookup <- function(crop) { | ||
| # median trees per acre by crop from DEMETER. | ||
| # TODO: promote to a bundled age keyed dataset when per parcel | ||
| # orchard age becomes available. | ||
| medians <- c( | ||
| almonds = 80L, | ||
| walnuts = 41L, | ||
| oranges = 110L, | ||
| pistachios = 119L | ||
| ) | ||
| key <- tolower(crop) | ||
| if (!key %in% names(medians)) { | ||
| PEcAn.logger::logger.warn( | ||
| "No DEMETER TPA available for crop '", crop, "'. ", | ||
| "Supported: ", paste(names(medians), collapse = ", "), "." | ||
| ) | ||
| return(NA_integer_) | ||
| } | ||
| medians[[key]] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.