User Guide
Installation and usage
Installation
First, download and install Julia: https://julialang.org/downloads/. If you like you can also install julia-vscode.org to get a complete IDE. Both these steps are nicely summarized by https://techytok.com/julia-vscode/. ALternatively, you can also follow the official documentation of the VS Code Julia: https://www.julia-vscode.org/docs/dev/gettingstarted/#Installation-and-Configuration-1.
To install LWFBrook90.jl open a Julia REPL, enter the Pkg REPL by pressing ] and add the package:
(@v1.7) pkg> add LWFBrook90
(@v1.7) pkg> statusDependencies of LWFBrook90.jl should automatically be installed. After that hit Ctrl-C to quit the Pkg REPL and return to the default Julia REPL. Now the installation can be tested with a single line using LWFBrook90; run_example().
Usage
Check out a step-by-step guide for a simulation in section Example Script 01
The steps in a typical simulation script are:
- load the package
using LWFBrook90 - read input data
model = loadSPAC()- optional arguments in
loadSPAC()can be used to define model parameters (-> type?loadSPACto see documentation)
- optional arguments in
- pre-process model for simulation
simulation = setup(model) - compute simulation
simulate!(simulation) - post-process and plot simulation results,
get_fluxes(simulation),get_states(simulation),get_forcing(simulation)
LWFBrook90.jl outputs quantities in daily resolution. Monthly or yearly quantities can be derived in the post processing. ~~For performance reasons, e.g. for Bayesian parameter estimation, computation of these additional quantities can also be deactivated during simulation, and they could be calculated in a post-processing step from the state vector.~~
Below is a code snippet providing a quick start. For more details refer to examples/example-script-01.jl.
# load problem defintion as SPAC object
input_path = "../../../examples/DAV2020-full/"; input_prefix = "DAV2020-full"; # make sure to get the path correct relative to `pwd()``
model = loadSPAC(input_path, input_prefix; simulate_isotopes = true);
# transform into a system of ODEs (space discretization etc...)
simulation = setup(model)
# run simulation
simulate!(simulation)
# Get simulation output (states, fluxes, forcing) as DataFrames
get_states(simulation)
get_fluxes(simulation)
get_forcing(simulation)
using Plots, Measures; gr();
pl2 = plotisotopes(simulation, :d18O, (d18O = :auto, d2H = :auto), :showRWUcentroid)
pl2
pl1 = plotamounts(simulation, :above_and_belowground, :showRWUcentroid)
pl1
pl3 = plotforcingandstates(simulation)
pl3Soil evaporation depth and isotope fractionation
Soil evaporation has two independent controls: the depth from which water is removed, and whether the evaporated vapor fractionates relative to the soil water.
| Setting | Where to set it | Default | Effect |
|---|---|---|---|
SLVPDEPTH_m | param.csv, or remakeSPAC(...; params = (...,)) | 0.0 m | Depth of the soil evaporation source zone, positive downward. |
simulate_evaporation_fractionation | loadSPAC, or remakeSPAC(...; solver_options = (...,)) | false | Enables Craig–Gordon fractionation of soil evaporation when simulate_isotopes = true. |
Choosing the evaporation depth
The default SLVPDEPTH_m = 0.0 draws evaporation from the uppermost input soil layer. A value smaller than this layer's thickness also uses its full thickness. For example, with a 3 cm surface layer, both 0.0 and 0.01 use the top 3 cm; 0.10 uses the top 10 cm. The requested depth must fit within the soil profile.
For a deeper source zone, the discretization is refined at its lower boundary when needed. Evaporation is apportioned by layer thickness within this zone. Thus a 3 cm layer and a 7 cm layer in a 10 cm source zone provide 30% and 70% of the soil evaporation flux, respectively. Layers below the zone do not directly supply soil evaporation, although water and isotopes can still move between layers. Use a sufficiently fine surface grid to resolve the enrichment pattern of interest.
Enabling evaporation fractionation
With simulate_evaporation_fractionation = false, the evaporating flux has the same isotopic composition as the soil water in each contributing layer. Soil isotope values can still change through precipitation, transport, and mixing.
With simulate_evaporation_fractionation = true, the model computes the evaporated vapor's δ¹⁸O and δ²H using Craig–Gordon fractionation for each contributing layer. The calculation uses daytime air temperature, atmospheric humidity, and a kinetic fractionation exponent that varies with local soil moisture. Atmospheric vapor is assumed to be in isotopic equilibrium with the precipitation forcing; a separate measured vapor-isotope time series is not used. The option changes the isotopic composition of the evaporation flux; water loss is calculated by the hydrologic model. It applies to soil evaporation, rather than being a general switch for fractionation in interception or snow stores.
For a model loaded with simulate_isotopes = true, configure and run a simulation as follows. remakeSPAC returns a simulation ready for simulate!, so another setup call is unnecessary.
simulation = remakeSPAC(model;
params = (SLVPDEPTH_m = 0.10,),
solver_options = (simulate_evaporation_fractionation = true,),
requested_tspan = model.tspan)
simulate!(simulation; save_everystep = false,
saveat = range(model.tspan...; step = 1.0))
# Postprocessing depths are in millimeters, unlike SLVPDEPTH_m.
soil_isotopes = get_soil_([:d18O, :d2H], simulation;
depths_to_read_out_mm = [15, 65, 150, 250])Alternatively, enable fractionation while loading the model:
model = loadSPAC(input_path, input_prefix;
simulate_isotopes = true,
simulate_evaporation_fractionation = true)Set SLVPDEPTH_m in param.csv, or change it afterward with remakeSPAC. Existing parameter files that omit it use the default 0.0 m.
Comparing the effects
Example Script 02 compares three simulations with identical forcing and initial conditions: 10 cm without fractionation, 10 cm with fractionation, and 30 cm with fractionation. The first pair isolates the fractionation option; the second pair isolates the evaporation depth. Heatmaps use a shared color scale for each isotope. Install Plots in the active Julia environment to run the plotting example.
During drying, a shallow source zone can enrich more rapidly near the surface, while a deeper source zone spreads the evaporative loss and enrichment over more soil. Rainfall, transport, and mixing can interrupt this pattern, so the meteorological example need not show monotonic enrichment. The default combination of 0.0 m and false preserves the previous soil-evaporation configuration.
Input data
Overview of input data
To run a simulation following input files are expected in a single folder
soil_horizons.csv- containing the hydraulic parameters of the different soil horizonsparam.csv- containing scalar model parametersmeteoveg.csv- containing daily values of meteorologic variables [and stand properties]meteoiso.csv- containing isotopic signatures of aggregate precipitation samplesinitial_conditions.csv- containing initial conditions of scalar state variablessoil_discretization.csv- containing the initial conditions of the soil water status in the form of the soil matric potential (kPa) and the initial isotopic signatures (vector state variables); parameters of relative root density distributions defined for the soil domain; as well as the definition of the numerical discretization of the soil domain (nodes with upper and lower limits).meteo_storm_durations.csv- containing parameters of sub-daily storm/precipitation event patterns for each month
Of these only the CSV files for the parameters soil_horizons.csv and param.csv and the forcings meteoveg.csv, meteoiso.csv are absolutely needed. The remaing can be provided by the user as arguments to loadSPAC() in the Julia script:
soil_discretizations.csv: not needed ifΔz_thickness_m,root_distribution, andIC_soilprovided.initial_conditions.csv: not needed ifIC_scalarprovided.meteo_storm_durations.csv: not needed ifstorm_durations_hprovided.meteoveg.csv: columns with vegetation parameters are not needed ifloadSPAC(canopy_evolution = ...)is provided.
The structure of the input CSV's is illustrated by the example input data sets isoBEA2010-18-* or DAV2020-bare-minimum or DAV2020-full located in the folder examples/, as well as below in this documentation. Please follow these examples closely when generating your own input files, including the exact column names and header lines containing the units.
As a special case, below canopy irrigation was implemented, avoiding interception losses. The example input data set DAV2020-irrigation illustrates the required inputs.
For convenience, input CSV files can be generated from a script that sets up a simulation with the R package LWFBrook90R (v0.4.3). Instead of running the simulation with run_LWFB90(), the same arguments can be used to generate the input files for LWFBrook90.jl using the R function provided in the file generate_LWFBrook90jl_Input.R. Note that the input file meteoiso.csv needs to be generated separately and the files containing the initial conditions (initial_conditions.csv and soil_discretization.csv) also need to be extended manually with the isotope values (see structure of these input files below).
To load input data and prepare a simulation follow the instructions in section Example Script 01 or alternatively use the sample script main_with_isotopes.jl. NOTE: these will be replaced with Jupyter-notebooks generated with Literate.jl.
In case you're unfamiliar with Julia, there are various ways to run a script such as main.jl: One possibility is to open the Julia REPL and run the script using include(“main.jl”). Alternatively, the editor VS Code in combination with the Julia extension (julia-vscode.org), provides a complete IDE for programming in Julia.
Structure of input data
soil_horizons.csv: (when using Mualem-VanGenuchten parametrization of the soil retention curve):
| HorizonNr | Upper_m | Lower_m | ths_volFrac | thr_volFrac | alpha_perMeter | npar_ | ksat_mmDay | tort_ | gravel_volFrac |
|---|---|---|---|---|---|---|---|---|---|
| - | m | m | volume fraction (-) | volume fraction (-) | perMeter | - | mm per day | - | volume fraction (-) |
| 1 | 0 | -0.04 | 0.7149 | 0.069 | 1147.88919 | 1.05123 | 24864.67839 | 4.67037 | 0.01 |
| 2 | -0.04 | -0.08 | 0.66873 | 0.069 | 1274.88602 | 1.05105 | 12881.48641 | 4.47828 | 0.175 |
| 3 | -0.08 | -0.2 | 0.6561 | 0.069 | 1215.92721 | 1.05106 | 10516.6166 | 4.50162 | 0.175 |
| 4 | -0.2 | -0.45 | 0.60159 | 0.069 | 795.20401 | 1.05099 | 3438.06275 | 4.34489 | 0.175 |
| 5 | -0.45 | -0.75 | 0.52331 | 0.069 | 352.36825 | 1.05097 | 450.35802 | 4.29122 | 0.01 |
| 6 | -0.75 | -1.1 | 0.56472 | 0.069 | 570.68168 | 1.05111 | 1488.20958 | 5.14818 | 0.01 |
| 7 | -1.1 | -1.2 | 0.46743 | 0.069 | 164.564 | 1.05103 | 67.97846 | 0.01 | 0.95 |
meteoveg.csv: contains time dependent parameters (meterologic variables and vegetation parameters):
| dates | globrad_MJDayM2 | tmax_degC | tmin_degC | vappres_kPa | windspeed_ms | prec_mmDay | densef_percent | height_percent | lai_percent | sai_percent |
|---|---|---|---|---|---|---|---|---|---|---|
| YYYY-MM-DD | MJ/Day/m2 | degree C | degree C | kPa | m per s | mm per day | percent | percent | percent | percent |
| 2010-01-01 | 3.45 | 1.85 | -6.39 | 0.42 | 2.87 | 0.2 | 100 | 100 | 60 | 100 |
| 2010-01-02 | 7.26 | -6.98 | -14.09 | 0.19 | 3.6 | 0 | 100 | 100 | 60 | 100 |
| 2010-01-03 | 5.21 | -1.84 | -13.02 | 0.12 | 1.86 | 0 | 100 | 100 | 60 | 100 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2018-12-29 | 6.49 | 8.15 | 0.3 | 0.28 | 0.1 | 0 | 100 | 100 | 60 | 100 |
| 2018-12-30 | 2.83 | 3.38 | -1.61 | 0.39 | 0.1 | 0 | 100 | 100 | 60 | 100 |
| 2018-12-31 | 4.62 | 6.34 | -0.94 | 0.55 | 0.1 | 0.4 | 100 | 100 | 60 | 100 |
If meteoveg.csv does not contain columns for DENSEF, HEIGHT, LAI, SAI, the parametrization of these should be provided to the function loadSPAC().
meteoiso.csv: contains time dependent isotopic signatures of precipiation. (Note that the dates contain the end dates of the collection interval of cumulative isotope samples. The values are backward interpolated with piecewise constants. The first row containng the NA is assumed to be the start date of the first collection interval.):
| dates | delta18O_permil | delta2H_permil |
|---|---|---|
| YYYY-MM-DD | permil | permil |
| 2010-01-01 | NA | NA |
| 2010-02-01 | -15.9 | -118 |
| 2010-03-01 | -14.5 | -107 |
| ... | ... | ... |
| 2021-09-30 | -13.8 | -97.67 |
| 2021-10-14 | -10.7 | -69.1 |
| 2021-10-28 | -9.13 | -58.4 |
meteo_storm_durations.csv:
| month | average_storm_duration_h |
|---|---|
| ### Typical average durations of a single storm event for each month ——- | NA |
| January | 4 |
| Februrary | 4 |
| March | 4 |
| April | 4 |
| May | 4 |
| June | 4 |
| July | 4 |
| August | 4 |
| September | 4 |
| October | 4 |
| November | 4 |
| December | 4 |
initial_conditions.csv:
| param_id | amount | u_delta18O_init_permil | u_delta2H_init_permil |
|---|---|---|---|
| ### Initial conditions (of vector states) ——- | NA | NA | NA |
| u_GWAT_init_mm | 1 | -13 | -95 |
| u_INTS_init_mm | 0 | -13 | -95 |
| u_INTR_init_mm | 0 | -13 | -95 |
| u_SNOW_init_mm | 0 | -13 | -95 |
| u_CC_init_MJ_per_m2 | 0 | NA | NA |
| u_SNOWLQ_init_mm | 0 | NA | NA |
soil_discretization.csv contains the initial conditions of the soil water status, root density distributions, and the definition of the numerical discretization of the soil domain (nodes with upper and lower limits):
| Upper_m | Lower_m | Rootden_ | uAux_PSIM_init_kPa | u_delta18O_init_permil | u_delta2H_init_permil |
|---|---|---|---|---|---|
| m | m | - | kPa | permil | permil |
| 0 | -0.04 | 0.02868 | -6.3 | -13 | -95 |
| -0.04 | -0.08 | 0.02539 | -6.3 | -13 | -95 |
| -0.08 | -0.2 | 0.02 | -6.3 | -13 | -95 |
| -0.2 | -0.45 | 0.01159 | -6.3 | -10 | -70 |
| -0.45 | -0.75 | 0.00507 | -6.3 | -10 | -70 |
| -0.75 | -1.1 | 0.00191 | -6.3 | -10 | -70 |
| -1.1 | -1.2 | 0.00092 | -6.3 | -10 | -70 |
param.csv contains scalar model parameters:
| param_id | x |
|---|---|
| ### Isotope transport parameters ——- | NA |
| ### TODO | 42.000 |
| ### TODO2 | 42.000 |
| VXYLEM_mm | 20 |
| DISPERSIVITY_mm | 40 |
| ### Meteorologic site parameters ——- | NA |
| LAT_DEG | 46.70052 |
| ESLOPE_DEG | 18.26 |
| ASPECT_DEG | 225 |
| ALB | 0.2 |
| ALBSN | 0.5 |
| C1 | 0.25 |
| C2 | 0.5 |
| C3 | 0.2 |
| WNDRAT | 0.3 |
| FETCH | 5000 |
| Z0W | 0.005 |
| ZW | 2 |
| ### Canopy parameters ——- | NA |
| MAXLAI | 2.9299999 |
| DENSEF_baseline_ | 1 |
| SAI_baseline_ | 1 |
| AGE_baseline_yrs | 200 |
| HEIGHT_baseline_m | 23.0 |
| LWIDTH | 0.1 |
| Z0G | 0.00325 |
| Z0S | 0.001 |
| LPC | 4 |
| CZS | 0.13 |
| CZR | 0.05 |
| HS | 1 |
| HR | 10 |
| ZMINH | 2 |
| RHOTP | 2 |
| NN | 2.5 |
| ### Interception parameters ——- | NA |
| FRINTLAI | 0.06 |
| FSINTLAI | 0.04 |
| FRINTSAI | 0.06 |
| FSINTSAI | 0.04 |
| CINTRL | 0.15 |
| CINTRS | 0.15 |
| CINTSL | 0.6 |
| CINTSS | 0.6 |
| RSTEMP | -0.5 |
| ### Snowpack parameters ——- | NA |
| MELFAC | 1.5 |
| CCFAC | 0.3 |
| LAIMLT | 0.2 |
| SAIMLT | 0.5 |
| GRDMLT | 0.35 |
| MAXLQF | 0.05 |
| KSNVP | 0.3 |
| SNODEN | 0.3 |
| ### Leaf evaporation parameters (affecting PE) ——- | NA |
| GLMAX | 0.0053 |
| GLMIN | 0.0003 |
| CR | 0.5 |
| RM | 1000 |
| R5 | 100 |
| CVPD | 2 |
| TL | 0 |
| T1 | 10 |
| T2 | 30 |
| TH | 40 |
| ### Plant parameters (affecting soil-water supply) ——- | NA |
| MXKPL | 8 |
| MXRTLN | 3000 |
| INITRLEN | 12 |
| INITRDEP | 0.25 |
| RGRORATE | 0.03 |
| RGROPER | 30 |
| FXYLEM | 0.5 |
| PSICR | -2 |
| RTRAD | 0.35 |
| NOOUTF | 1 |
| ### Soil parameters ——- | NA |
| IDEPTH_m | 0.040 |
| QDEPTH_m | 0.0 |
| RSSA | 100 |
| RSSB | 1 |
| INFEXP | 0 |
| BYPAR | 0 |
| QFPAR | 1 |
| QFFC | 0 |
| IMPERV | 0 |
| DSLOPE | 0 |
| LENGTH_SLOPE | 200 |
| DRAIN | 1 |
| GSC | 0 |
| GSP | 0 |
| ### Numerical solver parameters ——- | NA |
| DTIMAX | 0.5 |
| DSWMAX | 0.05 |
| DPSIMAX | 0.0005 |
Calibration data (calibration not yet implemented)
Roadmap to include calibration data intends include of:
- Throughfall amounts (for parametrisation of interception)
- Soil moisture (volumetric water content, θ)
- Soil matric potential (ψ)
as well as
- Isotopic composition of soil water (δ_soil)
- Isotopic composition of xylem water (δ_xylem)
Below the example structure of data sets for psi.csv contains the soil matric potential (ψ) of different sensor series or as site average:
| dates | depth_cm | depthnominalcm | psi_kPa | series |
|---|---|---|---|---|
| YYYY-MM-DD | cm | cm | kPa | SITEAVG |
| 2022-08-30 | 15 | 15 | -5.77 | SITEAVG |
| 2022-08-30 | 200 | 150 | -2.87 | SITEAVG |
| 2022-08-30 | 300 | 150 | -0.10 | SITEAVG |
| 2022-08-30 | 50 | 50 | -1.98 | SITEAVG |
| 2022-08-31 | 80 | 80 | -0.13 | SITEAVG |
| 2022-08-31 | 15 | 15 | -7.53 | SITEAVG |
| 2022-08-31 | 200 | 150 | -2.95 | SITEAVG |
| 2022-08-31 | 300 | 150 | -0.10 | SITEAVG |
| 2021-06-17 | 50 | 50 | -3.00 | SITEAVG |
| 2021-06-17 | 80 | 80 | -0.17 | SITEAVG |
| ... | ... | ... | ... | ... |
theta.csv contains the soil moisture (volumetric water content, θ):
| dates | depth_m | theta_m3m3 |
|---|---|---|
| YYYY-MM-DD | m | m3/m3 |
| 2020-12-23 | 0.15 | 0.24 |
| 2020-12-23 | 0.50 | 0.32 |
| 2020-12-23 | 0.80 | 0.30 |
| 2020-12-24 | 0.15 | 0.24 |
| 2020-12-24 | 0.50 | 0.32 |
| ... | ... | ... |
delta_soil.csv contains the isotopic signature of soil water:
| dates | depth_m | delta18O_permil | delta2h_permil |
|---|---|---|---|
| YYYY-MM-DD | cm | permil | permil |
| 2020-07-07 | 0 | -5.89 | -35.96 |
| 2020-07-07 | 15 | -8.75 | -57.99 |
| 2020-07-07 | 50 | -8.81 | -59.12 |
| 2020-07-07 | 80 | -9.72 | -65.41 |
| 2020-07-21 | 0 | -5.83 | -29.54 |
| 2020-07-21 | 15 | -8.73 | -53.58 |
| 2020-07-21 | 50 | -9.31 | -58.47 |
| 2020-07-21 | 80 | -10.14 | -64.71 |
| ... | ... | ... | ... |
delta_xylem.csv contains the isotopic signature of xylem water:
| dates | species | treeID | delta18O_permil | delta2H_permil |
|---|---|---|---|---|
| YYYY-MM-DD | - | - | permil | permil |
| 2021-08-03 | Beech | 271 | -8.416 | -72.562 |
| 2021-08-03 | Beech | 3518 | -8.946 | -76.543 |
| 2021-08-03 | Beech | 3519 | -8.484 | -72.523 |
| 2021-08-17 | Beech | 271 | -8.378 | -76.044 |
| 2021-08-17 | Beech | 3518 | -8.769 | -76.036 |
| 2021-08-17 | Beech | 3519 | -8.135 | -70.475 |
| ... | ... | ... | ... | ... |