"""Welltestpy subpackage providing flow datastructures for field-campaigns."""
from copy import deepcopy as dcopy
from ..tools import plotter
from . import data_io, testslib, varlib
__all__ = ["FieldSite", "Campaign"]
[docs]class FieldSite:
"""Class for a field site.
This is a class for a field site.
It has a name and a description.
Parameters
----------
name : :class:`str`
Name of the field site.
description : :class:`str`, optional
Description of the field site.
Default: ``"no description"``
coordinates : :class:`Variable`, optional
Coordinates of the field site (lat, lon).
Default: ``None``
"""
def __init__(self, name, description="Field site", coordinates=None):
self.name = data_io._formstr(name)
self.description = str(description)
self._coordinates = None
self.coordinates = coordinates
@property
def info(self):
""":class:`str`: Info about the field site."""
info = ""
info += "----" + "\n"
info += "Field-site: " + str(self.name) + "\n"
info += "Description: " + str(self.description) + "\n"
info += "--" + "\n"
if self._coordinates is not None:
info += self._coordinates.info + "\n"
info += "----" + "\n"
return info
@property
def pos(self):
""":class:`numpy.ndarray`: Position of the field site."""
if self._coordinates is not None:
return self._coordinates.value
return None
@property
def coordinates(self):
""":class:`numpy.ndarray`: Coordinates of the field site."""
if self._coordinates is not None:
return self._coordinates
return None
@coordinates.setter
def coordinates(self, coordinates):
if coordinates is not None:
if isinstance(coordinates, varlib.Variable):
self._coordinates = dcopy(coordinates)
else:
self._coordinates = varlib.CoordinatesVar(
coordinates[0], coordinates[1]
)
else:
self._coordinates = None
def __repr__(self):
"""Representation."""
return self.name
[docs] def save(self, path="", name=None):
"""Save a field site to file.
This writes the field site to a csv file.
Parameters
----------
path : :class:`str`, optional
Path where the variable should be saved. Default: ``""``
name : :class:`str`, optional
Name of the file. If ``None``, the name will be generated by
``"Field_"+name``. Default: ``None``
Notes
-----
The file will get the suffix ``".fds"``.
"""
return data_io.save_fieldsite(self, path, name)
[docs]class Campaign:
"""Class for a well based campaign.
This is a class for a well based test campaign on a field site.
It has a name, a description and a timeframe.
Parameters
----------
name : :class:`str`
Name of the campaign.
fieldsite : :class:`str` or :class:`Variable`, optional
The field site.
Default: ``"Fieldsite"``
wells : :class:`dict`, optional
The wells within the field site. Keys are the well names and values
are an instance of :class:`Well`.
Default: ``None``
wells : :class:`dict`, optional
The tests within the campaign. Keys are the test names and values
are an instance of :class:`Test`.
Default: ``None``
timeframe : :class:`str`, optional
Timeframe of the campaign.
Default: ``None``
description : :class:`str`, optional
Description of the field site.
Default: ``"Welltest campaign"``
"""
def __init__(
self,
name,
fieldsite="Fieldsite",
wells=None,
tests=None,
timeframe=None,
description="Welltest campaign",
):
self.name = data_io._formstr(name)
self.description = str(description)
self._fieldsite = None
self.fieldsite = fieldsite
self.__wells = {}
self.wells = wells
self.__tests = {}
self.tests = tests
self.timeframe = str(timeframe)
@property
def fieldsite(self):
""":class:`FieldSite`: Field site where the campaign was realised."""
return self._fieldsite
@fieldsite.setter
def fieldsite(self, fieldsite):
if fieldsite is not None:
if isinstance(fieldsite, FieldSite):
self._fieldsite = dcopy(fieldsite)
else:
self._fieldsite = FieldSite(str(fieldsite))
else:
self._fieldsite = None
@property
def wells(self):
""":class:`dict`: Wells within the campaign."""
return self.__wells
@wells.setter
def wells(self, wells):
if wells is not None:
if isinstance(wells, dict):
for k in wells.keys():
if not isinstance(wells[k], varlib.Well):
raise ValueError(
"Campaign: some 'wells' are not of " + "type Well"
)
if not k == wells[k].name:
raise ValueError(
"Campaign: 'well'-keys should be "
+ "the Well name"
)
self.__wells = dcopy(wells)
elif isinstance(wells, (list, tuple)):
for wel in wells:
if not isinstance(wel, varlib.Well):
raise ValueError(
"Campaign: some 'wells' " + "are not of type u"
)
self.__wells = {}
for wel in wells:
self.__wells[wel.name] = dcopy(wel)
else:
raise ValueError(
"Campaign: 'wells' should be given "
+ "as dictionary or list"
)
else:
self.__wells = {}
self.__updatewells()
[docs] def add_well(
self, name, radius, coordinates, welldepth=1.0, aquiferdepth=None
):
"""Add a single well to the campaign.
Parameters
----------
name : :class:`str`
Name of the Variable.
radius : :class:`Variable` or :class:`float`
Value of the Variable.
coordinates : :class:`Variable` or :class:`numpy.ndarray`
Value of the Variable.
welldepth : :class:`Variable` or :class:`float`, optional
Depth of the well. Default: 1.0
aquiferdepth : :class:`Variable` or :class:`float`, optional
Depth of the aquifer at the well. Default: ``"None"``
"""
well = varlib.Well(name, radius, coordinates, welldepth, aquiferdepth)
self.addwells(well)
[docs] def addwells(self, wells):
"""Add some specified wells.
This will add wells to the campaign.
Parameters
----------
wells : :class:`dict`
Wells to be added.
"""
if isinstance(wells, dict):
for k in wells.keys():
if not isinstance(wells[k], varlib.Well):
raise ValueError(
"Campaign_addwells: some 'wells' "
+ "are not of type Well"
)
if k in tuple(self.__wells.keys()):
raise ValueError(
"Campaign_addwells: some 'wells' "
+ "are already present"
)
if not k == wells[k].name:
raise ValueError(
"Campaign_addwells: 'well'-keys "
+ "should be the Well name"
)
for k in wells.keys():
self.__wells[k] = dcopy(wells[k])
elif isinstance(wells, (list, tuple)):
for wel in wells:
if not isinstance(wel, varlib.Well):
raise ValueError(
"Campaign_addwells: some 'wells' "
+ "are not of type Well"
)
if wel.name in tuple(self.__wells.keys()):
raise ValueError(
"Campaign_addwells: some 'wells' "
+ "are already present"
)
for wel in wells:
self.__wells[wel.name] = dcopy(wel)
elif isinstance(wells, varlib.Well):
self.__wells[wells.name] = dcopy(wells)
else:
raise ValueError(
"Campaign_addwells: 'wells' should be "
+ "given as dictionary, list or single 'Well'"
)
[docs] def delwells(self, wells):
"""Delete some specified wells.
This will delete wells from the campaign. You can give a
list of wells or a single well by name.
Parameters
----------
wells : :class:`list` of :class:`str` or :class:`str`
Wells to be deleted.
"""
if isinstance(wells, (list, tuple)):
for wel in wells:
if wel in tuple(self.__wells.keys()):
del self.__wells[wel]
else:
if wells in tuple(self.__wells.keys()):
del self.__wells[wells]
@property
def tests(self):
""":class:`dict`: Tests within the campaign."""
return self.__tests
@tests.setter
def tests(self, tests):
if tests is not None:
if isinstance(tests, dict):
for k in tests.keys():
if not isinstance(tests[k], testslib.Test):
raise ValueError(
"Campaign: 'tests' are not of " + "type Test"
)
if not k == tests[k].name:
raise ValueError(
"Campaign: 'tests'-keys "
+ "should be the Test name"
)
self.__tests = dcopy(tests)
elif isinstance(tests, (list, tuple)):
for tes in tests:
if not isinstance(tes, testslib.Test):
raise ValueError(
"Campaign: some 'tests' are not of " + "type Test"
)
self.__tests = {}
for tes in tests:
self.__tests[tes.name] = dcopy(tes)
elif isinstance(tests, testslib.Test):
self.__tests[tests.name] = dcopy(tests)
else:
raise ValueError(
"Campaign: 'tests' should be given "
+ "as dictionary, list or 'Test'"
)
else:
self.__tests = {}
[docs] def addtests(self, tests):
"""Add some specified tests.
This will add tests to the campaign.
Parameters
----------
tests : :class:`dict`
Tests to be added.
"""
if isinstance(tests, dict):
for k in tests.keys():
if not isinstance(tests[k], testslib.Test):
raise ValueError(
"Campaign_addtests: some 'tests' "
+ "are not of type Test"
)
if k in tuple(self.__tests.keys()):
raise ValueError(
"Campaign_addtests: some 'tests' "
+ "are already present"
)
if not k == tests[k].name:
raise ValueError(
"Campaign_addtests: 'tests'-keys "
+ "should be the Test name"
)
for k in tests.keys():
self.__tests[k] = dcopy(tests[k])
elif isinstance(tests, (list, tuple)):
for tes in tests:
if not isinstance(tes, testslib.Test):
raise ValueError(
"Campaign_addtests: some 'tests' "
+ "are not of type Test"
)
if tes.name in tuple(self.__tests.keys()):
raise ValueError(
"Campaign_addtests: some 'tests' "
+ "are already present"
)
for tes in tests:
self.__tests[tes.name] = dcopy(tes)
elif isinstance(tests, testslib.Test):
if tests.name in tuple(self.__tests.keys()):
raise ValueError("Campaign.addtests: 'test' already present")
self.__tests[tests.name] = dcopy(tests)
else:
raise ValueError(
"Campaign_addtests: 'tests' should be "
+ "given as dictionary, list or single 'Test'"
)
[docs] def deltests(self, tests):
"""Delete some specified tests.
This will delete tests from the campaign. You can give a
list of tests or a single test by name.
Parameters
----------
tests : :class:`list` of :class:`str` or :class:`str`
Tests to be deleted.
"""
if isinstance(tests, (list, tuple)):
for tes in tests:
if tes in tuple(self.__tests.keys()):
del self.__tests[tes]
else:
if tests in tuple(self.__tests.keys()):
del self.__tests[tests]
def __updatewells(self):
pass
[docs] def plot(self, select_tests=None, **kwargs):
"""Generate a plot of the tests within the campaign.
This will plot an overview of the tests within the campaign.
Parameters
----------
select_tests : :class:`list`, optional
Tests that should be plotted. If None, all will be displayed.
Default: ``None``
**kwargs
Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_plot`
"""
return plotter.campaign_plot(self, select_tests, **kwargs)
[docs] def plot_wells(self, **kwargs):
"""Generate a plot of the wells within the campaign.
This will plot an overview of the wells within the campaign.
Parameters
----------
**kwargs
Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_well_plot`.
"""
return plotter.campaign_well_plot(self, **kwargs)
[docs] def diagnostic_plot(self, pumping_test, observation_well, **kwargs):
"""Generate a diagnostic plot.
Parameters
----------
pumping_test : :class:`str`
The pumping well that is saved in the campaign.
observation_well : :class:`str`
Observation point to make the diagnostic plot.
**kwargs
Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_well_plot`.
"""
# check if this is a pumping test
if pumping_test in self.tests:
if not isinstance(self.tests[pumping_test], testslib.PumpingTest):
raise ValueError(
f"diagnostic_plot: test '{pumping_test}' is not of instance PumpingTest!"
)
# check if the well is present
if observation_well in self.wells:
return self.tests[pumping_test].diagnostic_plot(
observation_well=observation_well, **kwargs
)
else:
raise ValueError(
f"diagnostic_plot: well '{observation_well}' could not be found!"
)
else:
raise ValueError(
f"diagnostic_plot: test '{pumping_test}' could not be found!"
)
def __repr__(self):
"""Representation."""
return (
f"Campaign '{self.name}' at '{self.fieldsite}' with "
f"{len(self.wells)} wells and "
f"{len(self.tests)} tests"
)
[docs] def save(self, path="", name=None):
"""Save the campaign to file.
This writes the campaign to a csv file.
Parameters
----------
path : :class:`str`, optional
Path where the variable should be saved. Default: ``""``
name : :class:`str`, optional
Name of the file. If ``None``, the name will be generated by
``"Cmp_"+name``. Default: ``None``
Notes
-----
The file will get the suffix ``".cmp"``.
"""
return data_io.save_campaign(self, path, name)