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.637 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/v1.2.0/examples/Estimate_theis/2023-04-18_10-40-32_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...
THE POPULATION HAS CONVERGED TO A PRESPECIFIED SMALL PARAMETER SPACE
SEARCH WAS STOPPED AT TRIAL NUMBER: 2449
NUMBER OF DISCARDED TRIALS: 0
NORMALIZED GEOMETRIC RANGE = 0.000634
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 0.53 seconds
Total Repetitions: 2449
Minimal objective value: 77.2517
Corresponding parameter setting:
transmissivity: -9.21584
storage: -9.10161
******************************
Best parameter set:
transmissivity=-9.215836493127895, storage=-9.10160712051483
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/v1.2.0/examples/Estimate_theis/2023-04-18_10-40-34_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.07 seconds
Total Repetitions: 260
Minimal objective value: 371.85
Corresponding parameter setting:
transmissivity: -9.07071
storage: -9.99878
Maximal objective value: 2.70203e+06
Corresponding parameter setting:
transmissivity: -15.7779
storage: -11.4975
******************************
260
Total running time of the script: ( 0 minutes 3.277 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/v1.2.0/examples/Estimate_thiem/2023-04-18_10-40-35_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...
THE POPULATION HAS CONVERGED TO A PRESPECIFIED SMALL PARAMETER SPACE
SEARCH WAS STOPPED AT TRIAL NUMBER: 1548
NUMBER OF DISCARDED TRIALS: 0
NORMALIZED GEOMETRIC RANGE = 0.000539
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 0.28 seconds
Total Repetitions: 1548
Minimal objective value: 0.0672645
Corresponding parameter setting:
transmissivity: -9.21029
******************************
Best parameter set:
transmissivity=-9.210293557355184
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/v1.2.0/examples/Estimate_thiem/2023-04-18_10-40-37_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.06 seconds
Total Repetitions: 260
Minimal objective value: 1.87858
Corresponding parameter setting:
transmissivity: -9.21098
dummy: 0.525956
Maximal objective value: 2.65494e+06
Corresponding parameter setting:
transmissivity: -16.0939
dummy: 0.310133
******************************
260
Total running time of the script: ( 0 minutes 2.407 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/v1.2.0/examples/Est_steady_het/2023-04-18_10-40-38_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
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: 5114
NUMBER OF DISCARDED TRIALS: 42
NORMALIZED GEOMETRIC RANGE = 0.002453
THE BEST POINT HAS IMPROVED IN LAST 100 LOOPS BY 100000.000000 PERCENT
*** Final SPOTPY summary ***
Total Duration: 0.74 seconds
Total Repetitions: 5114
Minimal objective value: 6.56837e-05
Corresponding parameter setting:
trans_gmean: -9.18171
var: 0.0572588
len_scale: 45.7747
******************************
Best parameter set:
trans_gmean=-9.181156167806398, var=0.0583686149143951, len_scale=46.21740619954733
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/v1.2.0/examples/Est_steady_het/2023-04-18_10-40-41_sensitivity_db.csv' created.
*** Final SPOTPY summary ***
Total Duration: 0.14 seconds
Total Repetitions: 1158
Minimal objective value: 21.1665
Corresponding parameter setting:
trans_gmean: -7.41294
var: 3.62727
len_scale: 23.2994
Maximal objective value: 3.34939e+08
Corresponding parameter setting:
trans_gmean: -15.9791
var: 9.91827
len_scale: 46.5105
******************************
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 4.814 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.545 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.176 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.183 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.