Modeling infiltration in a cross-section#

This notebook shows how to model infiltration in a transient cross-section model.

import matplotlib.pyplot as plt
import numpy as np

import timflow.transient as tft

plt.rcParams["figure.figsize"] = (10, 4)

Single aquifer#

k = [20.0]
H = 10.0
Saq = [0.1]
L = 100.0
N = 1e-3
z = [0, -H]
ml = tft.ModelXsection(naq=1, tmin=0.1, tmax=1e3)

left = tft.XsectionMaq(
    model=ml,
    x1=-np.inf,
    x2=-L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    topboundary="conf",
    phreatictop=True,
)
rech = tft.XsectionMaq(
    model=ml,
    x1=-L / 2,
    x2=L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    topboundary="conf",
    phreatictop=True,
    tsandN=[(0.0, N)],
)
right = tft.XsectionMaq(
    model=ml,
    x1=L / 2,
    x2=np.inf,
    kaq=k,
    z=z,
    Saq=Saq,
    topboundary="conf",
    phreatictop=True,
)

ml.solve()
self.neq  4
solution complete
x = np.linspace(-100, 100, 100)
y = np.zeros(100)
plt.axvspan(-50, 50, color=3 * [0.9])
for t in np.logspace(-1, 1, 10):
    h = ml.headalongline(x, y, t)
    plt.plot(x, h[0, 0], label=f"t={t:.2f}")
plt.legend();
../../_images/dda2af8fe6e3c5ae1049d475c10f854a3436c44563050119e057685671815242.png
# check solution inside
x = 25
y = 0
t = 7
d = 1e-2

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = rech.T * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim, Qxnum: ", Qxtim, Qxnum)

# deq
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + d) - ml.head(x, y, t - d)) / (2 * d)
print("lhs: ", d2hdx2)
print("rhs: ", rech.Saq / rech.T * dhdt - N / rech.T)

# check solution outside
x = 75
y = 0
t = 7
d = 1e-2

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = rech.T * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim, Qxnum: ", Qxtim, Qxnum)

# deq
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + d) - ml.head(x, y, t - d)) / (2 * d)
print("lhs: ", d2hdx2)
print("rhs: ", rech.Saq / rech.T * dhdt)
Qxtim, Qxnum:  [[0.01914832]] [[0.01914832]]
lhs:  [[-3.83805002e-06]]
rhs:  [[-3.83809051e-06]]
Qxtim, Qxnum:  [[0.03293774]] [[0.03293774]]
lhs:  [[1.06543288e-06]]
rhs:  [[1.06545622e-06]]

2 aquifers#

k = [10.0, 20.0]
z = [0, -10, -12, -20]
c = [500]
Saq = [0.1, 1e-4]
L = 100.0
N = 1e-3
ml = tft.ModelXsection(naq=2, tmin=0.1, tmax=1e3)

left = tft.XsectionMaq(
    model=ml,
    x1=-np.inf,
    x2=-L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=c,
    topboundary="conf",
    phreatictop=True,
)

inf = tft.XsectionMaq(
    model=ml,
    x1=-L / 2,
    x2=L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=c,
    topboundary="conf",
    phreatictop=True,
    tsandN=[(0.0, N)],
)

right = tft.XsectionMaq(
    model=ml,
    x1=L / 2,
    x2=np.inf,
    kaq=k,
    z=z,
    Saq=Saq,
    c=c,
    topboundary="conf",
    phreatictop=True,
)

ml.solve()
self.neq  8
solution complete
x = np.linspace(-100, 100, 100)
y = np.zeros(100)
plt.axvspan(-50, 50, color=3 * [0.9])
t = np.logspace(-1, 1, 3)
h = ml.headalongline(x, y, t)
for i in range(len(t)):
    plt.plot(x, h[0, i], color="C0")
    plt.plot(x, h[1, i], color="C1")
../../_images/65a7f0b6bd08cf5b099715a49bdf1ffd89e40da8f64da77e27201bc5a7201b8f.png
print("check solution inside")
x = 25
y = 0
t = 0.7
d = 1e-3
dt = 0.001

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = inf.Tcol * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim: ", Qxtim[:, 0])
print("Qxnum: ", Qxnum[:, 0])

# deq
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + dt) - ml.head(x, y, t - dt)) / (2 * dt)
h = ml.head(x, y, t)
rhs1 = (
    inf.Saq[0] / inf.T[0] * dhdt[0, 0]
    + (h[0, 0] - h[1, 0]) / (inf.T[0] * inf.c[1])
    - N / inf.T[0]
)
rhs2 = inf.Saq[1] * inf.Haq[1] / inf.T[1] * dhdt[1, 0] - (h[0, 0] - h[1, 0]) / (
    inf.T[1] * inf.c[1]
)
print("lhs: ", d2hdx2[:, 0])
print("rhs: ", rhs1, rhs2)

print("check solution outside")
x = 75
y = 0
t = 0.7
d = 0.01
dt = 0.01

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = inf.Tcol * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim: ", Qxtim[:, 0])
print("Qxnum: ", Qxnum[:, 0])

# deq
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + dt) - ml.head(x, y, t - dt)) / (2 * dt)
h = ml.head(x, y, t)
rhs1 = inf.Saq[0] / inf.T[0] * dhdt[0, 0] + (h[0, 0] - h[1, 0]) / (inf.T[0] * inf.c[1])
rhs2 = inf.Saq[1] * inf.Haq[1] / inf.T[1] * dhdt[1, 0] - (h[0, 0] - h[1, 0]) / (
    inf.T[1] * inf.c[1]
)
print("lhs: ", d2hdx2[:, 0])
print("rhs: ", rhs1, rhs2)
check solution inside
Qxtim:  [0.00528708 0.00024616]
Qxnum:  [0.00528708 0.00024616]
lhs:  [-2.72873391e-06 -5.66048944e-08]
rhs:  -2.7292469695794058e-06 -5.660308111144264e-08
check solution outside
Qxtim:  [0.00559443 0.00046795]
Qxnum:  [0.00559443 0.00046795]
lhs:  [2.50199817e-06 1.18889273e-09]
rhs:  2.5019485046389376e-06 1.1887430107100334e-09

Example river, 1 aquifer#

k = [20.0]
z = [11, 10, 0]
Saq = [1e-3]
hstar = 2
L = 100

ml = tft.ModelXsection(naq=1, tmin=1e-4, tmax=1e3)

left = tft.XsectionMaq(
    model=ml,
    x1=-np.inf,
    x2=-L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
)

riv = tft.XsectionMaq(
    model=ml,
    x1=-L / 2,
    x2=L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
    tsandhstar=[(0.0, hstar), (5, 2 * hstar)],
)

right = tft.XsectionMaq(
    model=ml,
    x1=L / 2,
    x2=np.inf,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
)

ml.solve()
self.neq  4
solution complete
lab = np.sqrt(riv.T[0] * riv.c[0])
print(f"leakage factor: {lab} m")
leakage factor: 141.4213562373095 m
x = np.linspace(-100, 100, 100)
y = np.zeros(100)
plt.axvspan(-50, 50, color=3 * [0.9])
for t in np.logspace(-1, 1, 10):
    h = ml.headalongline(x, y, t)
    plt.plot(x, h[0, 0], label=f"t={t:.2f}")
plt.legend();
../../_images/043631ee897b64161f243995c9f671cf4368c8460050ecc7c8107a112f40bc90.png
# mf6 time step 0.1 day, cell size 10 m
hmf6 = np.loadtxt("./data/mf6_ttim_xsec_riv1.txt")  # load mf6 output to compare
t = np.linspace(0.01, 10, 100)
h = ml.head(25, 0, t)
plt.plot(t, h[0], label="timflow")
plt.plot(hmf6[0], hmf6[1], "--", label="mf6")
plt.xticks(np.linspace(0, 10, 5))
plt.legend()
plt.grid()
../../_images/dbeb1cadd4ba41193e7c39ec54e8ec2b3e01850a2d47f5f95d1f515596628bee.png
# check solution inside
x = 25
y = 0
t = 1
d = 0.01
dt = 1e-3

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = riv.T * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim, Qxnum: ", Qxtim, Qxnum)

# deq
h = ml.head(x, y, t)
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + dt) - ml.head(x, y, t - dt)) / (2 * dt)
print("lhs: ", d2hdx2)
print(
    "rhs: ",
    (riv.Saq[0] * riv.Haq[0]) / riv.T * dhdt
    + h / (riv.c[0] * riv.T[0])
    - hstar / (riv.c[0] * riv.T[0]),
)
# print(
#     "rhs: ",
#     (ml.aq.Saq[0] * ml.aq.Haq[0]) / ml.aq.T * dhdt
#     + (h - hstar) / (ml.aq.c[0] * ml.aq.T[0]),
# )  # - hstar / (ml.aq.c[0] * ml.aq.T[0]))
Qxtim, Qxnum:  [[0.34428389]] [[0.34428389]]
lhs:  [[-6.96093494e-05]]
rhs:  [[-6.95979439e-05]]

Very wide river#

k = [20.0]
z = [11, 10, 0]
c = 100
Saq = [1e-3]
hstar = 2
L = 2000

ml = tft.ModelXsection(naq=1, tmin=1e-2, tmax=1e3)

left = tft.XsectionMaq(
    model=ml,
    x1=-np.inf,
    x2=-L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
)

riv = tft.XsectionMaq(
    model=ml,
    x1=-L / 2,
    x2=L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
    tsandhstar=[(0.0, hstar)],
)

right = tft.XsectionMaq(
    model=ml,
    x1=L / 2,
    x2=np.inf,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
)

ml.solve()

x = np.linspace(-L, L, 100)
y = np.zeros(100)
plt.axvspan(-L / 2, L / 2, color=3 * [0.9])
for t in np.logspace(-1, 2, 10):
    h = ml.headalongline(x, y, t)
    plt.plot(x, h[0, 0], label=f"t={t:.2f}")
plt.legend()
plt.grid()
self.neq  4
solution complete
../../_images/d36764ed05811e65fe17246ae372b6f6b10856517f8fb65cd8888ef6d8ae3f3b.png

Two aquifer#

k = [20.0, 40]
z = [11, 10, 0, -2, -12]
c = 100
Saq = [1e-3, 1e-3]
hstar = 2
L = 100

ml = tft.ModelXsection(naq=2, tmin=1e-4, tmax=1e3)

left = tft.XsectionMaq(
    model=ml,
    x1=-np.inf,
    x2=-L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=c,
    topboundary="semi",
)

riv = tft.XsectionMaq(
    model=ml,
    x1=-L / 2,
    x2=L / 2,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
    tsandhstar=[(0.0, hstar), (5, 2 * hstar)],
)

right = tft.XsectionMaq(
    model=ml,
    x1=L / 2,
    x2=np.inf,
    kaq=k,
    z=z,
    Saq=Saq,
    c=[100],
    topboundary="semi",
)

ml.solve()
self.neq  8
solution complete
# check solution inside
x = 25
y = 0
t = 1
d = 0.01
dt = 1e-3

# discharge vector
Qxtim, _ = ml.disvec(x, y, t)
Qxnum = riv.Tcol * (ml.head(x - d, y, t) - ml.head(x + d, y, t)) / (2 * d)
print("Qxtim, Qxnum: ")
print(Qxtim)
print(Qxnum)

# deq
h = ml.head(x, y, t)
d2hdx2 = (
    ml.head(x - d, y, t)
    + ml.head(x + d, y, t)
    + ml.head(x, y - d, t)
    + ml.head(x, y + d, t)
    - 4 * ml.head(x, y, t)
) / d**2
dhdt = (ml.head(x, y, t + dt) - ml.head(x, y, t - dt)) / (2 * dt)
rhs0 = (
    (riv.Saq[0] * riv.Haq[0]) / riv.T[0] * dhdt[0, 0]
    + h[0, 0] / (riv.c[0] * riv.T[0])
    - hstar / (riv.c[0] * riv.T[0])
    + (h[0, 0] - h[1, 0]) / (riv.c[1] * riv.T[0])
)
rhs1 = (riv.Saq[1] * riv.Haq[1]) / riv.T[1] * dhdt[1, 0] + (h[1, 0] - h[0, 0]) / (
    riv.c[1] * riv.T[1]
)
print("lhs: ", d2hdx2[:, 0])
print("rhs: ", rhs0, rhs1)
# print(
#     "rhs: ",
#     (riv.Saq[0] * riv.Haq[0]) / riv.T * dhdt + (h - hstar) / (riv.c[0] * riv.T[0]),
# )  # - hstar / (riv.c[0] * riv.T[0]))
Qxtim, Qxnum: 
[[0.30854703]
 [0.05048272]]
[[0.30854703]
 [0.05048272]]
lhs:  [-6.29571351e-05 -4.76218509e-06]
rhs:  -6.296017641931811e-05 -4.757624397951093e-06
# mf6 time step 0.1 day, cell size 10 m
hmf6 = np.loadtxt("./data/mf6_ttim_xsec_riv2.txt")  # load mf6 output to compare
t = np.linspace(0.01, 10, 100)
h = ml.head(25, 0, t)
plt.plot(t, h[0], label="timflow lay 0")
plt.plot(t, h[1], label="timflow lay 1")
plt.plot(hmf6[0], hmf6[1], "--", label="mf6 lay 0")
plt.plot(hmf6[0], hmf6[2], "--", label="mf6 lay 1")
plt.xticks(np.linspace(0, 10, 5))
plt.legend()
plt.grid()
../../_images/f8720a2ba5a1cbb1f2c66a60a817685c5aaf0b98d2b306570873077f8b348b86.png