{ "cells": [ { "cell_type": "markdown", "metadata": { "editable": true, "slideshow": { "slide_type": "" }, "tags": [] }, "source": [ "# Test tidal response with analytic solutions\n", "\n", "This notebook shows some validation of tidal wave propagation in a ModelXsection against known formulas from literature (Bruggeman, 1999: `Analytical solutions of geohydrological problems`).\n", "\n", "Three situations are shown:\n", "1. Bruggeman 128.01: tidal fluctation of open water, in a confined aquifer with open boundary at x=0\n", "2. Bruggeman 128.03: tidal fluctation of open water, in a leaky aquifer with open boundary at x=0\n", "3. Bruggeman 128.04: tidal fluctation of open water, in a leaky aquifer with entry resistance at x=0" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import timflow.transient as tft" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we define some solutions from the book of Bruggeman. \n", "\n", "_(Note 1: Note how easily `numpy` deals with the complex algebra (splitting into an imaginary and a real part)._\n", "\n", "_(Note 2: We might move the Bruggeman code to a new repository: https://github.com/dbrakenhoff/bruggeman)._" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def bruggeman_128_01(x, t, h, S, k, D, tau):\n", " \"\"\"Tidal fluctuation open water, confined aquifer with open boundary (x = 0).\n", "\n", " From Bruggeman 128.01\n", "\n", " h = amplitude of tidal fluctuation, [m]\n", " k = hydraulic conductivity [m/d]\n", " D = aquifer thickness [m]\n", " S = storage coefficient [-]\n", " tau = tidal period [d]\n", " \"\"\"\n", " beta = np.sqrt(S / (k * D))\n", " omega = 2 * np.pi / tau\n", " omega_accent = beta * np.sqrt(omega / 2)\n", "\n", " return h * np.exp(-omega_accent * x) * np.sin(omega * t - omega_accent * x)\n", "\n", "\n", "def bruggeman_128_03(x, t, h, S, k, D, tau, c):\n", " \"\"\"Tidal fluctuation open water, leaky aquifer with open boundary (x = 0).\n", "\n", " From Bruggeman 128.03\n", "\n", " h = amplitude of tidal fluctuation, [m]\n", " k = hydraulic conductivity [m/d]\n", " D = aquifer thickness [m]\n", " S = storage coefficient [-]\n", " tau = tidal period [d]\n", " c = leakance [d]\n", " \"\"\"\n", " beta = np.sqrt(S / (k * D))\n", " eta = 1 / (c * S)\n", " omega = 2 * np.pi / tau\n", "\n", " a = np.real(np.sqrt(eta + 1j * omega))\n", " b = np.imag(np.sqrt(eta + 1j * omega))\n", "\n", " return h * np.exp(-beta * a * x) * np.sin(omega * t - beta * b * x)\n", "\n", "\n", "def bruggeman_128_04(x, t, h, S, k, D, tau, c, w):\n", " \"\"\"Tidal fluctuation open water, leaky aquifer with entrance resistance (x = 0).\n", "\n", " From Bruggeman 128.04\n", "\n", " h = amplitude of tidal fluctuation, [m]\n", " k = hydraulic conductivity [m/d]\n", " D = aquifer thickness [m]\n", " S = storage coefficient [-]\n", " tau = tidal period [d]\n", " c = leakance [d]\n", " w = entry resistance at x=0 [d]\n", " \"\"\"\n", " beta = np.sqrt(S / (k * D))\n", " eta = 1 / (c * S)\n", " omega = 2 * np.pi / tau\n", " theta = 1 / (np.power(beta, 2) * np.power(k, 2) * np.power(w, 2))\n", "\n", " a = np.real(np.sqrt(eta + 1j * omega))\n", " b = np.imag(np.sqrt(eta + 1j * omega))\n", "\n", " return (\n", " h\n", " * np.sqrt(theta)\n", " * np.exp(-beta * a * x)\n", " * np.sin(omega * t - beta * b * x - np.arctan(b / (a + np.sqrt(theta))))\n", " / (np.sqrt(np.power((a + np.sqrt(theta)), 2) + np.power(b, 2)))\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The analytic solution is for a river with varying head that has been varying for ever. \n", "In `timflow`, head is simulated for several days to get the model to spin-up. \n", "\n", "By using a `sine` function, the head starts at zero (as should be the case in `timflow`)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def head_river(h, t, tau, tp=0):\n", " return h * np.sin(2 * np.pi * (t - tp) / tau)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Below the `sine` (water level on river) is split into steps as shown in the example [How to model a fluctuating head boundary](https://ttim.readthedocs.io/en/latest/00tutorials/howto_fluctuating_head_boundary.html). We use a small timestep ($\\Delta t$)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Parameters of tidal wave\n", "tau = 0.5 # tidal period, d\n", "h_tidal = 1 # amplitude of tidal fluctuation, m" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "tmax = 5 # day\n", "delt = 0.01 # day\n", "t = np.arange(0, tmax, delt)\n", "hexact = head_river(h_tidal, t, tau)\n", "hmid = 0.5 * (\n", " head_river(h_tidal, t - delt / 2, tau) + head_river(h_tidal, t + delt / 2, tau)\n", ")\n", "tmid = np.hstack((0, 0.5 * (t[:-1] + t[1:])))\n", "# plot\n", "plt.figure(figsize=(10, 3))\n", "plt.plot(t, hexact, \"k\", label=\"observed\")\n", "plt.step(tmid, hmid, where=\"post\", label=\"mid\")\n", "plt.xlim(0, 1)\n", "plt.xlabel(\"time (d)\")\n", "plt.ylabel(\"head (m)\")\n", "plt.legend()\n", "plt.grid()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Bruggeman 128.01 - Confined, no entry resistance\n", "\n", "Create the `timflow` model for comparison with Bruggeman 128.01 (confined, no entry resistance).\n", "\n", "In this example, we use the 1D inhomogeneities because of the nice plotting. The same result could be reached with a 'normal' `timflow` model.\n", "In all the `timflow` models, the left-hand inhomogeneity (`river`) is shown for illustration purposes. That inhomogeneity is only created to satisfy the requirement of ModelXsection to have inhoms for $-\\infty