Figure 1: Animation
representing simulated solute transport, made with the
gganimate
R package. For the R code
to
make this animation and some background information,
go to https://ratey-atuwa.github.io/cybloRg/pulse.html.
This guide should enable you to create a working simulation of solute transport using Hydrus-1D.
Start by opening the Project Manager
, and click the
New
button:
In the New Project
dialog box, type the name (and
optional description), then click OK
:
This should open the input dialog
(the sub-window name
will be the name you gave it above under New Project
).
Pre-processing
pane
(left) to open them, which allows you to change input parameters.Post-processing
) will
have items in it after a Hydrus-1D project is runDouble-clicking on Main Processes
gives a new dialog box
– you should make sure that you check the ☑Solute Transport
checkbox:
You can just keep clicking the Next
button to get to
each part of the input set-up:
(Remember you can click the Help
button for assistance on
any screen!)
The instructions below get you a very basic simulation with a single substrate ("soil") material, reacting with a single solute. The reaction is defined by simple partitioning using a Kd value, and equilibrium is achieved instantaneously.
Geometry Information
Length Units
to desired valueDecline from Vertical...
of ≤ 0.1Depth
to desired value – this represents our flow
path length, so choose something realistic for groundwater!
(e.g. if your units are cm, Depth needs to be ≥ 1000)Time Information
You may need to return to the Time Information
dialog
later depending on the output of your initial simulation.
Print Information
Select Print Times...
and click
Default
(you can try the other settings later)
Iteration Criteria
Soil Hydraulic Model
Water Flow Parameters
Soil Catalog
Qr
= Residual soil water content,
qrQs
= Saturated soil water content,
qsAlpha
= Parameter a in the soil water retention
function [L−1]n
= Parameter n in the soil water retention
functionKs
= Saturated hydraulic conductivity,
Ks [LT−1]l
= Tortuosity parameter in the conductivity function
[-]The individual values can be set manually too.
Water Flow Boundary Conditions
Solute Transport
Mass Units
are mmol, but we recommend
changing to g.Number of Solutes
= 1Pulse Duration
to be a fraction (e.g.
≤ 0.1 × the Final Time
set earlier)
Solute Transport Parameters
Soil Specific Parameters
:Bulk.d.
= Bulk density, r
[ML−3]
Disp
= Longitudinal dispersivity, DL
[L]
Frac
= ...fraction of adsorption sites with instantaneous
sorption...; Set = 1 for equilibrium transport
ThIm
= Immobile water content. Set equal to 0 when
physical nonequilibrium is not considered.
Solute Specific Parameters
:Diffus.W
= diffusion coefficient in water,
Dw [L²T−1]
Diffus.G
= diffusion coefficient in soil air,
Da [L²T−1]
Solute Transport and Reaction Parameters
Kd
> 0 for reactive
transport (leave Kd
= 0 for non-reactive)Alpha
) parameterHelp
button for details!
Solute Transport Boundary Conditions
Bound. Cond
> 0 for each solute
(Sol. No.
) – this is the initial solute concentration, so
it can`t be zero!
Dimensions
Help
button for details of the other
options.
Do you want to run PROFILE application ?
Profile Information
dialogClick Pressure Head...
, then click the
Edit Condition
button, and select the whole column by
clicking top then bottom, then set Top
and
Bottom
value to 0 (zero) i.e. saturated
with water
You can also set other soil parameters, e.g. material types and observation points – see the video!
Make sure you click save 💾 (you might need to press the
[esc
] key first), check the soil profile table display,
then you are ready to run Hydrus – see the video!
👉 When the
Profile Information
window is saved and closed, the
Soil Profile Summary
will open automatically.
Soil Profile Summary
This is a good opportunity to check that the profile is set up the way you want it to be.
h [m]
column should
be zero (you might have h [cm]
depending on your length
units)Mat
column to check that all soil
materials are at the depth/distance (z [m]
) that you want
them to beSoil Profile Summary
tableWhen you click Next
in the
Soil Profile Summary
, the Hydrus-1D program will ask
Do you want to run HYDRUS-1D application ?
Usually you do,
so click OK
.
Look for the text
Calculations have finished successfully.
If it's there,
press Enter or just close the window.
After a successful Hydrus-1D simulation run, you can use the options in the post-processing window to visualise the output within Hydrus-1D. Alternatively, you can read the data file(s) into Excel® or R and make better graphs!
For many applications we would want to include more complexity into our simulation. For example, in the ENVT5503 class, we set an assignment to simulate remediation of groundwater contamination with a permeable reactive barrier (PRB). In this case we should consider including:
Geometry Information
dialog)
Material Distribution
options of the Profile Information
dialogSolute Transport
dialog)Diffus. W.
in the Solute Transport Parameters
dialog)Frac = 1
value of zero to match non-equilibrium
conditions (in the Solute Transport Parameters
dialog)Kd
value for the material representing the
PRB (and potentially a lower Kd
value for the surrounding
material) in the Solute Transport and Reaction parameters
dialogalpha
value (first-order reaction rate constant) for
each material (in the Solute Transport and Reaction parameters
dialog - scroll to the right; alpha
is at the end)
File name | Contents |
---|---|
Nod_Inf.out | Separate data matrices at selected `Print times` during simulation. |
Obs_Node.out | Side-by side data matrices for each node depth. |
Solute1.out | Single data matrix for each solute. Contains columns for time, fluxes and concentrations (including cumulative values) at start of flow path (Top), Root zone, end of flow path (Bot). |
More detail is available in the Hydrus-1D manual. We recommend the Hydrus-1D Tutorial (Rassam et al. 2018). Various versions of Hydrus manuals are available at https://www.pc-progress.com/, the most relevant probably being Šimůnek et al. (2008).
R
Users of Hydrus-1D either need to use the limited graphics in
Hydrus itself, open the output files in Excel, or devise a way of using
more flexible software like R
.
This is an easier alternative to reading the
data stored in the Nod_Inf.out
file (described below).
Right-click on Hydrus-1D plot of Profile Information: Concentration, and select Copy; this will copy the data table for the plot in tab-separated format.
Then run the following code:
On some Mac computers this does not work, giving an error message
such as:
Error in file(file, "rt") : X11 module cannot be loaded
In that case, copy the data from the Hydrus-1D plot into Excel, and
save as a .csv
file (e.g.
hydrus1.csv
) into your current RStudio working
directory.
Then just use read.csv
to input the Hydrus-1D data:
Then it's useful to input some of the Hydrus-1D information
(Final Time
(≡ total time), and number of
Print Times
):
# specify Final Time here
# (from Hydrus-1D 'Time Information' input)
# 👇
ft <- 365 # run this line!
# specify number of Print times here
# (from Hydrus-1D 'Print Information' input)
# 👇
pt <- 10 # run this line!
(simTimes <- seq(0,ft, len=pt+1))
## [1] 0.0 36.5 73.0 109.5 146.0 182.5 219.0 255.5 292.0 328.5 365.0
colnames(hydrus0)[seq(2,ncol(hydrus0),2)] <- paste("Day",round(simTimes))
colnames(hydrus0)[seq(3,ncol(hydrus0),2)] <- paste0("Dist",round(simTimes))
Then, reformat the data for easier plotting using
melt()
from the reshape2
package. If
Distance
is negative, we make it positive. To enhance
plots, strip off the last concentration at each time step.
library(reshape2)
# convert to long-form using melt()
hydrus1 <- melt(hydrus0[,2:ncol(hydrus0)],
id.vars = c("T0"),
measure.vars = paste("Day",round(simTimes)))
colnames(hydrus1) <- c("Distance","Time","Concentration")
# reverse sign of distance variable
if(min(hydrus1$Distance)<0) hydrus1$Distance <- -1*hydrus1$Distance
# make final concs NA
hydrus1[which(hydrus1$Distance==max(hydrus1$Distance)),"Concentration"] <- NA
Check the data file:
## 'data.frame': 1111 obs. of 3 variables:
## $ Distance : num 0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 ...
## $ Time : Factor w/ 11 levels "Day 0","Day 36",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ Concentration: num 0 0 0 0 0 0 0 0 0 0 ...
We plot the concentration (y-axis) vs. distance (x-axis) since this is relevant to horizontal transport, such as flow of groundwater containing solute(s).
par(mar=c(4,4,1,1), mgp=c(1.7,0.3,0), tcl=0.25,
font.lab=2, cex.lab=1.2)
palette(c("black",viridis::plasma(pt, alp=0.7)))
with(hydrus1, plot(Distance, Concentration, type="b",cex=1,
pch=rep(c(21:25),3)[Time],
bg=seq_along(simTimes)[Time],
xlab="Distance (m)",
ylab="Solute concentration (mg/L)"))
legend("topright", bty="n", inset=0.02, ncol=2,
title=expression(italic("Simulation times [days]")),
legend=round(simTimes[-1]), pt.bg=seq_along(simTimes)+1,
pch=c(22:25,21:25,21), pt.cex=1.2)
If you don't like all the symbols in the plot above, try the following (or, you can adapt the code to plot concentration on the horizontal axis - see below):
par(mar=c(4,4,1,1), mgp=c(1.7,0.3,0), tcl=0.25,
font.lab=2, cex.lab=1.2)
palette(c("black",viridis::turbo(pt)))
with(subset(hydrus1, hydrus1$Time==levels(hydrus1$Time)[1]),
plot(Concentration ~ Distance, type="l",
ylim=c(0, max(hydrus1$Concentration, na.rm=TRUE)),
xlab="Distance (m)", ylab="Solute concentration (mg/L)") )
for (i in 2:(pt+1)){
with(subset(hydrus1, hydrus1$Time==levels(hydrus1$Time)[i]),
lines(Concentration ~ Distance, col=i,lwd=3,lty=i))
}
legend("topright", bty="n", inset=0.02, ncol=2,
title=expression(italic("Simulation times [days]")),
lty=seq_along(simTimes)+1, seg.len=4.5, legend=round(simTimes[-1]),
col=seq_along(simTimes)+1, pch=NA, lwd=rep(3,pt),
x.intersp=0.5)
Note that the legend uses the levels names for the factor
Time
that we made after setting the Final Time and number
of Print Times (above).
require(ggplot2)
ggplot(hydrus1) +
geom_line(aes(x=Distance, y=Concentration, colour=Time,
linetype=Time), linewidth=1.5) +
scale_color_manual(values=c("grey",viridis::turbo(10, beg=0.05))) +
guides(color=guide_legend(title="Simulation times")) +
guides(linetype=guide_legend(title="Simulation times")) +
labs(x="Distance (m)", y = "Concentration (mg/m³)") +
theme_bw() +
theme(axis.title = element_text(face="bold", size=14),
legend.key.width = unit(4, "line"),
legend.title = element_text(face="bold", size=12))
Here's my code to read the node (Profile) information output files
from Hydrus-1D (Nod_Inf.out
in the relevant Hydrus-1D
project folder) into an R data frame.
# R code to read Hydrus-1D Nod_Inf.out files into an R data frame
# set full path to Hydrus-1D project directory (folder)
# -=-=-=-=- EDIT THIS TO MATCH THE FOLDER ON YOUR COMPUTER ! -=-=-=-=-
PCdir <- paste0(MyPC,"ENVT5503/Hydrus1D ENVT5503/PRB")
# read Nod_Inf.out file into a vector of character (text) strings
Nod_Inf_out <- readLines(paste0(PCdir,"/","Nod_Inf.out"))
# View(Nod_Inf_out) # optionally check what we just read
# find indices of some rows we want to delete
head_rows <- grep("Node", Nod_Inf_out)
# use indices to delete the rows
Nod_Inf_out <- Nod_Inf_out[-c(seq(1,7),head_rows-2, head_rows-1,
head_rows+1, head_rows+2)]
# find indices of some more rows we want to delete
ends <- grep("end",Nod_Inf_out)[-1*NROW(grep("end",Nod_Inf_out))]
# use indices to delete the rows
Nod_Inf_out <- Nod_Inf_out[-c(ends, ends+1, ends+2)]
# find indices of rows that have the Hydrus-1D print times
time_rows <- grep("Time:", Nod_Inf_out)
# extract the rows with Hydrus-1D print times into a vector
timz <- Nod_Inf_out[time_rows]
# get rid of text around the Hydrus-1D print time values...
timz <- gsub("Time: ","",timz)
timz <- gsub(" ","",timz)
# ...and convert to numbers...
timz <- as.numeric(timz)
# ...then delete the Hydrus-1D print time rows
Nod_Inf_out <- Nod_Inf_out[-c(time_rows)]
# find the rows with column names for each block of print data...
head_rows <- grep("Node", Nod_Inf_out)
# and remove all except the first one
Nod_Inf_out <- Nod_Inf_out[-c(head_rows[-1])]
# strip out all but single spaces from data...
while(NROW(grep(" ", Nod_Inf_out)) > 0) {
Nod_Inf_out <- gsub(" "," ", Nod_Inf_out)
}
# ...and (finally!) delete the very last row (an 'end' statement)
Nod_Inf_out <- Nod_Inf_out[-1*NROW(Nod_Inf_out)]
#
# write the edited output to a file...
writeLines(Nod_Inf_out, con="./NodInf3.out")
# ...and read the file in as space-delimited
node_data <- read.table(file = "NodInf3.out", header = TRUE, sep = " ")
# the is a blank column 1; rename it to "Time"
colnames(node_data)[1] <- "Time"
# use the print times vector to make a vector with
# the relevant time repeated for each block
timz0 <- rep(timz[1], NROW(node_data)/NROW(timz))
for (i in 2:NROW(timz)) {
timz0 <- append(timz0, rep(timz[i], NROW(node_data)/NROW(timz)))
}
# replace the Time colum with the vector we just made...
node_data$Time <- timz0
# ...and convert Time to a factor
node_data$Time <- as.factor(node_data$Time)
# remove temporary objects...
rm(list = c("i","head_rows","Nod_Inf_out","timz","timz0","ends"))
# ...and check the cleaned-up data
str(node_data)
This is the type of plot we would use for vertical transport, such as leaching through a soil profile.
# check it with a plot
# plot as concentration vs. depth
palette(c("black",viridis::rocket(6, beg=0.15, end=0.7)))
par(mar = c(1,4,4,1), mgp = c(1.7,0.3,0), tcl = 0.3, font.lab=2)
plot(-1*(node_data$Depth) ~ node_data[,13],
type = "l", col = 1,
xlim = c(0, max(node_data[,13], na.rm=T)),
ylim = c(-1*min(node_data$Depth, na.rm=T), 0),
subset = node_data$Time==levels(node_data$Time)[1],
xaxt = "n", xlab = "", ylab = "Depth (cm)")
axis(3)
mtext("Concentration", side = 3, line = 1.7, font = 2)
symz <- c(15,19,17,8,12,9)
for (i in 2:nlevels(node_data$Time)) {
points(-1*node_data$Depth ~ node_data[,13], col = i,
subset = node_data$Time==levels(node_data$Time)[i],
type = "o", pch = symz[i-1], cex = 0.6)
}
legend("bottomright", legend = levels(node_data$Time),
col = seq(1,nlevels(node_data$Time)),
pch = c(NA,symz),
lwd = 1,
pt.cex=0.85, bty = "n", inset = 0.02,
title = expression(bold("Time (days)")))
This type of plot is relevant to horizontal transport, such as flow of groundwater containing solute(s).
palette(c("black",viridis::plasma(6)))
symz <- rep(21:25,2)
par(mar = c(4,4,1,1), mgp = c(1.7,0.3,0), tcl = 0.3, font.lab=2)
node_data$Distance <- -1*node_data$Depth
plot(node_data[,13] ~ -1*node_data$Distance,
type = "l", col = 1,
ylim = c(0, max(node_data[,13], na.rm=T)),
xlim = c(0, max(node_data$Distance, na.rm=T)),
subset = node_data$Time==levels(node_data$Time)[1],
ylab = "Concentration", xlab = "Distance (cm)")
for (i in 2:nlevels(node_data$Time)) {
points(node_data[,13] ~ -1*node_data$Distance, bg = i,
subset = node_data$Time==levels(node_data$Time)[i],
type = "o", pch = symz[i-1], cex = 1)
}
legend("topright", legend = levels(node_data$Time),
pt.bg = seq(1,nlevels(node_data$Time)),
pch = c(NA,symz),
lwd = 1,
pt.cex=1, bty = "n", inset = 0.02,
title = expression(bold("Time (days)")))
Rassam, D., J. Šimůnek, D. Mallants, and M. Th. van Genuchten, The HYDRUS-1D Software Package for Simulating the One-Dimensional Movement of Water, Heat, and Multiple Solutes in Variably-Saturated Media: Tutorial, CSIRO Land and Water, Adelaide, Australia, 183 pp., ISBN 978-1-4863-1001-2, 2018. https://www.pc-progress.com/Downloads/Public_Lib_H1D/HYDRUS-1D_Tutorial_V1.00_2018.pdf
Šimůnek, J., M. Šejna, H. Saito, M. Sakai, and M. Th. van Genuchten, The Hydrus-1D Software Package for Simulating the Movement of Water, Heat, and Multiple Solutes in Variably Saturated Media, Version 4.0, HYDRUS Software Series 3, Department of Environmental Sciences, University of California Riverside, Riverside, California, USA, pp. 315, 2008. (PDF 2.7MB)
Garnier S, Ross N, Rudis R, Camargo AP, Sciaini M, Scherer C (2024).
viridis(Lite)
- Colorblind-Friendly Color Maps for R.
viridis
package version 0.6.5. https://sjmgarnier.github.io/viridis/
Wickham, H. (2007). Reshaping Data with the reshape
Package. Journal of Statistical Software,
21(12), 1-20. URL http://www.jstatsoft.org/v21/i12/.
Wickham, H. (2016). ggplot2
: Elegant Graphics for
Data Analysis. Springer-Verlag: New York. https://ggplot2.tidyverse.org
CC-BY-SA • All content by Ratey-AtUWA. My employer does not necessarily know about or endorse the content of this website.
Created with rmarkdown in RStudio. Currently using the free yeti theme from Bootswatch.