Skip to content

Commit

Permalink
Remove StressModel2 (#406)
Browse files Browse the repository at this point in the history
* Remove StressModel2

* Remove StressModel2 from all files

* make example_step and example_900 work

* Deal with old StressModel2 pas-files for version 0.22.0

* Load model transforms StressModel2 to RechargeModel
  • Loading branch information
raoulcollenteur committed Aug 17, 2022
1 parent ad3d922 commit 67c0671
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 233 deletions.
4 changes: 2 additions & 2 deletions examples/example_900.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
evap.series_original = s

# Create stress
sm = ps.StressModel2(stress=[rain, evap], rfunc=ps.Exponential,
name='recharge')
sm = ps.RechargeModel(rain, evap, rfunc=ps.Exponential,
recharge=ps.rch.Linear(), name='recharge')
ml.add_stressmodel(sm)

## Solve
Expand Down
4 changes: 2 additions & 2 deletions examples/example_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
parse_dates=True).squeeze("columns")

# create stress
sm = ps.StressModel2(stress=[rain, evap], rfunc=ps.Exponential,
name="recharge")
sm = ps.RechargeModel(rain, evap, rfunc=ps.Exponential,
recharge=ps.rch.Linear(), name="recharge")
ml.add_stressmodel(sm)

# add a stepmodel with an exponential response
Expand Down
116 changes: 57 additions & 59 deletions examples/notebooks/data_notebook_8/B28H1804_2.pas

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions pastas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
HantushWellModel, Kraijenhoff, One, Polder, Spline)
from .solver import LeastSquares, LmfitSolve
from .stressmodels import (Constant, LinearTrend, RechargeModel, StepModel,
StressModel, StressModel2, TarsoModel, WellModel,
ChangeModel, ReservoirModel)
StressModel, TarsoModel, WellModel, ChangeModel,
ReservoirModel)
from .timeseries import TimeSeries
from .transform import ThresholdTransform
from .utils import initialize_logger, set_log_level, show_versions
Expand Down
16 changes: 16 additions & 0 deletions pastas/io/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ def _load_model(data):

# Add stressmodels
for name, ts in data["stressmodels"].items():
# Deal with old StressModel2 files for version 0.22.0. Remove in 0.23.0.
if ts["stressmodel"] == "StressModel2":
logger.warning("StressModel2 is removed since Pastas 0.22.0 and "
"is replaced by the RechargeModel using a Linear "
"recharge model. Make sure to save this file "
"again using Pastas version 0.22.0 as this file "
"cannot be loaded in newer Pastas versions. This "
"will automatically update your model to the newer "
"RechargeModel stress model.")
ts["stressmodel"] = "RechargeModel"
ts["recharge"] = "Linear"
ts["prec"] = ts["stress"][0]
ts["evap"] = ts["stress"][1]
ts.pop("stress")
ts.pop("up")

stressmodel = getattr(ps.stressmodels, ts["stressmodel"])
ts.pop("stressmodel")
if "rfunc" in ts.keys():
Expand Down
170 changes: 2 additions & 168 deletions pastas/stressmodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@

logger = getLogger(__name__)

__all__ = ["StressModel", "StressModel2", "Constant", "StepModel",
"LinearTrend", "RechargeModel", "WellModel", "TarsoModel",
"ChangeModel"]
__all__ = ["StressModel", "Constant", "StepModel", "LinearTrend",
"RechargeModel", "WellModel", "TarsoModel", "ChangeModel"]


class StressModelBase:
Expand Down Expand Up @@ -315,171 +314,6 @@ def to_dict(self, series=True):
return data


class StressModel2(StressModelBase):
"""Time series model consisting of the convolution of two stresses with one
response function. The first stress causes the head to go up and the second
stress causes the head to go down.
Parameters
----------
stress: list of pandas.Series or list of pastas.timeseries
list of two pandas.Series or pastas.timeseries objects containing the
stresses. Usually the first is the precipitation and the second the
evaporation.
rfunc: pastas.rfunc instance
Response function used in the convolution with the stress.
name: str
Name of the stress
up: bool or None, optional
True if response function is positive (default), False if negative.
None if you don't want to define if response is positive or negative.
cutoff: float, optional
float between 0 and 1 to determine how long the response is (default
is 99.9% of the actual response time). Used to reduce computation
times.
settings: Tuple with two dicts, optional
The settings of the individual TimeSeries.
settings: list of dicts or strs, optional
The settings of the stresses. This can be a string referring to a
predefined settings dict, or a dict with the settings to apply.
Refer to the docstring of pastas.Timeseries for further information.
Default is ("prec", "evap").
metadata: list of dicts, optional
dictionary containing metadata about the stress. This is passed onto
the TimeSeries object.
Notes
-----
The order in which the stresses are provided is the order the metadata
and settings dictionaries or string are passed onto the TimeSeries
objects. By default, the precipitation stress is the first and the
evaporation stress the second stress.
See Also
--------
pastas.rfunc
pastas.timeseries.TimeSeries
"""
_name = "StressModel2"

def __init__(self, stress, rfunc, name, up=True, cutoff=0.999,
settings=("prec", "evap"), metadata=(None, None),
meanstress=None):

msg = "StressModel2 is deprecated. It will be removed in version " \
"0.22.0 and is replaced by the RechargeModel stress model. " \
"Please use ps.RechargeModel(prec, evap, " \
"recharge=ps.rch.Linear) for the same stress model."
warn(msg)

# First check the series, then determine tmin and tmax
stress0 = TimeSeries(stress[0], settings=settings[0],
metadata=metadata[0])
stress1 = TimeSeries(stress[1], settings=settings[1],
metadata=metadata[1])

# Select indices from validated stress where both series are available.
index = stress0.series.index.intersection(stress1.series.index)
if index.empty:
msg = ('The two stresses that were provided have no '
'overlapping time indices. Please make sure the '
'indices of the time series overlap.')
logger.error(msg)
raise Exception(msg)

# First check the series, then determine tmin and tmax
stress0.update_series(tmin=index.min(), tmax=index.max())
stress1.update_series(tmin=index.min(), tmax=index.max())

if meanstress is None:
meanstress = (stress0.series - stress1.series).std()

rfunc = rfunc(up=up, cutoff=cutoff, meanstress=meanstress)

StressModelBase.__init__(self, name=name, tmin=index.min(),
tmax=index.max(), rfunc=rfunc)
self.stress.append(stress0)
self.stress.append(stress1)

self.freq = stress0.settings["freq"]
self.set_init_parameters()

def set_init_parameters(self):
"""Set the initial parameters back to their default values."""
self.parameters = self.rfunc.get_init_parameters(self.name)
self.parameters.loc[self.name + '_f'] = \
(-1.0, -2.0, 0.0, True, self.name)

def simulate(self, p, tmin=None, tmax=None, freq=None, dt=1, istress=None):
"""Simulates the head contribution.
Parameters
----------
p: array_like
array_like object with the values as floats representing the
model parameters.
tmin: str, optional
tmax: str, optional
freq: str, optional
dt: int, optional
istress: int, optional
Returns
-------
pandas.Series
The simulated head contribution.
"""
b = self._get_block(p[:-1], dt, tmin, tmax)
stress = self.get_stress(p=p, tmin=tmin, tmax=tmax, freq=freq,
istress=istress)
if istress == 1:
stress = p[-1] * stress
npoints = stress.index.size
h = Series(data=fftconvolve(stress, b, 'full')[:npoints],
index=stress.index, name=self.name, fastpath=True)
if istress is not None:
if self.stress[istress].name is not None:
h.name = h.name + ' (' + self.stress[istress].name + ')'
return h

def get_stress(self, p=None, tmin=None, tmax=None, freq=None,
istress=None, **kwargs):
if tmin is None:
tmin = self.tmin
if tmax is None:
tmax = self.tmax

self.update_stress(tmin=tmin, tmax=tmax, freq=freq)

if istress is None:
if p is None:
p = self.parameters.initial.values
return self.stress[0].series.add(p[-1] * self.stress[1].series)
elif istress == 0:
return self.stress[0].series
else:
return self.stress[1].series

def to_dict(self, series=True):
"""Method to export the StressModel object.
Returns
-------
data: dict
dictionary with all necessary information to reconstruct the
StressModel object.
"""
data = {
"stressmodel": self._name,
"rfunc": self.rfunc._name,
"name": self.name,
"up": self.rfunc.up,
"cutoff": self.rfunc.cutoff,
"stress": self.dump_stress(series)
}
return data


class StepModel(StressModelBase):
"""Stressmodel that simulates a step trend.
Expand Down

0 comments on commit 67c0671

Please sign in to comment.