diff --git a/examples/additional_term.ipynb b/examples/additional_term.ipynb deleted file mode 100644 index ada0f4f..0000000 --- a/examples/additional_term.ipynb +++ /dev/null @@ -1,185 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "initial_id", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from scipy.special import erfc\n", - "from scipy.special import erfcx\n", - "import mibitrans as mbt" - ] - }, - { - "cell_type": "markdown", - "id": "9fe57bdbc2e44bc8", - "metadata": {}, - "source": [ - "## Effects of the untruncated Domenico solution\n", - "\n", - "#### Introduction\n", - "\n", - "The analytical equation used in BIOSCREEN uses Domenico (1987) analytical solution for multidimensional transport of a contaminant species, but with the addition of source depletation and source superposition (to allow for more source zones). However, this solution is a truncation of the full analytical equation, which contains an additional term with a product of a exp and erfc function. This additional term does not require a lot of additional computing time in the context of mibitrans, and is therefore included in the 'Domenico' solution of this package. See equations below.\n", - "\n", - "(equation_in_bioscreen; truncated)\n", - "\n", - "(equation_in_mibitrans; untruncated)\n", - "\n", - "This notebook gives an overview of the differences between the truncated and untruncated Domenico solution, and briefly discusses the implementation in mibitrans.\n", - "\n", - "Note that exact analytical models (like Wexler (1992), which is the basis for the Karanovic solution, as implemented in BIOSCREEN-AT) still give more accurate results than the Domenico solution, regardless of truncation (West et al., 2007).\n" - ] - }, - { - "cell_type": "markdown", - "id": "80a3493193019b8c", - "metadata": {}, - "source": [ - "#### Over- and underflow of the additional term\n", - "\n", - "In mibitrans, 64-bit float numpy arrays are used for the calculations and as a consequence, which can hold values between approximately $10^{-300}$ and $10^{+300}$. While these are generally beyond any reasonable order of magnitude, certain parameter combinations can cause an underflow (value < $10^{-300}$ and therefore set to 0), or overflow (value > $10^{+300}$ and therefore set to infinity) to occur. Especially at distances further from the source. However, this does necessarily mean that the additional term is negligibly small, as for $a \\to \\infty$, $\\exp(a) \\to \\infty$ and $\\text{erfc}(a) \\to 0$. To combat this, the erfcx function is used, where $\\text{erfcx}(a) = \\exp(a^2) \\cdot \\text{erfc}(a)$. This transforms the additional term as: $\\exp(b) \\cdot \\text{erfc}(a) = \\exp(b - a^2) \\cdot \\text{erfcx}(b)$. Under any reasonable field condition, this expression of the additional will not over- or underflow. And is therefore implemented as such in mibitrans." - ] - }, - { - "cell_type": "markdown", - "id": "f2fefae6a58bddd2", - "metadata": {}, - "source": [ - "#### Visualization of the additional term\n", - "\n", - "Consider the following parameters as example field conditions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75cc19132fea97b0", - "metadata": {}, - "outputs": [], - "source": [ - "hydro = mbt.HydrologicalParameters(\n", - " velocity=0.1,\n", - " porosity=0.3,\n", - " alpha_x=1,\n", - " alpha_y=0,\n", - " alpha_z=0,\n", - ")\n", - "\n", - "att = mbt.AttenuationParameters(\n", - " retardation=1,\n", - ")\n", - "\n", - "source = mbt.SourceParameters(\n", - " source_zone_boundary=np.array([5,10,15]),\n", - " source_zone_concentration=np.array([15,10,5]),\n", - " total_mass=\"inf\",\n", - " depth=10,\n", - ")\n", - "\n", - "model = mbt.ModelParameters(\n", - " model_length=200,\n", - " model_width=50,\n", - " model_time=5*365,\n", - " dx=1,\n", - " dy=0.1,\n", - " dt=5,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fdfcc37598d37513", - "metadata": {}, - "outputs": [], - "source": [ - "ndeg_obj = mbt.Anatrans(hydro,att, source, model)\n", - "ndeg_obj.run()\n", - "\n", - "ndeg_obj.centerline(time=73, color=\"green\", label=\"t = 73 days\")\n", - "ndeg_obj.centerline(time=365, color=\"blue\", label=\"t = 365 days\")\n", - "ndeg_obj.centerline(time=3*365, color=\"red\", label=\"t = 10 days\")\n", - "plt.title(\"Concentration distribution\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f39ffca3b2282a80", - "metadata": {}, - "outputs": [], - "source": [ - "365 * 3" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3fe5ad8151366b2d", - "metadata": {}, - "outputs": [], - "source": [ - "xxx = ndeg_obj.xxx\n", - "ttt = ndeg_obj.ttt\n", - "rv = ndeg_obj.rv\n", - "alpha_x = hydro.alpha_x\n", - "velocity = hydro.velocity\n", - "\n", - "\n", - "erfc_inner = (xxx + rv * ttt) / (2 * np.sqrt(alpha_x * rv * ttt))\n", - "term = np.exp(xxx / alpha_x - erfc_inner**2) * erfcx(erfc_inner)\n", - "plt.pcolormesh(xxx[None, None, :], ttt[:, None, None], term[:, 0, :])\n", - "plt.colorbar()\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1426ad092dec75e3", - "metadata": {}, - "outputs": [], - "source": [ - "exp_term = np.exp(xxx * rv / (alpha_x * rv))\n", - "erfc_term = erfc(erfc_inner)\n", - "term = exp_term * erfc_term\n", - "plt.pcolormesh(xxx[None, None, :], ttt[:, None, None], term[:, 0, :])\n", - "plt.colorbar()\n", - "plt.show()\n", - "\n", - "plt.plot(xxx[0, 0, :], np.log10(exp_term[0, 0, :]))\n", - "plt.show()\n", - "\n", - "plt.pcolormesh(xxx[None, None, :], ttt[:, None, None], np.log10(erfc_term[:, 0, :]))\n", - "plt.colorbar()\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/example_BIOSCREEN_comparison.ipynb b/examples/example_BIOSCREEN_comparison.ipynb new file mode 100644 index 0000000..7080e7b --- /dev/null +++ b/examples/example_BIOSCREEN_comparison.ipynb @@ -0,0 +1,417 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2231e3885ab4c960", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1c04100008cf2f67", + "metadata": {}, + "outputs": [], + "source": [ + "ft = 3.281 # factor to convert ft to m" + ] + }, + { + "cell_type": "markdown", + "id": "642abbcc74f16426", + "metadata": {}, + "source": [ + "This notebook compares the examples present in BIOSCREEN and BIOSCREEN-AT with the similar models (Bioscreen/Anatrans and Mibitrans respectively) in the mibitrans package." + ] + }, + { + "cell_type": "markdown", + "id": "8efc6ab254154e63", + "metadata": {}, + "source": [ + "### BIOSCREEN example dataset" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c855595dbf62b868", + "metadata": {}, + "outputs": [], + "source": [ + "bio_hydro = mbt.HydrologicalParameters(\n", + " velocity=1609.1 / ft / 365, #[m/d]\n", + " porosity=0.25, #[-]\n", + " alpha_x=28.5 / ft, #[m]\n", + " alpha_y=2.9 / ft, #[m]\n", + " alpha_z=0 / ft, #[m]\n", + ")\n", + "\n", + "bio_att = mbt.AttenuationParameters(\n", + " bulk_density=1700, #[kg/m3]\n", + " partition_coefficient=0.038, #[m3/kg]\n", + " fraction_organic_carbon=0.0008, #[-]\n", + " half_life=0, #[d]\n", + ")\n", + "\n", + "bio_source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([50, 75, 125]) / ft, #[m]\n", + " source_zone_concentration=np.array([9, 2.8, 0.07]), #[g/m3]\n", + " depth=10 / ft, #[m]\n", + " total_mass=\"inf\", #[g]\n", + ")\n", + "\n", + "bio_model = mbt.ModelParameters(\n", + " model_length=1500 / ft, #[m]\n", + " model_width=400 / ft, #[m]\n", + " model_time=5 * 365, #[d]\n", + " dx=5 / ft, #[m]\n", + " dy=5 / ft, #[m]\n", + " dt=365/2, #[d]\n", + ")\n", + "\n", + "bio_ea = mbt.ElectronAcceptors(\n", + " delta_oxygen=5.78, #[g/m3]\n", + " delta_nitrate=17, #[g/m3]\n", + " ferrous_iron=11.3, #[g/m3]\n", + " delta_sulfate=100, #[g/m3]\n", + " methane=0.414, #[g/m3]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aaf753b5640b909c", + "metadata": {}, + "outputs": [], + "source": [ + "#bio_bio; BIOSCREEN input with Bioscreen model\n", + "bio_bio_object = mbt.Bioscreen(bio_hydro, bio_att, bio_source, bio_model)\n", + "#nd; no decay\n", + "bio_bio_nd_results = bio_bio_object.run()\n", + "\n", + "bio_bio_object.attenuation_parameters.half_life = 0.1 * 365 #[d]\n", + "#dc; decay\n", + "bio_bio_dc_results = bio_bio_object.run()\n", + "\n", + "bio_bio_object.instant_reaction(bio_ea)\n", + "#inst; instant reaction\n", + "bio_bio_inst_results = bio_bio_object.run()\n", + "\n", + "#bio_ana; BIOSCREEN input with Anatrans model\n", + "bio_ana_object = mbt.Anatrans(bio_hydro, bio_att, bio_source, bio_model)\n", + "bio_ana_nd_results = bio_ana_object.run()\n", + "\n", + "bio_ana_object.attenuation_parameters.half_life = 0.1 * 365 #[d]\n", + "bio_ana_dc_results = bio_ana_object.run()\n", + "\n", + "bio_ana_object.instant_reaction(bio_ea)\n", + "bio_ana_inst_results = bio_ana_object.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e997f41098b2cb2", + "metadata": {}, + "outputs": [], + "source": [ + "time_bio = [365*0.5, 365, 2*365, 5*365] #[d]\n", + "BIOSCREEN_data_nodecay = np.array([\n", + " [9.000, 8.435, 7.267, 5.900, 4.091, 2.151, 0.780, 0.183, 0.027, 0.002, 0.000], # 0.5y\n", + " [9.000, 8.467, 7.465, 6.680, 6.070, 5.548, 5.006, 4.327, 3.441, 2.410, 1.432], # 1y\n", + " [9.000, 8.467, 7.466, 6.684, 6.089, 5.624, 5.250, 4.940, 4.679, 4.454, 4.256], # 2y\n", + " [9.000, 8.467, 7.466, 6.684, 6.089, 5.624, 5.250, 4.940, 4.679, 4.455, 4.260] # 5y\n", + "])\n", + "\n", + "BIOSCREEN_data_lineardecay = np.array([\n", + " [9.000, 4.348, 1.965, 0.889, 0.386, 0.146, 0.043, 0.009, 0.001, 0.000, 0.000],\n", + " [9.000, 4.348, 1.969, 0.905, 0.424, 0.201, 0.096, 0.046, 0.022, 0.010, 0.004],\n", + " [9.000, 4.348, 1.969, 0.905, 0.424, 0.201, 0.096, 0.047, 0.023, 0.011, 0.005],\n", + " [9.000, 4.348, 1.969, 0.905, 0.424, 0.201, 0.096, 0.047, 0.023, 0.011, 0.005]\n", + "])\n", + "\n", + "BIOSCREEN_data_instant = np.array([\n", + " [9.000, 8.332, 6.473, 2.364, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 8.466, 7.405, 6.331, 5.165, 3.757, 1.724, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 8.466, 7.407, 6.350, 5.268, 4.192, 3.152, 2.167, 1.244, 0.378, 0.000],\n", + " [9.000, 8.466, 7.407, 6.350, 5.268, 4.192, 3.152, 2.168, 1.245, 0.385, 0.000]\n", + "])\n", + "\n", + "bio_x = np.array([0, 145, 290, 435, 580, 725, 870, 1015, 1160, 1305, 1450]) / ft #[m]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d75411d12b26a2a0", + "metadata": {}, + "outputs": [], + "source": [ + "colors = [\"tomato\", \"sandybrown\", \"khaki\", \"greenyellow\", \"lightgreen\"]\n", + "colors_bio = [\"red\", \"orange\", \"gold\", \"yellowgreen\", \"green\"]\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_bio_nd_results, time=time_bio[i], color=colors[i], label=f\"Bioscreen, t={time_bio[i]}d\")\n", + " plt.plot(bio_x, BIOSCREEN_data_nodecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Bioscreen models without decay\")\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_bio_dc_results, time=time_bio[i], color=colors[i], label=f\"Bioscreen, t={time_bio[i]}d\")\n", + " plt.plot(bio_x, BIOSCREEN_data_lineardecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Bioscreen models with linear decay\")\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_bio_inst_results, time=time_bio[i], color=colors[i], label=f\"Bioscreen, t={time_bio[i]}d\")\n", + " plt.plot(bio_x, BIOSCREEN_data_instant[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Bioscreen models with instant reaction\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9feacaadcdce7de4", + "metadata": {}, + "outputs": [], + "source": [ + "colors = [\"tomato\", \"sandybrown\", \"khaki\", \"greenyellow\", \"lightgreen\"]\n", + "colors_bio = [\"red\", \"orange\", \"gold\", \"yellowgreen\", \"green\"]\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_ana_nd_results, time=time_bio[i], color=colors[i], label=f\"Anatrans, t={time_bio[i]}d\")\n", + " plt.plot(bio_x, BIOSCREEN_data_nodecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Anatrans models without decay\")\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_ana_dc_results, time=time_bio[i], color=colors[i], label=f\"Anatrans, t={time_bio[i]}\")\n", + " plt.plot(bio_x, BIOSCREEN_data_lineardecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Anatrans models with linear decay\")\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "for i in range(len(time_bio)):\n", + " mbt.centerline(bio_ana_inst_results, time=time_bio[i], color=colors[i], label=f\"Anatrans, t={time_bio[i]}\")\n", + " plt.plot(bio_x, BIOSCREEN_data_instant[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"BIOSCREEN, t={time_bio[i]}d\")\n", + "plt.title(\"Comparison of BIOSCREEN and Anatrans models with instant reaction\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "765613bdd5ab7be8", + "metadata": {}, + "source": [ + "### Input parameters\n", + "Input parameters are the default parameters when opening up BIOSCREEN-AT. All imperial units are converted to consistent metric units.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d10e013699b8f5d", + "metadata": {}, + "outputs": [], + "source": [ + "bat_hydro = mbt.HydrologicalParameters(\n", + " velocity=335.2 / ft / 365, #[m/d]\n", + " porosity=0.25, #[-]\n", + " alpha_x=28.887 / ft, #[m]\n", + " alpha_y=2.889 / ft, #[m]\n", + " alpha_z=0.289 / ft, #[m]\n", + ")\n", + "\n", + "bat_att = mbt.AttenuationParameters(\n", + " retardation=1.20672, #[-]\n", + " half_life=0, #[d]\n", + ")\n", + "\n", + "bat_source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([50 / ft]), #[m]\n", + " source_zone_concentration=np.array([9]), #[g/m3]\n", + " depth=10 / ft, #[m]\n", + " total_mass=\"inf\", #[g]\n", + ")\n", + "\n", + "bat_model = mbt.ModelParameters(\n", + " model_length=2500 / ft, #[m]\n", + " model_width=500 / ft, #[m]\n", + " model_time=5 * 365, #[d]\n", + " dx=20 / ft, #[m]\n", + " dy=5 / ft, #[m]\n", + " dt=365, #[d]\n", + ")\n", + "\n", + "bat_ea = mbt.ElectronAcceptors(\n", + " delta_oxygen=5.78, #[g/m3]\n", + " delta_nitrate=17, #[g/m3]\n", + " ferrous_iron=11.3, #[g/m3]\n", + " delta_sulfate=100, #[g/m3]\n", + " methane=0.414, #[g/m3]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "6fd5739844c6c6a4", + "metadata": {}, + "source": [ + "## Running model\n", + "As the exact solution uses an integral, run time is longer than that of the Bioscreen solution, depending on model resolution. Using verbose when running the model is recommended, to keep track of the calculation process." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e1098849ca30e36", + "metadata": {}, + "outputs": [], + "source": [ + "# bat_mbt; BIOSCREEN-AT data with Mibitrans model\n", + "bat_mbt_object = mbt.Mibitrans(\n", + " hydrological_parameters=bat_hydro,\n", + " attenuation_parameters=bat_att,\n", + " source_parameters=bat_source,\n", + " model_parameters=bat_model,\n", + ")\n", + "bat_mbt_nd_results = bat_mbt_object.run()\n", + "\n", + "bat_mbt_object.attenuation_parameters.half_life = 365 * 10 #[d]\n", + "bat_mbt_dc_results = bat_mbt_object.run()\n", + "\n", + "bat_mbt_object.instant_reaction(electron_acceptors=bat_ea)\n", + "bat_mbt_inst_results = bat_mbt_object.run()" + ] + }, + { + "cell_type": "markdown", + "id": "aa774fd6b9df0d69", + "metadata": {}, + "source": [ + "## Visualize" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0bb52e20f9ee6e0", + "metadata": {}, + "outputs": [], + "source": [ + "bat_mbt_nd_results.centerline(color=\"red\", label=\"No decay\")\n", + "bat_mbt_dc_results.centerline(color=\"blue\", label=\"Linear decay\")\n", + "bat_mbt_inst_results.centerline(color=\"green\", label=\"Instant reaction\")\n", + "\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1beab531604f46c3", + "metadata": {}, + "source": [ + "Running Bioscreen solution for comparison" + ] + }, + { + "cell_type": "markdown", + "id": "ff9ec557bdfad139", + "metadata": {}, + "source": [ + "## Comparing to BIOSCREEN-AT\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c122c684e746968", + "metadata": {}, + "outputs": [], + "source": [ + "bat_x = np.array([0, 250, 500, 750, 1000, 1250, 1500, 1750, 2000, 2250, 2500]) / ft #[m]\n", + "BIOSCREENAT_data_nodecay = np.array([\n", + " [9.000, 3.635, 0.236, 0.001, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.649, 2.144, 0.452, 0.022, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.696, 2.707, 1.505, 0.493, 0.064, 0.003, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.699, 2.767, 1.872, 1.161, 0.483, 0.104, 0.010, 0.000, 0.000, 0.000],\n", + " [9.000, 4.699, 2.772, 1.932, 1.423, 0.947, 0.459, 0.134, 0.021, 0.002, 0.000],\n", + "])\n", + "\n", + "BIOSCREENAT_data_lineardecay = np.array([\n", + " [9.000, 3.479, 0.222, 0.001, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.407, 1.944, 0.401, 0.019, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.448, 2.423, 1.289, 0.412, 0.053, 0.002, 0.000, 0.000, 0.000, 0.000],\n", + " [9.000, 4.450, 2.471, 1.580, 0.938, 0.380, 0.081, 0.008, 0.000, 0.000, 0.000],\n", + " [9.000, 4.450, 2.475, 1.624, 1.131, 0.721, 0.340, 0.097, 0.015, 0.001, 0.000],\n", + "])\n", + "\n", + "colors = [\"tomato\", \"sandybrown\", \"khaki\", \"greenyellow\", \"lightgreen\"]\n", + "colors_bio = [\"red\", \"orange\", \"gold\", \"yellowgreen\", \"green\"]\n", + "for i in range(len(bat_mbt_nd_results.t)):\n", + " mbt.centerline(bat_mbt_nd_results, time=bat_mbt_object.t[i],\n", + " color=colors[i], label=f\"Mibitrans, t={bat_mbt_object.t[i]}\")\n", + " plt.plot(bat_x, BIOSCREENAT_data_nodecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"bio_at, t={bat_mbt_object.t[i]}\")\n", + "plt.legend()\n", + "plt.show()\n", + "\n", + "\n", + "for i in range(len(bat_mbt_dc_results.t)):\n", + " mbt.centerline(bat_mbt_dc_results, time=bat_mbt_object.t[i],\n", + " color=colors[i], label=f\"Mibitrans, t={bat_mbt_object.t[i]}\")\n", + " plt.plot(bat_x, BIOSCREENAT_data_lineardecay[i, :], color=colors_bio[i],\n", + " linestyle=\":\", label=f\"bio_at, t={bat_mbt_object.t[i]}\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e9a6b6c841cf9302", + "metadata": {}, + "source": [ + "mibitrans output corresponds very well with the BIOSCREEN-AT output, and has higher resolution. Note that despite BIOSCREEN-AT taking in instant reaction parameters, it does not have the option to calculate biodegradation this way, therefore, instant reaction model can not be compared." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_additional_term.ipynb b/examples/example_additional_term.ipynb new file mode 100644 index 0000000..e30a500 --- /dev/null +++ b/examples/example_additional_term.ipynb @@ -0,0 +1,248 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "initial_id", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from scipy.special import erfc\n", + "from scipy.special import erfcx\n", + "import mibitrans as mbt" + ] + }, + { + "cell_type": "markdown", + "id": "9fe57bdbc2e44bc8", + "metadata": {}, + "source": [ + "## Effects of the untruncated Domenico solution\n", + "\n", + "#### Introduction\n", + "\n", + "The analytical equation used in BIOSCREEN uses Domenico (1987) analytical solution for multidimensional transport of a contaminant species, but with the addition of source depletion and source superposition (to allow for more source zones), see equation below;\n", + "\n", + "\\begin{align}\\tag{1}\n", + " C(x, y, z, t) &= \\sum_{i=1}^{n}\\left\\{ \\frac{C^*_{0,i}}{8} \\exp \\left[-k_s \\left(t-\\frac{xR}{v} \\right)\\right] \\right. \\\\\n", + " &\\quad \\cdot \\left( \\exp \\left[ \\frac{x\\left(1-P\\right)}{2\\alpha_x}\\right]\n", + " \\cdot\\operatorname{erfc} \\left[ \\frac{(x - Pvt)}{2\\sqrt{\\alpha_x vt }} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y_i/2}{2\\sqrt{\\alpha_y x}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y_i/2}{2\\sqrt{\\alpha_y x)}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left. \\left( \\operatorname{erf} \\left[ \\frac{Z}{2\\sqrt{\\alpha_z x)}} \\right] - \\operatorname{erf} \\left[ \\frac{-Z}{2\\sqrt{\\alpha_z x}} \\right] \\right) \\right\\} \\\\\n", + " & \\text{with} \\quad P = \\sqrt{1+4\\mu \\alpha_x/v}\n", + "\\end{align}\n", + "\n", + "However, this solution (implemented as the Bioscreen solution in this package) is a truncation of the full analytical equation, which contains an additional term comprised of a product of an exp and erfc. See equation 2 below;\n", + "\n", + "\\begin{align}\\tag{2}\n", + " C(x, y, z, t) &= \\sum_{i=1}^{n}\\left\\{ \\frac{C^*_{0,i}}{8} \\exp \\left(-k_s t\\right) \\right. \\\\\n", + " &\\quad \\cdot \\left( \\exp \\left[ \\frac{x\\left(1-P\\right)}{2\\alpha_x}\\right]\n", + " \\cdot \\operatorname{erfc} \\left[ \\frac{x - Pvt}{2\\sqrt{\\alpha_x vt }} \\right] \\right.\\\\\n", + " &\\quad \\: + \\left. \\exp \\left[ \\frac{x\\left(1+P\\right)}{2\\alpha_x}\\right]\n", + " \\cdot \\operatorname{erfc} \\left[ \\frac{x + Pvt}{2\\sqrt{\\alpha_x vt }} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y_i/2}{2\\sqrt{\\alpha_y x}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y_i/2}{2\\sqrt{\\alpha_y x)}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left. \\left( \\operatorname{erf} \\left[ \\frac{Z}{2\\sqrt{\\alpha_z x)}} \\right] - \\operatorname{erf} \\left[ \\frac{-Z}{2\\sqrt{\\alpha_z x}} \\right] \\right) \\right\\} \\\\\n", + " & \\text{with} \\quad P = \\sqrt{1+4\\left(\\mu - k_s \\right) \\alpha_x/v}\n", + "\\end{align}\n", + "\n", + "As in the current age, the added computing time required for this term is negligible, it is included in the Anatrans model of this package.\n", + "\n", + "This notebook gives an overview of the differences between the truncated and untruncated Domenico solution, and briefly discusses the implementation in the mibitrans package. For ease of explanation, consider a situation with no decay or source depletion, with $\\mu = 0$ and $k_s = 0$, thus $P = 1$.\n", + "\n", + "Note that exact analytical models (like Wexler (1992), which is the basis for the mibitrans solution, as implemented in BIOSCREEN-AT) still give more accurate results than the Anatrans or solution, regardless of truncation (West et al., 2007). Furthermore, note the change in the definition of the source depletion term and P in equation 2. This difference is explained and investigated in 'example_source_depletion.ipynb'.\n" + ] + }, + { + "cell_type": "markdown", + "id": "6101211c57b2581b", + "metadata": {}, + "source": [ + "#### Over- and underflow of the additional term\n", + "\n", + "In mibitrans, 64-bit float numpy arrays are used for the calculations, consequently, it only can hold values between approximately $10^{-300}$ and $10^{+300}$. While these are generally beyond any reasonable order of magnitude, certain parameter combinations may cause an underflow (value < $10^{-300}$ and therefore set to 0), or overflow (value > $10^{+300}$ and therefore set to infinity) to occur. Especially at distances further from the source. However, this does not necessarily mean that the additional term is negligibly small, as for $a \\to \\infty$, $\\exp(a) \\to \\infty$ and $\\text{erfc}(a) \\to 0$, and $\\infty \\cdot 0$ is indeterminate. To combat this, the erfcx function is used, where $\\text{erfcx}(a) = \\exp(a^2) \\cdot \\operatorname{erfc}(a)$. This transforms the additional term as: $\\exp(b) \\cdot \\text{erfc}(a) = \\frac{\\exp(b)}{\\exp(a^2)} \\cdot \\exp(a^2) \\cdot \\operatorname{erfc}(a) = \\exp(b - a^2) \\cdot \\text{erfcx}(a)$. Under any reasonable field conditions, this expression of the additional term will not cause over- or underflow. And is therefore implemented as such in the Anatrans solution." + ] + }, + { + "cell_type": "markdown", + "id": "ec21cdd2b1177f6c", + "metadata": {}, + "source": [ + "#### Visualization of the additional term\n", + "\n", + "Consider the following parameters as example field conditions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f1a89059aacfc33", + "metadata": {}, + "outputs": [], + "source": [ + "hydro = mbt.HydrologicalParameters(\n", + " velocity=0.1,\n", + " porosity=0.3,\n", + " alpha_x=1,\n", + " alpha_y=0,\n", + " alpha_z=0,\n", + ")\n", + "\n", + "att = mbt.AttenuationParameters(\n", + " retardation=1,\n", + ")\n", + "\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([5,10,15]),\n", + " source_zone_concentration=np.array([15,10,5]),\n", + " total_mass=\"inf\",\n", + " depth=10,\n", + ")\n", + "\n", + "model = mbt.ModelParameters(\n", + " model_length=175,\n", + " model_width=50,\n", + " model_time=5*365,\n", + " dx=1,\n", + " dy=5,\n", + " dt=1,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66f012e6a013c314", + "metadata": {}, + "outputs": [], + "source": [ + "ana_obj = mbt.Anatrans(hydro,att, source, model)\n", + "ana_ndeg = ana_obj.run()\n", + "\n", + "ana_ndeg.centerline(time=73, color=\"green\", label=\"t = 73 days\")\n", + "ana_ndeg.centerline(time=365, color=\"blue\", label=\"t = 365 days\")\n", + "ana_ndeg.centerline(time=3*365, color=\"red\", label=\"t = 3*365 days\")\n", + "plt.title(\"Anatrans concentration distribution at various times\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "536c63a4ccab3670", + "metadata": {}, + "source": [ + "Without considering transverse dispersion, source depletion, decay or multiple source zones, equation 1 would look like:\n", + "\n", + "\\begin{equation}\\tag{3}\n", + " C(x, t) = \\frac{C_{0}}{2} \\operatorname{erfc} \\left( \\frac{x - vt}{2\\sqrt{\\alpha_x vt }}\\right)\n", + "\\end{equation}\n", + "\n", + "And equation 2 would be:\n", + "\n", + "\\begin{equation}\\tag{4}\n", + " C(x, t) = \\frac{C_{0}}{2} \\left[ \\operatorname{erfc} \\left( \\frac{x - vt}{2\\sqrt{\\alpha_x vt }}\\right) + \\exp \\left(\\frac{x}{2\\alpha_x}\\right) \\cdot \\operatorname{erfc} \\left( \\frac{x + vt}{2\\sqrt{\\alpha_x vt }} \\right) \\right]\n", + "\\end{equation}\n", + "\n", + "The boundary value for continuous input is $C(x=0, t) = C_0$. However, when evaluated at $x=0$ and $t=0$, the erfc term in equation 3 resolves to $1$ and thus $C(x=0, t=0) = \\frac{C_0}{2}$. Therefore, equation 3 and by extension equation 1 do not abide by the boundary condition. This is visualized below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f33e9d250fa9a10d", + "metadata": {}, + "outputs": [], + "source": [ + "xxx = ana_obj.xxx\n", + "ttt = ana_obj.ttt\n", + "rv = ana_obj.rv\n", + "alpha_x = hydro.alpha_x\n", + "velocity = hydro.velocity\n", + "\n", + "# Calculate additional term under no decay conditions\n", + "erfc_inner = (xxx + rv * ttt) / (2 * np.sqrt(alpha_x * rv * ttt))\n", + "add_term = np.exp(xxx / alpha_x - erfc_inner**2) * erfcx(erfc_inner)\n", + "# Calculate the advection term\n", + "advec_term = erfc((xxx - rv * ttt) / (2 * np.sqrt(alpha_x * rv * ttt)))\n", + "\n", + "# Time does not evaluate at t=0, so add manually for visualization\n", + "t = np.zeros(len(ttt[:,0,0])+1)\n", + "t[1:] = ttt[:,0,0]\n", + "adv_x0 = np.zeros(len(ttt[:,0,0])+1)\n", + "add_x0 = np.zeros(len(ttt[:,0,0])+1)\n", + "# For x=0 and t=0, terms are equal to 1\n", + "adv_x0[0] = 1\n", + "add_x0[0] = 1\n", + "adv_x0[1:] = advec_term[:,0,0]\n", + "add_x0[1:] = add_term[:,0,0]\n", + "\n", + "plt.plot(t, adv_x0, color=\"blue\", label=\"advective term\")\n", + "plt.plot(t, add_x0, color=\"green\", label=\"additional term\")\n", + "plt.plot(t, adv_x0 + add_x0, linestyle=\":\", color=\"black\", label=\"sum\")\n", + "plt.xlabel(\"time [days]\")\n", + "plt.ylabel(\"value at x=0 [-]\")\n", + "plt.legend()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "26406a331e87db53", + "metadata": {}, + "source": [ + "Thus, only at around t = 150 days is the advective term is equal to $2$, and the boundary condition at $x=0$ satisfied. When including the additional term, the boundary condition is satisfied at all times. However, the error introduced by the truncation propagates, as shown in the figures below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faccdf43d9bc6b7a", + "metadata": {}, + "outputs": [], + "source": [ + "plt.pcolormesh(xxx[None, None, :], ttt[:, None, None], add_term[:, 0, :])\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(\"time [days]\")\n", + "plt.colorbar(label = \"value additional term [-]\")\n", + "plt.show()\n", + "\n", + "x = xxx[0, 0, :]\n", + "max_additional_x_term = np.max(add_term[:,0,:], axis = 0)\n", + "plt.plot(x, max_additional_x_term, color=\"black\")\n", + "plt.xlabel(\"time [days]\")\n", + "plt.ylabel(\"Maximum value additional term [-]\")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d4c6f54558068a02", + "metadata": {}, + "source": [ + "The value of the additional term is highest at $x=0$ and $t=0$. While the additional term decreases rapidly for higher values of $x$ and $t$, it remains relevant along the advective front. The size of the error that the truncation introduces is dependent on the Peclet number; $Pe = \\frac{vx}{D_x}$. And thus for input parameters, dependent on the flow velocity and longitudinal dispersivity." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_equation_formulation.ipynb b/examples/example_equation_formulation.ipynb deleted file mode 100644 index 1d84ef2..0000000 --- a/examples/example_equation_formulation.ipynb +++ /dev/null @@ -1,203 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "1bb35746ac89a12", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from mibitrans.data.parameters import AttenuationParameters\n", - "from mibitrans.data.parameters import HydrologicalParameters\n", - "from mibitrans.data.parameters import ModelParameters\n", - "from mibitrans.data.parameters import SourceParameters\n", - "from mibitrans.transport.models import Anatrans\n", - "from mibitrans.visualize.plot_line import centerline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb4e31a6383d8c4a", - "metadata": {}, - "outputs": [], - "source": [ - "ft = 3.281\n", - "hydro = HydrologicalParameters(\n", - " velocity=350/ft/365,\n", - " h_gradient=0.048, # Hydraulic gradient [-]\n", - " h_conductivity=0.495, # Hydraulic conductivity [m/day]\n", - " porosity=0.25, # Effective soil porosity [-]\n", - " alpha_x=13.3/ft, # Longitudinal dispersivity, in [m]\n", - " alpha_y=1.3/ft, # Transverse horizontal dispersivity, in [m]\n", - " alpha_z=0 # Transverse vertical dispersivity, in [m]\n", - ")\n", - "\n", - "att = AttenuationParameters(\n", - " # Soil bulk density in [g/m^3]\n", - " bulk_density=1.7,\n", - " # Partition coefficient of the transported contaminant to soil organic matter, in [m^3/g]\n", - " partition_coefficient=38,\n", - " # Fraction of organic material in the soil [-]\n", - " fraction_organic_carbon=5.7e-5,\n", - " # Molecular diffusion, in [m2/day]\n", - " diffusion=0,\n", - " # Contaminant half life, in [days]\n", - " half_life=0\n", - ")\n", - "\n", - "source = SourceParameters(\n", - " source_zone_boundary=np.array([30/ft, 35/ft, 40/ft]),\n", - " source_zone_concentration=np.array([13.68, 2.508, 0.057]),\n", - " depth=10/ft,\n", - " total_mass=50000\n", - ")\n", - "\n", - "model = ModelParameters(\n", - " # Model extent in the longitudinal (x) direction in [m].\n", - " model_length = 320/ft,\n", - " # Model extent in the transverse horizontal (y) direction in [m].\n", - " model_width = 100/ft,\n", - " # Model duration in [days].\n", - " model_time = 6 * 365,\n", - " # Model grid discretization step size in the longitudinal (x) direction, in [m].\n", - " dx = 1/ft,\n", - " # Model grid discretization step size in the transverse horizontal (y) direction, in [m].\n", - " dy = 1/ft,\n", - " # Model time discretization step size, in [days]\n", - " dt = 365 / 20\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3e7374d25890988b", - "metadata": {}, - "outputs": [], - "source": [ - "class AnatransAlternative(Anatrans):\n", - " \"\"\"Mibitrans model class with alternative calculations.\"\"\"\n", - "\n", - " def __init__(\n", - " self,\n", - " hydrological_parameters,\n", - " attenuation_parameters,\n", - " source_parameters,\n", - " model_parameters,\n", - " verbose=False,\n", - " ):\n", - " \"\"\"Initialize model class.\"\"\"\n", - " super().__init__(hydrological_parameters, attenuation_parameters, source_parameters, model_parameters, verbose)\n", - "\n", - " def _equation_term_source_decay(self, xxx, ttt):\n", - " #adapted source term\n", - " term = np.exp(-self.k_source * ttt)\n", - " # Term can be max 1; can not have 'generation' of solute ahead of advection.\n", - " return np.where(term > 1, 1, term)\n", - "\n", - " def _calculate_concentration_for_all_xyt(self, xxx, yyy, ttt):\n", - " cxyt = 0\n", - " #adapted decay_sqrt\n", - " decay_sqrt = np.sqrt(1 + 4 * (self._decay_rate - self.k_source) * self._hyd_pars.alpha_x / self.rv)\n", - " x_term = self._equation_term_x(xxx, ttt, decay_sqrt)\n", - " additional_x = self._equation_term_additional_x(xxx, ttt, decay_sqrt)\n", - " z_term = self._equation_term_z(xxx)\n", - " source_decay = self._equation_term_source_decay(xxx, ttt)\n", - " for i in range(len(self.c_source)):\n", - " y_term = self._equation_term_y(i, xxx, yyy)\n", - " cxyt_step = 1 / 8 * self.c_source[i] * source_decay * (x_term + additional_x) * y_term * z_term\n", - " cxyt += cxyt_step\n", - " if self._mode == \"instant_reaction\":\n", - " self.cxyt_noBC = cxyt.copy()\n", - " cxyt -= self.biodegradation_capacity\n", - " cxyt = np.where(cxyt < 0, 0, cxyt)\n", - " self.has_run = True\n", - " return cxyt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1af5e77b663d6c36", - "metadata": {}, - "outputs": [], - "source": [ - "ana_object = Anatrans(hydro, att, source, model)\n", - "ana_object.run()\n", - "\n", - "alt_object = AnatransAlternative(hydro, att, source, model)\n", - "alt_object.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f6401bd8fb02806", - "metadata": {}, - "outputs": [], - "source": [ - "ana_object.centerline(color=\"green\")\n", - "alt_object.centerline(color=\"red\", linestyle=\"--\")\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d63c6e5dba784862", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib notebook" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba19cf9692813b01", - "metadata": {}, - "outputs": [], - "source": [ - "anim = centerline(model=[ana_object, alt_object],\n", - " legend_names=[\"Anatrans model\", \"Alternative model\"],\n", - " linestyle=\":\",\n", - " animate=True)\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a1d736e61b017e54", - "metadata": {}, - "outputs": [], - "source": [ - "ana_object.plume_3d()\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/example_exact_equation.ipynb b/examples/example_exact_equation.ipynb deleted file mode 100644 index 667ebc8..0000000 --- a/examples/example_exact_equation.ipynb +++ /dev/null @@ -1,373 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "2231e3885ab4c960", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from mibitrans.data.parameter_information import ElectronAcceptors\n", - "from mibitrans.data.parameters import AttenuationParameters\n", - "from mibitrans.data.parameters import HydrologicalParameters\n", - "from mibitrans.data.parameters import ModelParameters\n", - "from mibitrans.data.parameters import SourceParameters\n", - "from mibitrans.transport.models import Bioscreen\n", - "from mibitrans.transport.models import Mibitrans\n", - "from mibitrans.visualize.plot_line import centerline\n", - "from mibitrans.visualize.plot_surface import plume_3d" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1c04100008cf2f67", - "metadata": {}, - "outputs": [], - "source": [ - "ft = 3.281 # factor to convert ft to m" - ] - }, - { - "cell_type": "markdown", - "id": "642abbcc74f16426", - "metadata": {}, - "source": [ - "This example compares the Domenico (1987) analytical solution as implemented in the original BIOSCREEN with the exact solution described in Karanovic et al. (2007), implemented in BIOSCREEN-AT. The solution from Karanovic is adapted as follows:" - ] - }, - { - "cell_type": "markdown", - "id": "8fa244fd5005f090", - "metadata": {}, - "source": [ - "\\begin{equation}\n", - "C(x,y,z,t) = \\sum_{i=1}^{n}\\left(C^*_{0,i}\\frac{x}{8\\sqrt{\\pi D^{'}_{x}}}\\exp(-\\gamma t)\n", - "\\cdot \\int_{0}^{t}\\left[\\frac{1}{\\tau^{\\frac{3}{2}}} \\exp\\left((\\gamma - \\lambda_{EFF})\\tau - \\frac{(x-v^{'}\\tau)^2}{4D^{'}_{x}\\tau}\\right)\n", - "\\cdot \\left\\{ERFC\\left(\\frac{y-Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right)-ERFC\\left(\\frac{y+Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right) \\right\\}\n", - "\\cdot \\left\\{ERFC\\left(\\frac{z-Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right)-ERFC\\left(\\frac{z+Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right) \\right\\}\\right] d\\tau \\right)\n", - "\\end{equation}" - ] - }, - { - "cell_type": "markdown", - "id": "8efc6ab254154e63", - "metadata": {}, - "source": [ - "Which allows for source superposition and therefore multiple source zones. For instant reaction, BC is added to the outer source zone and subtracted from the resulting concentrations. Where (C(x,y,z,t) < 0) = 0" - ] - }, - { - "cell_type": "markdown", - "id": "765613bdd5ab7be8", - "metadata": {}, - "source": [ - "## Input parameters\n", - "Input parameters are the default parameters when opening up BIOSCREEN-AT. All imperial units are converted to consistent metric units.\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d10e013699b8f5d", - "metadata": {}, - "outputs": [], - "source": [ - "hydro = HydrologicalParameters(\n", - " # Groundwater flow velocity, in [m/day]\n", - " velocity=335.2 / ft / 365,\n", - " # Effective soil porosity [-]\n", - " porosity=0.25,\n", - " # Longitudinal dispersivity, in [m]\n", - " alpha_x=28.887 / ft,\n", - " # Transverse horizontal dispersivity, in [m]\n", - " alpha_y=2.889 / ft,\n", - " # Transverse vertical dispersivity, in [m]\n", - " alpha_z=0.289 / ft,\n", - ")\n", - "\n", - "# Parameters for retardation and degradation\n", - "att = AttenuationParameters(\n", - " # Contaminant retardation factor [-]\n", - " retardation=1.20672,\n", - " # Contaminant half life, in [days]\n", - " half_life=0,\n", - ")\n", - "\n", - "# Parameters for source dimensions and concentrations\n", - "source = SourceParameters(\n", - " # Extent of source in the positive y-direction (total source extent is 2*50=100\n", - " source_zone_boundary=np.array([50 / ft]),\n", - " # Concentration in source zone\n", - " source_zone_concentration=np.array([9]),\n", - " # Source extent in z-direction\n", - " depth=10 / ft,\n", - " # Undissolved mass of contaminant source\n", - " total_mass=\"inf\",\n", - ")\n", - "\n", - "model = ModelParameters(\n", - " # Model extent in the longitudinal (x) direction in [m].\n", - " model_length=2500 / ft,\n", - " # Model extent in the transverse horizontal (y) direction in [m].\n", - " model_width=500 / ft,\n", - " # Model duration in [days].\n", - " model_time=5 * 365,\n", - " # Model grid discretization step size in the longitudinal (x) direction, in [m].\n", - " dx=20 / ft,\n", - " # Model grid discretization step size in the transverse horizontal (y) direction, in [m].\n", - " dy=5 / ft,\n", - " # Model time discretization step size, in [days]\n", - " dt=365,\n", - ")\n", - "\n", - "# Make object for electron acceptor concentration to use for instant reaction model\n", - "ea=ElectronAcceptors(\n", - " # Difference between background oxygen and current oxygen concentration in groundwater, in [g/m^3]\n", - " delta_oxygen=5.78,\n", - " # Difference between background nitrate and current nitrate concentration in groundwater, in [g/m^3]\n", - " delta_nitrate=17,\n", - " # Current ferrous iron concentration in groundwater, in [g/m^3]\n", - " ferrous_iron=11.3,\n", - " # Difference between background sulfate and current sulfate concentration in groundwater, in [g/m^3]\n", - " delta_sulfate=100,\n", - " # Current methane concentration in groundwater, in [g/m^3]\n", - " methane=0.414,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "6fd5739844c6c6a4", - "metadata": {}, - "source": [ - "## Running model\n", - "As the exact solution uses an integral, run time is longer than that of the Bioscreen solution, depending on model resolution. Using verbose when running the model is recommended, to keep track of the calculation process." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e1098849ca30e36", - "metadata": {}, - "outputs": [], - "source": [ - "att.half_life = 0\n", - "mbt_nodecay = Mibitrans(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "mbt_nodecay.run()\n", - "\n", - "# Set half life to a non-0 value to simulate linear decay\n", - "att.half_life = 10 * 365\n", - "\n", - "mbt_lindecay = Mibitrans(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "mbt_lindecay.run()\n", - "\n", - "mbt_instant = Mibitrans(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "mbt_instant.instant_reaction(electron_acceptors=ea)\n", - "mbt_instant.run()" - ] - }, - { - "cell_type": "markdown", - "id": "aa774fd6b9df0d69", - "metadata": {}, - "source": [ - "## Visualize" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0bb52e20f9ee6e0", - "metadata": {}, - "outputs": [], - "source": [ - "centerline(mbt_nodecay, color=\"red\", label=\"No decay\")\n", - "centerline(mbt_lindecay, color=\"blue\", label=\"Linear decay\")\n", - "centerline(mbt_instant, color=\"green\", label=\"Instant reaction\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8313008c644e49ae", - "metadata": {}, - "outputs": [], - "source": [ - "plume_3d(mbt_nodecay, cmap=\"viridis\")\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "1beab531604f46c3", - "metadata": {}, - "source": [ - "Running Bioscreen solution for comparison" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "47d5a37b375a3af4", - "metadata": {}, - "outputs": [], - "source": [ - "att.half_life = 0\n", - "bio_nodecay = Bioscreen(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "bio_nodecay.run()\n", - "\n", - "# Set half life to a non-0 value to simulate linear decay\n", - "att.half_life = 10 * 365\n", - "\n", - "bio_lindecay = Bioscreen(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "bio_lindecay.run()\n", - "\n", - "bio_instant = Bioscreen(\n", - " hydrological_parameters=hydro,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model,\n", - " verbose=True,\n", - ")\n", - "bio_instant.instant_reaction(electron_acceptors=ea)\n", - "bio_instant.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "895bdb769ea63009", - "metadata": {}, - "outputs": [], - "source": [ - "centerline(mbt_nodecay, color=\"red\", label=\"No decay Mibitrans\")\n", - "centerline(mbt_lindecay, color=\"blue\", label=\"Linear decay Mibitrans\")\n", - "centerline(mbt_instant, color=\"green\", label=\"Instant reaction Mibitrans\")\n", - "centerline(bio_nodecay, color=\"tomato\", linestyle=\":\", label=\"No decay Bioscreen\")\n", - "centerline(bio_lindecay, color=\"lightblue\", linestyle=\":\", label=\"Linear decay Bioscreen\")\n", - "centerline(bio_instant, color=\"lightgreen\", linestyle=\":\", label=\"Instant reaction Bioscreen\")\n", - "plt.title(\"Comparison between Mibitrans and Bioscreen, at t=1825 days.\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "ff9ec557bdfad139", - "metadata": {}, - "source": [ - "## Comparing to BIOSCREEN-AT\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7c122c684e746968", - "metadata": {}, - "outputs": [], - "source": [ - "bio_x = [0, 250 / ft, 500 / ft, 750 / ft, 1000 / ft, 1250 / ft, 1500 / ft, 1750 / ft, 2000 / ft, 2250 / ft, 2500 / ft]\n", - "bio_at_nodecay = np.array(\n", - " (\n", - " [\n", - " [9.000, 3.635, 0.236, 0.001, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.649, 2.144, 0.452, 0.022, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.696, 2.707, 1.505, 0.493, 0.064, 0.003, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.699, 2.767, 1.872, 1.161, 0.483, 0.104, 0.010, 0.000, 0.000, 0.000],\n", - " [9.000, 4.699, 2.772, 1.932, 1.423, 0.947, 0.459, 0.134, 0.021, 0.002, 0.000],\n", - " ]\n", - " )\n", - ")\n", - "\n", - "bio_at_lindecay = np.array(\n", - " (\n", - " [\n", - " [9.000, 3.479, 0.222, 0.001, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.407, 1.944, 0.401, 0.019, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.448, 2.423, 1.289, 0.412, 0.053, 0.002, 0.000, 0.000, 0.000, 0.000],\n", - " [9.000, 4.450, 2.471, 1.580, 0.938, 0.380, 0.081, 0.008, 0.000, 0.000, 0.000],\n", - " [9.000, 4.450, 2.475, 1.624, 1.131, 0.721, 0.340, 0.097, 0.015, 0.001, 0.000],\n", - " ]\n", - " )\n", - ")\n", - "colors = [\"tomato\", \"sandybrown\", \"khaki\", \"greenyellow\", \"lightgreen\"]\n", - "colors_bio = [\"red\", \"orange\", \"gold\", \"yellowgreen\", \"green\"]\n", - "for i in range(len(mbt_nodecay.t)):\n", - " centerline(mbt_nodecay, time=mbt_nodecay.t[i], color=colors[i], label=f\"Mibitrans, t={mbt_nodecay.t[i]}\")\n", - " plt.plot(bio_x, bio_at_nodecay[i, :], color=colors_bio[i], linestyle=\":\", label=f\"bio_at, t={mbt_nodecay.t[i]}\")\n", - "plt.legend()\n", - "plt.show()\n", - "\n", - "\n", - "for i in range(len(mbt_lindecay.t)):\n", - " centerline(mbt_lindecay, time=mbt_lindecay.t[i], color=colors[i], label=f\"Mibitrans, t={mbt_lindecay.t[i]}\")\n", - " plt.plot(bio_x, bio_at_lindecay[i, :], color=colors_bio[i], linestyle=\":\", label=f\"bio_at, t={mbt_lindecay.t[i]}\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "e9a6b6c841cf9302", - "metadata": {}, - "source": [ - "mibitrans output corresponds very well with the BIOSCREEN-AT output, and has higher resolution. Note that despite BIOSCREEN-AT taking in instant reaction parameters, it does not have the option to calculate biodegradation this way." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/example_keesler.ipynb b/examples/example_keesler.ipynb new file mode 100644 index 0000000..f01dbdd --- /dev/null +++ b/examples/example_keesler.ipynb @@ -0,0 +1,537 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ea64c92477bb5226", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt" + ] + }, + { + "cell_type": "markdown", + "id": "ab077fc2fd5e825", + "metadata": {}, + "source": [ + "### Field site example: Keesler Air-Force Base\n", + "\n", + "The distribution of BIOSCREEN v1.4 is accompanied by an field site example of the Keesler Air Force Base in Mississippi, USA. As the data is sourced from the USA, units are mixed imperial and metric. For the modelling below, all parameters are converted to metric for consistency [1].\n", + "\n", + "#### Site description\n", + "In 1987, during their removal, multiple underground storage tanks (UST) leaked a mixture of BTEX and lead to the groundwater. Remediation efforts included a 'bioventing' system and an in well aeration system. Monitoring wells were used to evaluate natural attenuation [2,3]. Local geology is a fine- to medium-grained sand, underlain by a clay layer at 20ft depth. Bottom of sandy layer has local presence of peat. Thickness and continuity of clay layer are unknown, but not every boring showed the clay layer at 20ft. Assumption of clay continuous clay layer was made. Unconfined aquifer with groundwater levels varying between 5 and 9 ft below ground level. Groundwater flow is to the north-east, which eventually discharges into the Back Bay of Biloxi, 2100ft downgradient [4]. Values of hydraulic gradient and conductivity vary, with values of 0.003 to 0.0083 ft/ft and 40, 61, 32 ft/day listed in [4]. In [1]. the values of 0.003 ft/ft and 0.011 cm/sec (31.2 ft/day) were used. Effective porosity is estimated to be 0.25 [4], resulting in groundwater velocity of 0.8 ft/day. Although [1] uses an effective porosity of 0.3, and results in groundwater velocity of 0.31 ft/day. For consistency, values reported in [1] are used in the modelling. Dispersivity values were based on estimated plume length of 280ft [1]. With 13.3ft, 1.3ft and 0ft for longitudinal, transverse horizontal and transverse vertical dispersivity respectively. After calibration, these values were changed to 32.5ft, 3.25ft and 0ft respectively [1]." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f868ccbb35cc62f3", + "metadata": {}, + "outputs": [], + "source": [ + "ft = 3.281 #Conversion factor ft/m\n", + "\n", + "hydro_pars = mbt.HydrologicalParameters(\n", + " h_conductivity = 0.011 / 100 * 3600 * 24, #m/d\n", + " h_gradient = 0.003, # m/m\n", + " porosity = 0.3,\n", + " alpha_x = 13.3 /ft,\n", + " alpha_y = 1.3/ft,\n", + " alpha_z = 0\n", + ")\n", + "print(hydro_pars.velocity)\n", + "print(hydro_pars.h_conductivity)" + ] + }, + { + "cell_type": "markdown", + "id": "c43a620f2aebcd82", + "metadata": {}, + "source": [ + "The fraction of organic carbon is 0.000057 based of lab analysis, soil bulk density is estimated to be 1.7 (kg/L) [1]. Partition coefficient of BTEX differs by order of magnitude (38, 135, 95, 240 L/kg for B, T, E, X), [1] uses the value of benzene; 38 L/kg. Electron acceptor concentrations are based on groundwater sampling performed in 1995. For oxygen, nitrate and sulfate, the difference between background concentration in the aquifer and minimum concentrations in the plume are used. For ferrous iron and methane, average concentrations in the plume area are used. Values differ somewhat from those reported in [4] (elaborate?)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21ace43c3711a552", + "metadata": {}, + "outputs": [], + "source": [ + "att_pars = mbt.AttenuationParameters(\n", + " bulk_density=1.7, # kg/L (note; for consistency, should use g/m3, but using more intuitive kg/L instead)\n", + " partition_coefficient=38, # L/kg (units cancel out with bulk density)\n", + " fraction_organic_carbon=0.000057,\n", + " decay_rate = 0, # 1/day, for now, we do not consider linear decay rate.\n", + ")\n", + "att_pars.calculate_retardation(hydro_pars.porosity)\n", + "print(\"Retardation based on input values =\", att_pars.retardation)\n", + "\n", + "electron_acceptor_concentrations = mbt.ElectronAcceptors(\n", + " delta_oxygen=2.05 - 0.4, #background conc - minimum conc, [g/m3]\n", + " delta_nitrate=0.07 - 0, #background conc - minimum conc, [g/m3]\n", + " ferrous_iron=16.6, #average conc, [g/m3]\n", + " delta_sulfate=26.2 - 3.8, #background conc - minimum conc, [g/m3]\n", + " methane=6.6, #average conc, [g/m3]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a5c180d376ce7795", + "metadata": {}, + "source": [ + "Conditions and parameters of the contaminant source were inferred from BTEX monitoring data and geologic logs [1]. The source thickness is estimated to be 10ft, with a total 2000kg of pure BTEX. Total source width of 130ft, with 28ft zone of 0.057 g/m3, 30ft zone of 2.508 g/m3, 14ft zone of 13.68, 30ft zone of 2.508 g/m3, 28ft zone of 0.057 g/m3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15bd8aad71c1ec1e", + "metadata": {}, + "outputs": [], + "source": [ + "source_pars = mbt.SourceParameters(\n", + " # Note; as plume is symmetric, source is described from center outwards in y-direction.\n", + " source_zone_boundary=np.array([7/ft, 37/ft, 65/ft]), #[m]\n", + " source_zone_concentration=np.array([13.68, 2.508, 0.057]), #[g/m3]\n", + " depth=10/ft, #[m]\n", + " total_mass=2000000, #[g]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f51ce97cbb15f0e9", + "metadata": {}, + "source": [ + "Model dimensions used for the BIOSCREEN model are; length = 320ft, width = 200ft, duration=6y [1]. In BIOSCREEN resolution is always a fraction of model dimensions, 1/10 for length, 1/4 for width and 1/10 for time. For the mibitrans package, model resolution is freely changeable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c657f6368a591bb", + "metadata": {}, + "outputs": [], + "source": [ + "model_pars = mbt.ModelParameters(model_length=500/ft,\n", + " model_width=200/ft,\n", + " model_time=6*365,\n", + " dx=2/ft,\n", + " dy=1/ft,\n", + " dt=365/5)" + ] + }, + { + "cell_type": "markdown", + "id": "ac1f32016fb4d14f", + "metadata": {}, + "source": [ + "#### Models\n", + "\n", + "With the model parameters entered, the model is run with the three models implemented in mibitrans. The Bioscreen model class performs the same calculations as those implemented in BIOSCREEN. The Anatrans model class uses the untruncated form of the Bioscreen model, the additional term increases accuracy for small times and distances. The Mibitrans model class uses an analytical solution based on Wexler (1992). For further details about each analytical model, see the 'example_walkthrough' notebook and the theory that accompany this package." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fd5d083a23264c5", + "metadata": {}, + "outputs": [], + "source": [ + "bio_model = mbt.Bioscreen(hydrological_parameters=hydro_pars,\n", + " attenuation_parameters=att_pars,\n", + " source_parameters=source_pars,\n", + " model_parameters=model_pars,)\n", + "bio_results = bio_model.run()\n", + "bio_results.centerline()\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfe447c47e49d8e6", + "metadata": {}, + "outputs": [], + "source": [ + "ana_model = mbt.Anatrans(hydrological_parameters=hydro_pars,\n", + " attenuation_parameters=att_pars,\n", + " source_parameters=source_pars,\n", + " model_parameters=model_pars,)\n", + "ana_results = ana_model.run()\n", + "ana_results.centerline()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "451b06b489120021", + "metadata": {}, + "outputs": [], + "source": [ + "mbt_model = mbt.Mibitrans(hydrological_parameters=hydro_pars,\n", + " attenuation_parameters=att_pars,\n", + " source_parameters=source_pars,\n", + " model_parameters=model_pars)\n", + "mbt_results =mbt_model.run()\n", + "mbt_results.centerline()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a680dad770bc67b4", + "metadata": {}, + "source": [ + "Comparing the three models at the first time step shows that the Bioscreen model underestimates the source concentrations. The Anatrans and Mibitrans models start at the correct source zone concentration, but show different behaviour along the domain." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b315985d03b544d3", + "metadata": {}, + "outputs": [], + "source": [ + "plt.clf() #Clear any previous plots\n", + "\n", + "time_point = 365/5\n", + "mbt_results.centerline(time=time_point, label=\"Mibitrans\", color=\"blue\")\n", + "ana_results.centerline(time=time_point, label=\"Anatrans\", color=\"red\", linestyle=\"--\")\n", + "bio_results.centerline(time=time_point, label=\"Bioscreen\", color=\"green\", linestyle=\":\")\n", + "plt.xlim((-5,100)) # Reduce the x-axis limit to better observe differences\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "631469add3ef8e24", + "metadata": {}, + "outputs": [], + "source": [ + "# Needed to show animations in Jupyter Notebook\n", + "%matplotlib ipympl" + ] + }, + { + "cell_type": "markdown", + "id": "8c2ca032cca0abb4", + "metadata": {}, + "source": [ + "Over time, the source concentration for the Bioscreen model increases to the expected value, and the results resolve to that of the Anatrans model. Differences between the Anatrans and Mibitrans model decrease when plume front is transported away from source." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab773f981180e3f7", + "metadata": {}, + "outputs": [], + "source": [ + "anim = mbt.centerline(\n", + " [mbt_results, ana_results, bio_results],\n", + " legend_names=[\"Mibitrans\", \"Anatrans\", \"Bioscreen\"],\n", + " animate=True\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4621161235a48efc", + "metadata": {}, + "source": [ + "The models above do not consider biodegradation. Using the electron acceptor concentrations, a biodegradation capacity (BC) is determined and applied to the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb54e5a725c36ff6", + "metadata": {}, + "outputs": [], + "source": [ + "bio_model.instant_reaction(electron_acceptors=electron_acceptor_concentrations)\n", + "ana_model.instant_reaction(electron_acceptors=electron_acceptor_concentrations)\n", + "mbt_model.instant_reaction(electron_acceptors=electron_acceptor_concentrations)\n", + "bio_results_instant = bio_model.run()\n", + "ana_results_instant = ana_model.run()\n", + "mbt_results_instant = mbt_model.run()" + ] + }, + { + "cell_type": "markdown", + "id": "db4ba13e0de6882a", + "metadata": {}, + "source": [ + "At the plume fringes, electron acceptor concentrations remain high enough to allow for full biodegradation of the contaminant plume." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29b7a6eb6edf945a", + "metadata": {}, + "outputs": [], + "source": [ + "anim = mbt.centerline(\n", + " [mbt_results_instant, ana_results_instant, bio_results_instant],\n", + " legend_names=[\"Mibitrans\", \"Anatrans\", \"Bioscreen\"],\n", + " animate=True\n", + ")\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6a972fba6a3162d", + "metadata": {}, + "source": [ + "## Comparison to field data\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9cf9686cf13961ea", + "metadata": {}, + "outputs": [], + "source": [ + "time_point = 365 * 6\n", + "plt.figure()\n", + "mbt_results_instant.centerline(time=time_point, label=\"Mibitrans\", color=\"blue\")\n", + "ana_results_instant.centerline(time=time_point, label=\"Anatrans\", color=\"red\", linestyle=\"--\")\n", + "bio_results_instant.centerline(time=time_point, label=\"Bioscreen\", color=\"green\", linestyle=\":\")\n", + "\n", + "field_data_x = np.array([0, 32/ft, 64/ft, 192/ft, 288/ft])\n", + "field_data_c = np.array([12,5,1,0.5,0.001])\n", + "plt.scatter(field_data_x, field_data_c, label=\"field data\", color=\"black\")\n", + "plt.xlim(0,150)\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d26bd567fade9a58", + "metadata": {}, + "source": [ + "Prediction is off from the field data. As mentioned before, dispersivity was calibrated to better represent observed concentrations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c19d1d712c57336", + "metadata": {}, + "outputs": [], + "source": [ + "new_hydrology = mbt.HydrologicalParameters(\n", + " h_conductivity = 0.011 / 100 * 3600 * 24, #m/d\n", + " h_gradient = 0.003, # m/m\n", + " porosity = 0.3,\n", + " alpha_x = 32.5 / ft, #m\n", + " alpha_y = 3.25 / ft, #m\n", + " alpha_z = 0\n", + ")\n", + "mbt_model.hydrological_parameters = new_hydrology\n", + "ana_model.hydrological_parameters = new_hydrology\n", + "bio_model.hydrological_parameters = new_hydrology\n", + "mbt_model.mode = \"instant\"\n", + "ana_model.mode = \"instant\"\n", + "bio_model.mode = \"instant\"\n", + "bio_results_adapted = bio_model.run()\n", + "ana_results_adapted = ana_model.run()\n", + "mbt_results_adapted = mbt_model.run()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "44af697835da362d", + "metadata": {}, + "source": [ + "With adapted dispersivity values, model predicts contaminant distribution better. But does not represent the steep drop in concentration well." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a836625240e2a3e", + "metadata": {}, + "outputs": [], + "source": [ + "time_point = 365 * 6\n", + "plt.figure()\n", + "mbt_results_adapted.centerline(time=time_point, label=\"Mibitrans\", color=\"blue\")\n", + "ana_results_adapted.centerline(time=time_point, label=\"Anatrans\", color=\"red\", linestyle=\"--\")\n", + "bio_results_adapted.centerline(time=time_point, label=\"Bioscreen\", color=\"green\", linestyle=\":\")\n", + "\n", + "plt.scatter(field_data_x, field_data_c, label=\"field data\", color=\"black\")\n", + "plt.title(\"Comparison instant reaction models to field data, t=6 years\")\n", + "plt.xlim(-5,150)\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "412d2cbae313db57", + "metadata": {}, + "source": [ + "Instead of comparing to instant reaction model, use linear decay model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8f90e8c373cbbc6", + "metadata": {}, + "outputs": [], + "source": [ + "half_life = 0.15*365\n", + "mbt_model.attenuation_parameters.half_life = half_life\n", + "ana_model.attenuation_parameters.half_life = half_life\n", + "bio_model.attenuation_parameters.half_life = half_life\n", + "mbt_model.mode = \"linear\"\n", + "ana_model.mode = \"linear\"\n", + "bio_model.mode = \"linear\"\n", + "bio_results_linear = bio_model.run()\n", + "ana_results_linear = ana_model.run()\n", + "mbt_results_linear = mbt_model.run()" + ] + }, + { + "cell_type": "markdown", + "id": "d9e312a3a0e67643", + "metadata": {}, + "source": [ + "Linear decay model with given half life represents the field data well. Although modelled concentrations are lower than the field data at the latter part of the plume." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31b9248ddafadb04", + "metadata": {}, + "outputs": [], + "source": [ + "time_point = 365 * 6\n", + "plt.figure()\n", + "mbt_results_linear.centerline(time=time_point, label=\"Mibitrans\", color=\"blue\")\n", + "ana_results_linear.centerline(time=time_point, label=\"Anatrans\", color=\"red\", linestyle=\"--\")\n", + "bio_results_linear.centerline(time=time_point, label=\"Bioscreen\", color=\"green\", linestyle=\":\")\n", + "plt.title(\"Comparison linear decay models to field data, t=6 years\")\n", + "plt.scatter(field_data_x, field_data_c, label=\"field data\", color=\"black\")\n", + "plt.xlim(-5,150)\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "10b6d77b3e52e17d", + "metadata": {}, + "source": [ + "Changing the dispersivity in the linear decay model has only a slight influence on the modelled results; the influence of contaminant decay overshadows the effects of dispersivity." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "64397222b20cb884", + "metadata": {}, + "outputs": [], + "source": [ + "new_hydrology = mbt.HydrologicalParameters(\n", + " h_conductivity = 0.011 / 100 * 3600 * 24, #m/d\n", + " h_gradient = 0.003, # m/m\n", + " porosity = 0.3,\n", + " alpha_x = 13.3 /ft,\n", + " alpha_y = 1.3/ft,\n", + " alpha_z = 0\n", + ")\n", + "mbt_model.hydrological_parameters = new_hydrology\n", + "ana_model.hydrological_parameters = new_hydrology\n", + "bio_model.hydrological_parameters = new_hydrology\n", + "mbt_model.mode = \"linear\"\n", + "ana_model.mode = \"linear\"\n", + "bio_model.mode = \"linear\"\n", + "bio_results_linear = bio_model.run()\n", + "ana_results_linear = ana_model.run()\n", + "mbt_results_linear = mbt_model.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "406077924c0f2889", + "metadata": {}, + "outputs": [], + "source": [ + "time_point = 365 * 6\n", + "plt.figure()\n", + "mbt_results_linear.centerline(time=time_point, label=\"Mibitrans\", color=\"blue\")\n", + "ana_results_linear.centerline(time=time_point, label=\"Anatrans\", color=\"red\", linestyle=\"--\")\n", + "bio_results_linear.centerline(time=time_point, label=\"Bioscreen\", color=\"green\", linestyle=\":\")\n", + "plt.title(\"Comparison linear decay models to field data, t=6 years\")\n", + "plt.scatter(field_data_x, field_data_c, label=\"field data\", color=\"black\")\n", + "plt.xlim(-5,150)\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "eb969db6ab0c1424", + "metadata": {}, + "source": [ + "(1) Newell, C. J., McLeod, R. K., & Gonzales, J. R. (1997). BIOSCREEN natural attenuation decision support\n", + " system version 1.4 revisions, Tech. rep., U.S. EPA.\n", + "\n", + "(2)https://webapp1.dlib.indiana.edu/virtual_disk_library/index.cgi/4908754/FID2958/abstracts/00000166.html (make actual citation, maybe not include, since is summary of (3)\n", + "\n", + "(3) COST AND PERFORMANCE CASE STUDY REPORT - Keesler Air Force Base Base Exchange Service Station (make actual citation)\n", + "\n", + "(4) Corrective action plan for the risk-based closure of the base exchange service station, area of concern - A (ST-06) Keesler Air Froce Base, Mississippi (make actual citation)\n" + ] + }, + { + "cell_type": "markdown", + "id": "a95d32a3185dea0", + "metadata": {}, + "source": [ + "https://www.sunherald.com/news/local/military/article234197522.html" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_kohler.ipynb b/examples/example_kohler.ipynb new file mode 100644 index 0000000..7ad0c1e --- /dev/null +++ b/examples/example_kohler.ipynb @@ -0,0 +1,179 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "ac421af9f5d936aa", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "362a06a996195edc", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "524f38b7b357f749", + "metadata": {}, + "source": [ + "Note: For comparison to steady state, value of flow velocity does not matter, since it only determines how fast steady state will be accomplished." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8734f631ad0baf94", + "metadata": {}, + "outputs": [], + "source": [ + "hydro = mbt.HydrologicalParameters(\n", + " velocity=20.8/3.281/365,\n", + " porosity=0.25,\n", + " alpha_x=21.6/3.281,\n", + " alpha_y=21.6/3.281/10,\n", + " alpha_z=0\n", + ")\n", + "att = mbt.AttenuationParameters(\n", + " decay_rate=0,#0.0034,\n", + " retardation=1\n", + ")\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([5]),\n", + " source_zone_concentration=np.array([26.125]),\n", + " total_mass=\"inf\",\n", + " depth=50/3.281,\n", + ")\n", + "model = mbt.ModelParameters(\n", + " model_length=100,\n", + " model_width=20,\n", + " model_time=20*365,\n", + " dx=0.5,\n", + " dy=0.1,\n", + " dt=365 / 5,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23b7c98fb94ec32a", + "metadata": {}, + "outputs": [], + "source": [ + "ana_obj = mbt.Anatrans(hydro, att, source, model)\n", + "#ana_obj.instant_reaction(mbt.ElectronAcceptors(8.93-0.15, 5.57, 45.3, 76.7, 14.63))\n", + "results_ana = ana_obj.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecaadf8f8cbc3343", + "metadata": {}, + "outputs": [], + "source": [ + "# Needed to show animations in Jupyter Notebooks\n", + "%matplotlib ipympl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a17c9965f5dadfc0", + "metadata": {}, + "outputs": [], + "source": [ + "anim = results_ana.centerline(animate=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40d59891433008e2", + "metadata": {}, + "outputs": [], + "source": [ + "anim = results_ana.plume_3d(animate=True, cmap=\"viridis\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35cc9be16b364665", + "metadata": {}, + "outputs": [], + "source": [ + "mbt_obj = mbt.Mibitrans(hydro, att, source, model)\n", + "#mbt_obj.instant_reaction(mbt.ElectronAcceptors(8.93-0.15, 5.57, 45.3, 76.7, 14.63))\n", + "results_mbt = mbt_obj.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10ce7f57ea8be225", + "metadata": {}, + "outputs": [], + "source": [ + "anim = results_mbt.centerline(animate=True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b161f395cf35c99", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "results_ana.centerline(color=\"orange\", label=\"anatrans\")\n", + "results_mbt.centerline(color=\"green\", label=\"mibitrans\", linestyle=\"--\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4e911cc72a4ee39", + "metadata": {}, + "outputs": [], + "source": [ + "anim = mbt.centerline([results_ana, results_mbt], legend_names=[\"anatrans\", \"mibitrans\"], animate=True)\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_mibitrans_anatrans.ipynb b/examples/example_mibitrans_anatrans.ipynb new file mode 100644 index 0000000..3fe1cb1 --- /dev/null +++ b/examples/example_mibitrans_anatrans.ipynb @@ -0,0 +1,483 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "8e86b7c802bf511e", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt\n", + "from mibitrans.visualize.animation import animate_1d" + ] + }, + { + "cell_type": "markdown", + "id": "d24df86e0eb6973d", + "metadata": {}, + "source": [ + "# Exact solution versus untruncated solution\n", + "\n", + "The mibitrans solution uses the exact solution of the 3D transport ADE, as described in Wexler et al. (1992). The anatrans solution approximates this solution by the method described in Domenico (1987), but without truncating the x part of the equation. Both are adapted for multiple source zones and source depletion, see equations below. The approximation for transverse dispersivity terms in the anatrans solution introduces an error, of which the size depends on parameter choices." + ] + }, + { + "cell_type": "markdown", + "id": "7e66c813a939d441", + "metadata": {}, + "source": [ + "## Anatrans solution\n", + "untruncated Domenico (1987)\n", + "\\begin{aligned}\n", + " C(x, y, z, t) &= \\sum_{i=1}^{n}\\left\\{ \\frac{C^*_{0,i}}{8} \\exp \\left[-k_s \\left(t-\\frac{xR}{v} \\right)\\right] \\right. \\\\\n", + " &\\quad \\cdot \\left( \\exp \\left[ \\frac{x\\left(1-\\sqrt{1+4\\mu \\alpha_x/v}\\right)}{2\\alpha_x}\\right] \\right. \\\\\n", + " &\\quad \\quad \\cdot \\operatorname{erfc} \\left[ \\frac{x - vt\\sqrt{1+4\\mu \\alpha_x/v}}{2\\sqrt{\\alpha_x vt }} \\right] \\\\\n", + " &\\quad \\ \\, + \\exp \\left[ \\frac{x\\left(1+\\sqrt{1+4\\mu \\alpha_x/v}\\right)}{2\\alpha_x}\\right] \\\\\n", + " &\\quad \\quad \\cdot \\left. \\operatorname{erfc} \\left[ \\frac{x + vt\\sqrt{1+4\\mu \\alpha_x/v}}{2\\sqrt{\\alpha_x vt }} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y_i/2}{2\\sqrt{\\alpha_y x}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y_i/2}{2\\sqrt{\\alpha_y x)}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left. \\left( \\operatorname{erf} \\left[ \\frac{Z}{2\\sqrt{\\alpha_z x)}} \\right] - \\operatorname{erf} \\left[ \\frac{-Z}{2\\sqrt{\\alpha_z x}} \\right] \\right) \\right\\}\n", + "\\end{aligned}\n" + ] + }, + { + "cell_type": "markdown", + "id": "b773e32c9f5886da", + "metadata": {}, + "source": [ + "## Mibitrans solution\n", + "Wexler (1992)\n", + "$$\n", + "\\begin{equation}\\tag{2}\n", + "\\begin{aligned}\n", + " C(x,y,z,t) &= \\sum_{i=1}^{n}\\left(C^*_{0,i}\\frac{x}{8\\sqrt{\\pi \\alpha_{x}\\frac{v\\tau}{R}}}\\exp(-k_s t) \\right. \\\\\n", + " &\\quad \\cdot \\int_{0}^{t}\\left[\\frac{1}{\\tau^{\\frac{3}{2}}} \\exp\\left((k_s - \\mu)\\tau - \\frac{(x-\\frac{v\\tau}{R})^2}{4\\alpha_{x}\\frac{v\\tau}{R}}\\right) \\right. \\\\\n", + " &\\quad \\cdot \\left\\{\\operatorname{erfc}\\left(\\frac{y-Y_i}{2 \\sqrt{\\alpha_{y}\\frac{v\\tau}{R}}}\\right)-\\operatorname{erfc}\\left(\\frac{y+Y_i}{2 \\sqrt{\\alpha_{y}\\frac{v\\tau}{R}}}\\right) \\right\\} \\\\\n", + " &\\quad \\left. \\left. \\cdot \\left\\{\\operatorname{erfc}\\left(\\frac{-Z}{2 \\sqrt{\\alpha_{z}\\frac{v\\tau}{R}}}\\right)-\\operatorname{erfc}\\left(\\frac{Z}{2 \\sqrt{\\alpha_{z}\\frac{v\\tau}{R}}}\\right) \\right\\}\\right] d\\tau \\right)\n", + "\\end{aligned}\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b59009fc4543720f", + "metadata": {}, + "outputs": [], + "source": [ + "hydro = mbt.HydrologicalParameters(\n", + " velocity=0.277, # Flow velocity [m/d]\n", + " porosity=0.25, # Effective soil porosity [-]\n", + " alpha_x=10, # Longitudinal dispersivity, in [m]\n", + " alpha_y=1, # Transverse horizontal dispersivity, in [m]\n", + " alpha_z=0.1 # Transverse vertical dispersivity, in [m]\n", + ")\n", + "\n", + "att = mbt.AttenuationParameters(\n", + " retardation=1,\n", + " # Molecular diffusion, in [m2/day]\n", + " diffusion=0,\n", + " # Contaminant half life, in [days]\n", + " half_life=0#5*365\n", + ")\n", + "\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([10]),\n", + " source_zone_concentration=np.array([11]),\n", + " depth=2.5,\n", + " total_mass=\"inf\"\n", + ")\n", + "\n", + "model = mbt.ModelParameters(\n", + " # Model extent in the longitudinal (x) direction in [m].\n", + " model_length = 800,\n", + " # Model extent in the transverse horizontal (y) direction in [m].\n", + " model_width = 150,\n", + " # Model duration in [days].\n", + " model_time = 10*365,\n", + " # Model grid discretization step size in the longitudinal (x) direction, in [m].\n", + " dx = 2,\n", + " # Model grid discretization step size in the transverse horizontal (y) direction, in [m].\n", + " dy = 1,\n", + " # Model time discretization step size, in [days]\n", + " dt = 25\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69a3fbdc2a9feae8", + "metadata": {}, + "outputs": [], + "source": [ + "mbt_object = mbt.Mibitrans(hydro, att, source, model)\n", + "mbt_results = mbt_object.run()\n", + "\n", + "ana_object = mbt.Anatrans(hydro, att, source, model)\n", + "ana_results = ana_object.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af63a154159d99d0", + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib ipympl" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3eae04b06adb0c90", + "metadata": {}, + "outputs": [], + "source": [ + "ani = animate_1d(x_axis_parameter=mbt_results.y,\n", + " y_axis_parameter=[mbt_results.cxyt[:,:,25], mbt_results.cxyt[:,:,50],\n", + " mbt_results.cxyt[:,:,100],mbt_results.cxyt[:,:,200],\n", + " ana_results.cxyt[:,:,25], ana_results.cxyt[:,:,50],\n", + " ana_results.cxyt[:,:,100], ana_results.cxyt[:,:,200]],\n", + " time_parameter=mbt_results.t,\n", + " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\",\n", + " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\"],\n", + " y_names=[\"Mibitrans x=50m\", \"Mibitrans x=100m\", \"Mibitrans x=200m\", \"Mibitrans x=400m\",\n", + " \"Anatrans x=50m\", \"Anatrans x=100m\", \"Anatrans x=200m\", \"Anatrans x=400m\"],\n", + " linestyle=[\"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\"])\n", + "plt.xlabel(\"y-position [m]\")\n", + "plt.ylabel(r\"Concentration [$g/m^3$]\")\n", + "#ani.save(\"transverse_animation_2.gif\", fps=10)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "544205a3d5565f79", + "metadata": {}, + "outputs": [], + "source": [ + "ani1 = animate_1d(x_axis_parameter=mbt_results.x,\n", + " y_axis_parameter=[mbt_results.cxyt[:,75,:], mbt_results.cxyt[:,84,:],\n", + " mbt_results.cxyt[:,100,:], mbt_results.cxyt[:,125,:],\n", + " ana_results.cxyt[:,75,:], ana_results.cxyt[:,84,:],\n", + " ana_results.cxyt[:,100,:], ana_results.cxyt[:,125,:]],\n", + " time_parameter=mbt_results.t,\n", + " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\",\n", + " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\"],\n", + " y_names=[\"Mibitrans y=0m\", \"Mibitrans y=9m\", \"Mibitrans y=25m\", \"Mibitrans y=50m\",\n", + " \"Anatrans y=0m\", \"Anatrans y=9m\", \"Anatrans y=25m\", \"Anatrans y=50m\"],\n", + " linestyle=[\"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\"])\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(r\"Concentration [$g/m^3$]\")\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbdedb3c17415339", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "mbt_results.breakthrough(50, 0, color=\"darkgreen\", linestyle=\"-\", label=\"Mibitrans 50m\")\n", + "mbt_results.breakthrough(100, 0, color=\"limegreen\", linestyle=\"-\", label=\"Mibitrans 100m\")\n", + "mbt_results.breakthrough(200, 0, color=\"cornflowerblue\", linestyle=\"-\", label=\"Mibitrans 200m\")\n", + "mbt_results.breakthrough(400, 0, color=\"blue\", linestyle=\"-\", label=\"Mibitrans 400m\")\n", + "ana_results.breakthrough(50, 0, color=\"green\", linestyle=\"--\", label=\"Anatrans 50m\")\n", + "ana_results.breakthrough(100, 0, color=\"greenyellow\", linestyle=\"--\", label=\"Anatrans 100m\")\n", + "ana_results.breakthrough(200, 0, color=\"darkturquoise\", linestyle=\"--\", label=\"Anatrans 200m\")\n", + "ana_results.breakthrough(400, 0, color=\"dodgerblue\", linestyle=\"--\", label=\"Anatrans 400m\")\n", + "plt.title(\"Breakthrough plot of Mibitrans and Anatrans model, at various x locations.\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c38f7396fbd67f73", + "metadata": {}, + "source": [ + "#### Comparing model differences for various flow velocities and dispersivities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97f04f852e9eb61b", + "metadata": {}, + "outputs": [], + "source": [ + "parameter_list = [0.1, 0.5, 1, 5, 10]\n", + "output_list_mbt = []\n", + "output_list_ana = []\n", + "for par in parameter_list:\n", + " print(f\"starting run with par = {par}\")\n", + " mbt_object_alpha = mbt.Mibitrans(hydro, att, source, model)\n", + " mbt_object_alpha.hydrological_parameters.alpha_x = par\n", + " mbt_object_alpha.hydrological_parameters.alpha_y = par / 10\n", + " mbt_object_alpha.hydrological_parameters.alpha_z = par / 100\n", + " mbt_res_alp = mbt_object_alpha.run()\n", + " output_list_mbt.append(mbt_res_alp.cxyt)\n", + "\n", + " ana_object_alpha = mbt.Anatrans(hydro, att, source, model)\n", + " ana_object_alpha.hydrological_parameters.alpha_x = par\n", + " ana_object_alpha.hydrological_parameters.alpha_y = par / 10\n", + " ana_object_alpha.hydrological_parameters.alpha_z = par / 100\n", + " ana_res_alp = ana_object_alpha.run()\n", + " output_list_ana.append(ana_res_alp.cxyt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3cc41c418dd5871", + "metadata": {}, + "outputs": [], + "source": [ + "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", + "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", + "plt.figure()\n", + "for i, conc in enumerate(output_list_mbt):\n", + " plt.plot(mbt_res_alp.x, conc[-1,75,:], color=colors_mbt[i],\n", + " label=f\"Mibitrans a={parameter_list[i]}m\")\n", + "for i, conc in enumerate(output_list_ana):\n", + " plt.plot(ana_res_alp.x, conc[-1,75,:], color=colors_ana[i], linestyle=\"--\",\n", + " label=f\"Anatrans a={parameter_list[i]}m\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea1d7ea112addeff", + "metadata": {}, + "outputs": [], + "source": [ + "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", + "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", + "plt.figure()\n", + "for i, conc in enumerate(output_list_mbt):\n", + " plt.plot(mbt_res_alp.y, conc[-1,:,175], color=colors_mbt[i],\n", + " label=f\"Mibitrans a={parameter_list[i]}m\")\n", + "for i, conc in enumerate(output_list_ana):\n", + " plt.plot(ana_res_alp.y, conc[-1,:,175], color=colors_ana[i],\n", + " linestyle=\"--\", label=f\"Anatrans a={parameter_list[i]}m\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bda62df4b1341dfb", + "metadata": {}, + "outputs": [], + "source": [ + "parameter_list_v = [0.1, 0.1, 0.1, 0.1, 0.1]\n", + "parameter_list_a = [0., 5, 2, 1, 0.5]\n", + "output_list_mbt_v = []\n", + "output_list_ana_v = []\n", + "for i in range(len(parameter_list_v)):\n", + " parv = parameter_list_v[i]\n", + " para = parameter_list_a[i]\n", + " print(f\"starting run with parv = {parv} and para = {para}\")\n", + " mbt_va = mbt.Mibitrans(hydro, att, source, model)\n", + " mbt_va.hydrological_parameters.velocity = parv\n", + " mbt_va.hydrological_parameters.alpha_x = para\n", + " mbt_va.hydrological_parameters.alpha_y = para / 10\n", + " mbt_va.hydrological_parameters.alpha_z = 0#para / 100\n", + " mbt_res_va = mbt_va.run()\n", + " output_list_mbt_v.append(mbt_res_va.relative_cxyt)\n", + "\n", + " ana_va = mbt.Anatrans(hydro, att, source, model)\n", + " ana_va.hydrological_parameters.velocity = parv\n", + " ana_va.hydrological_parameters.alpha_x = para\n", + " ana_va.hydrological_parameters.alpha_y = para / 10\n", + " ana_va.hydrological_parameters.alpha_z = 0#para / 100\n", + " ana_res_va = ana_va.run()\n", + " output_list_ana_v.append(ana_res_va.relative_cxyt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df501e36f3fd38fc", + "metadata": {}, + "outputs": [], + "source": [ + "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", + "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", + "plt.figure()\n", + "for i, conc in enumerate(output_list_mbt_v):\n", + " plt.plot(mbt_res_va.x, conc[-1,75,:], color=colors_mbt[i],\n", + " label=f\"Mbt v={parameter_list_v[i]}m/d, a={parameter_list_a[i]}m\")\n", + "for i, conc in enumerate(output_list_ana_v):\n", + " plt.plot(ana_res_va.x, conc[-1,75,:], color=colors_ana[i],\n", + " linestyle=\"--\", label=f\"Ana v={parameter_list_v[i]}m/d, a={parameter_list_a[i]}m\")\n", + "plt.xlim((0,300))\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f38d34e6b66ce99e", + "metadata": {}, + "outputs": [], + "source": [ + "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", + "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", + "plt.figure()\n", + "for i, conc in enumerate(output_list_mbt_v):\n", + " plt.plot(mbt_res_va.y, conc[-1, :, 75], color=colors_mbt[i], label=f\"Mibitrans a={parameter_list_v[i]}m\")\n", + "for i, conc in enumerate(output_list_ana_v):\n", + " plt.plot(ana_res_va.y, conc[-1, :, 75], color=colors_ana[i], linestyle=\"--\",\n", + " label=f\"Anatrans a={parameter_list[i]}m\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c644d7afc0855414", + "metadata": {}, + "outputs": [], + "source": [ + "mbtv=output_list_mbt_v\n", + "anav=output_list_ana_v\n", + "ani = animate_1d(x_axis_parameter=mbt_res_va.x,\n", + " y_axis_parameter=[mbtv[0][:,75,:], mbtv[1][:,75,:],mbtv[2][:,75,:],\n", + " mbtv[3][:,75,:], mbtv[4][:,75,:],\n", + " anav[0][:,75,:], anav[1][:,75,:], anav[2][:,75,:],\n", + " anav[3][:,75,:], anav[4][:,75,:]],\n", + " time_parameter=mbt_res_va.t,\n", + " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\",\n", + " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"violet\"],\n", + " y_names=[r\"Mibitrans $\\alpha_l=10$\", r\"Mibitrans $\\alpha_l=5$\", r\"Mibitrans $\\alpha_l=1$\",\n", + " r\"Mibitrans $\\alpha_l=0.5$\", r\"Mibitrans $\\alpha_l=0.1$\", r\"Anatrans $\\alpha_l=10$\",\n", + " r\"Anatrans $\\alpha_l=5$\", r\"Anatrans $\\alpha_l=1$\", r\"Anatrans $\\alpha_l=0.5$\",\n", + " r\"Anatrans $\\alpha_l=0.1$\",\n", + " ],\n", + " linestyle=[\"-\", \"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\", \"--\"])\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(r\"Concentration [$g/m^3$]\")\n", + "plt.xlim((0,350))\n", + "plt.show()\n", + "#ani.save(\"centerline_animation.gif\", fps=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c7bc203fd0dfa00", + "metadata": {}, + "outputs": [], + "source": [ + "mbtv=output_list_mbt_v\n", + "anav=output_list_ana_v\n", + "ani = animate_1d(x_axis_parameter=mbt_results.y,\n", + " y_axis_parameter=[mbtv[0][:,:,50], mbtv[1][:,:,50], mbtv[2][:,:,50],\n", + " mbtv[3][:,:,50], mbtv[4][:,:,50],\n", + " anav[0][:,:,50], anav[1][:,:,50], anav[2][:,:,50],\n", + " anav[3][:,:,50], anav[4][:,:,50]],\n", + " time_parameter=mbt_res_va.t,\n", + " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\",\n", + " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"violet\"],\n", + " y_names=[r\"Mibitrans $\\alpha_l=10$\", r\"Mibitrans $\\alpha_l=5$\", r\"Mibitrans $\\alpha_l=1$\",\n", + " r\"Mibitrans $\\alpha_l=0.5$\", r\"Mibitrans $\\alpha_l=0.1$\", r\"Anatrans $\\alpha_l=10$\",\n", + " r\"Anatrans $\\alpha_l=5$\", r\"Anatrans $\\alpha_l=1$\", r\"Anatrans $\\alpha_l=0.5$\",\n", + " r\"Anatrans $\\alpha_l=0.1$\",\n", + " ],\n", + " linestyle=[\"-\", \"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\", \"--\"])\n", + "plt.xlabel(\"y-position [m]\")\n", + "plt.ylabel(r\"Concentration [$g/m^3$]\")\n", + "#ani.save(\"transverse_animation.gif\", fps=10)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67219dd9a181a53c", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import animation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a68f423fc54c093e", + "metadata": {}, + "outputs": [], + "source": [ + "plt.clf()\n", + "mbt_a10 = mbtv[0]\n", + "ana_a10 = anav[0]\n", + "\n", + "err_aprx_raw = (ana_a10 - mbt_a10) / mbt_a10 * 100\n", + "err_aprx_raw = np.where(np.isnan(err_aprx_raw), 0, err_aprx_raw)\n", + "err_aprx = np.log10(abs(err_aprx_raw))\n", + "err_abs = ana_a10 - mbt_a10\n", + "# print(err_aprx[-1,len(mbt_va.y)//2, :])\n", + "# plt.plot(mbt_va.x, mbt_a10[-1,len(mbt_va.y)//2, :], color = \"green\")\n", + "# plt.plot(mbt_va.x, ana_a10[-1,len(mbt_va.y)//2, :], color = \"red\")\n", + "# plt.show()\n", + "#\n", + "# plt.plot(mbt_va.x, err_aprx[-1,len(mbt_va.y)//2, :], color = \"blue\")\n", + "# plt.show()\n", + "# plt.pcolormesh(mbt_va.x, mbt_va.y, err_abs[10, :, :], cmap=\"jet\")\n", + "# plt.colorbar()\n", + "# plt.show()\n", + "\n", + "fig, ax = plt.subplots()\n", + "mesh = ax.pcolormesh(\n", + " mbt_va.x[:150], mbt_va.y[35:115], err_abs[0, 35:115, :150], vmin=-0.2, vmax=0.2, cmap=\"Spectral\"\n", + ")\n", + "cbar = fig.colorbar(mesh, ax=ax)\n", + "cbar.set_label(\"Absolute error in rel. concentration\")\n", + "ax.set_xlabel(\"Distance from source (m)\")\n", + "ax.set_ylabel(\"Distance from plume center (m)\")\n", + "\n", + "def update(frame):\n", + " \"\"\"Update animation frame to the next frame.\"\"\"\n", + " mesh.set_array(err_abs[frame, 35:115, :150])\n", + " ax.set_title(f\"Concentration distribution at t={mbt_va.t[frame]} days\")\n", + " return mesh\n", + "\n", + "ani = animation.FuncAnimation(fig=fig, func=update, frames=len(mbt_va.t))\n", + "plt.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_mibitrans_anatrans_comparison.ipynb b/examples/example_mibitrans_anatrans_comparison.ipynb deleted file mode 100644 index c4110de..0000000 --- a/examples/example_mibitrans_anatrans_comparison.ipynb +++ /dev/null @@ -1,387 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "initial_id", - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from mibitrans.data.parameters import AttenuationParameters\n", - "from mibitrans.data.parameters import HydrologicalParameters\n", - "from mibitrans.data.parameters import ModelParameters\n", - "from mibitrans.data.parameters import SourceParameters\n", - "from mibitrans.transport.models import Anatrans\n", - "from mibitrans.transport.models import Mibitrans\n", - "from mibitrans.visualize.animation import animate_1d" - ] - }, - { - "cell_type": "markdown", - "id": "d24df86e0eb6973d", - "metadata": {}, - "source": [ - "### Exact solution versus untruncated solution\n", - "\n", - "\\begin{equation}\n", - "C(x,y,z,t) = \\sum_{i=1}^{n}\\left(C^*_{0,i}\\frac{x}{8\\sqrt{\\pi D^{'}_{x}}}\\exp(-\\gamma t)\n", - "\\cdot \\int_{0}^{t}\\left[\\frac{1}{\\tau^{\\frac{3}{2}}} \\exp\\left((\\gamma - \\lambda_{EFF})\\tau - \\frac{(x-v^{'}\\tau)^2}{4D^{'}_{x}\\tau}\\right)\n", - "\\cdot \\left\\{ERFC\\left(\\frac{y-Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right)-ERFC\\left(\\frac{y+Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right) \\right\\}\n", - "\\cdot \\left\\{ERFC\\left(\\frac{z-Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right)-ERFC\\left(\\frac{z+Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right) \\right\\}\\right] d\\tau \\right)\n", - "\\end{equation}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29cec10a2865e2ca", - "metadata": {}, - "outputs": [], - "source": [ - "hydro = HydrologicalParameters(\n", - " velocity=0.277, # Flow velocity [m/d]\n", - " porosity=0.25, # Effective soil porosity [-]\n", - " alpha_x=10, # Longitudinal dispersivity, in [m]\n", - " alpha_y=1, # Transverse horizontal dispersivity, in [m]\n", - " alpha_z=0.1 # Transverse vertical dispersivity, in [m]\n", - ")\n", - "\n", - "att = AttenuationParameters(\n", - " retardation=1,\n", - " # Molecular diffusion, in [m2/day]\n", - " diffusion=0,\n", - " # Contaminant half life, in [days]\n", - " half_life=0#5*365\n", - ")\n", - "\n", - "source = SourceParameters(\n", - " source_zone_boundary=np.array([10]),\n", - " source_zone_concentration=np.array([11]),\n", - " depth=2.5,\n", - " total_mass=\"inf\"\n", - ")\n", - "\n", - "model = ModelParameters(\n", - " # Model extent in the longitudinal (x) direction in [m].\n", - " model_length = 800,\n", - " # Model extent in the transverse horizontal (y) direction in [m].\n", - " model_width = 150,\n", - " # Model duration in [days].\n", - " model_time = 5*365,\n", - " # Model grid discretization step size in the longitudinal (x) direction, in [m].\n", - " dx = 2,\n", - " # Model grid discretization step size in the transverse horizontal (y) direction, in [m].\n", - " dy = 1,\n", - " # Model time discretization step size, in [days]\n", - " dt = 25\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "54e9e7e491c986fe", - "metadata": {}, - "outputs": [], - "source": [ - "mbt_object = Mibitrans(hydro, att, source, model)\n", - "mbt_object.run()\n", - "\n", - "ana_object = Anatrans(hydro, att, source, model)\n", - "ana_object.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e323802bcb322adf", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib notebook" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b3d53214555ede3a", - "metadata": {}, - "outputs": [], - "source": [ - "ani = animate_1d(x_axis_parameter=mbt_object.y,\n", - " y_axis_parameter=[mbt_object.cxyt[:,:,25], mbt_object.cxyt[:,:,50],\n", - " mbt_object.cxyt[:,:,100],mbt_object.cxyt[:,:,200],\n", - " ana_object.cxyt[:,:,25], ana_object.cxyt[:,:,50],\n", - " ana_object.cxyt[:,:,100], ana_object.cxyt[:,:,200]],\n", - " time_parameter=mbt_object.t,\n", - " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\",\n", - " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\"],\n", - " y_names=[\"Mibitrans x=50m\", \"Mibitrans x=100m\", \"Mibitrans x=200m\", \"Mibitrans x=400m\",\n", - " \"Anatrans x=50m\", \"Anatrans x=100m\", \"Anatrans x=200m\", \"Anatrans x=400m\"],\n", - " linestyle=[\"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\"])\n", - "plt.xlabel(\"y-position [m]\")\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78a4f70e30d859ea", - "metadata": {}, - "outputs": [], - "source": [ - "ani1 = animate_1d(x_axis_parameter=mbt_object.x,\n", - " y_axis_parameter=[mbt_object.cxyt[:,75,:], mbt_object.cxyt[:,84,:],\n", - " mbt_object.cxyt[:,100,:], mbt_object.cxyt[:,125,:],\n", - " ana_object.cxyt[:,75,:], ana_object.cxyt[:,84,:],\n", - " ana_object.cxyt[:,100,:], ana_object.cxyt[:,125,:]],\n", - " time_parameter=mbt_object.t,\n", - " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\",\n", - " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\"],\n", - " y_names=[\"Mibitrans y=0m\", \"Mibitrans y=9m\", \"Mibitrans y=25m\", \"Mibitrans y=50m\",\n", - " \"Anatrans y=0m\", \"Anatrans y=9m\", \"Anatrans y=25m\", \"Anatrans y=50m\"],\n", - " linestyle=[\"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\"])\n", - "plt.xlabel(\"x-position [m]\")\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77b3c43e8ed23283", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure()\n", - "mbt_object.breakthrough(50, 0, color=\"darkgreen\", linestyle=\"-\", label=\"Mibitrans 50m\")\n", - "mbt_object.breakthrough(100, 0, color=\"limegreen\", linestyle=\"-\", label=\"Mibitrans 100m\")\n", - "mbt_object.breakthrough(200, 0, color=\"cornflowerblue\", linestyle=\"-\", label=\"Mibitrans 200m\")\n", - "mbt_object.breakthrough(400, 0, color=\"blue\", linestyle=\"-\", label=\"Mibitrans 400m\")\n", - "ana_object.breakthrough(50, 0, color=\"green\", linestyle=\"--\", label=\"Anatrans 50m\")\n", - "ana_object.breakthrough(100, 0, color=\"greenyellow\", linestyle=\"--\", label=\"Anatrans 100m\")\n", - "ana_object.breakthrough(200, 0, color=\"darkturquoise\", linestyle=\"--\", label=\"Anatrans 200m\")\n", - "ana_object.breakthrough(400, 0, color=\"dodgerblue\", linestyle=\"--\", label=\"Anatrans 400m\")\n", - "plt.title(\"Breakthrough plot of Mibitrans and Anatrans model, at various x locations.\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "c38f7396fbd67f73", - "metadata": {}, - "source": [ - "#### Comparing model differences for various flow velocities and dispersivities" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ae416dc23dd6b334", - "metadata": {}, - "outputs": [], - "source": [ - "parameter_list = [0.1, 0.5, 1, 5, 10]\n", - "output_list_mbt = []\n", - "output_list_ana = []\n", - "for par in parameter_list:\n", - " print(f\"starting run with par = {par}\")\n", - " mbt_object = Mibitrans(hydro, att, source, model)\n", - " mbt_object.hydrological_parameters.alpha_x = par\n", - " mbt_object.hydrological_parameters.alpha_y = par / 10\n", - " mbt_object.hydrological_parameters.alpha_z = par / 100\n", - " mbt_object.run()\n", - " output_list_mbt.append(mbt_object.cxyt)\n", - "\n", - " ana_object = Anatrans(hydro, att, source, model)\n", - " ana_object.hydrological_parameters.alpha_x = par\n", - " ana_object.hydrological_parameters.alpha_y = par / 10\n", - " ana_object.hydrological_parameters.alpha_z = par / 100\n", - " ana_object.run()\n", - " output_list_ana.append(ana_object.cxyt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93a0274d7116f5f", - "metadata": {}, - "outputs": [], - "source": [ - "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", - "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", - "plt.figure()\n", - "for i, conc in enumerate(output_list_mbt):\n", - " plt.plot(mbt_object.x, conc[-1,75,:], color=colors_mbt[i], label=f\"Mibitrans a={parameter_list[i]}m\")\n", - "for i, conc in enumerate(output_list_ana):\n", - " plt.plot(ana_object.x, conc[-1,75,:], color=colors_ana[i], linestyle=\"--\", label=f\"Anatrans a={parameter_list[i]}m\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea1d7ea112addeff", - "metadata": {}, - "outputs": [], - "source": [ - "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", - "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", - "plt.figure()\n", - "for i, conc in enumerate(output_list_mbt):\n", - " plt.plot(mbt_object.y, conc[-1,:,175], color=colors_mbt[i],\n", - " label=f\"Mibitrans a={parameter_list[i]}m\")\n", - "for i, conc in enumerate(output_list_ana):\n", - " plt.plot(ana_object.y, conc[-1,:,175], color=colors_ana[i],\n", - " linestyle=\"--\", label=f\"Anatrans a={parameter_list[i]}m\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e8640aff2864ca7", - "metadata": {}, - "outputs": [], - "source": [ - "parameter_list_v = [0.1, 0.1, 0.1, 0.1, 0.1]\n", - "parameter_list_a = [10, 5, 2, 1, 0.5]\n", - "output_list_mbt_v = []\n", - "output_list_ana_v = []\n", - "for i in range(len(parameter_list_v)):\n", - " parv = parameter_list_v[i]\n", - " para = parameter_list_a[i]\n", - " print(f\"starting run with parv = {parv} and para = {para}\")\n", - " mbt_object = Mibitrans(hydro, att, source, model)\n", - " mbt_object.hydrological_parameters.velocity = parv\n", - " mbt_object.hydrological_parameters.alpha_x = para\n", - " mbt_object.hydrological_parameters.alpha_y = para / 10\n", - " mbt_object.hydrological_parameters.alpha_z = 0#para / 100\n", - " mbt_object.run()\n", - " output_list_mbt_v.append(mbt_object.cxyt)\n", - "\n", - " ana_object = Anatrans(hydro, att, source, model)\n", - " ana_object.hydrological_parameters.velocity = parv\n", - " ana_object.hydrological_parameters.alpha_x = para\n", - " ana_object.hydrological_parameters.alpha_y = para / 10\n", - " ana_object.hydrological_parameters.alpha_z = 0#para / 100\n", - " ana_object.run()\n", - " output_list_ana_v.append(ana_object.cxyt)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f5383ae968db9bf6", - "metadata": {}, - "outputs": [], - "source": [ - "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", - "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", - "plt.figure()\n", - "for i, conc in enumerate(output_list_mbt_v):\n", - " plt.plot(mbt_object.x, conc[-1,75,:], color=colors_mbt[i],\n", - " label=f\"Mbt v={parameter_list_v[i]}m/d, a={parameter_list_a[i]}m\")\n", - "for i, conc in enumerate(output_list_ana_v):\n", - " plt.plot(ana_object.x, conc[-1,75,:], color=colors_ana[i],\n", - " linestyle=\"--\", label=f\"Ana v={parameter_list_v[i]}m/d, a={parameter_list_a[i]}m\")\n", - "plt.xlim((0,300))\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f38d34e6b66ce99e", - "metadata": {}, - "outputs": [], - "source": [ - "colors_mbt = [\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\"]\n", - "colors_ana = [\"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"darkviolet\"]\n", - "plt.figure()\n", - "for i, conc in enumerate(output_list_mbt_v):\n", - " plt.plot(mbt_object.y, conc[-1, :, 75], color=colors_mbt[i], label=f\"Mibitrans a={parameter_list_v[i]}m\")\n", - "for i, conc in enumerate(output_list_ana_v):\n", - " plt.plot(ana_object.y, conc[-1, :, 75], color=colors_ana[i], linestyle=\"--\",\n", - " label=f\"Anatrans a={parameter_list[i]}m\")\n", - "plt.legend()\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f01c7d5012b64963", - "metadata": {}, - "outputs": [], - "source": [ - "mbtv=output_list_mbt_v\n", - "anav=output_list_ana_v\n", - "ani = animate_1d(x_axis_parameter=mbt_object.x,\n", - " y_axis_parameter=[mbtv[0][:,75,:], mbtv[1][:,75,:],mbtv[2][:,75,:],\n", - " mbtv[3][:,75,:], mbtv[4][:,75,:],\n", - " anav[0][:,75,:], anav[1][:,75,:], anav[2][:,75,:],\n", - " anav[3][:,75,:], anav[4][:,75,:]],\n", - " time_parameter=mbt_object.t,\n", - " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\",\n", - " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"violet\"],\n", - " y_names=[\"Mibitrans a=0.1\", \"Mibitrans a=0.5\", \"Mibitrans a=1\", \"Mibitrans a=5\", \"Mibitrans a=10\",\n", - " \"Anatrans a=0.1\", \"Anatrans a=0.5\", \"Anatrans a=1\", \"Anatrans a=5\", \"Anatrans a=10\"],\n", - " linestyle=[\"-\", \"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\", \"--\"])\n", - "plt.xlabel(\"y-position [m]\")\n", - "plt.xlim((0,350))\n", - "plt.show()\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4f70026152c8958e", - "metadata": {}, - "outputs": [], - "source": [ - "mbtv=output_list_mbt_v\n", - "anav=output_list_ana_v\n", - "ani = animate_1d(x_axis_parameter=mbt_object.y,\n", - " y_axis_parameter=[mbtv[0][:,:,50], mbtv[1][:,:,50], mbtv[2][:,:,50],\n", - " mbtv[3][:,:,50], mbtv[4][:,:,50],\n", - " anav[0][:,:,50], anav[1][:,:,50], anav[2][:,:,50],\n", - " anav[3][:,:,50], anav[4][:,:,50]],\n", - " time_parameter=mbt_object.t,\n", - " y_colors=[\"darkgreen\", \"limegreen\", \"cornflowerblue\", \"blue\", \"indigo\",\n", - " \"green\", \"greenyellow\", \"darkturquoise\", \"dodgerblue\", \"violet\"],\n", - " y_names=[\"Mibitrans a=0.1\", \"Mibitrans a=0.5\", \"Mibitrans a=1\", \"Mibitrans a=5\", \"Mibitrans a=10\",\n", - " \"Anatrans a=0.1\", \"Anatrans a=0.5\", \"Anatrans a=1\", \"Anatrans a=5\", \"Anatrans a=10\"],\n", - " linestyle=[\"-\", \"-\", \"-\", \"-\", \"-\", \"--\", \"--\", \"--\", \"--\", \"--\"])\n", - "plt.xlabel(\"y-position [m]\")\n", - "\n", - "plt.show()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 2 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/example_source_depletion.ipynb b/examples/example_source_depletion.ipynb new file mode 100644 index 0000000..7140c30 --- /dev/null +++ b/examples/example_source_depletion.ipynb @@ -0,0 +1,412 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1bb35746ac89a12", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt" + ] + }, + { + "cell_type": "markdown", + "id": "83193b7ca94fe709", + "metadata": {}, + "source": [ + "# Formulation of the source depletion term\n", + "\n", + "In the original BIOSCREEN, the source depletion term is expressed as; $C_0 \\exp(-k_s(t - \\frac{x}{v}))$. Although this represents both the decline in source concentration over time, and that concentrations at the advective front originate from the initial source concentration. However, this expression does not come forth from the exact solution, but is instead superimposed onto the analytical solution. When taking the exact solution of Karanovic (2007) as base, expression of source depletion is integrated into the longitudinal advection and dispersion term. This, mathematically sound, method of source depletion is implemented in the Anatrans model class.\n", + "\n", + "Below, differences between the two implementations and the Mibitrans solution are visualized. Simultaneously, this example showcases how to adapt a model class to fit the desired behaviour." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47a67bc4465e9ddb", + "metadata": {}, + "outputs": [], + "source": [ + "ft = 3.281\n", + "hydro = mbt.HydrologicalParameters(\n", + " velocity=350/ft/365, # [m/days]\n", + " h_gradient=0.048, # [-]\n", + " h_conductivity=0.495, # [m/day]\n", + " porosity=0.25, # [-]\n", + " alpha_x=13.3/ft, # [m]\n", + " alpha_y=1.13/ft, # [m]\n", + " alpha_z=0 # T[m]\n", + ")\n", + "\n", + "att = mbt.AttenuationParameters(\n", + " bulk_density=1.7, #[g/m3]\n", + "\n", + " partition_coefficient=38, # [m^3/g]\n", + " fraction_organic_carbon=5.7e-5, # [-]\n", + " diffusion=0, # [m2/day]\n", + " half_life=0 # [days]\n", + ")\n", + "\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([30/ft, 35/ft, 40/ft]), # [m]\n", + " source_zone_concentration=np.array([13.68, 2.508, 0.057]), # [g/m3]\n", + " depth=10/ft, # [m]\n", + " total_mass=25000 # [g]\n", + ")\n", + "\n", + "model = mbt.ModelParameters(\n", + " model_length = 1500/ft, # [m]\n", + " model_width = 300/ft, # [m]\n", + " model_time = 10 * 365, # [days]\n", + " dx = 5/ft, # [m]\n", + " dy = 2/ft, # [m]\n", + " dt = 365/5 # [days]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "21df3fcbe62407b4", + "metadata": {}, + "source": [ + "To visualize the effect of the 'BIOSCREEN'-formulation of source depletion, swap out some equation elements from the Anatrans model class.\n", + "\n", + "This is shown below. Make a custom class and initialize it with the same arguments. To inherrit the functionalities of Anatrans, assign it as the parent class by putting it between parentheses behind the class name. Then in the \\_\\_init\\_\\_, call the initialization of the parent class with super().\\_\\_init\\_\\_() and the class arguments. Then, the relevant portions of Anatrans can be adapted, while keeping all other functionalities the same." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "210f6092a5188ee", + "metadata": {}, + "outputs": [], + "source": [ + "# Give the custom class a new name and make it inherit from Anatrans as a child class.\n", + "class AnatransAlternativeSource(mbt.Anatrans):\n", + " \"\"\"Mibitrans model class with alternative source depletion.\"\"\"\n", + " # Unless otherwise specified, all functionalities are now the same as Anatrans\n", + " # Change the equation terms to fit the alternative source depletion formulation.\n", + " def _equation_term_source_depletion(self, xxx, ttt):\n", + " term = np.exp(-self.k_source * (ttt - xxx / self.rv))\n", + " # Term can be max 1; can not have 'generation' of solute ahead of advection.\n", + " return np.where(term > 1, 1, term)\n", + "\n", + " def _equation_decay_sqrt(self):\n", + " return np.sqrt(1 + 4 * self._decay_rate * self._hyd_pars.alpha_x / self.rv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1af5e77b663d6c36", + "metadata": {}, + "outputs": [], + "source": [ + "ana_object = mbt.Anatrans(hydro, att, source, model)\n", + "ana_results = ana_object.run()\n", + "\n", + "alt_object = AnatransAlternativeSource(hydro, att, source, model)\n", + "alt_results = alt_object.run()\n", + "\n", + "mbt_object = mbt.Mibitrans(hydro, att, source, model)\n", + "mbt_results = mbt_object.run()\n" + ] + }, + { + "cell_type": "markdown", + "id": "43d7f0abc996155f", + "metadata": {}, + "source": [ + "There is a noticeable difference between the two implementations when the source mass is low (and therefore, $k_s$ is high). Over the model run time, these differences become more pronounced." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f6401bd8fb02806", + "metadata": {}, + "outputs": [], + "source": [ + "time = 365 * 10\n", + "alt_results.centerline(time = time, color=\"red\", linestyle=\"--\", label=\"anatrans alternative\")\n", + "ana_results.centerline(time = time, color=\"green\", label=\"anatrans\")\n", + "mbt_results.centerline(time = time, color=\"blue\", linestyle=\":\", label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3020a36bdb0a98f5", + "metadata": {}, + "outputs": [], + "source": [ + "time = 365 * 10\n", + "alt_results.transverse(time = time, x_position=300, color=\"red\", linestyle=\"--\", label=\"anatrans alternative\")\n", + "ana_results.transverse(time = time, x_position=300, color=\"green\", label=\"anatrans\")\n", + "mbt_results.transverse(time = time, x_position=300, color=\"blue\", linestyle=\":\", label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb390d69b8b7ddc1", + "metadata": {}, + "outputs": [], + "source": [ + "def absolute_error(a, b):\n", + " \"\"\"Calculate the absolute error.\"\"\"\n", + " return b - a\n", + "def relative_error(a, b):\n", + " \"\"\"Calculate the relative error.\"\"\"\n", + " return (b - a) / a" + ] + }, + { + "cell_type": "markdown", + "id": "1afaad98b86fa5ff", + "metadata": {}, + "source": [ + "Visualizing the relative error of both formulations of source depletion shows that for Anatrans, the error with Mibitrans gradually changes of the plume length. However, for the alternative, there is peak in error at the advective front. In front and behind the advection, the alternative method resolves to the regular Anatrans results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ecfe5b0e68228801", + "metadata": {}, + "outputs": [], + "source": [ + "diff_ana = relative_error(mbt_results.cxyt[:,len(ana_results.y)//2,:],\n", + " ana_results.cxyt[:,len(ana_results.y)//2,:])\n", + "diff_alt = relative_error(mbt_results.cxyt[:,len(ana_results.y)//2,:],\n", + " alt_results.cxyt[:,len(ana_results.y)//2,:])\n", + "y_plottable = [diff_ana[9,:], diff_ana[29,:], diff_ana[49,:],\n", + " diff_alt[9,:], diff_alt[29,:], diff_alt[49,:]]\n", + "color = [\"blue\", \"green\", \"red\", \"lightblue\", \"lightgreen\", \"tomato\"]\n", + "linestyle = [\"-\", \"-\", \"-\", \"--\", \"--\", \"--\"]\n", + "label = [\"anatrans, t=2y\", \"anatrans, t=6y\", \"anatrans, t=8y\",\n", + " \"alternative, t=2y\", \"alternative, t=6y\", \"alternative, t=8y\"]\n", + "\n", + "for i in range(len(y_plottable)):\n", + " plt.plot(ana_results.x,\n", + " y_plottable[i],\n", + " color=color[i],\n", + " linestyle=linestyle[i],\n", + " label=label[i]\n", + " )\n", + "plt.axhline(y=0, color='black', label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(\"Relative error\")\n", + "plt.title(\"Relative error with Mibitrans along plume centerline\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c625ecab3bec482", + "metadata": {}, + "outputs": [], + "source": [ + "lin_att = mbt.AttenuationParameters(\n", + " bulk_density=1.7, #[g/m3]\n", + " partition_coefficient=38, # [m^3/g]\n", + " fraction_organic_carbon=5.7e-5, # [-]\n", + " diffusion=0, # [m2/day]\n", + " half_life=2 * 365 # [days]\n", + ")\n", + "\n", + "ana_lin_object = mbt.Anatrans(hydro, lin_att, source, model)\n", + "ana_lin_results = ana_lin_object.run()\n", + "\n", + "alt_lin_object = AnatransAlternativeSource(hydro, lin_att, source, model)\n", + "alt_lin_results = alt_lin_object.run()\n", + "\n", + "mbt_lin_object = mbt.Mibitrans(hydro, lin_att, source, model)\n", + "mbt_lin_results = mbt_lin_object.run()" + ] + }, + { + "cell_type": "markdown", + "id": "524d40f20311c18e", + "metadata": {}, + "source": [ + "When also modelling linear decay, the plumes and their differences show similar behavior as before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c4ed92e2b48d0da", + "metadata": {}, + "outputs": [], + "source": [ + "time = 365 * 5\n", + "ana_lin_results.centerline(time = time, color=\"green\", label=\"anatrans\")\n", + "alt_lin_results.centerline(time = time, color=\"red\", linestyle=\"--\", label=\"anatrans alternative\")\n", + "mbt_lin_results.centerline(time = time, color=\"blue\", linestyle=\":\", label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39d0e8e689351364", + "metadata": {}, + "outputs": [], + "source": [ + "diff_ana = relative_error(mbt_lin_results.cxyt[:,len(ana_results.y)//2,:],\n", + " ana_lin_results.cxyt[:,len(ana_results.y)//2,:])\n", + "diff_alt = relative_error(mbt_lin_results.cxyt[:,len(ana_results.y)//2,:],\n", + " alt_lin_results.cxyt[:,len(ana_results.y)//2,:])\n", + "y_plottable = [diff_ana[9,:], diff_ana[29,:], diff_ana[49,:],\n", + " diff_alt[9,:], diff_alt[29,:], diff_alt[49,:]]\n", + "color = [\"blue\", \"green\", \"red\", \"lightblue\", \"lightgreen\", \"tomato\"]\n", + "linestyle = [\"-\", \"-\", \"-\", \"--\", \"--\", \"--\"]\n", + "label = [\"anatrans, t=2y\", \"anatrans, t=6y\", \"anatrans, t=8y\",\n", + " \"alternative, t=2y\", \"alternative, t=6y\", \"alternative, t=8y\"]\n", + "\n", + "for i in range(len(y_plottable)):\n", + " plt.plot(ana_results.x,\n", + " y_plottable[i],\n", + " color=color[i],\n", + " linestyle=linestyle[i],\n", + " label=label[i]\n", + " )\n", + "\n", + "plt.axhline(y=0, color='black', label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(\"Relative error\")\n", + "plt.title(\"Relative error with Mibitrans along plume centerline\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40d94b4c9e00b281", + "metadata": {}, + "outputs": [], + "source": [ + "large_source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([30/ft, 35/ft, 40/ft]), # [m]\n", + " source_zone_concentration=np.array([13.68, 2.508, 0.057]), # [g/m3]\n", + " depth=10/ft, # [m]\n", + " total_mass=500000 # [g]\n", + ")\n", + "\n", + "ana_large_object = mbt.Anatrans(hydro, att, large_source, model)\n", + "ana_large_results = ana_large_object.run()\n", + "\n", + "alt_large_object = AnatransAlternativeSource(hydro, att, large_source, model)\n", + "alt_large_results = alt_large_object.run()\n", + "\n", + "mbt_large_object = mbt.Mibitrans(hydro, att, large_source, model)\n", + "mbt_large_results = mbt_large_object.run()\n" + ] + }, + { + "cell_type": "markdown", + "id": "e1f291ad2f3e90e4", + "metadata": {}, + "source": [ + "When increasing source mass, differences between two source depletion formulations become less apparent. Arguably, the alternative formulation comes closer to the exact Mibitrans solution than Anatrans does." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25629ba2d67e3cc7", + "metadata": {}, + "outputs": [], + "source": [ + "time = 365*10\n", + "ana_large_results.centerline(time = time, color=\"green\", label=\"anatrans\")\n", + "alt_large_results.centerline(time = time, color=\"red\", linestyle=\"--\", label=\"anatrans alternative\")\n", + "mbt_large_results.centerline(time = time, color=\"blue\", linestyle=\":\", label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1327e66cd2f48442", + "metadata": {}, + "outputs": [], + "source": [ + "time = 365*10\n", + "ana_large_results.transverse(time = time, x_position=150, color=\"green\", label=\"anatrans\")\n", + "alt_large_results.transverse(time = time, x_position=150, color=\"red\", linestyle=\"--\", label=\"anatrans alternative\")\n", + "mbt_large_results.transverse(time = time, x_position=150, color=\"blue\", linestyle=\":\", label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f34316918ad378f", + "metadata": {}, + "outputs": [], + "source": [ + "# The absolute error does a better job visualizing the differences here\n", + "diff_ana = absolute_error(mbt_large_results.cxyt[:,len(ana_results.y)//2,:],\n", + " ana_large_results.cxyt[:,len(ana_results.y)//2,:])\n", + "diff_alt = absolute_error(mbt_large_results.cxyt[:,len(ana_results.y)//2,:],\n", + " alt_large_results.cxyt[:,len(ana_results.y)//2,:])\n", + "y_plottable = [diff_ana[9,:], diff_ana[29,:], diff_ana[49,:],\n", + " diff_alt[9,:], diff_alt[29,:], diff_alt[49,:]]\n", + "color = [\"blue\", \"green\", \"red\", \"lightblue\", \"lightgreen\", \"tomato\"]\n", + "linestyle = [\"-\", \"-\", \"-\", \"--\", \"--\", \"--\"]\n", + "label = [\"anatrans, t=2y\", \"anatrans, t=6y\", \"anatrans, t=8y\",\n", + " \"alternative, t=2y\", \"alternative, t=6y\", \"alternative, t=8y\"]\n", + "\n", + "for i in range(len(y_plottable)):\n", + " plt.plot(ana_results.x,\n", + " y_plottable[i],\n", + " color=color[i],\n", + " linestyle=linestyle[i],\n", + " label=label[i]\n", + " )\n", + "plt.axhline(y=0, color='black', label=\"mibitrans\")\n", + "plt.legend()\n", + "plt.xlabel(\"x-position [m]\")\n", + "plt.ylabel(\"Absolute error [g/m3]\")\n", + "plt.title(\"Absolute error with Mibitrans along plume centerline\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_validity_substitution.ipynb b/examples/example_validity_substitution.ipynb new file mode 100644 index 0000000..34851ec --- /dev/null +++ b/examples/example_validity_substitution.ipynb @@ -0,0 +1,308 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1bb35746ac89a12", + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "from scipy.special import erf\n", + "import mibitrans as mbt\n" + ] + }, + { + "cell_type": "markdown", + "id": "e50f93803d773894", + "metadata": {}, + "source": [ + "The governing equation for advective-dispersive transport with adsorption and decay given by Bear (1979) is:\n", + "\n", + "\\begin{equation}\\tag{1}\n", + " R\\frac{\\partial C}{\\partial t} = -v\\frac{\\partial C}{\\partial x} + D_{x}\\frac{\\partial ^2 C}{\\partial x^2} + D_y \\frac{\\partial^2 C}{\\partial y^2} + D_z \\frac{\\partial^2 C}{\\partial z^2} - \\mu C\n", + "\\end{equation}\n", + "\n", + "With $0\\leq x \\leq \\infty$, $-\\infty \\leq y \\leq \\infty$ and $-\\infty \\leq z \\leq \\infty$. With the contaminant source modeled as a continuous planar block located at x=0 and of width Y and thickness Z, one gets the following exact analytical equation given by Wexler (1992):\n", + "\n", + "\\begin{align}\\tag{2}\n", + " C(x,y,z,t) = & \\frac{C_{0}x}{8\\sqrt{\\pi \\alpha_x v/R}}\n", + " \\int_0^t \\tau^{-3/2} \\exp \\left(-\\mu\\tau - \\frac{(x-v\\tau/R)^2}{4\\alpha_xv\\tau/R}\\right) \\cdot\\\\\n", + " & \\quad \\left[\\operatorname{erf} \\left( \\frac{y+Y/2}{2\\sqrt{D_y\\tau/R}} \\right) -\n", + " \\operatorname{erf} \\left( \\frac{y-Y/2}{2\\sqrt{D_y\\tau/R}} \\right)\\right]\\cdot \\nonumber \\\\\n", + " & \\quad\\left[\\operatorname{erf} \\left( \\frac{z+Z/2}{2\\sqrt{D_z\\tau/R}} \\right) -\n", + " \\operatorname{erf} \\left( \\frac{z-Z/2}{2\\sqrt{D_z\\tau/R}} \\right)\\right] d\\tau\n", + " \\nonumber\n", + "\\end{align}\n", + "\n", + "Where $\\tau$ is the integration variable for time. There is however, no full exact analytical solution of this equation.\n", + "\n", + "The paper of Domenico and Robbins (1985) suggests an approximation to the 3D solution by using the product of the 1D solution in each direction:\n", + "\n", + "\\begin{equation}\\tag{3}\n", + " C(x,y,z,t) \\approx C(x,t) \\cdot C(y,t) \\cdot C(z,t)\n", + "\\end{equation}\n", + "\n", + "Individually, each 1D solution can be derived in a full analytical equation, leading to:\n", + "\n", + "\\begin{align}\\tag{4}\n", + " C(x, y, z, t) &= \\frac{C_{0}}{8} \\left( \\exp \\left[ \\frac{xv\\left(1-\\sqrt{1+4\\mu R D_x/v^2}\\right)}{2D_x}\\right] \\right. \\\\\n", + " &\\quad \\quad \\cdot \\operatorname{erfc} \\left[ \\frac{x - \\frac{vt}{R}\\sqrt{1+4\\mu R D_x/v^2}}{2\\sqrt{D_x t / R }} \\right] \\\\\n", + " &\\quad \\ \\, + \\exp \\left[ \\frac{xv\\left(1+\\sqrt{1+4\\mu R D_x/v^2}\\right)}{2D_x}\\right] \\\\\n", + " &\\quad \\quad \\cdot \\left. \\operatorname{erfc} \\left[ \\frac{x + \\frac{vt}{R}\\sqrt{1+4\\mu R D_x/v^2}}{2\\sqrt{D_x t/R }} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y/2}{2\\sqrt{D_y t / R}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y/2}{2\\sqrt{D_y t / R)}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{z + Z/2}{2\\sqrt{D_z t / R)}} \\right] - \\operatorname{erf} \\left[ \\frac{z - Z/2}{2\\sqrt{D_z t / R}} \\right] \\right)\n", + "\\end{align}\n", + "\n", + "However, the equation presented in Domenico (1987) is different:\n", + "\n", + "\\begin{align}\\tag{5}\n", + " C(x, y, z, t) &= \\frac{C_{0}}{8} \\left( \\exp \\left[ \\frac{xv\\left(1-\\sqrt{1+4\\mu R D_x/v^2}\\right)}{2D_x}\\right] \\right. \\\\\n", + " &\\quad \\quad \\cdot \\operatorname{erfc} \\left[ \\frac{x - \\frac{vt}{R}\\sqrt{1+4\\mu R D_x/v^2}}{2\\sqrt{D_x t / R }} \\right] \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y/2}{2\\sqrt{\\alpha_y x}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y/2}{2\\sqrt{\\alpha_y x)}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{z + Z/2}{2\\sqrt{\\alpha_z x)}} \\right] - \\operatorname{erf} \\left[ \\frac{z - Z/2}{2\\sqrt{\\alpha_z x}} \\right] \\right)\n", + "\\end{align}\n", + "\n", + "Notice the missing second exponent and erfc term of the $C(x,t)$ solution, and that in the transverse dispersion terms, $D_{y,z}t/R$ is replaced by $\\alpha_{y,z}x$. The first difference is due to a truncation of the x-term in the solution. But equation \\tag{5} can be untruncated and include the additional x-term without any issue, and is implemented as such in the Anatrans solution of this package. The second difference is accomplished by substituting $t$ for $x/v$ (technically $xR/v$ since we consider retardation). Assuming the effect of molecular diffusion in transport is negligible, $D_{y,z} = \\alpha_{y,z}v$. Therefore making $2\\sqrt{D_{y,z} t / R)} = 2\\sqrt{\\alpha_{y,z}v\\frac{xR}{v}/R} = 2\\sqrt{\\alpha_{y,z}x}$. $x/v$ can be seen as the time it takes for the advective front to reach position $x$ under flow velocity $v$.\n", + "\n", + "Albeit this substitution in equation 4 lacks a mathematical basis, as put in West et al. (2007), it is essential for approximating the exact equation. For a continuous source, $\\lim_{t \\to \\infty}C(x=0,y,z,t)$ should be $C_0$, absent any source decay or depletion. However, for equation 4, $\\lim_{t \\to \\infty}C(x=0,y,z,t) = 0$, as both transverse dispersion terms go to 0 for $t \\to \\infty$. Therefore $C(x,y,z,t) \\not \\approx C(x,t) \\cdot C(y,t) \\cdot C(z,t)$, meaning equation 4 (presented as equation 22 in West et al. (2007) as the 'final approximate nontruncated solution') is not a correct approximate solution in the first place. This is shown below by comparing equation 4 and 5 to the exact solution, equation 2. To more accurately compare the two equations, equation 5 is used in its untruncated form." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47a67bc4465e9ddb", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "hydro = mbt.HydrologicalParameters(\n", + " velocity=0.3, #[m/d]\n", + " porosity=0.25, #[-]\n", + " alpha_x=4, #[m]\n", + " alpha_y=0.4, #[m]\n", + " alpha_z=0.04 #[m]\n", + ")\n", + "\n", + "att = mbt.AttenuationParameters(\n", + " bulk_density=1.7, #[g/m^3]\n", + " partition_coefficient=38, #[m^3/g]\n", + " fraction_organic_carbon=5.7e-5, #[-]\n", + " diffusion=0, #[m2/day]\n", + " half_life=0 #[days]\n", + ")\n", + "\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([11]),\n", + " source_zone_concentration=np.array([14]),\n", + " depth=3,\n", + " total_mass=\"inf\"\n", + ")\n", + "\n", + "model = mbt.ModelParameters(\n", + " model_length = 450, #[m]\n", + " model_width = 100, #[m]\n", + " model_time = 4 * 365, #[days]\n", + " dx = 1, #[m]\n", + " dy = 1, #[m]\n", + " dt = 25 #[days]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "858c8ae1fc0c6ff5", + "metadata": {}, + "outputs": [], + "source": [ + "class AnatransWithoutSubstitution(mbt.Anatrans):\n", + " \"\"\"Anatrans model class without substitution.\"\"\"\n", + "\n", + " def _equation_term_z(self, ttt):\n", + " inner_term = self._src_pars.depth / (2 * np.sqrt(self._hyd_pars.alpha_z * self.rv * ttt))\n", + " return erf(inner_term) - erf(-inner_term)\n", + "\n", + " def _equation_term_y(self, i, yyy, ttt):\n", + " div_term = 2 * np.sqrt(self._hyd_pars.alpha_y * self.rv * ttt)\n", + " term = erf((yyy + self.y_source[i]) / div_term) - erf((yyy - self.y_source[i]) / div_term)\n", + " term[np.isnan(term)] = 0\n", + " return term\n", + "\n", + " def _calculate_concentration_for_all_xyt(self, xxx, yyy, ttt):\n", + " cxyt = 0\n", + " decay_sqrt = np.sqrt(1 + 4 * (self._decay_rate - self.k_source) * self._hyd_pars.alpha_x / self.rv)\n", + " x_term = self._equation_term_x(xxx, ttt, decay_sqrt)\n", + " additional_x = self._equation_term_additional_x(xxx, ttt, decay_sqrt)\n", + " z_term = self._equation_term_z(ttt)\n", + " source_decay = self._equation_term_source_depletion(xxx, ttt)\n", + " for i in range(len(self.c_source)):\n", + " y_term = self._equation_term_y(i, yyy,ttt)\n", + " cxyt_step = 1 / 8 * self.c_source[i] * source_decay * (x_term + additional_x) * y_term * z_term\n", + " cxyt += cxyt_step\n", + " if self._mode == \"instant_reaction\":\n", + " self.cxyt_noBC = cxyt.copy()\n", + " cxyt -= self.biodegradation_capacity\n", + " cxyt = np.where(cxyt < 0, 0, cxyt)\n", + " self.has_run = True\n", + " return cxyt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c7df9302fb5805f", + "metadata": {}, + "outputs": [], + "source": [ + "ana_object = mbt.Anatrans(hydro, att, source, model)\n", + "ana_results = ana_object.run()\n", + "alt_disp_object = AnatransWithoutSubstitution(hydro, att, source, model)\n", + "alt_disp_results = alt_disp_object.run()\n", + "mbt_object = mbt.Mibitrans(hydro, att, source, model)\n", + "mbt_results = mbt_object.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d96482cdc4854c1e", + "metadata": {}, + "outputs": [], + "source": [ + "mbt_results.centerline(time=182, color=\"green\", label=\"eq2, t=182days\")\n", + "mbt_results.centerline(time=365, color=\"blue\", label=\"eq2, t=365days\")\n", + "mbt_results.centerline(time=3*365, color=\"red\", label=\"eq2, t=1095days\")\n", + "ana_results.centerline(time=182, color=\"limegreen\", linestyle=\"-.\", label=\"eq5, t=182days\")\n", + "ana_results.centerline(time=365, color=\"cornflowerblue\", linestyle=\"-.\", label=\"eq5, t=365days\")\n", + "ana_results.centerline(time=3*365, color=\"orangered\", linestyle=\"-.\", label=\"eq5, t=1095days\")\n", + "alt_disp_results.centerline(time=182, color=\"lightgreen\", linestyle=\":\", label=\"eq4, t=182days\")\n", + "alt_disp_results.centerline(time=365, color=\"lightblue\", linestyle=\":\", label=\"eq4, t=365days\")\n", + "alt_disp_results.centerline(time=3*365, color=\"tomato\", linestyle=\":\", label=\"eq4, t=1095days\")\n", + "\n", + "plt.title(\"Centerline plot of various models\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb5f171cbfb23c90", + "metadata": {}, + "outputs": [], + "source": [ + "mbt_results.transverse(time=182, x_position=75, color=\"green\", label=\"eq2, t=182days\")\n", + "mbt_results.transverse(time=365, x_position=75, color=\"blue\", label=\"eq2, t=365days\")\n", + "mbt_results.transverse(time=3*365, x_position=75, color=\"red\", label=\"eq2, t=1095days\")\n", + "ana_results.transverse(time=182, x_position=75, color=\"limegreen\", linestyle=\"-.\", label=\"ana, t=182days\")\n", + "ana_results.transverse(time=365, x_position=75, color=\"cornflowerblue\", linestyle=\"-.\", label=\"ana, t=365days\")\n", + "ana_results.transverse(time=3*365, x_position=75, color=\"orangered\", linestyle=\"-.\", label=\"ana, t=1095days\")\n", + "alt_disp_results.transverse(time=182, x_position=75, color=\"lightgreen\", linestyle=\":\", label=\"eq4, t=182days\")\n", + "alt_disp_results.transverse(time=365, x_position=75, color=\"lightblue\", linestyle=\":\", label=\"eq4, t=365days\")\n", + "alt_disp_results.transverse(time=3*365, x_position=75, color=\"tomato\", linestyle=\":\", label=\"eq4, t=1095days\")\n", + "plt.ylim(0, 10)\n", + "plt.title(\"Transverse plot of various models, at x=75m\")\n", + "plt.legend()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d91bbb7193af3400", + "metadata": {}, + "source": [ + "As these graphs show, equation 4, without substitution, does not come close to the exact solution of equation 2 for larger times. Equation 5, with substitution has a small error with the exact solution, but follows the same general trend. As equation 4 does not correspond with the boundary condition $\\lim_{t \\to \\infty}C(x=0,y,z,t) = C_0$, source concentrations do not stay constant. The expressions for transverse dispersion in equation 4 do not involve any representation of x-position and therefore can not abide to a boundary condition at x=0." + ] + }, + { + "cell_type": "markdown", + "id": "9d7f83bc24f06990", + "metadata": {}, + "source": [ + "Instead of equation 3, the solution from Domenico (1987), is more akin to $C(x,y,z,t) \\approx C(x,t) \\cdot C(x,y) \\cdot (x,z)$. But these solutions can not be mathematically derived from the governing equations. Therefore derivation of the Domenico solution is best represented by starting at equation 2. Assume that transverse dispersion is not dependent on time, but on position along the flow direction. Diffusion is removed from the dispersion coefficient, since it is independent from flow velocity and position. The time integration variable $\\tau$ therefore is converted into $\\frac{xR}{v}$, resolving to:\n", + "\\begin{align}\\tag{6}\n", + " C(x,y,z,t) = & \\frac{C_{0}x}{8\\sqrt{\\pi \\alpha_x v/R}}\n", + " \\int_0^t \\tau^{-3/2} \\exp \\left(-\\mu\\tau - \\frac{(x-v\\tau/R)^2}{4\\alpha_xv\\tau/R}\\right) \\cdot\\\\\n", + " & \\quad \\left[\\operatorname{erf} \\left( \\frac{y+Y/2}{2\\sqrt{\\alpha_y x}} \\right) -\n", + " \\operatorname{erf} \\left( \\frac{y-Y/2}{2\\sqrt{\\alpha_y x}} \\right)\\right]\\cdot \\nonumber \\\\\n", + " & \\quad\\left[\\operatorname{erf} \\left( \\frac{z+Z/2}{2\\sqrt{\\alpha_z x}} \\right) -\n", + " \\operatorname{erf} \\left( \\frac{z-Z/2}{2\\sqrt{\\alpha_z x}} \\right)\\right] d\\tau\n", + " \\nonumber\n", + "\\end{align}\n", + "\n", + "Now independent of time, the transverse dispersion terms can be taken outside of the integral. Resulting in:\n", + "\n", + "\\begin{align}\\tag{4}\n", + " C(x, y, z, t) &= \\frac{C_{0}}{8} \\left( \\exp \\left[ \\frac{xv\\left(1-\\sqrt{1+4\\mu R D_x/v^2}\\right)}{2D_x}\\right] \\right. \\\\\n", + " &\\quad \\quad \\cdot \\operatorname{erfc} \\left[ \\frac{x - \\frac{vt}{R}\\sqrt{1+4\\mu R D_x/v^2}}{2\\sqrt{D_x t / R }} \\right] \\\\\n", + " &\\quad \\ \\, + \\exp \\left[ \\frac{xv\\left(1+\\sqrt{1+4\\mu R D_x/v^2}\\right)}{2D_x}\\right] \\\\\n", + " &\\quad \\quad \\cdot \\left. \\operatorname{erfc} \\left[ \\frac{x + \\frac{vt}{R}\\sqrt{1+4\\mu R D_x/v^2}}{2\\sqrt{D_x t/R }} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{y + Y/2}{2\\sqrt{\\alpha_y x}} \\right] - \\operatorname{erf} \\left[ \\frac{y - Y/2}{2\\sqrt{\\alpha_y x}} \\right] \\right) \\\\\n", + " &\\quad \\cdot \\left( \\operatorname{erf} \\left[ \\frac{z + Z/2}{2\\sqrt{\\alpha_z x}} \\right] - \\operatorname{erf} \\left[ \\frac{z - Z/2}{2\\sqrt{\\alpha_z x}} \\right] \\right)\n", + "\\end{align}" + ] + }, + { + "cell_type": "markdown", + "id": "74362679a9d1935c", + "metadata": {}, + "source": [ + "It should be noted that equation 5 also does not respect the boundary condition $\\lim_{t \\to \\infty}C(x=0,y,z,t) = C_0$ for smaller times due to the truncation, see the example below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91b08c061186d19f", + "metadata": {}, + "outputs": [], + "source": [ + "bio_object = mbt.Bioscreen(hydro, att, source, model)\n", + "bio_results = bio_object.run()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b727beb694d3b4a", + "metadata": {}, + "outputs": [], + "source": [ + "plot_times = [25,50,100,200]\n", + "mbt_color = [\"blue\", \"deepskyblue\", \"lime\", \"yellowgreen\"]\n", + "bio_color = [\"lightblue\", \"skyblue\", \"lightgreen\", \"lawngreen\"]\n", + "for i in range(len(plot_times)):\n", + " mbt_results.centerline(time=plot_times[i], color = mbt_color[i],\n", + " label=f\"eq 2, t={plot_times[i]} days\")\n", + " bio_results.centerline(time=plot_times[i], linestyle = \"-.\", color = bio_color[i],\n", + " label=f\"eq 5, t={plot_times[i]} days\")\n", + "plt.legend()\n", + "plt.xlim(-10,125)\n", + "plt.title(\"Difference in source concentration for different transport equations\")\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/example_walkthrough.ipynb b/examples/example_walkthrough.ipynb index 940cbaf..1ded41f 100644 --- a/examples/example_walkthrough.ipynb +++ b/examples/example_walkthrough.ipynb @@ -1,39 +1,19 @@ { "cells": [ { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "import matplotlib.pyplot as plt\n", - "import numpy as np\n", - "from mibitrans.data.parameter_information import ElectronAcceptors\n", - "from mibitrans.data.parameter_information import UtilizationFactor\n", - "from mibitrans.data.parameters import AttenuationParameters\n", - "from mibitrans.data.parameters import HydrologicalParameters\n", - "from mibitrans.data.parameters import ModelParameters\n", - "from mibitrans.data.parameters import SourceParameters\n", - "from mibitrans.transport.models import Anatrans\n", - "from mibitrans.transport.models import Bioscreen\n", - "from mibitrans.transport.models import Mibitrans\n", - "from mibitrans.visualize.plot_line import breakthrough\n", - "from mibitrans.visualize.plot_line import centerline\n", - "from mibitrans.visualize.plot_line import transverse\n", - "from mibitrans.visualize.plot_surface import plume_2d\n", - "from mibitrans.visualize.plot_surface import plume_3d" + "# Features of mibitrans" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "The showcase of mibitrans below uses example data originating from BIOSCREEN, the Excel based modeling software this package took inspiration from. Describes field conditions on the Keesler Air Force Base in Mississippi, USA. Movement of mixed BTEX plume from 1989 to 1995. For more details, see Newell et al. (1997)\n", + "This example present the features implemented in the mibitrans package, and explains how to use them. The showcase uses example data originating from BIOSCREEN, the Excel based modeling software this package took inspiration from. Describes field conditions on the Keesler Air Force Base in Mississippi, USA. Movement of mixed BTEX plume from 1989 to 1995 due to leakage of underground storage tanks. For more details, see Newell et al. (1997). The example_keesler notebook goes into detail of origin of parameters and how to use mibitrans to investigate field sites.\n", "\n", - "As it was developed in the USA, length units are in ft. We'll use a conversion factor to express length in m instead.\n", - "\n", - "Newell, C. J., McLeod, R. K., & Gonzales, J. R. (1997). BIOSCREEN natural attenuation decision support\n", - "system version 1.4 revisions, Tech. rep., U.S. EPA." + "As it was developed in the USA, original units are in ft. They have been converted and rounded to m for sake of clarity." ] }, { @@ -42,25 +22,27 @@ "metadata": {}, "outputs": [], "source": [ - "ft = 3.281 # factor to convert ft to m" + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import mibitrans as mbt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Input by dataclasses\n", + "## Input by dataclasses\n", "\n", - "mibitrans use dataclasses located in mibitrans.data.read to handle data input. This can be There are five input dataclasses, each for a different category of parameters. To avoid any mistakes, units of input parameters should be the ones specified. When using different units, make sure that they are consistent throughout the entire modelling process." + "mibitrans use dataclasses located in mibitrans.data.read to handle data input. This can be There are five input dataclasses, each for a different category of parameters. To avoid any mishaps, units of input parameters should be the ones specified in each section. When using different units, make sure that they are consistent throughout the entire modelling process." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Hydrological parameters\n", + "### Hydrological parameters\n", "\n", - "Contains parameters that are inherent to the aquifer properties; flow velocity, porosity and dispersivity. Flow velocity can alternatively be calculated from the hydraulic conductivity and hydraulic gradient. " + "Contains parameters that are inherent to the aquifer properties; flow velocity, porosity and dispersivity. Flow velocity can alternatively be calculated from the hydraulic conductivity and hydraulic gradient." ] }, { @@ -69,21 +51,21 @@ "metadata": {}, "outputs": [], "source": [ - "hydro = HydrologicalParameters(\n", - " velocity=113.8/ft/365, # Groundwater flow velocity, in [m/day]\n", + "hydro = mbt.HydrologicalParameters(\n", + " velocity=0.1, # Groundwater flow velocity, in [m/day]\n", " porosity=0.25, # Effective soil porosity [-]\n", - " alpha_x=13.3/ft, # Longitudinal dispersivity, in [m]\n", - " alpha_y=1.3/ft, # Transverse horizontal dispersivity, in [m]\n", + " alpha_x=4.1, # Longitudinal dispersivity, in [m]\n", + " alpha_y=0.4, # Transverse horizontal dispersivity, in [m]\n", " alpha_z=0 # Transverse vertical dispersivity, in [m]\n", ")\n", "\n", "# Alternative by specifying hydraulic gradient and hydraulic conductivity\n", - "hydro = HydrologicalParameters(\n", + "hydro = mbt.HydrologicalParameters(\n", " h_gradient=0.048, # Hydraulic gradient [-]\n", " h_conductivity=0.495, # Hydraulic conductivity [m/day]\n", " porosity=0.25, # Effective soil porosity [-]\n", - " alpha_x=13.3/ft, # Longitudinal dispersivity, in [m]\n", - " alpha_y=1.3/ft, # Transverse horizontal dispersivity, in [m]\n", + " alpha_x=4.1, # Longitudinal dispersivity, in [m]\n", + " alpha_y=0.4, # Transverse horizontal dispersivity, in [m]\n", " alpha_z=0 # Transverse vertical dispersivity, in [m]\n", ")\n", "\n", @@ -99,10 +81,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Attenuation parameters\n", + "### Attenuation parameters\n", "Handles all parameters related to adsorption, diffusion and degradation.\n", "\n", - "##### Attenuation: retardation and diffusion\n", + "#### Attenuation: retardation and diffusion\n", "Retardation can be simply given as a value >= 1. Alternatively, the adsorption is calculated from the soil bulk density, paratition coefficient and the fraction of organic carbon in the soil. Note that calculation of retardation factor requires porosity as well, which is already provided in HydrologicalParameters. The calculation of retardation will therefore be automatically performed in the analytical equation. It can be manually calculated using the calculate_retardation method as well.\n", "\n", "Here, molecular diffusion can be given as well. It is 0 by default." @@ -114,13 +96,13 @@ "metadata": {}, "outputs": [], "source": [ - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Retardation factor for transported contaminants\n", " retardation=1\n", ")\n", "\n", "# Alternatively, calculate the retardation factor by supplying soil and contaminant properties\n", - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Soil bulk density in [g/m^3]\n", " bulk_density=1.7,\n", " # Partition coefficient of the transported contaminant to soil organic matter, in [m^3/g]\n", @@ -135,7 +117,7 @@ "# And then check the value:\n", "print(\"The calculated retardation value is: \", att.retardation)\n", "\n", - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Retardation factor for transported contaminants\n", " retardation=1,\n", " # Molecular diffusion, in [m2/day]\n", @@ -147,7 +129,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Attenuation: degradation parameters\n", + "#### Attenuation: degradation parameters\n", "\n", "Linear decay models only need either the contaminant decay rate or half life. Input for the instant reaction model is somewhat more involved, and is therefore done with a class method of the model classes." ] @@ -158,13 +140,13 @@ "metadata": {}, "outputs": [], "source": [ - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Contaminant first order decay rate in [1/days]\n", " decay_rate=0.0127\n", ")\n", "\n", "# Alternatively, specify the contaminant half life\n", - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Contaminant half life, in [days]\n", " half_life=54.75,\n", ")\n" @@ -174,7 +156,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For the examples below, we'll set the attenuation parameters to the desired settings" + "For the examples below, we'll set the attenuation parameters to these settings" ] }, { @@ -183,7 +165,7 @@ "metadata": {}, "outputs": [], "source": [ - "att = AttenuationParameters(\n", + "att = mbt.AttenuationParameters(\n", " # Soil bulk density in [g/m^3]\n", " bulk_density=1.7,\n", " # Partition coefficient of the transported contaminant to soil organic matter, in [m^3/g]\n", @@ -201,7 +183,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Source parameters\n", + "### Source parameters\n", "\n", "Takes input of the dimensions of and concentrations at the contaminant source. The source is treated as a seperate phase, which dissolves into the groundwater over time. The source is assumed to be in symmetrical in its center, and concentrations decrease from the center to the fringes. Furthermore, the source is assumed to be constant over its depth. The transverse horizontal dimension (or width) of the source is divided into zones, which span a certain distance measured from the center of the source, each with an associated concentration. For example, a source can have a concentration of $10g/m^3$ $7m$ left and right from the source center, and a concentration of $5g/m^3$ up to $20m$ from the source center. This would then be entered as source_zone_boundary = $[7,20]$ and source_zone_concentration = $[10,5]$. The source can be a single zone with a single concentration as well.\n", "\n", @@ -214,24 +196,24 @@ "metadata": {}, "outputs": [], "source": [ - "# Input for a simple source zone with a width of 19.8m (65ft) and a continuous input (infinite source mass)\n", - "source = SourceParameters(\n", + "# Input for a simple source zone with a width of 20m and a continuous input (infinite source mass)\n", + "source = mbt.SourceParameters(\n", " # Source zone boundaries, in [m] (simply using a float instead of a numpy array for\n", " # single source zone input will work as well)\n", - " source_zone_boundary=np.array([65/ft]),\n", + " source_zone_boundary=np.array([20]),\n", " # Source zone concentrations, in [g/m^3]\n", " source_zone_concentration=np.array([5]),\n", " # Source depth extent, in [m]\n", - " depth=10/ft,\n", - " # Source mass, considered infinite\n", + " depth=3,\n", + " # Source mass, in [g] or as np.inf / \"infinite\"\n", " total_mass=\"inf\"\n", ")\n", "\n", "# Alternatively, specify a source mass to allow for source decay\n", - "source = SourceParameters(\n", - " source_zone_boundary=np.array([7/ft, 37/ft, 65/ft]),\n", + "source = mbt.SourceParameters(\n", + " source_zone_boundary=np.array([2, 11, 20]),\n", " source_zone_concentration=np.array([13.68, 2.508, 0.057]),\n", - " depth=10/ft,\n", + " depth=3,\n", " total_mass=2000000\n", ")\n", "\n", @@ -244,7 +226,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Model parameters\n", + "### Model parameters\n", "\n", "Accepts input for the model dimensions and discretization. Model length is the extent in the (x) direction parallel to the groundwater flow direction. The model width is the extent of the model perpendicular (y) to the groundwater flow direction. Step size of the spatial dimensions is handled with dx and dy. Ensure that the source zone fits inside of the given model width. If step sizes are not given, a ratio of model_length (1/100), model_width (1/50) and model_time (1/10) is used by default." ] @@ -255,17 +237,17 @@ "metadata": {}, "outputs": [], "source": [ - "model = ModelParameters(\n", + "model = mbt.ModelParameters(\n", " # Model extent in the longitudinal (x) direction in [m].\n", - " model_length = 320/ft,\n", + " model_length = 100,\n", " # Model extent in the transverse horizontal (y) direction in [m].\n", - " model_width = 100/ft,\n", + " model_width = 50,\n", " # Model duration in [days].\n", " model_time = 6 * 365,\n", " # Model grid discretization step size in the longitudinal (x) direction, in [m].\n", - " dx = 1/ft,\n", + " dx = 0.5,\n", " # Model grid discretization step size in the transverse horizontal (y) direction, in [m].\n", - " dy = 1/ft,\n", + " dy = 0.5,\n", " # Model time discretization step size, in [days]\n", " dt = 365 / 5\n", ")" @@ -275,9 +257,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "##### Input checking\n", + "### Input checking\n", + "\n", + "The dataclass inputs evaluates if the required input parameters are present, if they are of the correct data type and if they are in the expected domain.\n", "\n", - "The dataclass inputs evaluates if the required input parameters are present, if they are of the correct data type and if they are in the expected domain." + "**!The cells below are intended to raise an error!** " ] }, { @@ -286,9 +270,9 @@ "metadata": {}, "outputs": [], "source": [ - "# If not all required parameters are specified, an error will be shown;\n", + "# Raise error if not all required parameters are specified;\n", "# HydrologicalParameters needs the porosity, longitudinal dispersivity and transverse horizontal dispersivity as well.\n", - "fake_hydro = HydrologicalParameters(velocity = 1)" + "bad_hydro = mbt.HydrologicalParameters(velocity = 1)" ] }, { @@ -297,9 +281,9 @@ "metadata": {}, "outputs": [], "source": [ - "# If the input datatype is not what is expected; source_zone_concentration expects a numpy array\n", - "# of the same length as the array given in source_zone_boundary.\n", - "fake_source = SourceParameters(\n", + "# Raise error if the input datatype is not what is expected;\n", + "# source_zone_concentration expects a numpy array of the same length as the array given in source_zone_boundary.\n", + "bad_source = mbt.SourceParameters(\n", " source_zone_boundary = np.array([1,2,3]),\n", " source_zone_concentration = \"this is a string, not an array\",\n", " depth = 10,\n", @@ -313,8 +297,9 @@ "metadata": {}, "outputs": [], "source": [ - "# Same goes for if the input parameter has a value outside its valid domain; retardation should have a value >= 1\n", - "fake_att = AttenuationParameters(\n", + "# Raise error if a parameter has a value outside its valid domain;\n", + "# Retardation should have a value >= 1\n", + "bad_att = mbt.AttenuationParameters(\n", " retardation = 0.1\n", ")" ] @@ -323,7 +308,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Analytical models\n", + "## Analytical models\n", "\n", "The various models located in mibitrans.transport.models. Currently, three models are implemented; 'Mibitrans', 'Anatrans' and 'Bioscreen'. Each with a distinct analytical solution, which are introduced below.\n", "\n", @@ -334,13 +319,27 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Mibitrans model\n", + "### Mibitrans model\n", "\n", "The Mibitrans model class uses the exact analytical solution implemented by Karanovic (2007) in the Excel based BIOSCREEN-AT, and added source depletion, akin to that implemented in its predecessor BIOSCREEN by Newell et al. (1997). This model is based on the Wexler (1992) solution. The Mibitrans model allows for the same method as used in BIOSCREEN-AT, but expands it by allowing multiple source zones (by means of superposition) and including the instant reaction model. These were present in the original BIOSCREEN, but not reimplemented in BIOSCREEN-AT. Using a single source zone in this model, and not using the instant reaction option will make the Mibitrans solution resolve to the equation described in Karanovic (2007). Which in turn resolves to the Wexler (1992) solution if source depletion is disabled.\n", "\n", "As the namesake model of this package, it is the recommended model to use. The other models introduce a margin of error by making some assumptions. However, since this model requires evaluation of an integral, computation time might be longer, depending on model discretization. The exact nature of these differences is too much to go into detail here, but is elaborated upon in the theoretical background.\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\begin{align}\\tag{1}\n", + " C(x,y,z,t) &= \\sum_{i=1}^{n}\\left(C^*_{0,i}\\frac{x}{8\\sqrt{\\pi D^{'}_{x}}}\\exp(-\\gamma t) \\right. \\\\\n", + " &\\quad \\cdot \\int_{0}^{t}\\left[\\frac{1}{\\tau^{\\frac{3}{2}}} \\exp\\left((\\gamma - \\lambda_{eff})\\tau - \\frac{(x-v^{'}\\tau)^2}{4D^{'}_{x}\\tau}\\right) \\right. \\\\\n", + " &\\quad \\cdot \\left\\{\\operatorname{erfc}\\left(\\frac{y-Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right)-\\operatorname{erfc}\\left(\\frac{y+Y_i}{2 \\sqrt{D^{'}_{y}\\tau}}\\right) \\right\\} \\\\\n", + " &\\quad \\left. \\left. \\cdot \\left\\{\\operatorname{erfc}\\left(\\frac{z-Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right)-\\operatorname{erfc}\\left(\\frac{z+Z}{2 \\sqrt{D^{'}_{z}\\tau}}\\right) \\right\\}\\right] d\\tau \\right)\n", + "\\end{align}\n", + "$$" + ] + }, { "cell_type": "code", "execution_count": null, @@ -348,7 +347,7 @@ "outputs": [], "source": [ "# Initializing the model object\n", - "mbt_object = Mibitrans(\n", + "mbt_object = mbt.Mibitrans(\n", " hydrological_parameters=hydro,\n", " attenuation_parameters=att,\n", " source_parameters=source,\n", @@ -356,21 +355,20 @@ ")\n", "\n", "# Can use model object to request various attributes\n", - "print(\"x discretization: \", mbt_object.x)\n", "print(\"Retarded flow velocity: \", mbt_object.rv)\n", "print(\"x steps\", len(mbt_object.x), \"y steps\", len(mbt_object.y), \"t steps\", len(mbt_object.t))\n", "\n", - "# Run the model once initialized and obtain the results\n", + "# Run the model once initialized and obtain the results object\n", "mbt_results = mbt_object.run()\n" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "?Mibitrans" + "### Model results\n", + "\n", + "All models return a Results object when run. This object is a snapshot of the parameters used to generate the concentration distribution. This ensures that it is always clear which conditions lead to which result, even when re-running the model. Furthermore, the object has plotting functionalities and can generate a mass balance. This is further elaborated upon in the Visualization and Mass Balance section respectively." ] }, { @@ -379,14 +377,17 @@ "metadata": {}, "outputs": [], "source": [ - "# Once the model has run, results are contained in the cxyt attribute\n", - "model_cxyt = mbt_object.cxyt\n", + "# Concentration distribution is contained in the cxyt attribute\n", + "model_cxyt = mbt_results.cxyt\n", "\n", "# cxyt is indexed as [time, y-position, x-position]\n", "# Thus to get the concentration at the last time step, in the center of the plume for all x:\n", "plume_center = model_cxyt[-1, 132//2, :]\n", "\n", - "# mibitrans has build-in visualization methods (see visualization section for more details).\n", + "# The Results object also contains all information of parameters used to generate the output contained within\n", + "print(\"The flow velocity of the model was:\", mbt_results.hydrological_parameters.velocity, \"m/d\")\n", + "\n", + "# The Results object has build-in visualization methods (see visualization section for more details).\n", "mbt_results.centerline()\n", "plt.show()" ] @@ -395,6 +396,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### Instant reaction model\n", + "\n", "To perform the model with biodegradation modelled as an instant reaction, use the instant_reaction class method. Input for the instant reaction model is somewhat more involved, needing electron donor and acceptor concentrations. For utilization factors (amount of electron donor/acceptor used/generated by biodegradation), the values for BTEX degradation are used by default, but custom values can be given. For more specifics about the underlying principles and assumptions, see the theory." ] }, @@ -405,7 +408,7 @@ "outputs": [], "source": [ "# For streamlined input, use the ElectronAcceptor dataclass;\n", - "ea = ElectronAcceptors(\n", + "ea = mbt.ElectronAcceptors(\n", " # Difference between background oxygen and current oxygen concentration in groundwater, in [g/m^3]\n", " delta_oxygen=1.65,\n", " # Difference between background nitrate and current nitrate concentration in groundwater, in [g/m^3]\n", @@ -421,7 +424,7 @@ "mbt_object.instant_reaction(electron_acceptors=ea)\n", "\n", "# Can adapt utilization factors if needed\n", - "uf = UtilizationFactor(\n", + "uf = mbt.UtilizationFactor(\n", " # utilization factor of oxygen, as mass of oxygen consumed per mass of biodegraded contaminant [g/g].\n", " util_oxygen=2,\n", " # utilization factor of nitrate, as mass of nitrate consumed per mass of biodegraded contaminant [g/g].\n", @@ -451,7 +454,13 @@ " \"methane\":6.6,\n", " }\n", ")\n", - "print(mbt_object.biodegradation_capacity)\n", + "\n", + "# Or as a list\n", + "mbt_object.instant_reaction(\n", + " electron_acceptors=[1.65, 0.7, 16.6, 22.4, 6.6],\n", + " utilization_factor=[3.14, 4.9, 21.8, 4.7, 0.78]\n", + ")\n", + "\n", "# Note that using instant_reaction also resets the utilization factor to default.\n", "print(\"electron acceptor concentrations: \", mbt_object.electron_acceptors)\n", "print(\"electron acceptor utilization factors: \", mbt_object.utilization_factor)" @@ -461,7 +470,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Be aware that electron acceptor concentrations and utilization factors can only be changed using the instant_reaction method. Changing the properties directly will not work.\n", + "Be aware that electron acceptor concentrations and utilization factors should only be changed using the instant_reaction method. Changing the properties directly will not work.\n", "\n", "Now that instant reaction parameters are provided, the model can be run:" ] @@ -490,6 +499,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "### Sample\n", "Use the sample method to get the concentration at a specific location and time." ] }, @@ -499,7 +509,7 @@ "metadata": {}, "outputs": [], "source": [ - "# Instead, sample a specific location at any point along the plume and any point in time\n", + "# Instead of distribution, sample a specific location at any point along the plume and any point in time\n", "concentration = mbt_object.sample(\n", " x_position=100,\n", " y_position=0,\n", @@ -515,7 +525,7 @@ "source": [ "# Using the verbose flag, integration steps are printed to console.\n", "# Usefull for longer runs to track progress\n", - "mbt_object = Mibitrans(\n", + "mbt_object = mbt.Mibitrans(\n", " hydrological_parameters=hydro,\n", " attenuation_parameters=att,\n", " source_parameters=source,\n", @@ -529,11 +539,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Other models\n", "\n", - "The two models other than the Mibitrans model share the same functionalities and properties as showcase above. The only difference is the implementation of calculation.\n", "\n", - "#### Anatrans model\n", + "### Anatrans model\n", "The equation used for the Anatrans model has the assumption that C(x,y,z,t) = C(x,t) * C(y,t) * C(z,t). Then, the 3D ADE can be broken up in three separate differential equations which can be solved individually. For C(x,t) the solution is given in Bear (1979), C(y,t) and C(z,t) can be derived from Crank (1975). The Anatrans model is the combination of these solutions, with addition of source depletion, source superposition and instant reaction model, described in Newell et al. (1997). The solution of Newell et al. (1997) is based of the Domenico (1987) solution, a truncated version of the equation described above, which introduces an error with a size dependent on the ratio of flow velocity and longitudinal dispersivity. Anatrans instead uses the fully untruncated version." ] }, @@ -543,7 +551,7 @@ "metadata": {}, "outputs": [], "source": [ - "ana_object = Anatrans(\n", + "ana_object = mbt.Anatrans(\n", " hydrological_parameters=hydro,\n", " attenuation_parameters=att,\n", " source_parameters=source,\n", @@ -559,7 +567,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Bioscreen model\n", + "### Bioscreen model\n", "\n", "This model is an exact implementation of the transport equations implemented in the BIOSCREEN screening model of Newell et al. (1997), which is based on the Domenico (1987) analytical model. Using a truncated version of the equation used in the Anatrans model. This model is implemented as a method of comparison with the original BIOSCREEN software. And is included for legacy reasons, since it is the first model implemented in the mibitrans package, serving as a basis for the other models. However, caution should be taken when using this model, since a varying error is introduced by using the truncated analytical solution. The error is most prominent for shorter times and distances from the source, and depends on the ratio of flow velocity and longitudinal dispersivity. For modelling, the Anatrans (untruncated approximate solution) and Mibitrans (exact analytical solution) models are recommended instead." ] @@ -570,7 +578,7 @@ "metadata": {}, "outputs": [], "source": [ - "bio_object = Bioscreen(\n", + "bio_object = mbt.Bioscreen(\n", " hydrological_parameters=hydro,\n", " attenuation_parameters=att,\n", " source_parameters=source,\n", @@ -581,28 +589,6 @@ "plt.show()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The model class check if they receive all required and the correct input dataclasses, ensuring model calculations will be performed without error." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Will not work; hydrological_parameters should be a HydrologicalParameters class object.\n", - "fake_mbt = Mibitrans(\n", - " hydrological_parameters=model,\n", - " attenuation_parameters=att,\n", - " source_parameters=source,\n", - " model_parameters=model\n", - ")" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -618,10 +604,10 @@ "metadata": {}, "outputs": [], "source": [ - "# Lets make some different model objects to use in the plotting, takes a couple of seconds to run\n", + "# Make some different model objects to use in the plotting, takes a couple of seconds to run\n", "\n", "#Mibitrans model\n", - "mbt_object = Mibitrans(hydro, att, source, model)\n", + "mbt_object = mbt.Mibitrans(hydro, att, source, model)\n", "mbt_lineardecay = mbt_object.run()\n", "\n", "mbt_object.attenuation_parameters.decay_rate = 0\n", @@ -631,7 +617,7 @@ "mbt_instant = mbt_object.run()\n", "\n", "#Anatrans model\n", - "ana_object = Anatrans(hydro, att, source, model)\n", + "ana_object = mbt.Anatrans(hydro, att, source, model)\n", "ana_lineardecay = ana_object.run()\n", "\n", "ana_object.attenuation_parameters.decay_rate = 0\n", @@ -641,22 +627,23 @@ "ana_instant = ana_object.run()\n", "\n", "#Bioscreen model\n", - "bio_object = Bioscreen(hydro, att, source, model)\n", + "bio_object = mbt.Bioscreen(hydro, att, source, model)\n", "bio_lineardecay = bio_object.run()\n", "\n", "bio_object.attenuation_parameters.decay_rate = 0\n", "bio_nodecay = bio_object.run()\n", "\n", "bio_object.instant_reaction(electron_acceptors=ea)\n", - "bio_instant = bio_object.run()\n", - "\n" + "bio_instant = bio_object.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Plotting centerline" + "### Centerline\n", + "\n", + "The centerline plot is a concentration distribution in the longitudinal (x) direction, over center of the plume by default (y=0). y-position along the plume and point of time can be adapted with function arguments." ] }, { @@ -665,18 +652,18 @@ "metadata": {}, "outputs": [], "source": [ - "# Pass model object to plotting function, by default, the last time step is used.\n", - "centerline(mbt_nodecay)\n", + "# Pass result object to plotting function, by default, the last time step is used.\n", + "mbt.centerline(mbt_nodecay)\n", "plt.show()\n", "\n", "# If you want to plot somewhere and sometime specific, use the time and y_position arguments.\n", "# It gives the concentration profile at the step closest to what you specified.\n", - "centerline(mbt_nodecay, time=3*365, y_position=5)\n", + "mbt.centerline(mbt_nodecay, time=3*365, y_position=5)\n", "#plt.title(\"No degradation Domenico model 5m away from center, t=6years\")\n", "plt.show()\n", "\n", "# If you want you can change the plot settings to the ones you prefer\n", - "centerline(mbt_nodecay, time=6*365)\n", + "mbt.centerline(mbt_nodecay, time=6*365)\n", "plt.title(\"Better title than the one generated automatically\")\n", "plt.xlabel(\"I have changed!\")\n", "plt.ylabel(\"And so have I\")\n", @@ -686,15 +673,15 @@ "\n", "# Instead of a single model, all line visualization functions accept a list of models to be displayed\n", "# together in a single plot.\n", - "centerline([mbt_nodecay, mbt_lineardecay, mbt_instant], time=6*365, legend_names=legend)\n", + "mbt.centerline([mbt_nodecay, mbt_lineardecay, mbt_instant], time=6*365, legend_names=legend)\n", "plt.title(\"Mibitrans model at plume center for different models, t=6years\")\n", "plt.show()\n", "\n", "# Keyword arguments for plt.plot can be passed on through the function\n", "# Calling function separately per model gives more control over plot layout\n", - "centerline(mbt_nodecay, time=6*365, linestyle=\"--\", color=\"green\", label=legend[0])\n", - "centerline(mbt_lineardecay, time=6*365, linestyle=\"-.\", color=\"red\", label=legend[1])\n", - "plt.title(\"No and linear degradation Mibitrans model at plume center, t=6years\")\n", + "mbt.centerline(mbt_nodecay, time=6*365, linestyle=\"--\", color=\"green\", label=legend[0])\n", + "mbt.centerline(mbt_lineardecay, time=6*365, linestyle=\"-.\", color=\"red\", label=legend[1])\n", + "plt.title(\"No- and linear degradation Mibitrans model at plume center, t=6years\")\n", "plt.legend()\n", "plt.show()\n", "\n", @@ -711,7 +698,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Plotting transverse distribution" + "### Transverse distribution\n", + "Transverse plot visualizes the concentration distribution in the horizontal transverse direction, at a specified x-location along the plume." ] }, { @@ -721,8 +709,8 @@ "outputs": [], "source": [ "# Concentration distribution can also be plotted in the transverse direction\n", - "transverse(ana_nodecay, x_position=80, time=6*365, linestyle=\"--\", color=\"green\", label=\"no degradation\")\n", - "transverse(ana_instant, x_position=80, time=6*365, linestyle=\"-.\", color=\"red\", label=\"instant reaction\")\n", + "mbt.transverse(ana_nodecay, x_position=80, time=6*365, linestyle=\"--\", color=\"green\", label=\"no degradation\")\n", + "mbt.transverse(ana_instant, x_position=80, time=6*365, linestyle=\"-.\", color=\"red\", label=\"instant reaction\")\n", "\n", "plt.title(\"No degradation and instant reaction Domenico model at x=80m, t=6years\")\n", "plt.legend()\n", @@ -737,7 +725,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Plotting breakthrough curve" + "### Breakthrough curve\n", + "Breakthrough curve shows concentration change over time for a specified point in the contaminant plume." ] }, { @@ -747,7 +736,7 @@ "outputs": [], "source": [ "# Concentration distribution can also be plotted in the transverse direction\n", - "breakthrough([bio_nodecay, bio_lineardecay, bio_instant], x_position=80,\n", + "mbt.breakthrough([bio_nodecay, bio_lineardecay, bio_instant], x_position=80,\n", " legend_names=[\"no degradation\", \"linear decay\", \"instant reaction\"])\n", "plt.title(\"Breakthrough curve of Bioscreen model for different degradation settings, at x=80m, t=6years\")\n", "plt.show()\n" @@ -757,7 +746,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Plotting in 2D" + "### 2-dimensional plume\n", + "Using plume_2d, concentration distribution at desired point in time is visualized as a mesh." ] }, { @@ -767,13 +757,13 @@ "outputs": [], "source": [ "# Plot the x and y concentration distribution for the Mibitrans model, uses plt.pcolormesh\n", - "plume_2d(mbt_nodecay, time=6*365)\n", + "mbt.plume_2d(mbt_nodecay, time=6*365)\n", "plt.title(\"Contaminant plume with no degradation Mibitrans model, t = 6 years\")\n", "plt.show()\n", "\n", "# Function passes plt.colormesh keyword arguments\n", - "plume_2d(mbt_lineardecay, time=6*365, cmap=\"magma\")\n", - "plt.title(\"Contaminant plume with linear degradation Domenico model, t = 6 years\")\n", + "mbt.plume_2d(mbt_lineardecay, time=6*365, cmap=\"magma\")\n", + "plt.title(\"Contaminant plume with linear degradation Mibitrans model, t = 6 years\")\n", "plt.show()\n", "\n", "# Once again also can be accessed through the class method\n", @@ -785,7 +775,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Plotting 2D in 3D" + "### 3-dimensional plume\n", + "\n", + "Using plot_3d, concentration distribution at desired point in time is visualized as a three-dimensional plume, with the vertical axis representing the concentration." ] }, { @@ -795,12 +787,12 @@ "outputs": [], "source": [ "# Plot the x and y concentration distribution for no degradation decay model, uses plot_surface\n", - "plume_3d(ana_nodecay, time=6*365)\n", + "mbt.plume_3d(ana_nodecay, time=6*365)\n", "plt.title(\"Contaminant plume with no degradation Anatrans model, t = 6 years\")\n", "plt.show()\n", "\n", "# Function passes plot_surface keyword arguments\n", - "plume_3d(ana_lineardecay, time=6*365, cmap=\"viridis\")\n", + "mbt.plume_3d(ana_lineardecay, time=6*365, cmap=\"viridis\")\n", "plt.title(\"Contaminant plume with linear degradation Anatrans model, t = 6 years\")\n", "plt.show()\n", "\n", @@ -816,7 +808,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### Animate plots\n", + "### Animate plots\n", "\n", "All plots mentioned above in the visualization section have the option to be animated, which also can be saved as a file. Multiple models can be combined in a single animation. Make sure that parameters passed to the functions are inside the domain of all models. As each animation frame is a model time step, all models should have the exact same dt, otherwise, the animation will not show the correct temporal change in concentration." ] @@ -828,7 +820,7 @@ "outputs": [], "source": [ "# Needed to show animations in Jupyter Notebooks\n", - "%matplotlib notebook" + "%matplotlib ipympl" ] }, { @@ -838,17 +830,28 @@ "outputs": [], "source": [ "# Output needs to be assigned to variable for animation to work\n", - "ani = centerline([mbt_nodecay, mbt_lineardecay, mbt_instant], time=6*365, legend_names=legend, animate=True)\n", - "plt.show()\n", - "\n", + "ani = mbt.centerline([mbt_nodecay, mbt_lineardecay, mbt_instant], time=6*365, legend_names=legend, animate=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# Animation of breakthrough curve instead shows a timelapse of drawing of each curve\n", - "ani1 = breakthrough([bio_nodecay, bio_lineardecay, bio_instant], x_position=20, legend_names=legend, animate=True)\n", - "plt.show()\n", - "\n", + "ani1 = mbt.breakthrough([bio_nodecay, bio_lineardecay, bio_instant], x_position=20, legend_names=legend, animate=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# For the 3d surface plot, an entirely new plot needs to be generated per time step\n", "# This can cause slightly longer execution times\n", - "ani2 = ana_instant.plume_3d(time=6*365, animate=True, cmap=\"viridis\")\n", - "plt.show()" + "ani2 = ana_instant.plume_3d(time=6*365, animate=True, cmap=\"viridis\")" ] }, { @@ -894,7 +897,7 @@ "metadata": {}, "outputs": [], "source": [ - "mbt_object.model_parameters.model_length = 500 / ft\n", + "mbt_object.model_parameters.model_length = 150\n", "results = mbt_object.run()\n", "mb = results.mass_balance()" ] @@ -912,8 +915,18 @@ "metadata": {}, "outputs": [], "source": [ + "plt.clf()\n", "results.centerline()\n", - "plt.show()\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.clf()\n", "results.transverse(20, label=\"x = 20m\")\n", "results.transverse(60, label=\"x = 60m\")\n", "results.transverse(100, label=\"x = 100m\")\n", @@ -942,6 +955,22 @@ "plt.title(\"Degraded plume mass of instant reaction model, compared to no decay.\")\n", "plt.show()" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sources\n", + "\n", + "Newell, C. J., McLeod, R. K., & Gonzales, J. R. (1997). BIOSCREEN natural attenuation decision support\n", + "system version 1.4 revisions, Tech. rep., U.S. EPA.\n", + "\n", + "Karanovic\n", + "\n", + "Wexler\n", + "\n", + "Domenico" + ] } ], "metadata": { diff --git a/mibitrans/transport/model_parent.py b/mibitrans/transport/model_parent.py index 8d57f15..be99029 100644 --- a/mibitrans/transport/model_parent.py +++ b/mibitrans/transport/model_parent.py @@ -15,7 +15,7 @@ class Transport3D(ABC): - """Parent class for all 3-dimensional analytical solutions.""" + """Parent class for all 3-dimensional analytical transport solutions.""" def __init__( self, hydrological_parameters, attenuation_parameters, source_parameters, model_parameters, verbose=False @@ -299,7 +299,7 @@ class Results: """Object that holds model results and input parameters for individual runs.""" def __init__(self, model): - """Records input parameters and resulting output based given model. + """Records input parameters and resulting output of given model run. Args: model (Transport3D): Model object from which to initialize results. Should be child class of Transport3D. @@ -652,6 +652,7 @@ def mass_balance(self, time="all", verbose=False): def _check_instant_reaction_acceptor_input(electron_acceptors, utilization_factor): + """Check if electron acceptor and utilization factor are of correct datatype. Then pass them to dataclasses.""" if isinstance(electron_acceptors, (list, np.ndarray)): electron_acceptors_out = ElectronAcceptors(*electron_acceptors) elif isinstance(electron_acceptors, dict): diff --git a/mibitrans/transport/models.py b/mibitrans/transport/models.py index 6a14506..1ac6a22 100644 --- a/mibitrans/transport/models.py +++ b/mibitrans/transport/models.py @@ -77,6 +77,16 @@ def __init__( Raises: TypeError : If input is not of the correct Dataclass. + Example:: + + obj = Mibitrans( + hydrological_parameters=HydrologicalParameters(), + attenuation_parameters=AttenuationParameters(), + source_parameters=SourceParameters(), + model_parameters=ModelParameters() + ) + results = obj.run() + """ super().__init__(hydrological_parameters, attenuation_parameters, source_parameters, model_parameters, verbose) @@ -122,7 +132,7 @@ def integrand(t, sz): / (t**3) * ( np.exp( - (-self.k_source - self._decay_rate) * t**4 + (self.k_source - self._decay_rate) * t**4 - (x_position - self.rv * t**4) ** 2 / (4 * self.disp_x * t**4) ) * ( @@ -198,7 +208,7 @@ def _equation_integrand(self, t, sz): def _equation_term_x(self, t): term = np.exp( - (-self.k_source - self._decay_rate) * t - (self.xxx[:, :, 1:] - self.rv * t) ** 2 / (4 * self.disp_x * t) + (self.k_source - self._decay_rate) * t - (self.xxx[:, :, 1:] - self.rv * t) ** 2 / (4 * self.disp_x * t) ) term[np.isnan(term)] = 0 return term @@ -364,10 +374,8 @@ def _equation_term_z(self, xxx): inner_term = self._src_pars.depth / (2 * np.sqrt(self._hyd_pars.alpha_z * xxx)) return erf(inner_term) - erf(-inner_term) - def _equation_term_source_decay(self, xxx, ttt): - term = np.exp(-self.k_source * (ttt - xxx / self.rv)) - # Term can be max 1; can not have 'generation' of solute ahead of advection. - return np.where(term > 1, 1, term) + def _equation_term_source_depletion(self, xxx, ttt): + return np.exp(-self.k_source * ttt) def _equation_term_y(self, i, xxx, yyy): div_term = 2 * np.sqrt(self._hyd_pars.alpha_y * xxx) @@ -375,16 +383,19 @@ def _equation_term_y(self, i, xxx, yyy): term[np.isnan(term)] = 0 return term + def _equation_decay_sqrt(self): + return np.sqrt(1 + 4 * (self._decay_rate - self.k_source) * self._hyd_pars.alpha_x / self.rv) + def _calculate_concentration_for_all_xyt(self, xxx, yyy, ttt): cxyt = 0 - decay_sqrt = np.sqrt(1 + 4 * self._decay_rate * self._hyd_pars.alpha_x / self.rv) + decay_sqrt = self._equation_decay_sqrt() x_term = self._equation_term_x(xxx, ttt, decay_sqrt) additional_x = self._equation_term_additional_x(xxx, ttt, decay_sqrt) z_term = self._equation_term_z(xxx) - source_decay = self._equation_term_source_decay(xxx, ttt) + source_depletion = self._equation_term_source_depletion(xxx, ttt) for i in range(len(self.c_source)): y_term = self._equation_term_y(i, xxx, yyy) - cxyt_step = 1 / 8 * self.c_source[i] * source_decay * (x_term + additional_x) * y_term * z_term + cxyt_step = 1 / 8 * self.c_source[i] * source_depletion * (x_term + additional_x) * y_term * z_term cxyt += cxyt_step if self._mode == "instant_reaction": self.cxyt_noBC = cxyt.copy() @@ -394,9 +405,6 @@ def _calculate_concentration_for_all_xyt(self, xxx, yyy, ttt): return cxyt -# bioscreen ; Domenico - additional term - - class Bioscreen(Anatrans): """Model class using the analytical solution implemented in the BIOSCREEN screening model, Newell et al. (1997). @@ -471,17 +479,25 @@ def short_description(self): """Short description of model type.""" return "Bioscreen model" + def _equation_term_source_depletion(self, xxx, ttt): + term = np.exp(-self.k_source * (ttt - xxx / self.rv)) + # Term can be max 1; can not have 'generation' of solute ahead of advection. + return np.where(term > 1, 1, term) + + def _equation_decay_sqrt(self): + return np.sqrt(1 + 4 * self._decay_rate * self._hyd_pars.alpha_x / self.rv) + def _calculate_concentration_for_all_xyt(self, xxx, yyy, ttt): - # Difference with the Anatrans solution is the lack of additional term. + # Difference with the Anatrans solution is the lack of additional term and alternative source decay cxyt = 0 with np.errstate(divide="ignore", invalid="ignore"): - decay_sqrt = np.sqrt(1 + 4 * self._decay_rate * self._hyd_pars.alpha_x / self.rv) + decay_sqrt = self._equation_decay_sqrt() x_term = self._equation_term_x(xxx, ttt, decay_sqrt) z_term = self._equation_term_z(xxx) - source_decay = self._equation_term_source_decay(xxx, ttt) + source_depletion = self._equation_term_source_depletion(xxx, ttt) for i in range(len(self.c_source)): y_term = self._equation_term_y(i, xxx, yyy) - cxyt_step = 1 / 8 * self.c_source[i] * source_decay * x_term * y_term * z_term + cxyt_step = 1 / 8 * self.c_source[i] * source_depletion * x_term * y_term * z_term cxyt += cxyt_step if self._mode == "instant_reaction": self.cxyt_noBC = cxyt.copy() diff --git a/pyproject.toml b/pyproject.toml index c40a714..b585362 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ "cycler", "fonttools", "iniconfig", + "ipympl", "kiwisolver", "matplotlib", "numpy", diff --git a/tests/conftest.py b/tests/conftest.py index 6cea416..b7555a6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,19 +9,30 @@ from mibitrans.transport.models import Bioscreen from mibitrans.transport.models import Mibitrans +# Test parameters loosely based on Keesler site. Some adaptations to allow for more robust tests. + @pytest.fixture(scope="session") def test_hydro_pars(): """HydrologicalParameters fixture with example data for tests.""" - return HydrologicalParameters(velocity=10 / 365, porosity=0.25, alpha_x=10, alpha_y=1, alpha_z=0.1) + return HydrologicalParameters( + h_gradient=0.048, # [m/m] + h_conductivity=0.495, # [m/d] + porosity=0.25, # [-] + alpha_x=4.1, # [m] + alpha_y=0.4, # [m] + alpha_z=0.01, # [m] + ) @pytest.fixture(scope="session") def test_att_pars(): """AttenuationParameters fixture with example data for tests.""" return AttenuationParameters( - retardation=1, - half_life=0.1 * 365, + bulk_density=1.7, # [kg/L] + partition_coefficient=38, # [L/kg] + fraction_organic_carbon=0.000057, # [-] + half_life=0.5 * 365, # [1/day] ) @@ -29,36 +40,51 @@ def test_att_pars(): def test_att_pars_nodecay(): """AttenuationParameters fixture with example data for tests.""" return AttenuationParameters( - retardation=1, - decay_rate=0, + bulk_density=1.7, # [kg/L] + partition_coefficient=38, # [L/kg] + fraction_organic_carbon=0.000057, # [-] + decay_rate=0, # [-] ) -electron_acceptor_dict = dict(delta_oxygen=0.5, delta_nitrate=0.5, ferrous_iron=0.5, delta_sulfate=0.5, methane=0.5) +electron_acceptor_dict = dict( + delta_oxygen=2.05 - 0.4, # [g/m3] + delta_nitrate=0.07 - 0, # [g/m3] + ferrous_iron=16.6, # [g/m3] + delta_sulfate=26.2 - 3.8, # [g/m3] + methane=6.6, # [g/m3] +) @pytest.fixture(scope="session") def test_source_pars(): """SourceParameters fixture with example data for tests.""" return SourceParameters( - source_zone_boundary=np.array([5, 10, 15]), - source_zone_concentration=np.array([10, 5, 2]), - depth=10, - total_mass=1000000, + source_zone_boundary=np.array([2, 11, 20]), # [m] + source_zone_concentration=np.array([13.68, 2.508, 0.057]), # [g/m3] + depth=3, # [m] + total_mass=2000000, # [g] ) @pytest.fixture(scope="session") def test_model_pars(): """ModelParameters fixture with example data for tests.""" - return ModelParameters(model_length=50, model_width=30, model_time=3 * 365, dx=10, dy=5, dt=1 * 365) + return ModelParameters( + model_length=100, # [m] + model_width=40, # [m] + model_time=5 * 365, # [days] + dx=20, # [m] + dy=10, # [m] + dt=365, # [days] + ) @pytest.fixture(scope="session") def test_model_pars_short(test_source_pars, test_model_pars): """Model Parameters fixture with smaller model width for testing.""" short_model_pars = copy.copy(test_model_pars) - short_model_pars.model_width = test_source_pars.source_zone_boundary[-1] - 1 + short_model_pars.model_width = test_source_pars.source_zone_boundary[-1] return short_model_pars diff --git a/tests/test_anatrans.py b/tests/test_anatrans.py index 0bf82e4..bce131c 100644 --- a/tests/test_anatrans.py +++ b/tests/test_anatrans.py @@ -24,8 +24,8 @@ def test_transport_equation_numerical_anatrans(model, expected, request): @pytest.mark.parametrize( "x, y, t, expected", [ - (16, 0, 393, 0.2403406438598838), - (24, -5, 283, 0.031529981399875194), + (16, 0, 393, 4.041372051306399), + (24, -5, 283, 1.5760137786262713), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError), @@ -46,8 +46,8 @@ def test_anatrans_sample_linear(x, y, t, expected, test_anatrans_model_lineardec @pytest.mark.parametrize( "x, y, t, expected", [ - (20, 0, 476, 3.076798202181921), - (11, 7, 193, 2.0747279062256183), + (20, 0, 476, 5.540354132380653), + (54, 3, 1045, 3.501165953555688), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError), diff --git a/tests/test_bioscreen.py b/tests/test_bioscreen.py index eb1aa22..553aeab 100644 --- a/tests/test_bioscreen.py +++ b/tests/test_bioscreen.py @@ -24,8 +24,8 @@ def test_transport_equation_numerical_bioscreen(model, expected, request): @pytest.mark.parametrize( "x, y, t, expected", [ - (9, 0, 629, 1.2260728205395477), - (15, -7, 256, 0.21033402922523056), + (9, 0, 629, 6.222919410416837), + (15, -7, 256, 1.5292214426149926), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError), @@ -46,8 +46,8 @@ def test_bioscreen_sample_linear(x, y, t, expected, test_bioscreen_model_lineard @pytest.mark.parametrize( "x, y, t, expected", [ - (13, 0, 354, 2.721953070355462), - (11, 3, 752, 5.01432266465888), + (13, 0, 354, 5.134230309076454), + (11, 3, 752, 5.645872356690198), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError), diff --git a/tests/test_example_data.py b/tests/test_example_data.py index ad13c73..9780977 100644 --- a/tests/test_example_data.py +++ b/tests/test_example_data.py @@ -8,31 +8,39 @@ testingdata_nodecay_anatrans = np.array( [ [ - [0.0, 1.04119211, 0.70235024, 0.28178549, 0.06874379, 0.01026263], - [1.99151804, 2.77902449, 1.49534136, 0.51880451, 0.11462681, 0.01593092], - [4.97879509, 5.0070735, 2.34665292, 0.7467676, 0.15558745, 0.02072489], - [9.95759018, 6.1418951, 2.72894706, 0.84307586, 0.17224172, 0.02262182], - [4.97879509, 5.0070735, 2.34665292, 0.7467676, 0.15558745, 0.02072489], - [1.99151804, 2.77902449, 1.49534136, 0.51880451, 0.11462681, 0.01593092], - [0.0, 1.04119211, 0.70235024, 0.28178549, 0.06874379, 0.01026263], - ], - [ - [0.0, 1.26811687, 1.28567955, 0.97182081, 0.56931262, 0.2614138], - [1.98307205, 3.38470473, 2.73728078, 1.78925118, 0.94930018, 0.40579866], - [4.95768011, 6.0983505, 4.29563986, 2.57544947, 1.28852225, 0.52791258], - [9.91536023, 7.48050315, 4.99544422, 2.90759705, 1.4264472, 0.57623172], - [4.95768011, 6.0983505, 4.29563986, 2.57544947, 1.28852225, 0.52791258], - [1.98307205, 3.38470473, 2.73728078, 1.78925118, 0.94930018, 0.40579866], - [0.0, 1.26811687, 1.28567955, 0.97182081, 0.56931262, 0.2614138], - ], - [ - [0.0, 1.34821759, 1.55360571, 1.44377422, 1.12223112, 0.74311474], - [1.97466187, 3.59849991, 3.30770995, 2.65818009, 1.87126399, 1.15355412], - [4.93665468, 6.48355336, 5.19081959, 3.82618639, 2.53993977, 1.50068444], - [9.87330937, 7.95300981, 6.03645803, 4.31963755, 2.81181794, 1.6380401], - [4.93665468, 6.48355336, 5.19081959, 3.82618639, 2.53993977, 1.50068444], - [1.97466187, 3.59849991, 3.30770995, 2.65818009, 1.87126399, 1.15355412], - [0.0, 1.34821759, 1.55360571, 1.44377422, 1.12223112, 0.74311474], + [0.00000000e00, 5.15094278e-02, 7.68809285e-02, 2.54392936e-02, 1.97695247e-03, 3.59680678e-05], + [2.50471057e00, 1.55246828e00, 9.52222337e-01, 1.91704880e-01, 1.05811778e-02, 1.50150442e-04], + [1.36620576e01, 5.96227228e00, 2.43155983e00, 3.91260068e-01, 1.88411715e-02, 2.43895960e-04], + [2.50471057e00, 1.55246828e00, 9.52222337e-01, 1.91704880e-01, 1.05811778e-02, 1.50150442e-04], + [0.00000000e00, 5.15094278e-02, 7.68809285e-02, 2.54392936e-02, 1.97695247e-03, 3.59680678e-05], + ], + [ + [0.00000000e00, 5.79192855e-02, 1.59653540e-01, 2.17198355e-01, 1.62412180e-01, 6.37391410e-02], + [2.50142545e00, 1.74565817e00, 1.97741716e00, 1.63675868e00, 8.69273378e-01, 2.66082133e-01], + [1.36441388e01, 6.70422027e00, 5.04945951e00, 3.34054257e00, 1.54785499e00, 4.32208899e-01], + [2.50142545e00, 1.74565817e00, 1.97741716e00, 1.63675868e00, 8.69273378e-01, 2.66082133e-01], + [0.00000000e00, 5.79192855e-02, 1.59653540e-01, 2.17198355e-01, 1.62412180e-01, 6.37391410e-02], + ], + [ + [0.00000000e00, 5.82768590e-02, 1.70977009e-01, 2.93071143e-01, 3.66114067e-01, 3.32049611e-01], + [2.49814463e00, 1.75643526e00, 2.11766599e00, 2.20851920e00, 1.95954030e00, 1.38615719e00], + [1.36262435e01, 6.74560979e00, 5.40759375e00, 4.50747718e00, 3.48921789e00, 2.25159603e00], + [2.49814463e00, 1.75643526e00, 2.11766599e00, 2.20851920e00, 1.95954030e00, 1.38615719e00], + [0.00000000e00, 5.82768590e-02, 1.70977009e-01, 2.93071143e-01, 3.66114067e-01, 3.32049611e-01], + ], + [ + [0.00000000e00, 5.82348597e-02, 1.71971516e-01, 3.05437286e-01, 4.28425559e-01, 5.01951093e-01], + [2.49486813e00, 1.75516942e00, 2.12998362e00, 2.30170771e00, 2.29304806e00, 2.09541916e00], + [1.36083716e01, 6.74074833e00, 5.43904760e00, 4.69767027e00, 4.08307209e00, 3.40368141e00], + [2.49486813e00, 1.75516942e00, 2.12998362e00, 2.30170771e00, 2.29304806e00, 2.09541916e00], + [0.00000000e00, 5.82348597e-02, 1.71971516e-01, 3.05437286e-01, 4.28425559e-01, 5.01951093e-01], + ], + [ + [0.00000000e00, 5.81615084e-02, 1.71870672e-01, 3.06710818e-01, 4.39468968e-01, 5.49534752e-01], + [2.49159591e00, 1.75295865e00, 2.12873460e00, 2.31130477e00, 2.35215533e00, 2.29405945e00], + [1.35905232e01, 6.73225784e00, 5.43585816e00, 4.71725738e00, 4.18832032e00, 3.72634156e00], + [2.49159591e00, 1.75295865e00, 2.12873460e00, 2.31130477e00, 2.35215533e00, 2.29405945e00], + [0.00000000e00, 5.81615084e-02, 1.71870672e-01, 3.06710818e-01, 4.39468968e-01, 5.49534752e-01], ], ] ) @@ -40,31 +48,39 @@ testingdata_lineardecay_anatrans = np.array( [ [ - [0.00000000e00, 1.64871457e-01, 2.44919161e-02, 3.14034072e-03, 3.47905205e-04, 3.10817940e-05], - [1.99151804e00, 4.40055023e-01, 5.21446040e-02, 5.78178433e-03, 5.80114442e-04, 4.82489846e-05], - [4.97879509e00, 7.92863774e-01, 8.18310059e-02, 8.32230465e-03, 7.87412011e-04, 6.27681864e-05], - [9.95759018e00, 9.72561344e-01, 9.51621267e-02, 9.39560597e-03, 8.71697529e-04, 6.85132756e-05], - [4.97879509e00, 7.92863774e-01, 8.18310059e-02, 8.32230465e-03, 7.87412011e-04, 6.27681864e-05], - [1.99151804e00, 4.40055023e-01, 5.21446040e-02, 5.78178433e-03, 5.80114442e-04, 4.82489846e-05], - [0.00000000e00, 1.64871457e-01, 2.44919161e-02, 3.14034072e-03, 3.47905205e-04, 3.10817940e-05], + [0.00000000e00, 2.76361560e-02, 2.81216130e-02, 7.77848501e-03, 5.56690595e-04, 9.71508537e-06], + [2.50471057e00, 8.32939862e-01, 3.48305211e-01, 5.86169396e-02, 2.97955679e-03, 4.05560947e-05], + [1.36620576e01, 3.19891511e00, 8.89419338e-01, 1.19634241e-01, 5.30549072e-03, 6.58770465e-05], + [2.50471057e00, 8.32939862e-01, 3.48305211e-01, 5.86169396e-02, 2.97955679e-03, 4.05560947e-05], + [0.00000000e00, 2.76361560e-02, 2.81216130e-02, 7.77848501e-03, 5.56690595e-04, 9.71508537e-06], + ], + [ + [0.00000000e00, 2.87351067e-02, 4.13485527e-02, 3.36274105e-02, 1.79177815e-02, 5.75408718e-03], + [2.50142545e00, 8.66061682e-01, 5.12129812e-01, 2.53408715e-01, 9.59007536e-02, 2.40207158e-02], + [1.36441388e01, 3.32611984e00, 1.30775579e00, 5.17194508e-01, 1.70763840e-01, 3.90179040e-02], + [2.50142545e00, 8.66061682e-01, 5.12129812e-01, 2.53408715e-01, 9.59007536e-02, 2.40207158e-02], + [0.00000000e00, 2.87351067e-02, 4.13485527e-02, 3.36274105e-02, 1.79177815e-02, 5.75408718e-03], + ], + [ + [0.00000000e00, 2.87161384e-02, 4.17780652e-02, 3.66139095e-02, 2.53757095e-02, 1.45537551e-02], + [2.49814463e00, 8.65489988e-01, 5.17449615e-01, 2.75914309e-01, 1.35817577e-01, 6.07553559e-02], + [1.36262435e01, 3.32392424e00, 1.32134025e00, 5.63127300e-01, 2.41840966e-01, 9.86875939e-02], + [2.49814463e00, 8.65489988e-01, 5.17449615e-01, 2.75914309e-01, 1.35817577e-01, 6.07553559e-02], + [0.00000000e00, 2.87161384e-02, 4.17780652e-02, 3.66139095e-02, 2.53757095e-02, 1.45537551e-02], ], [ - [0.00000000e00, 1.64220271e-01, 2.45988950e-02, 3.24164212e-03, 4.00276470e-04, 4.75980035e-05], - [1.98307205e00, 4.38316957e-01, 5.23723679e-02, 5.96829367e-03, 6.67440892e-04, 7.38874769e-05], - [4.95768011e00, 7.89732236e-01, 8.21884379e-02, 8.59076634e-03, 9.05943616e-04, 9.61218762e-05], - [9.91536023e00, 9.68720062e-01, 9.55777881e-02, 9.69869031e-03, 1.00291690e-03, 1.04919784e-04], - [4.95768011e00, 7.89732236e-01, 8.21884379e-02, 8.59076634e-03, 9.05943616e-04, 9.61218762e-05], - [1.98307205e00, 4.38316957e-01, 5.23723679e-02, 5.96829367e-03, 6.67440892e-04, 7.38874769e-05], - [0.00000000e00, 1.64220271e-01, 2.45988950e-02, 3.24164212e-03, 4.00276470e-04, 4.75980035e-05], + [0.00000000e00, 2.86788433e-02, 4.17361209e-02, 3.66969967e-02, 2.59643206e-02, 1.61364696e-02], + [2.49486813e00, 8.64365933e-01, 5.16930107e-01, 2.76540436e-01, 1.38967980e-01, 6.73624743e-02], + [1.36083716e01, 3.31960730e00, 1.32001365e00, 5.64405193e-01, 2.47450672e-01, 1.09419827e-01], + [2.49486813e00, 8.64365933e-01, 5.16930107e-01, 2.76540436e-01, 1.38967980e-01, 6.73624743e-02], + [0.00000000e00, 2.86788433e-02, 4.17361209e-02, 3.66969967e-02, 2.59643206e-02, 1.61364696e-02], ], [ - [0.00000000e00, 1.63523832e-01, 2.44946191e-02, 3.24171853e-03, 4.00357148e-04, 4.76590838e-05], - [1.97466187e00, 4.36458104e-01, 5.21503589e-02, 5.96843435e-03, 6.67575420e-04, 7.39822933e-05], - [4.93665468e00, 7.86383070e-01, 8.18400370e-02, 8.59096883e-03, 9.06126216e-04, 9.62452250e-05], - [9.87330937e00, 9.64611829e-01, 9.51726292e-02, 9.69891892e-03, 1.00311904e-03, 1.05054423e-04], - [4.93665468e00, 7.86383070e-01, 8.18400370e-02, 8.59096883e-03, 9.06126216e-04, 9.62452250e-05], - [1.97466187e00, 4.36458104e-01, 5.21503589e-02, 5.96843435e-03, 6.67575420e-04, 7.39822933e-05], - [0.00000000e00, 1.63523832e-01, 2.44946191e-02, 3.24171853e-03, 4.00357148e-04, 4.76590838e-05], + [0.00000000e00, 2.86412369e-02, 4.16817095e-02, 3.66532141e-02, 2.59597664e-02, 1.62344017e-02], + [2.49159591e00, 8.63232494e-01, 5.16256185e-01, 2.76210501e-01, 1.38943605e-01, 6.77712966e-02], + [1.35905232e01, 3.31525431e00, 1.31829275e00, 5.63731811e-01, 2.47407269e-01, 1.10083895e-01], + [2.49159591e00, 8.63232494e-01, 5.16256185e-01, 2.76210501e-01, 1.38943605e-01, 6.77712966e-02], + [0.00000000e00, 2.86412369e-02, 4.16817095e-02, 3.66532141e-02, 2.59597664e-02, 1.62344017e-02], ], ] ) @@ -72,31 +88,39 @@ testingdata_instantreaction_anatrans = np.array( [ [ - [0.0, 0.37775248, 0.0, 0.0, 0.0, 0.0], - [1.98480819, 2.38673103, 0.75942263, 0.0, 0.0, 0.0], - [4.96977484, 4.70247987, 1.66982103, 0.0, 0.0, 0.0], - [9.94471925, 5.84605042, 2.06717574, 0.0, 0.0, 0.0], - [4.96977484, 4.70247987, 1.66982103, 0.0, 0.0, 0.0], - [1.98480819, 2.38673103, 0.75942263, 0.0, 0.0, 0.0], - [0.0, 0.37775248, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.35751237, 0.0, 0.0, 0.0, 0.0, 0.0], + [13.43082864, 4.15251038, 0.0, 0.0, 0.0, 0.0], + [2.35751237, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.20835401, 1.33975922, 0.24351093, 0.0, 0.0, 0.0], + [13.18385824, 6.33339313, 3.80213221, 0.0, 0.0, 0.0], + [2.20835401, 1.33975922, 0.24351093, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [0.0, 0.68359367, 0.59867174, 0.16364701, 0.0, 0.0], - [1.96969251, 3.12853079, 2.24695425, 1.08840019, 0.08763987, 0.0], - [4.93970113, 5.94680893, 3.9134741, 1.94461756, 0.46189714, 0.0], - [9.88971551, 7.3385401, 4.64084741, 2.29931647, 0.61202226, 0.0], - [4.93970113, 5.94680893, 3.9134741, 1.94461756, 0.46189714, 0.0], - [1.96969251, 3.12853079, 2.24695425, 1.08840019, 0.08763987, 0.0], - [0.0, 0.68359367, 0.59867174, 0.16364701, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.06051319, 1.31903162, 1.18223909, 0.23641044, 0.0, 0.0], + [12.93906936, 6.30614278, 4.96699928, 3.47149581, 0.50349427, 0.0], + [2.06051319, 1.31903162, 1.18223909, 0.23641044, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [0.0, 0.79052499, 0.93688906, 0.74411488, 0.32657928, 0.0], - [1.95465257, 3.38788638, 2.92712105, 2.11796363, 1.17467, 0.3252468], - [4.90977814, 6.38186393, 4.93937386, 3.38999296, 1.91240731, 0.71193944], - [9.83498741, 7.86035955, 5.81764661, 3.91694724, 2.20833448, 0.86347462], - [4.90977814, 6.38186393, 4.93937386, 3.38999296, 1.91240731, 0.71193944], - [1.95465257, 3.38788638, 2.92712105, 2.11796363, 1.17467, 0.3252468], - [0.0, 0.79052499, 0.93688906, 0.74411488, 0.32657928, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.91397827, 1.18853384, 1.15644927, 0.75166046, 0.0, 0.0], + [12.69644273, 6.13457882, 4.93499671, 4.09963864, 2.95459379, 0.93646409], + [1.91397827, 1.18853384, 1.15644927, 0.75166046, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.76873771, 1.05053556, 1.02946157, 0.70145777, 0.18347740, 0.0], + [12.45595925, 5.95315404, 4.77741777, 4.03843640, 3.27764482, 2.29352416], + [1.76873771, 1.05053556, 1.02946157, 0.70145777, 0.18347740, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], ] ) @@ -104,31 +128,39 @@ testingdata_nodecay_mibitrans = np.array( [ [ - [1.99151804e00, 8.01554517e-01, 4.52622845e-01, 1.64844210e-01, 3.80093542e-02, 5.49309885e-03], - [4.97879509e00, 2.56998867e00, 1.35186278e00, 4.72834457e-01, 1.06531042e-01, 1.51816855e-02], - [9.95759018e00, 5.22746226e00, 2.62549664e00, 8.93532734e-01, 1.98188547e-01, 2.79800829e-02], - [9.95759018e00, 6.80026270e00, 3.33844274e00, 1.12085955e00, 2.46720295e-01, 3.46763177e-02], - [9.95759018e00, 5.22746226e00, 2.62549664e00, 8.93532734e-01, 1.98188547e-01, 2.79800829e-02], - [4.97879509e00, 2.56998867e00, 1.35186278e00, 4.72834457e-01, 1.06531042e-01, 1.51816855e-02], - [1.99151804e00, 8.01554517e-01, 4.52622845e-01, 1.64844210e-01, 3.80093542e-02, 5.49309885e-03], + [5.69252401e-02, 4.58655625e-02, 3.90255339e-02, 8.96862490e-03, 5.32129779e-04, 7.98682366e-06], + [2.50471057e00, 1.53375416e00, 8.36005251e-01, 1.61715039e-01, 8.91028965e-03, 1.28892936e-04], + [1.36620576e01, 6.59641390e00, 2.83179036e00, 4.98247612e-01, 2.63789498e-02, 3.74275771e-04], + [2.50471057e00, 1.53375416e00, 8.36005251e-01, 1.61715039e-01, 8.91028965e-03, 1.28892936e-04], + [5.69252401e-02, 4.58655625e-02, 3.90255339e-02, 8.96862490e-03, 5.32129779e-04, 7.98682366e-06], + ], + [ + [5.68505783e-02, 6.81042656e-02, 1.43636130e-01, 1.63249250e-01, 1.02952664e-01, 3.51019523e-02], + [2.50142545e00, 1.77279686e00, 1.89160110e00, 1.55821397e00, 8.37626901e-01, 2.62585163e-01], + [1.36441388e01, 7.17629015e00, 5.33202716e00, 3.64352788e00, 1.79202159e00, 5.37857929e-01], + [2.50142545e00, 1.77279686e00, 1.89160110e00, 1.55821397e00, 8.37626901e-01, 2.62585163e-01], + [5.68505783e-02, 6.81042656e-02, 1.43636130e-01, 1.63249250e-01, 1.02952664e-01, 3.51019523e-02], + ], + [ + [5.67760144e-02, 7.12301289e-02, 1.72756124e-01, 2.73763611e-01, 3.15357372e-01, 2.63812763e-01], + [2.49814463e00, 1.78799606e00, 2.04698456e00, 2.14036310e00, 1.92467181e00, 1.38999780e00], + [1.36262435e01, 7.19843992e00, 5.60813338e00, 4.67845591e00, 3.70468280e00, 2.49346106e00], + [2.49814463e00, 1.78799606e00, 2.04698456e00, 2.14036310e00, 1.92467181e00, 1.38999780e00], + [5.67760144e-02, 7.12301289e-02, 1.72756124e-01, 2.73763611e-01, 3.15357372e-01, 2.63812763e-01], ], [ - [1.98307205e00, 1.05863751e00, 9.50641745e-01, 6.78487445e-01, 3.85488198e-01, 1.74322606e-01], - [4.95768011e00, 3.18639308e00, 2.54178563e00, 1.68716469e00, 9.16307826e-01, 4.02464559e-01], - [9.91536023e00, 6.26828198e00, 4.63189722e00, 2.92801637e00, 1.54285778e00, 6.64735162e-01], - [9.91536023e00, 8.04415259e00, 5.73595388e00, 3.54603967e00, 1.84410777e00, 7.88202589e-01], - [9.91536023e00, 6.26828198e00, 4.63189722e00, 2.92801637e00, 1.54285778e00, 6.64735162e-01], - [4.95768011e00, 3.18639308e00, 2.54178563e00, 1.68716469e00, 9.16307826e-01, 4.02464559e-01], - [1.98307205e00, 1.05863751e00, 9.50641745e-01, 6.78487445e-01, 3.85488198e-01, 1.74322606e-01], + [5.67015483e-02, 7.15062607e-02, 1.76971815e-01, 2.99582537e-01, 4.05306818e-01, 4.58477235e-01], + [2.49486813e00, 1.78700991e00, 2.06057741e00, 2.23289583e00, 2.24833573e00, 2.08380398e00], + [1.36083716e01, 7.19111142e00, 5.62605483e00, 4.82006101e00, 4.20374834e00, 3.56044346e00], + [2.49486813e00, 1.78700991e00, 2.06057741e00, 2.23289583e00, 2.24833573e00, 2.08380398e00], + [5.67015483e-02, 7.15062607e-02, 1.76971815e-01, 2.99582537e-01, 4.05306818e-01, 4.58477235e-01], ], [ - [1.97466187e00, 1.17386221e00, 1.23604339e00, 1.10357596e00, 8.44494958e-01, 5.56449310e-01], - [4.93665468e00, 3.41083262e00, 3.10544033e00, 2.52726539e00, 1.82120160e00, 1.15279266e00], - [9.87330937e00, 6.60043957e00, 5.47750648e00, 4.18989775e00, 2.90031733e00, 1.78770040e00], - [9.87330937e00, 8.42153416e00, 6.70381927e00, 4.99150381e00, 3.39849046e00, 2.07306879e00], - [9.87330937e00, 6.60043957e00, 5.47750648e00, 4.18989775e00, 2.90031733e00, 1.78770040e00], - [4.93665468e00, 3.41083262e00, 3.10544033e00, 2.52726539e00, 1.82120160e00, 1.15279266e00], - [1.97466187e00, 1.17386221e00, 1.23604339e00, 1.10357596e00, 8.44494958e-01, 5.56449310e-01], + [5.66271799e-02, 7.14518018e-02, 1.77288676e-01, 3.03326188e-01, 4.24772481e-01, 5.23557841e-01], + [2.49159591e00, 1.78477885e00, 2.05944649e00, 2.24178193e00, 2.30230626e00, 2.26717215e00], + [1.35905232e01, 7.18184037e00, 5.62091401e00, 4.83055062e00, 4.27913354e00, 3.81986900e00], + [2.49159591e00, 1.78477885e00, 2.05944649e00, 2.24178193e00, 2.30230626e00, 2.26717215e00], + [5.66271799e-02, 7.14518018e-02, 1.77288676e-01, 3.03326188e-01, 4.24772481e-01, 5.23557841e-01], ], ] ) @@ -136,31 +168,39 @@ testingdata_lineardecay_mibitrans = np.array( [ [ - [1.99151804e00, 1.15460753e-01, 1.39779749e-02, 1.66729845e-03, 1.80447573e-04, 1.60040845e-05], - [4.97879509e00, 3.96569754e-01, 4.55738667e-02, 5.13035685e-03, 5.29922012e-04, 4.55133917e-05], - [9.95759018e00, 8.40429884e-01, 9.35602124e-02, 1.01496717e-02, 1.01669074e-03, 8.54776276e-05], - [9.95759018e00, 1.11467694e00, 1.22196524e-01, 1.30175277e-02, 1.28447353e-03, 1.06880942e-04], - [9.95759018e00, 8.40429884e-01, 9.35602124e-02, 1.01496717e-02, 1.01669074e-03, 8.54776276e-05], - [4.97879509e00, 3.96569754e-01, 4.55738667e-02, 5.13035685e-03, 5.29922012e-04, 4.55133917e-05], - [1.99151804e00, 1.15460753e-01, 1.39779749e-02, 1.66729845e-03, 1.80447573e-04, 1.60040845e-05], + [5.69252401e-02, 2.13852839e-02, 1.32216153e-02, 2.66226452e-03, 1.48031634e-04, 2.14520312e-06], + [2.50471057e00, 8.11792437e-01, 3.01596391e-01, 4.91567828e-02, 2.50295905e-03, 3.47757905e-05], + [1.36620576e01, 3.68067444e00, 1.05444133e00, 1.53363080e-01, 7.44785137e-03, 1.01215029e-04], + [2.50471057e00, 8.11792437e-01, 3.01596391e-01, 4.91567828e-02, 2.50295905e-03, 3.47757905e-05], + [5.69252401e-02, 2.13852839e-02, 1.32216153e-02, 2.66226452e-03, 1.48031634e-04, 2.14520312e-06], ], [ - [1.98307205e00, 1.15020755e-01, 1.40024094e-02, 1.72856547e-03, 2.12281807e-04, 2.58220087e-05], - [4.95768011e00, 3.95016164e-01, 4.55963116e-02, 5.28401874e-03, 6.10824823e-04, 7.02847995e-05], - [9.91536023e00, 8.37092113e-01, 9.35438840e-02, 1.04152947e-02, 1.15813681e-03, 1.28616723e-04], - [9.91536023e00, 1.11022513e00, 1.22140889e-01, 1.33372852e-02, 1.45582545e-03, 1.59070200e-04], - [9.91536023e00, 8.37092113e-01, 9.35438840e-02, 1.04152947e-02, 1.15813681e-03, 1.28616723e-04], - [4.95768011e00, 3.95016164e-01, 4.55963116e-02, 5.28401874e-03, 6.10824823e-04, 7.02847995e-05], - [1.98307205e00, 1.15020755e-01, 1.40024094e-02, 1.72856547e-03, 2.12281807e-04, 2.58220087e-05], + [5.68505783e-02, 2.49566391e-02, 2.84481264e-02, 2.15344163e-02, 1.04480147e-02, 3.03744263e-03], + [2.50142545e00, 8.52488603e-01, 4.68088838e-01, 2.34825540e-01, 9.12767077e-02, 2.35779114e-02], + [1.36441388e01, 3.78077423e00, 1.46227339e00, 5.87628221e-01, 2.02435702e-01, 4.91720644e-02], + [2.50142545e00, 8.52488603e-01, 4.68088838e-01, 2.34825540e-01, 9.12767077e-02, 2.35779114e-02], + [5.68505783e-02, 2.49566391e-02, 2.84481264e-02, 2.15344163e-02, 1.04480147e-02, 3.03744263e-03], ], [ - [1.97466187e00, 1.14532975e-01, 1.39430733e-02, 1.72130045e-03, 2.11445188e-04, 2.57583457e-05], - [4.93665468e00, 3.93340951e-01, 4.54030381e-02, 5.26174621e-03, 6.08366660e-04, 7.00818864e-05], - [9.87330937e00, 8.33542087e-01, 9.31473205e-02, 1.03713356e-02, 1.15342995e-03, 1.28218392e-04], - [9.87330937e00, 1.10551676e00, 1.21623071e-01, 1.32809674e-02, 1.44988830e-03, 1.58565805e-04], - [9.87330937e00, 8.33542087e-01, 9.31473205e-02, 1.03713356e-02, 1.15342995e-03, 1.28218392e-04], - [4.93665468e00, 3.93340951e-01, 4.54030381e-02, 5.26174621e-03, 6.08366660e-04, 7.00818864e-05], - [1.97466187e00, 1.14532975e-01, 1.39430733e-02, 1.72130045e-03, 2.11445188e-04, 2.57583457e-05], + [5.67760144e-02, 2.50576742e-02, 2.95923313e-02, 2.57252568e-02, 1.78738782e-02, 1.01891729e-02], + [2.49814463e00, 8.52127129e-01, 4.74096396e-01, 2.57767602e-01, 1.31093707e-01, 6.05779265e-02], + [1.36262435e01, 3.77719624e00, 1.47239927e00, 6.28891841e-01, 2.73688795e-01, 1.14529271e-01], + [2.49814463e00, 8.52127129e-01, 4.74096396e-01, 2.57767602e-01, 1.31093707e-01, 6.05779265e-02], + [5.67760144e-02, 2.50576742e-02, 2.95923313e-02, 2.57252568e-02, 1.78738782e-02, 1.01891729e-02], + ], + [ + [5.67015483e-02, 2.50286883e-02, 2.95994517e-02, 2.59551922e-02, 1.87258624e-02, 1.19669029e-02], + [2.49486813e00, 8.51024089e-01, 4.73646879e-01, 2.58413920e-01, 1.34166926e-01, 6.70723478e-02], + [1.36083716e01, 3.77226501e00, 1.47073779e00, 6.29605339e-01, 2.78388880e-01, 1.24592779e-01], + [2.49486813e00, 8.51024089e-01, 4.73646879e-01, 2.58413920e-01, 1.34166926e-01, 6.70723478e-02], + [5.67015483e-02, 2.50286883e-02, 2.95994517e-02, 2.59551922e-02, 1.87258624e-02, 1.19669029e-02], + ], + [ + [5.66271799e-02, 2.49959648e-02, 2.95620621e-02, 2.59317814e-02, 1.87515748e-02, 1.21114805e-02], + [2.49159591e00, 8.49908207e-01, 4.73029820e-01, 2.58105843e-01, 1.34136464e-01, 6.74465909e-02], + [1.35905232e01, 3.76731783e00, 1.46881476e00, 6.28823699e-01, 2.78231721e-01, 1.25089190e-01], + [2.49159591e00, 8.49908207e-01, 4.73029820e-01, 2.58105843e-01, 1.34136464e-01, 6.74465909e-02], + [5.66271799e-02, 2.49959648e-02, 2.95620621e-02, 2.59317814e-02, 1.87515748e-02, 1.21114805e-02], ], ] ) @@ -168,31 +208,39 @@ testingdata_instantreaction_mibitrans = np.array( [ [ - [1.98480819, 0.13464753, 0.0, 0.0, 0.0, 0.0], - [4.96977484, 2.23701414, 0.66335351, 0.0, 0.0, 0.0], - [9.94471925, 4.92028989, 1.96263113, 0.0, 0.0, 0.0], - [9.94471925, 6.49249355, 2.67604441, 0.21615243, 0.0, 0.0], - [9.94471925, 4.92028989, 1.96263113, 0.0, 0.0, 0.0], - [4.96977484, 2.23701414, 0.66335351, 0.0, 0.0, 0.0], - [1.98480819, 0.13464753, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.35751237, 0.0, 0.0, 0.0, 0.0, 0.0], + [13.43082864, 4.78241455, 0.0, 0.0, 0.0, 0.0], + [2.35751237, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [1.96969251, 0.46935686, 0.25623422, 0.0, 0.0, 0.0], - [4.93970113, 2.98031895, 2.10160863, 1.0233045, 0.07694492, 0.0], - [9.88971551, 6.10733056, 4.25752951, 2.31745682, 0.73489692, 0.0], - [9.88971551, 7.8847924, 5.36824061, 2.94301855, 1.04148795, 0.0], - [9.88971551, 6.10733056, 4.25752951, 2.31745682, 0.73489692, 0.0], - [4.93970113, 2.98031895, 2.10160863, 1.0233045, 0.07694492, 0.0], - [1.96969251, 0.46935686, 0.25623422, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.20835401, 1.31154004, 0.22305599, 0.0, 0.0, 0.0], + [13.18385824, 6.79238283, 4.06824236, 0.0, 0.0, 0.0], + [2.20835401, 1.31154004, 0.22305599, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.06051319, 1.28341184, 1.09837108, 0.24624916, 0.0, 0.0], + [12.93906936, 6.73942235, 5.12143805, 3.60761611, 0.79563498, 0.0], + [2.06051319, 1.28341184, 1.09837108, 0.24624916, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [1.95465257, 0.61070925, 0.60959608, 0.39221492, 0.0400149, 0.0], - [4.90977814, 3.24249625, 2.76727224, 2.01777733, 1.14882233, 0.34221937], - [9.83498741, 6.48351156, 5.22465662, 3.76411963, 2.29262399, 1.01924874], - [9.83498741, 8.30731613, 6.46277784, 4.58169461, 2.80553108, 1.31527806], - [9.83498741, 6.48351156, 5.22465662, 3.76411963, 2.29262399, 1.01924874], - [4.90977814, 3.24249625, 2.76727224, 2.01777733, 1.14882233, 0.34221937], - [1.95465257, 0.61070925, 0.60959608, 0.39221492, 0.0400149, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.91397827, 1.15201201, 1.05938521, 0.69569679, 0.0, 0.0], + [12.69644273, 6.56148289, 5.06671121, 4.14314700, 3.03878990, 1.17789561], + [1.91397827, 1.15201201, 1.05938521, 0.69569679, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.76873771, 1.01417715, 0.93110472, 0.63196474, 0.15972572, 0.0], + [12.45595925, 6.37599267, 4.90480591, 4.06227650, 3.28852099, 2.36272490], + [1.76873771, 1.01417715, 0.93110472, 0.63196474, 0.15972572, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], ] ) @@ -200,31 +248,39 @@ testingdata_nodecay_bioscreen = np.array( [ [ - [0.00000000e00, 7.29338809e-01, 4.61369298e-01, 1.76413628e-01, 4.15239315e-02, 6.03372787e-03], - [1.51405147e00, 1.94666325e00, 9.82280001e-01, 3.24800918e-01, 6.92390684e-02, 9.36629471e-03], - [3.78512866e00, 3.50737679e00, 1.54150103e00, 4.67518683e-01, 9.39808944e-02, 1.21848229e-02], - [7.57025733e00, 4.30230160e00, 1.79262755e00, 5.27813089e-01, 1.04040721e-01, 1.33000836e-02], - [3.78512866e00, 3.50737679e00, 1.54150103e00, 4.67518683e-01, 9.39808944e-02, 1.21848229e-02], - [1.51405147e00, 1.94666325e00, 9.82280001e-01, 3.24800918e-01, 6.92390684e-02, 9.36629471e-03], - [0.00000000e00, 7.29338809e-01, 4.61369298e-01, 1.76413628e-01, 4.15239315e-02, 6.03372787e-03], + [0.00000000e00, 4.68668935e-02, 6.29165282e-02, 1.89769059e-02, 1.37627350e-03, 2.38011764e-05], + [2.45315202e00, 1.41254463e00, 7.79263787e-01, 1.43005758e-01, 7.36618348e-03, 9.93591644e-05], + [1.33808292e01, 5.42489388e00, 1.98989926e00, 2.91867596e-01, 1.31164535e-02, 1.61393456e-04], + [2.45315202e00, 1.41254463e00, 7.79263787e-01, 1.43005758e-01, 7.36618348e-03, 9.93591644e-05], + [0.00000000e00, 4.68668935e-02, 6.29165282e-02, 1.89769059e-02, 1.37627350e-03, 2.38011764e-05], + ], + [ + [0.00000000e00, 5.71924530e-02, 1.52647475e-01, 1.96653382e-01, 1.38081866e-01, 5.10719186e-02], + [2.49656982e00, 1.72375181e00, 1.89064230e00, 1.48193631e00, 7.39051039e-01, 2.13202199e-01], + [1.36176536e01, 6.62008862e00, 4.82787443e00, 3.02455786e00, 1.31597707e00, 3.46313699e-01], + [2.49656982e00, 1.72375181e00, 1.89064230e00, 1.48193631e00, 7.39051039e-01, 2.13202199e-01], + [0.00000000e00, 5.71924530e-02, 1.52647475e-01, 1.96653382e-01, 1.38081866e-01, 5.10719186e-02], ], [ - [0.00000000e00, 1.00434327e00, 9.62188073e-01, 6.92059770e-01, 3.88730984e-01, 1.72346862e-01], - [1.66844725e00, 2.68067203e00, 2.04855005e00, 1.27417395e00, 6.48189376e-01, 2.67538002e-01], - [4.17111812e00, 4.82986815e00, 3.21480841e00, 1.83404693e00, 8.79812781e-01, 3.48046189e-01], - [8.34223623e00, 5.92452728e00, 3.73853409e00, 2.07057816e00, 9.73988987e-01, 3.79902396e-01], - [4.17111812e00, 4.82986815e00, 3.21480841e00, 1.83404693e00, 8.79812781e-01, 3.48046189e-01], - [1.66844725e00, 2.68067203e00, 2.04855005e00, 1.27417395e00, 6.48189376e-01, 2.67538002e-01], - [0.00000000e00, 1.00434327e00, 9.62188073e-01, 6.92059770e-01, 3.88730984e-01, 1.72346862e-01], + [0.00000000e00, 5.81859528e-02, 1.69666475e-01, 2.85860846e-01, 3.45998354e-01, 3.00917744e-01], + [2.49763825e00, 1.75369539e00, 2.10143414e00, 2.15418400e00, 1.85187563e00, 1.25619570e00], + [1.36234814e01, 6.73508730e00, 5.36614469e00, 4.39658175e00, 3.29750686e00, 2.04049388e00], + [2.49763825e00, 1.75369539e00, 2.10143414e00, 2.15418400e00, 1.85187563e00, 1.25619570e00], + [0.00000000e00, 5.81859528e-02, 1.69666475e-01, 2.85860846e-01, 3.45998354e-01, 3.00917744e-01], ], [ - [0.00000000e00, 1.14678447e00, 1.26173994e00, 1.12151632e00, 8.36842100e-01, 5.34290144e-01], - [1.75678621e00, 3.06085890e00, 2.68631207e00, 2.06486049e00, 1.39539214e00, 8.29390894e-01], - [4.39196553e00, 5.51486523e00, 4.21565419e00, 2.97216173e00, 1.89402030e00, 1.07897322e00], - [8.78393106e00, 6.76477462e00, 4.90242805e00, 3.35547203e00, 2.09675848e00, 1.17773021e00], - [4.39196553e00, 5.51486523e00, 4.21565419e00, 2.97216173e00, 1.89402030e00, 1.07897322e00], - [1.75678621e00, 3.06085890e00, 2.68631207e00, 2.06486049e00, 1.39539214e00, 8.29390894e-01], - [0.00000000e00, 1.14678447e00, 1.26173994e00, 1.12151632e00, 8.36842100e-01, 5.34290144e-01], + [0.00000000e00, 5.82239437e-02, 1.71778282e-01, 3.03974560e-01, 4.22126629e-01, 4.85054549e-01], + [2.49481282e00, 1.75484042e00, 2.12759029e00, 2.29068494e00, 2.25933450e00, 2.02488372e00], + [1.36080699e01, 6.73948479e00, 5.43293609e00, 4.67517334e00, 4.02304069e00, 3.28910759e00], + [2.49481282e00, 1.75484042e00, 2.12759029e00, 2.29068494e00, 2.25933450e00, 2.02488372e00], + [0.00000000e00, 5.82239437e-02, 1.71778282e-01, 3.03974560e-01, 4.22126629e-01, 4.85054549e-01], + ], + [ + [0.00000000e00, 5.81602048e-02, 1.71844605e-01, 3.06471593e-01, 4.38129864e-01, 5.44528609e-01], + [2.49158971e00, 1.75291936e00, 2.12841174e00, 2.30950202e00, 2.34498809e00, 2.27316107e00], + [1.35904893e01, 6.73210694e00, 5.43503371e00, 4.71357806e00, 4.17555811e00, 3.69239539e00], + [2.49158971e00, 1.75291936e00, 2.12841174e00, 2.30950202e00, 2.34498809e00, 2.27316107e00], + [0.00000000e00, 5.81602048e-02, 1.71844605e-01, 3.06471593e-01, 4.38129864e-01, 5.44528609e-01], ], ] ) @@ -232,31 +288,39 @@ testingdata_lineardecay_bioscreen = np.array( [ [ - [0.00000000e00, 1.64750580e-01, 2.43834704e-02, 3.08739472e-03, 3.33028964e-04, 2.86132827e-05], - [1.99136794e00, 4.39732393e-01, 5.19137172e-02, 5.68430372e-03, 5.55309057e-04, 4.44170576e-05], - [4.97841984e00, 7.92282480e-01, 8.14686732e-02, 8.18199099e-03, 7.53742693e-04, 5.77831466e-05], - [9.95683968e00, 9.71848303e-01, 9.47407663e-02, 9.23719650e-03, 8.34424206e-04, 6.30719617e-05], - [4.97841984e00, 7.92282480e-01, 8.14686732e-02, 8.18199099e-03, 7.53742693e-04, 5.77831466e-05], - [1.99136794e00, 4.39732393e-01, 5.19137172e-02, 5.68430372e-03, 5.55309057e-04, 4.44170576e-05], - [0.00000000e00, 1.64750580e-01, 2.43834704e-02, 3.08739472e-03, 3.33028964e-04, 2.86132827e-05], + [0.00000000e00, 2.66397630e-02, 2.50186546e-02, 6.31005071e-03, 4.18130412e-04, 6.87752976e-06], + [2.49416415e00, 8.02909075e-01, 3.09872972e-01, 4.75511441e-02, 2.23794568e-03, 2.87105813e-05], + [1.36045317e01, 3.08358153e00, 7.91280190e-01, 9.70495057e-02, 3.98495510e-03, 4.66358586e-05], + [2.49416415e00, 8.02909075e-01, 3.09872972e-01, 4.75511441e-02, 2.23794568e-03, 2.87105813e-05], + [0.00000000e00, 2.66397630e-02, 2.50186546e-02, 6.31005071e-03, 4.18130412e-04, 6.87752976e-06], + ], + [ + [0.00000000e00, 2.87022579e-02, 4.09882298e-02, 3.25232761e-02, 1.65762639e-02, 5.04440452e-03], + [2.50118215e00, 8.65071637e-01, 5.07666970e-01, 2.45088203e-01, 8.87205932e-02, 2.10581111e-02], + [1.36428117e01, 3.32231756e00, 1.29635965e00, 5.00212759e-01, 1.57978625e-01, 3.42056151e-02], + [2.50118215e00, 8.65071637e-01, 5.07666970e-01, 2.45088203e-01, 8.87205932e-02, 2.10581111e-02], + [0.00000000e00, 2.87022579e-02, 4.09882298e-02, 3.25232761e-02, 1.65762639e-02, 5.04440452e-03], + ], + [ + [0.00000000e00, 2.87199369e-02, 4.17753582e-02, 3.65368003e-02, 2.51210294e-02, 1.41395801e-02], + [2.49813835e00, 8.65604474e-01, 5.17416088e-01, 2.75333232e-01, 1.34454461e-01, 5.90263624e-02], + [1.36262092e01, 3.32436393e00, 1.32125463e00, 5.61941349e-01, 2.39413760e-01, 9.58791138e-02], + [2.49813835e00, 8.65604474e-01, 5.17416088e-01, 2.75333232e-01, 1.34454461e-01, 5.90263624e-02], + [0.00000000e00, 2.87199369e-02, 4.17753582e-02, 3.65368003e-02, 2.51210294e-02, 1.41395801e-02], ], [ - [0.00000000e00, 1.64220190e-01, 2.45987826e-02, 3.24153453e-03, 4.00200991e-04, 4.75580882e-05], - [1.98307196e00, 4.38316741e-01, 5.23721285e-02, 5.96809559e-03, 6.67315036e-04, 7.38255155e-05], - [4.95767991e00, 7.89731846e-01, 8.21880621e-02, 8.59048121e-03, 9.05772787e-04, 9.60412692e-05], - [9.91535982e00, 9.68719584e-01, 9.55773511e-02, 9.69836842e-03, 1.00272778e-03, 1.04831800e-04], - [4.95767991e00, 7.89731846e-01, 8.21880621e-02, 8.59048121e-03, 9.05772787e-04, 9.60412692e-05], - [1.98307196e00, 4.38316741e-01, 5.23721285e-02, 5.96809559e-03, 6.67315036e-04, 7.38255155e-05], - [0.00000000e00, 1.64220190e-01, 2.45987826e-02, 3.24153453e-03, 4.00200991e-04, 4.75580882e-05], + [0.00000000e00, 2.86837628e-02, 4.17499178e-02, 3.67112198e-02, 2.59613117e-02, 1.60935121e-02], + [2.49486795e00, 8.64514203e-01, 5.17100991e-01, 2.76647619e-01, 1.38951876e-01, 6.71831461e-02], + [1.36083707e01, 3.32017673e00, 1.32045001e00, 5.64623947e-01, 2.47421996e-01, 1.09128536e-01], + [2.49486795e00, 8.64514203e-01, 5.17100991e-01, 2.76647619e-01, 1.38951876e-01, 6.71831461e-02], + [0.00000000e00, 2.86837628e-02, 4.17499178e-02, 3.67112198e-02, 2.59613117e-02, 1.60935121e-02], ], [ - [0.00000000e00, 1.63523832e-01, 2.44946190e-02, 3.24171843e-03, 4.00357048e-04, 4.76590051e-05], - [1.97466187e00, 4.36458104e-01, 5.21503587e-02, 5.96843416e-03, 6.67575252e-04, 7.39821710e-05], - [4.93665468e00, 7.86383070e-01, 8.18400367e-02, 8.59096855e-03, 9.06125988e-04, 9.62450659e-05], - [9.87330937e00, 9.64611829e-01, 9.51726288e-02, 9.69891861e-03, 1.00311879e-03, 1.05054250e-04], - [4.93665468e00, 7.86383070e-01, 8.18400367e-02, 8.59096855e-03, 9.06125988e-04, 9.62450659e-05], - [1.97466187e00, 4.36458104e-01, 5.21503587e-02, 5.96843416e-03, 6.67575252e-04, 7.39821710e-05], - [0.00000000e00, 1.63523832e-01, 2.44946190e-02, 3.24171843e-03, 4.00357048e-04, 4.76590051e-05], + [0.00000000e00, 2.86461833e-02, 4.16960900e-02, 3.66720177e-02, 2.59766081e-02, 1.62442682e-02], + [2.49159591e00, 8.63381576e-01, 5.16434297e-01, 2.76352200e-01, 1.39033747e-01, 6.78124850e-02], + [1.35905231e01, 3.31582686e00, 1.31874757e00, 5.64021013e-01, 2.47567777e-01, 1.10150799e-01], + [2.49159591e00, 8.63381576e-01, 5.16434297e-01, 2.76352200e-01, 1.39033747e-01, 6.78124850e-02], + [0.00000000e00, 2.86461833e-02, 4.16960900e-02, 3.66720177e-02, 2.59766081e-02, 1.62442682e-02], ], ] ) @@ -265,39 +329,49 @@ [ [ [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], - [1.26161914, 1.36288124, 0.14490394, 0.0, 0.0, 0.0], - [3.53093985, 2.98502714, 0.74293872, 0.0, 0.0, 0.0], - [7.31314103, 3.78608045, 1.00395844, 0.0, 0.0, 0.0], - [3.53093985, 2.98502714, 0.74293872, 0.0, 0.0, 0.0], - [1.26161914, 1.36288124, 0.14490394, 0.0, 0.0, 0.0], + [2.00991536, 0.0, 0.0, 0.0, 0.0, 0.0], + [12.85529149, 2.48231964, 0.0, 0.0, 0.0, 0.0], + [2.00991536, 0.0, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.17586495, 1.14229829, 0.0, 0.0, 0.0, 0.0], + [13.13006415, 6.07379347, 3.01127398, 0.0, 0.0, 0.0], + [2.17586495, 1.14229829, 0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + ], + [ + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [2.05715049, 1.29444684, 1.06379123, 0.0, 0.0, 0.0], + [12.93350153, 6.27382145, 4.82001743, 3.03997834, 0.0, 0.0], + [2.05715049, 1.29444684, 1.06379123, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [0.0, 0.32682189, 0.18847172, 0.0, 0.0, 0.0], - [1.49351837, 2.26320188, 1.42202775, 0.47810322, 0.0, 0.0], - [3.99231952, 4.49526641, 2.66923241, 1.08783867, 0.0, 0.0], - [8.15698811, 5.59751169, 3.21359039, 1.34042931, 0.09067114, 0.0], - [3.99231952, 4.49526641, 2.66923241, 1.08783867, 0.0, 0.0], - [1.49351837, 2.26320188, 1.42202775, 0.47810322, 0.0, 0.0], - [0.0, 0.32682189, 0.18847172, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.91361378, 1.18552918, 1.13897273, 0.68020431, 0.0, 0.0], + [12.69583921, 6.13062863, 4.91331009, 4.01252624, 2.70628757, 0.43809525], + [1.91361378, 1.18552918, 1.13897273, 0.68020431, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], [ - [0.0, 0.51828367, 0.56707794, 0.34776144, 0.0, 0.0], - [1.62516006, 2.72758114, 2.18341803, 1.41496002, 0.61359912, 0.0], - [4.25422983, 5.27423754, 3.81764204, 2.4030658, 1.16372612, 0.22197704], - [8.63601278, 6.53183559, 4.53091942, 2.81240115, 1.38439752, 0.33092893], - [4.25422983, 5.27423754, 3.81764204, 2.4030658, 1.16372612, 0.22197704], - [1.62516006, 2.72758114, 2.18341803, 1.41496002, 0.61359912, 0.0], - [0.0, 0.51828367, 0.56707794, 0.34776144, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + [1.76869713, 1.05010518, 1.02697617, 0.68965378, 0.13994631, 0.0], + [12.45589207, 5.95258822, 4.77433364, 4.02404611, 3.22495857, 2.14668156], + [1.76869713, 1.05010518, 1.02697617, 0.68965378, 0.13994631, 0.0], + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ], ] ) testing_massbalance_nodecay_bio = { - "t": np.array([365, 730, 1095]), - "_plume_mass_t": np.array([4673.88175389, 8134.17708612, 10882.8752081]), - "_source_mass_t": np.array([995759.01846931, 991536.02286297, 987330.93690299]), - "_delta_source_t": np.array([4240.98153069, 8463.97713703, 12669.06309701]), + "t": np.array([365, 730, 1095, 1460, 1825]), + "_plume_mass_t": np.array([2547.87145767, 4991.61757796, 6613.25096702, 7190.45509978, 7319.3088115]), + "_source_mass_t": np.array( + [1997376.84643129, 1994757.1333299, 1992140.85618339, 1989528.01048526, 1986918.59173488] + ), + "_delta_source_t": np.array([2623.15356871, 5242.8666701, 7859.14381661, 10471.98951474, 13081.40826512]), "_degraded_mass_t": None, "_electron_acceptor_change_t": None, "_instant_reaction_degraded_mass_t": None, @@ -307,11 +381,13 @@ } testing_massbalance_lineardecay_ana = { - "t": np.array([365, 730, 1095]), - "_plume_mass_t": np.array([1695.49626089, 1689.27120085, 1682.14524122]), - "_source_mass_t": np.array([995759.01846931, 991536.02286297, 987330.93690299]), - "_delta_source_t": np.array([4240.98153069, 8463.97713703, 12669.06309701]), - "_degraded_mass_t": np.array([5009.69193858, 8944.92003192, 11683.6189408]), + "t": np.array([365, 730, 1095, 1460, 1825]), + "_plume_mass_t": np.array([1625.91225957, 1958.42344739, 2011.60695846, 2013.67822011, 2011.28905331]), + "_source_mass_t": np.array( + [1997376.84643129, 1994757.1333299, 1992140.85618339, 1989528.01048526, 1986918.59173488] + ), + "_delta_source_t": np.array([2623.15356871, 5242.8666701, 7859.14381661, 10471.98951474, 13081.40826512]), + "_degraded_mass_t": np.array([1215.93229094, 3312.18208233, 4756.57543617, 5226.36475666, 5319.36988327]), "_electron_acceptor_change_t": None, "_instant_reaction_degraded_mass_t": None, "source_mass_finite": True, @@ -320,170 +396,66 @@ } testing_massbalance_instant_mbt = { - "t": np.array([365, 730, 1095]), - "_plume_mass_t": np.array([5486.89557135, 9074.16458818, 12039.64270149]), - "_source_mass_t": np.array([994988.88209135, 990002.87548539, 985041.85434643]), - "_delta_source_t": np.array([5011.11790865, 9997.12451461, 14958.14565357]), - "_degraded_mass_t": np.array([1415.58024519, 1893.57017734, 1670.60614156]), + "t": np.array([365, 730, 1095, 1460, 1825]), + "_plume_mass_t": np.array([1274.95518137, 2664.20593542, 3767.11945785, 4436.89399159, 4471.80347887]), + "_source_mass_t": np.array( + [1982333.73934155, 1964823.52706592, 1947467.98477742, 1930265.74625588, 1913215.45734916] + ), + "_delta_source_t": np.array([17666.26065845, 35176.47293408, 52532.01522258, 69734.25374412, 86784.54265084]), + "_degraded_mass_t": np.array([1573.35761266, 2634.42858236, 3017.64717164, 2794.12241441, 2838.73740743]), "_electron_acceptor_change_t": { - "oxygen": np.array([1270.70443741, 1827.39892317, 1931.61471693]), - "nitrate": np.array([1270.70443741, 1827.39892317, 1931.61471693]), - "ferrous_iron": np.array([1270.70443741, 1827.39892317, 1931.61471693]), - "sulfate": np.array([1270.70443741, 1827.39892317, 1931.61471693]), - "methane": np.array([1270.70443741, 1827.39892317, 1931.61471693]), + "oxygen": np.array([1936.17805868, 3472.4727154, 4337.57185311, 4529.31972161, 4541.44224634]), + "nitrate": np.array([82.14088734, 147.31702429, 184.01819983, 192.15295789, 192.66724681]), + "ferrous_iron": np.array([19479.12471152, 34935.1800458, 43638.60167375, 45567.70144168, 45689.66138744]), + "sulfate": np.array([26285.08394807, 47141.44777265, 58885.82394531, 61488.94652371, 61653.51898065]), + "methane": np.array([7744.7122347, 13889.89086158, 17350.28741246, 18117.27888645, 18165.76898537]), }, - "_instant_reaction_degraded_mass_t": np.array([2621.77047178, 3770.36578757, 3985.3881663]), + "_instant_reaction_degraded_mass_t": np.array( + [17048.60779573, 30576.1265814, 38193.57469641, 39881.97013662, 39988.71247299] + ), "source_mass_finite": True, "model_degradation": True, "model_instant_reaction": True, } testing_massbalance_instant_mbt_inf = { - "t": np.array([365, 730, 1095]), - "_plume_mass_t": np.array([5539.00845817, 9248.17128318, 12374.47283209]), - "_source_mass_t": np.inf, - "_delta_source_t": np.array([5023.71566351, 10047.43132702, 15071.14699053]), - "_degraded_mass_t": np.array([1404.7379325, 1850.99155079, 1579.25170252]), + "t": np.array([365, 730, 1095, 1460, 1825]), + "_plume_mass_t": np.array([1323.58680137, 2860.86002677, 4194.058875, 5147.84088742, 5451.49338433]), + "_source_mass_t": float("inf"), + "_delta_source_t": np.array([17744.74738066, 35489.49476131, 53234.24214197, 70978.98952263, 88723.73690328]), + "_degraded_mass_t": np.array([1526.73062587, 2445.16948716, 2606.17833265, 2107.91520193, 1893.34623264]), "_electron_acceptor_change_t": { - "oxygen": np.array([1273.22026095, 1831.27321473, 1932.51181133]), - "nitrate": np.array([1273.22026095, 1831.27321473, 1932.51181133]), - "ferrous_iron": np.array([1273.22026095, 1831.27321473, 1932.51181133]), - "sulfate": np.array([1273.22026095, 1831.27321473, 1932.51181133]), - "methane": np.array([1273.22026095, 1831.27321473, 1932.51181133]), + "oxygen": np.array([1940.69129974, 3486.40514084, 4363.93209705, 4567.22770608, 4593.63418442]), + "nitrate": np.array([82.33235817, 147.90809688, 185.13651321, 193.76117541, 194.88145025]), + "ferrous_iron": np.array([19524.53065196, 35075.3486897, 43903.80170365, 45949.07873994, 46214.74391599]), + "sulfate": np.array([26346.35461469, 47330.59100296, 59243.68422661, 62003.57613101, 62362.06407941]), + "methane": np.array([7762.76519897, 13945.62056337, 17455.7283882, 18268.91082431, 18374.53673768]), }, - "_instant_reaction_degraded_mass_t": np.array([2626.961224, 3778.35938776, 3987.23908892]), + "_instant_reaction_degraded_mass_t": np.array( + [17088.34818868, 30698.80561705, 38425.68426828, 40215.76090374, 40448.27758293] + ), "source_mass_finite": False, "model_degradation": True, "model_instant_reaction": True, } testing_massbalance_instant_ana_inf = { - "t": np.array([365, 730, 1095]), - "_plume_mass_t": np.array([5248.12131322, 8706.47934067, 11728.94763204]), - "_source_mass_t": np.inf, - "_delta_source_t": np.array([5023.71566351, 10047.43132702, 15071.14699053]), - "_degraded_mass_t": np.array([1464.30682696, 1957.90718873, 1705.30840063]), + "t": np.array([365, 730, 1095, 1460, 1825]), + "_plume_mass_t": np.array([1307.95320597, 2843.96453853, 4179.04985145, 5156.20424127, 5504.06469115]), + "_source_mass_t": float("inf"), + "_delta_source_t": np.array([17744.74738066, 35489.49476131, 53234.24214197, 70978.98952263, 88723.73690328]), + "_degraded_mass_t": np.array([1535.89343133, 2434.01066762, 2604.5437654, 2108.51531352, 1860.85042709]), "_electron_acceptor_change_t": { - "oxygen": np.array([1269.42660124, 1832.46497738, 1936.67681991]), - "nitrate": np.array([1269.42660124, 1832.46497738, 1936.67681991]), - "ferrous_iron": np.array([1269.42660124, 1832.46497738, 1936.67681991]), - "sulfate": np.array([1269.42660124, 1832.46497738, 1936.67681991]), - "methane": np.array([1269.42660124, 1832.46497738, 1936.67681991]), + "oxygen": np.array([1916.73334639, 3443.78850861, 4332.8979254, 4553.38677448, 4582.8217764]), + "nitrate": np.array([81.31596015, 146.10011855, 183.81991199, 193.17398437, 194.42274203]), + "ferrous_iron": np.array([19283.49912124, 34646.59954116, 43591.57912829, 45809.83057965, 46105.96453829]), + "sulfate": np.array([26021.10724793, 46752.03793506, 58822.37183576, 61815.67499904, 62215.27744925]), + "methane": np.array([7666.93338555, 13775.15403444, 17331.59170161, 18213.54709793, 18331.28710558]), }, - "_instant_reaction_degraded_mass_t": np.array([2619.13398683, 3780.81828224, 3995.8325086]), + "_instant_reaction_degraded_mass_t": np.array( + [16877.39148017, 30323.55384448, 38152.4194111, 40093.88749791, 40353.07120307] + ), "source_mass_finite": False, "model_degradation": True, "model_instant_reaction": True, } - -######## Decrepit ###################################################################################################### - -testing_massbalance_nodecay_dom = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(10884.801775892505), - "transport_outside_extent_nodecay": np.float64(1784.261321114951), -} - -testing_massbalance_lindecay_dom = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(10884.801775892505), - "transport_outside_extent_nodecay": np.float64(1784.261321114951), - "plume_mass_linear_decay": np.float64(1682.1452404554184), - "transport_outside_extent_lineardecay": np.float64(275.74105168269006), - "plume_mass_degraded_linear": np.float64(10711.176804869348), -} - -testing_massbalance_instant_dom = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(10884.801775892505), - "transport_outside_extent_nodecay": np.float64(1784.261321114951), - "source_mass_instant_t": np.float64(985041.8543464276), - "source_mass_instant_change": np.float64(14958.1456535724), - "plume_mass_no_decay_instant_reaction": np.float64(12729.593887083935), - "plume_mass_instant_reaction": np.float64(8758.752888532472), - "plume_mass_degraded_instant": np.float64(3970.8409985514627), - "electron_acceptor_mass_change": np.array( - [1924.56408043, 1924.56408043, 1924.56408043, 1924.56408043, 1924.56408043] - ), -} - -testing_massbalance_instant_dom_inf = { - "time": np.int64(1095), - "source_mass_0": np.inf, - "source_mass_t": np.inf, - "source_mass_change": np.float64(12750.0), - "plume_mass_no_decay": np.float64(10943.20737211077), - "transport_outside_extent_nodecay": np.float64(1806.79262788923), - "source_mass_instant_t": np.inf, - "source_mass_instant_change": np.float64(15071.146990534271), - "plume_mass_no_decay_instant_reaction": np.float64(12810.491497436684), - "plume_mass_instant_reaction": np.float64(8839.650498885221), - "plume_mass_degraded_instant": np.float64(3970.8409985514627), - "electron_acceptor_mass_change": np.array( - [1924.56408043, 1924.56408043, 1924.56408043, 1924.56408043, 1924.56408043] - ), -} -######################################################################################################################## -testing_massbalance_nodecay_kar = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(13710.24884304135), - "transport_outside_extent": 0, -} - -testing_massbalance_lindecay_kar = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(13710.24884304135), - "transport_outside_extent": 0, - "plume_mass_linear_decay": np.float64(1711.1646817402216), - "transport_outside_extent_lineardecay": np.float64(0.0), - "plume_mass_degraded_linear": np.float64(11999.084161301129), -} - -testing_massbalance_instant_kar = { - "time": np.int64(1095), - "source_mass_0": 1000000, - "source_mass_t": np.float64(987330.9369029925), - "source_mass_change": np.float64(12669.063097007456), - "plume_mass_no_decay": np.float64(13710.24884304135), - "transport_outside_extent": 0, - "source_mass_instant_t": np.float64(985041.8543464276), - "source_mass_instant_change": np.float64(14958.1456535724), - "plume_mass_no_decay_instant_reaction": np.float64(16025.030867780724), - "plume_mass_instant_reaction": np.float64(12039.642701485522), - "plume_mass_degraded_instant": np.float64(3985.3881662952026), - "electron_acceptor_mass_change": np.array( - [1931.61471693, 1931.61471693, 1931.61471693, 1931.61471693, 1931.61471693] - ), -} - -testing_massbalance_instant_kar_inf = { - "time": np.int64(1095), - "source_mass_0": np.inf, - "source_mass_t": np.inf, - "source_mass_change": np.float64(12750.0), - "plume_mass_no_decay": np.float64(13953.724534605748), - "transport_outside_extent": 0, - "source_mass_instant_t": np.inf, - "source_mass_instant_change": np.float64(15071.146990534271), - "plume_mass_no_decay_instant_reaction": np.float64(16361.711921001373), - "plume_mass_instant_reaction": np.float64(12374.472832085463), - "plume_mass_degraded_instant": np.float64(3987.2390889159105), - "electron_acceptor_mass_change": np.array( - [1932.51181133, 1932.51181133, 1932.51181133, 1932.51181133, 1932.51181133] - ), -} diff --git a/tests/test_mass_balance.py b/tests/test_mass_balance.py index df0bdba..8d64cf3 100644 --- a/tests/test_mass_balance.py +++ b/tests/test_mass_balance.py @@ -10,6 +10,7 @@ from mibitrans.transport.models import Anatrans from mibitrans.transport.models import Bioscreen from mibitrans.transport.models import Mibitrans +from tests.conftest import electron_acceptor_dict from tests.test_example_data import testing_massbalance_instant_ana_inf from tests.test_example_data import testing_massbalance_instant_mbt from tests.test_example_data import testing_massbalance_instant_mbt_inf @@ -20,17 +21,24 @@ @pytest.fixture(scope="module") def test_model_pars(): """ModelParameters fixture with increased spatial resolution, specifically for testing mass balance.""" - return ModelParameters(model_length=50, model_width=30, model_time=3 * 365, dx=1, dy=1, dt=1 * 365) + return ModelParameters( + model_length=100, # [m] + model_width=40, # [m] + model_time=5 * 365, # [days] + dx=1, # [m] + dy=1, # [m] + dt=365, # [days] + ) @pytest.fixture(scope="module") def test_source_pars_inf(): """SourceParameters fixture with example data for tests.""" return SourceParameters( - source_zone_boundary=np.array([5, 10, 15]), - source_zone_concentration=np.array([10, 5, 2]), - depth=10, - total_mass="inf", + source_zone_boundary=np.array([2, 11, 20]), # [m] + source_zone_concentration=np.array([13.68, 2.508, 0.057]), # [g/m3] + depth=3, # [m] + total_mass=np.inf, # [g] ) @@ -54,7 +62,7 @@ def test_anatrans_lineardecay_model_mb(test_hydro_pars, test_att_pars, test_sour def test_mibitrans_instantreaction_model_mb(test_hydro_pars, test_att_pars, test_source_pars, test_model_pars): """Mibitrans with instant reaction fixture mass balance object for testing.""" obj = Mibitrans(test_hydro_pars, test_att_pars, test_source_pars, test_model_pars) - obj.instant_reaction(dict(delta_oxygen=0.5, delta_nitrate=0.5, ferrous_iron=0.5, delta_sulfate=0.5, methane=0.5)) + obj.instant_reaction(electron_acceptor_dict) res = obj.run() return res.mass_balance() @@ -63,16 +71,16 @@ def test_mibitrans_instantreaction_model_mb(test_hydro_pars, test_att_pars, test def test_mibitrans_instantreaction_model_mb_inf(test_hydro_pars, test_att_pars, test_source_pars_inf, test_model_pars): """Mibitrans with instant reaction and infinite source mass fixture mass balance object for testing.""" obj = Mibitrans(test_hydro_pars, test_att_pars, test_source_pars_inf, test_model_pars) - obj.instant_reaction(dict(delta_oxygen=0.5, delta_nitrate=0.5, ferrous_iron=0.5, delta_sulfate=0.5, methane=0.5)) + obj.instant_reaction(electron_acceptor_dict) res = obj.run() return res.mass_balance() @pytest.fixture(scope="module") def test_anatrans_instantreaction_model_mb_inf(test_hydro_pars, test_att_pars, test_source_pars_inf, test_model_pars): - """Anatrans with instant reaction and infinite source mass fixture mass balance object for testing.""" + """Anatrans with instant reaction and infinite source mass fixture mass balance object for testing.""" obj = Anatrans(test_hydro_pars, test_att_pars, test_source_pars_inf, test_model_pars) - obj.instant_reaction(dict(delta_oxygen=0.5, delta_nitrate=0.5, ferrous_iron=0.5, delta_sulfate=0.5, methane=0.5)) + obj.instant_reaction(electron_acceptor_dict) res = obj.run() return res.mass_balance() diff --git a/tests/test_mibitrans.py b/tests/test_mibitrans.py index 9693322..20693ae 100644 --- a/tests/test_mibitrans.py +++ b/tests/test_mibitrans.py @@ -24,8 +24,8 @@ def test_transport_equation_numerical_mibitrans(model, expected, request): @pytest.mark.parametrize( "x, y, t, expected", [ - (16, 0, 393, 0.2980583920684923), - (24, -5, 283, 0.03725197246248769), + (16, 0, 393, 4.6652115692165195), + (24, -5, 527, 1.7909410090260753), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError), @@ -46,8 +46,8 @@ def test_mibitrans_linear_sample(x, y, t, expected, test_mibitrans_model_lineard @pytest.mark.parametrize( "x, y, t, expected", [ - (20, 0, 476, 3.8101869779573443), - (11, 7, 193, 2.0276832492832924), + (20, 0, 476, 6.3213083960634435), + (35, 7, 745, 2.5356628358944633), (-16, 0, 393, DomainValueError), ("nonsense", 0, 393, TypeError), (16, "nonsense", 393, TypeError),