From 25d3ea9c82d84640d326b088c937952967e1ed2a Mon Sep 17 00:00:00 2001 From: Damian-Oswald Date: Tue, 19 Mar 2024 17:07:38 +0100 Subject: [PATCH] Start working on the summary table (#4) - Compute cumulative nitrification - Also: Render project with quarto 1.4 --- docs/data-preparation.html | 674 ++++++++++++----- docs/index.html | 247 ++++-- docs/introduction.html | 279 +++++-- docs/references.html | 244 ++++-- docs/results.html | 599 +++++++++++---- docs/running-the-model.html | 275 +++++-- docs/search.json | 159 +++- docs/sensitivity-analysis.html | 384 +++++++--- docs/site_libs/bootstrap/bootstrap-icons.css | 148 ++-- docs/site_libs/bootstrap/bootstrap-icons.woff | Bin 164168 -> 176200 bytes docs/site_libs/bootstrap/bootstrap.min.css | 12 +- docs/site_libs/bootstrap/bootstrap.min.js | 6 +- docs/site_libs/quarto-html/anchor.min.js | 6 +- docs/site_libs/quarto-html/popper.min.js | 4 +- docs/site_libs/quarto-html/quarto.js | 29 +- docs/site_libs/quarto-nav/quarto-nav.js | 22 +- .../quarto-search/autocomplete.umd.js | 4 +- docs/site_libs/quarto-search/quarto-search.js | 226 +++++- docs/uncertainty-analysis.html | 706 +++++++++++------- results.qmd | 85 ++- uncertainty-analysis.qmd | 2 +- 21 files changed, 3031 insertions(+), 1080 deletions(-) diff --git a/docs/data-preparation.html b/docs/data-preparation.html index 597ca5e..2d595df 100644 --- a/docs/data-preparation.html +++ b/docs/data-preparation.html @@ -2,7 +2,7 @@ - + @@ -22,7 +22,7 @@ } /* CSS for syntax highlighting */ pre > code.sourceCode { white-space: pre; position: relative; } -pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } +pre > code.sourceCode > span { line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } .sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } @@ -58,6 +58,7 @@ div.csl-bib-body { } div.csl-entry { clear: both; + margin-bottom: 0em; } .hanging-indent div.csl-entry { margin-left:2em; @@ -100,7 +101,13 @@ "collapse-after": 3, "panel-placement": "end", "type": "overlay", - "limit": 20, + "limit": 50, + "keyboard-shortcut": [ + "f", + "/", + "s" + ], + "show-item-context": false, "language": { "search-no-results-text": "No results", "search-matching-documents-text": "matching documents", @@ -109,12 +116,12 @@ "search-more-match-text": "more match in this document", "search-more-matches-text": "more matches in this document", "search-clear-button-title": "Clear", + "search-text-placeholder": "", "search-detached-cancel-button-title": "Cancel", "search-submit-button-title": "Submit", "search-label": "Search" } } - @@ -126,21 +133,48 @@ + +
-
-
@@ -268,32 +294,66 @@

3 

3.1 Description of the measurements

+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 9640 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 9640 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1248 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1248 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1117 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1117 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1117 rows.
+ℹ Did you mean to use `annotate()`?
+
+
+
Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 1117 rows.
+ℹ Did you mean to use `annotate()`?
+
- -
+
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +
ColumnPlot OverviewMissingMeanMedianSD
moisture 0.190.330.0%0.30.30.0
N2O 03087.1%3.31.34.9
SP -333788.4%8.07.79.2
d18O 236088.4%42.542.14.9
Source: This data was measured in Zürich for the Process Rate Estimator.
+++++++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ColumnPlot OverviewMissingMeanMedianSD
moisture + + + 0.190.33 +0.0%0.30.30.0
N2O + + + 030 +87.1%3.31.34.9
SP + + + -3337 +88.4%8.07.79.2
d18O + + + 2360 +88.4%42.542.14.9
Source: This data was measured in Zürich for the Process Rate Estimator.
+ +

The study uses data collected from a mesocosm experiment – i.e. an outdoor experiment that examines the natural environment under controlled conditions. The experiment was set up as a randomized complete block design, with 4 varieties and 3 replicates, using 12 non-weighted lysimeters. A non-weighted lysimeter is a device to measure the amount of water that drains through soil, and to determine the types and amounts of dissolved nutrients or contaminants in the water. Each lysimeter had five sampling ports with soil moisture probes and custom-built pore gas sample, at depths of 7.5, 30, 60, 90 and 120 cm below soil surface.

\[4 \times 3 \times 5 \times 161 = 9660 \tag{3.1}\]

-

Equation 3.1 shows how many observations we should expect to have. In reality, some observations are missing. Hence, the measurements data frame only includes 9558 rows, with 9 variables. Table 3.1 gives an overview of those variables.

+

Equation 3.1 shows how many observations we should expect to have. In reality, some observations are missing. Hence, the measurements data frame only includes 9558 rows, with 9 variables. Table 3.1 gives an overview of those variables.

-
+
+
+
+Table 3.1: Overview of the variables included in the measurements data as included in the R package PRE. +
+
-@@ -863,12 +969,14 @@

Accessing the measurement data in R

In the R package PRE, this data is available as the object measurements. We can load the data with the data function in R.

-
data("measurements", package = "PRE")
+
data("measurements", package = "PRE")

Upon doing so, a data frame called measurements will appear in our global environment. Alternatively, the data can be directly accessed as PRE::measurements (the prefix PRE:: tells R that the object can be found in the PRE package.)

You can see an interactive overview of the measurements data in the column below. For many dates, there are no measurements of \(\ce{N2O}\), site preference, or \(\delta^{18}\text{O}\).

@@ -876,9 +984,8 @@

Access
- -
- +
+

@@ -890,13 +997,13 @@

Access

3.2 Calculating volumetric and area N2O-N

In a first step, we are calculating the N2O-N per volume and subsequently per area. This is a necessary step, because the process rate estimator requires these variables.

Since both N2ONvolume as well as N2ONarea are dependent on initial parameters, their computation is part of the workflow and might change slightly for a changed experimental set-up or different assumptions regarding the environment.

-

The volumetric N2O-N is calculated from the N2O concentration (Equation 3.2).

+

The volumetric N2O-N is calculated from the N2O concentration (Equation 3.2).

\[\text N_2 \text {O-N}_\text{volume} = \frac{28[\text{N}_2\text O]}{\text {R} \cdot \text {T}} \tag{3.2}\]

Here, \(\text {R}\) is the gas constant and \(T\) is the temperature. In a next step, we calculate the per-area N2O-N from the volumetric N2O-N and the soil moisture as well as the total porosity and the increment in meters.

\[\text N_2 \text {O-N}_\text{area} = \text N_2 \text {O-N}_\text{volume} \times \frac{1}{100} \texttt{increment} \times \frac{10^4}{10^3} \left(\theta_t - \texttt{moisture} \right) \tag{3.3}\]

All of these equations are implemented in the getN2ON function. This function returns a data frame very similar to measurements, but with two extra columns for the computed N2ONvolume and N2ONarea variables.

-
data <- getN2ON(data = PRE::measurements, parameters = getParameters())
+
data <- getN2ON(data = PRE::measurements, parameters = getParameters())

Note that the getParameters function simply returns a list with the default values for all necessary parameters such as the gas constant \(\text R\) or the assumed air temperature \(\text T\). If we wish to change these, we can pass alternative parameter values to the function. For example, we could assume an air temperature of 300 K with getParameters(temperature = 300). This makes sure that depended parameter values are updated as well.

@@ -905,39 +1012,55 @@

The N2O concentration, site preference as well as \(\delta\)18O are estimated as a function of time for every depth and every column, separately. To achieve this function approximation, Kernel Regression as implemented in npreg is used (Hayfield and Racine 2008). The model requires a single parameter, the bandwidth (bws), which defines how flexible it is. However, the bandwidth needs to be actively chosen and cannot be intrinsically estimated – it’s a hypertuning parameter a.k.a. a hyperparameter.

Choosing optimal bandwidths for the interpolation

-

Figure 3.1 gives an intuitive understanding on why we don’t want to use the same bandwidth for every single combination of depth and column: In some instances we seem to have a very clear signal, while in others, there seems to be much noise – we are dealing with unequal variance, a.k.a. heteroskedasticity.

-
-
-

-
Figure 3.1: Animated explanation on two examplary subsets of the data. On the left, there is a very high sinal to noise ratio, thus the optimal bandwith hyperparameter will be smaller than on the right.
+

Figure 3.1 gives an intuitive understanding on why we don’t want to use the same bandwidth for every single combination of depth and column: In some instances we seem to have a very clear signal, while in others, there seems to be much noise – we are dealing with unequal variance, a.k.a. heteroskedasticity.

+
+
+
+ +
+
+Figure 3.1: Animated explanation on two examplary subsets of the data. On the left, there is a very high sinal to noise ratio, thus the optimal bandwith hyperparameter will be smaller than on the right. +
-

The bandwidth hyperparameter is individually tuned using 3-fold 10 times repeated cross-validation for every combination of column and depth and variable1, respectively. You can see an overview of the found hyperparameters in figure 3.2, 3.3, and 3.4.

  • 1 I.e. the three variables N2O concentration, site preference and \(\delta\)18O.

  • -
    +

    The bandwidth hyperparameter is individually tuned using 3-fold 10 times repeated cross-validation for every combination of column and depth and variable1, respectively. You can see an overview of the found hyperparameters in figure 3.2, 3.3, and 3.4.

    +

    1 I.e. the three variables N2O concentration, site preference and \(\delta\)18O.

    -
    -
    -

    -
    Figure 3.2: Optimal hyperparameter size by depth and column for the N2O-N concentration.
    +
    +
    +
    + +
    +
    +Figure 3.2: Optimal hyperparameter size by depth and column for the N2O-N concentration. +
    -
    -
    -

    -
    Figure 3.3: Optimal hyperparameter size by depth and column for the site preference.
    +
    +
    +
    + +
    +
    +Figure 3.3: Optimal hyperparameter size by depth and column for the site preference. +
    -
    -
    -

    -
    Figure 3.4: Optimal hyperparameter size by depth and column for the stable isotope ratio \(\delta\)18O concentration.
    +
    +
    +
    + +
    +
    +Figure 3.4: Optimal hyperparameter size by depth and column for the stable isotope ratio \(\delta\)18O concentration. +
    @@ -954,18 +1077,22 @@

    \(h\) is a small positive number known as the step size. The notations \(f(t + h)\) and \(f(t - h)\) represent the function values at \(t + h\) and \(t - h\), respectively. The smaller h is, the more accurate the approximation should be. However, if h is too small, then round-off errors from computer arithmetic can become significant. So, an appropriate balance must be struck. The central difference method is implemented as the fderiv function in the pracma R package.

    Both the function estimation as well as the estimation of the derivative are performed with a single function call in the R package PRE.

    -
    data <- getMissing(data = data, hyperparameters = PRE::hyperparameters)
    +
    data <- getMissing(data = data, hyperparameters = PRE::hyperparameters)
    -

    Here, data is the data frame that we slightly extended with the getN2ON function. The object hyperparameters is a three-dimensional array containing the optimized hyperparameters for this project. It is also included in the PRE package2.

  • 2 To optimize the hyperparameters, you can run the R script hypertuning.R.

  • -

    +

    Here, data is the data frame that we slightly extended with the getN2ON function. The object hyperparameters is a three-dimensional array containing the optimized hyperparameters for this project. It is also included in the PRE package2.

    +

    2 To optimize the hyperparameters, you can run the R script hypertuning.R.

    3.4 Calculating the fluxes

    Model parameters

    -
    +
    +
    +
    +Table 3.2: Overview of the parameters used in the model. +
    +

    Table 3.1: Overview of the variables included in the measurements data as included in the R package PRE.
    -@@ -1022,28 +1149,28 @@

    Model parameters

    - + - + - + - + @@ -1057,7 +1184,7 @@

    Model parameters

    - + @@ -1070,16 +1197,18 @@

    Model parameters

    Table 3.2: Overview of the parameters used in the model.
    \(D_{\text{s}}\) D_s Gas diffusion coefficientEquation 3.6Equation 3.6 m2s-1
    \(D_{\text{fw}}\) D_fw Diffusivity of N2O in waterEquation 3.8Equation 3.8
    \(D_{\text{fa}}\) D_fa Diffusivity of N2O in airEquation 3.9Equation 3.9
    \(D_{\text{fa,NTP}}\) Free air diffusion coefficient under standard conditionsEquation 3.9Equation 3.9
    \(H\) H Dimensionless Henry’s solubility constantEquation 3.7Equation 3.7
    -

    The diffusion fluxes between soil increments are described by Frick’s law (Equation 3.5).

    +
    +
    +

    The diffusion fluxes between soil increments are described by Frick’s law (Equation 3.5).

    \[F_{\text{calc}} = \frac{dC}{dZ} D_{\text s} \rho \tag{3.5}\]

    Here, \(D_s\) is the gas diffusion coefficient, \(\rho\) is the gas density of N2O, and \(\frac{dC}{dZ}\) is the N2O concentration gradient from lower to upper depth. The fluxes are calculated based on N2O concentration gradients between 105-135 cm, 75-105 cm, 45-75 cm, 15-45 cm, and 0-15 cm depth layers, and ambient air above the soil surface.

    \(\theta_w\) is the soil volumetric water content, \(\theta_a\) the air-filled porosity, and \(\theta_T\) is the total soil porosity.

    -

    The gas diffusion coefficient \(D_{\text s}\) was calculated according Equation 3.6 as established by Millington and Quirk in 1961 (Millington and Quirk 1961).

    +

    The gas diffusion coefficient \(D_{\text s}\) was calculated according Equation 3.6 as established by Millington and Quirk in 1961 (Millington and Quirk 1961).

    \[D_{\text s} = \left( \frac{\theta_w^{\frac{10}{3}} + D_{\text fw}}{H} + \theta_a^{\frac{10}{3}} \times D_{\text fa} \right) \times \theta_T^{-2} \tag{3.6}\]

    Here, \(H\) represents a dimensionless form of Henry’s solubility constant (\(H'\)) for N2O in water at a given temperature. The constant \(H\) for N2O is calculated as follows:

    \[H = \frac{8.5470 \times 10^5 \times \exp \frac{-2284}{\text T}}{\text R \times \text T} \tag{3.7}\]

    Here, \(\text R\) is the gas constant, and \(\text T\) is the temperature (\(\text T = 298 \; \text K\)).

    -

    \(D_{\text{fw}}\) was calculated according to Equation 3.8 as documented by Versteeg and Van Swaaij (1988) (Versteeg and Van Swaaij 1988).

    +

    \(D_{\text{fw}}\) was calculated according to Equation 3.8 as documented by Versteeg and Van Swaaij (1988) (Versteeg and Van Swaaij 1988).

    \[D_{\text{fw}} = 5.07 \times 10^{-6} \times \exp \frac{-2371}{\text T} \tag{3.8}\]

    \[D_{\text{fa}} = D_{\text{fa, NTP}} \times \left( \frac{\text T}{273.15} \right)^n \times \left( \frac{101'325}{\text P} \right) \tag{3.9}\]

    Calculate mean moisture between depth increments:

    @@ -1089,7 +1218,7 @@

    Model parameters

    Where \(m\) is the moisture and \(d\) is the depth index.

    -

    interactive: true, interactiveBorder: 10, theme: 'quarto', - placement: 'bottom-start' + placement: 'bottom-start', }; + if (contentFn) { + config.content = contentFn; + } + if (onTriggerFn) { + config.onTrigger = onTriggerFn; + } + if (onUntriggerFn) { + config.onUntrigger = onUntriggerFn; + } window.tippy(el, config); } const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]'); @@ -1213,7 +1368,130 @@

    Model parameters

    try { href = new URL(href).hash; } catch {} const id = href.replace(/^#\/?/, ""); const note = window.document.getElementById(id); - return note.innerHTML; + if (note) { + return note.innerHTML; + } else { + return ""; + } + }); + } + const xrefs = window.document.querySelectorAll('a.quarto-xref'); + const processXRef = (id, note) => { + // Strip column container classes + const stripColumnClz = (el) => { + el.classList.remove("page-full", "page-columns"); + if (el.children) { + for (const child of el.children) { + stripColumnClz(child); + } + } + } + stripColumnClz(note) + if (id === null || id.startsWith('sec-')) { + // Special case sections, only their first couple elements + const container = document.createElement("div"); + if (note.children && note.children.length > 2) { + container.appendChild(note.children[0].cloneNode(true)); + for (let i = 1; i < note.children.length; i++) { + const child = note.children[i]; + if (child.tagName === "P" && child.innerText === "") { + continue; + } else { + container.appendChild(child.cloneNode(true)); + break; + } + } + if (window.Quarto?.typesetMath) { + window.Quarto.typesetMath(container); + } + return container.innerHTML + } else { + if (window.Quarto?.typesetMath) { + window.Quarto.typesetMath(note); + } + return note.innerHTML; + } + } else { + // Remove any anchor links if they are present + const anchorLink = note.querySelector('a.anchorjs-link'); + if (anchorLink) { + anchorLink.remove(); + } + if (window.Quarto?.typesetMath) { + window.Quarto.typesetMath(note); + } + // TODO in 1.5, we should make sure this works without a callout special case + if (note.classList.contains("callout")) { + return note.outerHTML; + } else { + return note.innerHTML; + } + } + } + for (var i=0; i res.text()) + .then(html => { + const parser = new DOMParser(); + const htmlDoc = parser.parseFromString(html, "text/html"); + const note = htmlDoc.getElementById(id); + if (note !== null) { + const html = processXRef(id, note); + instance.setContent(html); + } + }).finally(() => { + instance.enable(); + instance.show(); + }); + } + } else { + // See if we can fetch a full url (with no hash to target) + // This is a special case and we should probably do some content thinning / targeting + fetch(url) + .then(res => res.text()) + .then(html => { + const parser = new DOMParser(); + const htmlDoc = parser.parseFromString(html, "text/html"); + const note = htmlDoc.querySelector('main.content'); + if (note !== null) { + // This should only happen for chapter cross references + // (since there is no id in the URL) + // remove the first header + if (note.children.length > 0 && note.children[0].tagName === "HEADER") { + note.children[0].remove(); + } + const html = processXRef(null, note); + instance.setContent(html); + } + }).finally(() => { + instance.enable(); + instance.show(); + }); + } + }, function(instance) { }); } let selectedAnnoteEl; @@ -1257,6 +1535,7 @@

    Model parameters

    } div.style.top = top - 2 + "px"; div.style.height = height + 4 + "px"; + div.style.left = 0; let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter"); if (gutterDiv === null) { gutterDiv = window.document.createElement("div"); @@ -1282,8 +1561,8 @@

    Model parameters

    }); selectedAnnoteEl = undefined; }; - // Handle positioning of the toggle - window.addEventListener( + // Handle positioning of the toggle + window.addEventListener( "resize", throttle(() => { elRect = undefined; @@ -1291,23 +1570,23 @@

    Model parameters

    selectCodeLines(selectedAnnoteEl); } }, 10) - ); - function throttle(fn, ms) { - let throttle = false; - let timer; - return (...args) => { - if(!throttle) { // first call gets through + ); + function throttle(fn, ms) { + let throttle = false; + let timer; + return (...args) => { + if(!throttle) { // first call gets through + fn.apply(this, args); + throttle = true; + } else { // all the others get throttled + if(timer) clearTimeout(timer); // cancel #2 + timer = setTimeout(() => { fn.apply(this, args); - throttle = true; - } else { // all the others get throttled - if(timer) clearTimeout(timer); // cancel #2 - timer = setTimeout(() => { - fn.apply(this, args); - timer = throttle = false; - }, ms); - } - }; - } + timer = throttle = false; + }, ms); + } + }; + } const annoteTargets = window.document.querySelectorAll('.code-annotation-anchor'); for (let i=0; iModel parameters options: { flipVariations: false, // true by default allowedAutoPlacements: ['right'], - fallbackPlacements: ['right', 'top', 'top-start', 'top-end'], + fallbackPlacements: ['right', 'top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left'], }, }, { @@ -1408,12 +1687,12 @@

    Model parameters