Contents
Welcome to welltestpy
Purpose
welltestpy provides a framework to handle, process, plot and analyse data from well based field campaigns.
Installation
You can install the latest version with the following command:
pip install welltestpy
Or from conda
conda install -c conda-forge welltestpy
Documentation for welltestpy
You can find the documentation including tutorials and examples under https://welltestpy.readthedocs.io.
Citing welltestpy
If you are using this package you can cite our Groundwater publication by:
Müller, S., Leven, C., Dietrich, P., Attinger, S. and Zech, A. (2021): How to Find Aquifer Statistics Utilizing Pumping Tests? Two Field Studies Using welltestpy. Groundwater, https://doi.org/10.1111/gwat.13121
To cite the code, please visit the Zenodo page.
Provided Subpackages
welltestpy.data # Subpackage to handle data from field campaigns
welltestpy.estimate # Subpackage to estimate field parameters
welltestpy.process # Subpackage to pre- and post-process data
welltestpy.tools # Subpackage with tools for plotting and triagulation
Requirements
Contact
You can contact us via info@geostat-framework.org.
License
welltestpy Tutorial
In the following you will find several Tutorials on how to use welltestpy to explore its whole beauty and power.
Gallery
Note
Go to the end to download the full example code
Creating a pumping test campaign
In the following we are going to create an artificial pumping test campaign on a field site.
import anaflow as ana
import numpy as np
import welltestpy as wtp
Create the field-site and the campaign
field = wtp.FieldSite(name="UFZ", coordinates=[51.353839, 12.431385])
campaign = wtp.Campaign(name="UFZ-campaign", fieldsite=field)
Add 4 wells to the campaign
campaign.add_well(name="well_0", radius=0.1, coordinates=(0.0, 0.0))
campaign.add_well(name="well_1", radius=0.1, coordinates=(1.0, -1.0))
campaign.add_well(name="well_2", radius=0.1, coordinates=(2.0, 2.0))
campaign.add_well(name="well_3", radius=0.1, coordinates=(-2.0, -1.0))
Generate artificial drawdown data with the Theis solution
rate = -1e-4
time = np.geomspace(10, 7200, 10)
transmissivity = 1e-4
storage = 1e-4
rad = [
campaign.wells["well_0"].radius, # well radius of well_0
campaign.wells["well_0"] - campaign.wells["well_1"], # distance 0-1
campaign.wells["well_0"] - campaign.wells["well_2"], # distance 0-2
campaign.wells["well_0"] - campaign.wells["well_3"], # distance 0-3
]
drawdown = ana.theis(
time=time,
rad=rad,
storage=storage,
transmissivity=transmissivity,
rate=rate,
)
Create a pumping test at well_0
pumptest = wtp.PumpingTest(
name="well_0",
pumpingwell="well_0",
pumpingrate=rate,
description="Artificial pump test with Theis",
)
Add the drawdown observation at the 4 wells
pumptest.add_transient_obs("well_0", time, drawdown[:, 0])
pumptest.add_transient_obs("well_1", time, drawdown[:, 1])
pumptest.add_transient_obs("well_2", time, drawdown[:, 2])
pumptest.add_transient_obs("well_3", time, drawdown[:, 3])
Add the pumping test to the campaign
campaign.addtests(pumptest)
# optionally make the test (quasi)steady
# campaign.tests["well_0"].make_steady()
Plot the well constellation and a test overview
campaign.plot_wells()
campaign.plot()
Save the whole campaign to a file
campaign.save()
Total running time of the script: ( 0 minutes 0.692 seconds)
Note
Go to the end to download the full example code
Estimate homogeneous parameters
Here we estimate transmissivity and storage from a pumping test campaign with the classical theis solution.
import welltestpy as wtp
campaign = wtp.load_campaign("Cmp_UFZ-campaign.cmp")
estimation = wtp.estimate.Theis("Estimate_theis", campaign, generate=True)
estimation.run()
Initializing the Shuffled Complex Evolution (SCE-UA) algorithm with 5000 repetitions
The objective function will be minimized
Starting burn-in sampling...
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Estimate_theis/2023-04-18_09-58-22_db.csv' created.
Burn-in sampling completed...
Starting Complex Evolution...
ComplexEvo loop #1 in progress...
ComplexEvo loop #2 in progress...
ComplexEvo loop #3 in progress...
ComplexEvo loop #4 in progress...
ComplexEvo loop #5 in progress...
ComplexEvo loop #6 in progress...
ComplexEvo loop #7 in progress...
ComplexEvo loop #8 in progress...
ComplexEvo loop #9 in progress...
ComplexEvo loop #10 in progress...
ComplexEvo loop #11 in progress...
ComplexEvo loop #12 in progress...
ComplexEvo loop #13 in progress...
ComplexEvo loop #14 in progress...
ComplexEvo loop #15 in progress...
ComplexEvo loop #16 in progress...
ComplexEvo loop #17 in progress...
ComplexEvo loop #18 in progress...
ComplexEvo loop #19 in progress...
ComplexEvo loop #20 in progress...
THE POPULATION HAS CONVERGED TO A PRESPECIFIED SMALL PARAMETER SPACE
SEARCH WAS STOPPED AT TRIAL NUMBER: 2706
NUMBER OF DISCARDED TRIALS: 0
NORMALIZED GEOMETRIC RANGE = 0.000706
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 0.64 seconds
Total Repetitions: 2706
Minimal objective value: 77.2517
Corresponding parameter setting:
transmissivity: -9.21583
storage: -9.10167
******************************
Best parameter set:
transmissivity=-9.215828396011176, storage=-9.101673860745393
In addition, we run a sensitivity analysis, to get an impression of the impact of each parameter
estimation.sensitivity()
Initializing the Fourier Amplitude Sensitivity Test (FAST) with 260 repetitions
Starting the FAST algotrithm with 260 repetitions...
Creating FAST Matrix
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Estimate_theis/2023-04-18_09-58-25_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.08 seconds
Total Repetitions: 260
Minimal objective value: 516.425
Corresponding parameter setting:
transmissivity: -9.03998
storage: -9.9678
Maximal objective value: 2.77063e+06
Corresponding parameter setting:
transmissivity: -15.8137
storage: -11.467
******************************
260
Total running time of the script: ( 0 minutes 3.639 seconds)
Note
Go to the end to download the full example code
Estimate steady homogeneous parameters
Here we estimate transmissivity from the quasi steady state of a pumping test campaign with the classical thiem solution.
import welltestpy as wtp
campaign = wtp.load_campaign("Cmp_UFZ-campaign.cmp")
estimation = wtp.estimate.Thiem("Estimate_thiem", campaign, generate=True)
estimation.run()
Initializing the Shuffled Complex Evolution (SCE-UA) algorithm with 5000 repetitions
The objective function will be minimized
Starting burn-in sampling...
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Estimate_thiem/2023-04-18_09-58-26_db.csv' created.
Burn-in sampling completed...
Starting Complex Evolution...
ComplexEvo loop #1 in progress...
ComplexEvo loop #2 in progress...
ComplexEvo loop #3 in progress...
ComplexEvo loop #4 in progress...
ComplexEvo loop #5 in progress...
ComplexEvo loop #6 in progress...
ComplexEvo loop #7 in progress...
ComplexEvo loop #8 in progress...
ComplexEvo loop #9 in progress...
ComplexEvo loop #10 in progress...
ComplexEvo loop #11 in progress...
ComplexEvo loop #12 in progress...
ComplexEvo loop #13 in progress...
ComplexEvo loop #14 in progress...
ComplexEvo loop #15 in progress...
ComplexEvo loop #16 in progress...
ComplexEvo loop #17 in progress...
ComplexEvo loop #18 in progress...
ComplexEvo loop #19 in progress...
ComplexEvo loop #20 in progress...
ComplexEvo loop #21 in progress...
ComplexEvo loop #22 in progress...
ComplexEvo loop #23 in progress...
ComplexEvo loop #24 in progress...
ComplexEvo loop #25 in progress...
ComplexEvo loop #26 in progress...
ComplexEvo loop #27 in progress...
ComplexEvo loop #28 in progress...
ComplexEvo loop #29 in progress...
ComplexEvo loop #30 in progress...
ComplexEvo loop #31 in progress...
ComplexEvo loop #32 in progress...
ComplexEvo loop #33 in progress...
ComplexEvo loop #34 in progress...
ComplexEvo loop #35 in progress...
ComplexEvo loop #36 in progress...
ComplexEvo loop #37 in progress...
ComplexEvo loop #38 in progress...
ComplexEvo loop #39 in progress...
ComplexEvo loop #40 in progress...
ComplexEvo loop #41 in progress...
ComplexEvo loop #42 in progress...
ComplexEvo loop #43 in progress...
ComplexEvo loop #44 in progress...
ComplexEvo loop #45 in progress...
ComplexEvo loop #46 in progress...
ComplexEvo loop #47 in progress...
ComplexEvo loop #48 in progress...
ComplexEvo loop #49 in progress...
ComplexEvo loop #50 in progress...
ComplexEvo loop #51 in progress...
ComplexEvo loop #52 in progress...
ComplexEvo loop #53 in progress...
ComplexEvo loop #54 in progress...
ComplexEvo loop #55 in progress...
ComplexEvo loop #56 in progress...
ComplexEvo loop #57 in progress...
ComplexEvo loop #58 in progress...
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
*** OPTIMIZATION SEARCH TERMINATED BECAUSE THE LIMIT
ON THE MAXIMUM NUMBER OF TRIALS
5000
HAS BEEN EXCEEDED.
SEARCH WAS STOPPED AT TRIAL NUMBER: 5057
NUMBER OF DISCARDED TRIALS: 21
NORMALIZED GEOMETRIC RANGE = 0.264139
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 1.01 seconds
Total Repetitions: 5057
Minimal objective value: 0.0672645
Corresponding parameter setting:
transmissivity: -9.21029
******************************
Best parameter set:
transmissivity=-9.21029355734211
since we only have one parameter, we need a dummy parameter to estimate sensitivity
estimation.gen_setup(dummy=True)
estimation.sensitivity()
Initializing the Fourier Amplitude Sensitivity Test (FAST) with 260 repetitions
Starting the FAST algotrithm with 260 repetitions...
Creating FAST Matrix
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Estimate_thiem/2023-04-18_09-58-28_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.06 seconds
Total Repetitions: 260
Minimal objective value: 42.4221
Corresponding parameter setting:
transmissivity: -9.22575
dummy: 0.37107
Maximal objective value: 2.50666e+06
Corresponding parameter setting:
transmissivity: -16.0365
dummy: 0.311892
******************************
260
Total running time of the script: ( 0 minutes 3.409 seconds)
Note
Go to the end to download the full example code
Estimate steady heterogeneous parameters
Here we demonstrate how to estimate parameters of heterogeneity, namely mean, variance and correlation length of log-transmissivity, with the aid the the extended Thiem solution in 2D.
Initializing the Shuffled Complex Evolution (SCE-UA) algorithm with 5000 repetitions
The objective function will be minimized
Starting burn-in sampling...
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Est_steady_het/2023-04-18_09-58-29_db.csv' created.
Burn-in sampling completed...
Starting Complex Evolution...
ComplexEvo loop #1 in progress...
ComplexEvo loop #2 in progress...
ComplexEvo loop #3 in progress...
ComplexEvo loop #4 in progress...
ComplexEvo loop #5 in progress...
ComplexEvo loop #6 in progress...
ComplexEvo loop #7 in progress...
ComplexEvo loop #8 in progress...
ComplexEvo loop #9 in progress...
ComplexEvo loop #10 in progress...
ComplexEvo loop #11 in progress...
ComplexEvo loop #12 in progress...
ComplexEvo loop #13 in progress...
ComplexEvo loop #14 in progress...
ComplexEvo loop #15 in progress...
ComplexEvo loop #16 in progress...
ComplexEvo loop #17 in progress...
ComplexEvo loop #18 in progress...
ComplexEvo loop #19 in progress...
ComplexEvo loop #20 in progress...
ComplexEvo loop #21 in progress...
ComplexEvo loop #22 in progress...
ComplexEvo loop #23 in progress...
ComplexEvo loop #24 in progress...
ComplexEvo loop #25 in progress...
ComplexEvo loop #26 in progress...
ComplexEvo loop #27 in progress...
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
Skipping saving
*** OPTIMIZATION SEARCH TERMINATED BECAUSE THE LIMIT
ON THE MAXIMUM NUMBER OF TRIALS
5000
HAS BEEN EXCEEDED.
SEARCH WAS STOPPED AT TRIAL NUMBER: 5084
NUMBER OF DISCARDED TRIALS: 35
NORMALIZED GEOMETRIC RANGE = 0.002615
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 0.81 seconds
Total Repetitions: 5084
Minimal objective value: 0.000102931
Corresponding parameter setting:
trans_gmean: -9.18665
var: 0.0473718
len_scale: 41.5893
******************************
Best parameter set:
trans_gmean=-9.186654615190932, var=0.04737184562132764, len_scale=41.58933403842806
Initializing the Fourier Amplitude Sensitivity Test (FAST) with 1158 repetitions
Starting the FAST algotrithm with 1158 repetitions...
Creating FAST Matrix
Initialize database...
['csv', 'hdf5', 'ram', 'sql', 'custom', 'noData']
* Database file '/home/docs/checkouts/readthedocs.org/user_builds/welltestpy/checkouts/latest/examples/Est_steady_het/2023-04-18_09-58-32_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.16 seconds
Total Repetitions: 1158
Minimal objective value: 77.4336
Corresponding parameter setting:
trans_gmean: -7.55702
var: 3.3744
len_scale: 30.7267
Maximal objective value: 2.76442e+08
Corresponding parameter setting:
trans_gmean: -15.8022
var: 9.88788
len_scale: 46.9621
******************************
1158
import welltestpy as wtp
campaign = wtp.load_campaign("Cmp_UFZ-campaign.cmp")
estimation = wtp.estimate.ExtThiem2D("Est_steady_het", campaign, generate=True)
estimation.run()
estimation.sensitivity()
Total running time of the script: ( 0 minutes 5.340 seconds)
Note
Go to the end to download the full example code
Point triangulation
Often, we only know the distances between wells within a well base field campaign. To retrieve their spatial positions, we provide a routine, that triangulates their positions from a given distance matrix.
If the solution is not unique, all possible constellations will be returned.
import numpy as np
from welltestpy.tools import plot_well_pos, sym, triangulate
dist_mat = np.zeros((4, 4), dtype=float)
dist_mat[0, 1] = 3 # distance between well 0 and 1
dist_mat[0, 2] = 4 # distance between well 0 and 2
dist_mat[1, 2] = 2 # distance between well 1 and 2
dist_mat[0, 3] = 1 # distance between well 0 and 3
dist_mat[1, 3] = 3 # distance between well 1 and 3
dist_mat[2, 3] = -1 # unknown distance between well 2 and 3
dist_mat = sym(dist_mat) # make the distance matrix symmetric
well_const = triangulate(dist_mat, prec=0.1)
Startingconstelation 0 1
add point 0
add point 1
number of temporal results: 8
number of overall results: 8
Now we can plot all possible well constellations
plot_well_pos(well_const)

Total running time of the script: ( 0 minutes 0.608 seconds)
Note
Go to the end to download the full example code
Diagnostic plot
A diagnostic plot is a simultaneous plot of the drawdown and the logarithmic derivative of the drawdown in a log-log plot. Often, this plot is used to identify the right approach for the aquifer estimations.

import welltestpy as wtp
campaign = wtp.load_campaign("Cmp_UFZ-campaign.cmp")
campaign.diagnostic_plot("well_0", "well_1")
Total running time of the script: ( 0 minutes 0.198 seconds)
Note
Go to the end to download the full example code
Correcting drawdown: The Cooper-Jacob method
Here we demonstrate the correction established by Cooper and Jacob in 1946. This method corrects drawdown data for the reduction in saturated thickness resulting from groundwater withdrawal by a pumping well and thereby enables pumping tests in an unconfined aquifer to be interpreted by methods for confined aquifers.

import welltestpy as wtp
campaign = wtp.load_campaign("Cmp_UFZ-campaign.cmp")
campaign.tests["well_0"].correct_observations()
campaign.plot()
Total running time of the script: ( 0 minutes 0.207 seconds)
welltestpy API
welltestpy - a Python package to handle well-based Field-campaigns.
welltestpy provides a framework to handle and plot data from well based field campaigns as well as a parameter estimation module.
Subpackages
welltestpy subpackage providing datastructures. |
|
welltestpy subpackage providing routines to estimate pump test parameters. |
|
welltestpy subpackage providing routines to pre process test data. |
|
welltestpy subpackage providing miscellaneous tools. |
Classes
Campaign classes
The following classes can be used to handle field campaigns.
|
Class for a well based campaign. |
|
Class for a field site. |
Field Test classes
The following classes can be used to handle field test within a campaign.
|
Class for a pumping test. |
Loading routines
Campaign related loading routines
|
Load a campaign from file. |
Changelog
All notable changes to welltestpy will be documented in this file.
1.2.0 - 2023-04
Enhancements
added archive support
simplify documentation
new arguments
val_fit_type
andval_fit_name
for all estimators to select fitting transformationval_fit_name
will be incorporated into the generated plots and the header of the estimation result file
Changes
move to
src/
based package structureuse hatchling as build backend
drop py36 support
value names for all arguments in the estimators now need to match the call signatures of the used type-curves
Bugfixes
minor fixes for the plotting routines and the estimators
1.1.0 - 2021-07
Enhancements
added
cooper_jacob_correction
toprocess
(thanks to Jarno Herrmann)added
diagnostic_plots
module (thanks to Jarno Herrmann)added
screensize
,screen
,aquifer
andis_piezometer
attribute toWell
classadded version information to output files
added
__repr__
toCampaign
Changes
modernized packaging workflow using
pyproject.toml
removed
setup.py
(usepip>21.1
for editable installs)removed
dev
as extra install dependenciesbetter exceptions in loading routines
removed pandas dependency
simplified readme
Bugfixes
loading steady pumping tests was not possible due to a bug
1.0.3 - 2021-02
Enhancements
Estimations: run method now provides
plot_style
keyword to control plotting
Changes
Fit plot style for transient pumping tests was updated
Bugfixes
Estimations: run method was throwing an Error when setting
run=False
Plotter: all plotting routines now respect setted font-type from matplotlib
1.0.2 - 2020-09-03
Bugfixes
StdyHeadObs
andStdyObs
weren’t usable due to an unnecessarytime
check
1.0.1 - 2020-04-09
Bugfixes
Wrong URL in setup
1.0.0 - 2020-04-09
Enhancements
new estimators
ExtTheis3D
ExtTheis2D
Neuman2004
Theis
ExtThiem3D
ExtThiem2D
Neuman2004Steady
Thiem
better plotting
unit-tests run with py35-py38 on Linux/Win/Mac
coverage calculation
sphinx gallery for examples
allow style setting in plotting routines
Bugfixes
estimation results stored as dict (order could alter before)
Changes
py2 support dropped
Fieldsite.coordinates
now returns aVariable
;Fieldsite.pos
as shortcutFieldsite.pumpingrate
now returns aVariable
;Fieldsite.rate
as shortcutFieldsite.auqiferradius
now returns aVariable
;Fieldsite.radius
as shortcutFieldsite.auqiferdepth
now returns aVariable
;Fieldsite.depth
as shortcutWell.coordinates
now returns aVariable
;Well.pos
as shortcutWell.welldepth
now returns aVariable
;Well.depth
as shortcutWell.wellradius
added and returns the radiusVariable
Well.aquiferdepth
now returns aVariable
Fieldsite.addobservations
renamed toFieldsite.add_observations
Fieldsite.delobservations
renamed toFieldsite.del_observations
Observation
has changed order of inputs/outputs. Now:observation
,time
0.3.2 - 2019-03-08
Bugfixes
adopt AnaFlow API
0.3.1 - 2019-03-08
Bugfixes
update travis workflow
0.3.0 - 2019-02-28
Enhancements
added documentation
0.2.0 - 2018-04-25
Enhancements
added license
0.1.0 - 2018-04-25
First alpha release of welltespy.