Beispiel #1
0
def pastas_hook(obj):
    for key, value in obj.items():
        if key in ["tmin", "tmax", "date_modified", "date_created"]:
            val = pd.Timestamp(value)
            if val is pd.NaT:
                val = None
            obj[key] = val
        elif key == "series":
            try:
                obj[key] = pd.read_json(value, typ='series', orient="split")
            except:
                try:
                    obj[key] = ps.TimeSeries(**value)
                except:
                    obj[key] = value
        elif key == "time_offset":
            obj[key] = pd.Timedelta(value)
        elif key == "parameters":
            # Necessary to maintain order when using the JSON format!
            value = json.loads(value, object_pairs_hook=OrderedDict)
            param = pd.DataFrame(data=value, columns=value.keys()).T
            obj[key] = param.apply(pd.to_numeric, errors="ignore")
        else:
            try:
                obj[key] = json.loads(value, object_hook=pastas_hook)
            except:
                obj[key] = value
    return obj
Beispiel #2
0
    def _parse_stressmodel_dict(self, d: Dict,
                                onam: Optional[str] = None) -> Dict:
        """Internal method to parse StressModel dictionary.

        Note: supports 'nearest' or 'nearest <kind>' as input to 'stress',
        which will automatically select nearest stress with kind=<kind>.
        Requires "x" and "y" locations to be present in both oseries and
        stresses metadata.

        Parameters
        ----------
        d : dict
            dictionary containing WellModel information
        onam : str, optional
            name of oseries used when 'nearest <kind>' is provided as stress,
            by default None

        Returns
        -------
        d : dict
            dictionary that can be read by ps.io.base.load(),
            containing stresses obtained from PastaStore, and setting
            defaults if they were not already provided.
        """

        # get stress
        snam = d.pop("stress")

        # if list, obtain first and only entry
        if isinstance(snam, list):
            snam = snam[0]
        # if str, either name of single series or 'nearest <kind>'
        if snam.startswith("nearest"):
            if len(snam.split()) > 1:
                kind = snam.split()[-1]
            else:
                kind = None
            logger.info(f"  | using nearest stress with kind='{kind}'")
            if kind == "oseries":
                snam = self.pstore.get_nearest_oseries(onam).iloc[0, 0]
            else:
                snam = self.pstore.get_nearest_stresses(
                    onam, kind=kind).iloc[0, 0]

        s, smeta = self.pstore.get_stresses(snam, return_metadata=True)
        s = ps.TimeSeries(s, snam, settings=d.pop("settings", None),
                          metadata=smeta)
        d["stress"] = [s.to_dict()]

        # use stress name if not provided
        if "name" not in d:
            d["name"] = snam

        # rfunc
        if "rfunc" not in d:
            logger.info("  | no 'rfunc' provided, using 'Gamma'")
            d["rfunc"] = "Gamma"

        return d
Beispiel #3
0
    def add_recharge(self,
                     ml: ps.Model,
                     rfunc=ps.Gamma,
                     recharge=ps.rch.Linear(),
                     recharge_name: str = "recharge") -> None:
        """Add recharge to a pastas model.

        Uses closest precipitation and evaporation timeseries in database.
        These are assumed to be labeled with kind = 'prec' or 'evap'.

        Parameters
        ----------
        ml : pastas.Model
            pastas.Model object
        rfunc : pastas.rfunc, optional
            response function to use for recharge in model,
            by default ps.Gamma (for different response functions, see
            pastas documentation)
        recharge : ps.RechargeModel
            recharge model to use, default is ps.rch.Linear()
        recharge_name : str
            name of the RechargeModel
        """
        # get nearest prec and evap stns
        names = []
        for var in ("prec", "evap"):
            try:
                name = self.get_nearest_stresses(ml.oseries.name,
                                                 kind=var).iloc[0, 0]
            except AttributeError:
                msg = "No precipitation or evaporation timeseries found!"
                raise Exception(msg)
            if isinstance(name, float):
                if np.isnan(name):
                    raise ValueError(f"Unable to find nearest '{var}' stress! "
                                     "Check X and Y coordinates.")
            else:
                names.append(name)
        if len(names) == 0:
            msg = "No precipitation or evaporation timeseries found!"
            raise Exception(msg)

        # get data
        tsdict = self.conn.get_stresses(names)
        stresses = []
        for (k, s), setting in zip(tsdict.items(), ("prec", "evap")):
            metadata = self.conn.get_metadata("stresses", k, as_frame=False)
            stresses.append(
                ps.TimeSeries(s, name=k, settings=setting, metadata=metadata))

        # add recharge to model
        rch = ps.RechargeModel(stresses[0],
                               stresses[1],
                               rfunc,
                               name=recharge_name,
                               recharge=recharge,
                               settings=("prec", "evap"),
                               metadata=[i.metadata for i in stresses])
        ml.add_stressmodel(rch)
Beispiel #4
0
def test_add_pastas_timeseries(request, conn):
    o1 = pd.DataFrame(data=1.0, columns=["test_df"],
                      index=pd.date_range("2000", periods=10, freq="D"))
    o1.index.name = "test_idx"
    ts = ps.TimeSeries(o1, metadata={"x": 100000., "y": 400000.})
    conn.add_oseries(ts, "test_pastas_ts", metadata=None)
    conn.add_stress(ts, "test_pastas_ts", kind="test",
                    metadata={"x": 200000., "y": 500000.})
    conn.del_oseries("test_pastas_ts")
    conn.del_stress("test_pastas_ts")
    return
Beispiel #5
0
    def add_series(self, series, name=None, kind=None, metadata=None,
                   settings=None, **kwargs):
        """Method to add series to the oseries or stresses database.

        Parameters
        ----------
        series: pandas.Series / pastas.TimeSeries
            Series object.
        name: str
            String with the name of the series that will be maintained in
            the database.
        kind: str
            The kind of series that is added. When oseries are added it is
            necessary to state "oseries" here.
        metadata: dict
            Dictionary with any metadata that will be passed to the
            TimeSeries object that is created internally.
        settings: dict or str
            Dictionary with any settings that will be passed to the
            TimeSeries object that is created internally.

        Returns
        -------

        """
        if name is None:
            name = series.name

        if kind == "oseries":
            data = self.oseries
        else:
            data = self.stresses

        if name in data.index:
            logger.error("Time series with name %s is already present in the \
                         database. Please provide a different name." % name)

        try:
            ts = ps.TimeSeries(series=series, name=name, settings=settings,
                               metadata=metadata, **kwargs)
        except:
            logger.warning("Time series %s is ommitted from the database."
                           % name)
            return

        data.at[name, "name"] = name
        data.at[name, "series"] = ts  # Do not add as first!
        data.at[name, "kind"] = kind

        # Transfer x, y and z to dataframe as well to increase speed.
        for i in ts.metadata.keys():
            value = ts.metadata[i]
            data.at[name, i] = value
Beispiel #6
0
    def create_model(self,
                     name: str,
                     modelname: str = None,
                     add_recharge: bool = True,
                     recharge_name: str = "recharge") -> ps.Model:
        """Create a pastas Model.

        Parameters
        ----------
        name : str
            name of the oseries to create a model for
        modelname : str, optional
            name of the model, default is None, which uses oseries name
        add_recharge : bool, optional
            add recharge to the model by looking for the closest
            precipitation and evaporation timeseries in the stresses
            library, by default True
        recharge_name : str
            name of the RechargeModel

        Returns
        -------
        pastas.Model
            model for the oseries

        Raises
        ------
        KeyError
            if data is stored as dataframe and no column is provided
        ValueError
            if timeseries is empty
        """
        # get oseries metadata
        meta = self.conn.get_metadata("oseries", name, as_frame=False)
        ts = self.conn.get_oseries(name)

        # convert to Timeseries and create model
        if not ts.dropna().empty:
            ts = ps.TimeSeries(ts,
                               name=name,
                               settings="oseries",
                               metadata=meta)
            if modelname is None:
                modelname = name
            ml = ps.Model(ts, name=modelname, metadata=meta)

            if add_recharge:
                self.add_recharge(ml, recharge_name=recharge_name)
            return ml
        else:
            raise ValueError("Empty timeseries!")
Beispiel #7
0
def create_pastas_project(oc,
                          pr=None,
                          project_name='',
                          obs_column='stand_m_tov_nap',
                          kind='oseries',
                          add_metadata=True,
                          verbose=False):
    """add observations to a new or existing pastas project

    Parameters
    ----------
    oc : observation.ObsCollection
        collection of observations
    pr : pastas.project, optional
        Existing pastas project, if None a new project is created
    project_name : str, optional
        Name of the pastas project only used if pr is None
    obs_column : str, optional
        Name of the column in the Obs dataframe to be used
    kind : str, optional
        The kind of series that is added to the pastas project
    add_metadata : boolean, optional
        If True metadata from the observations added to the project.
    verbose : boolean, optional
        Print additional information to the screen (default is False).

    Returns
    -------
    pr : pastas.project
        the pastas project with the series from the ObsCollection
    """

    if pr is None:
        pr = ps.Project(project_name)

    for o in oc.obs.values:
        if verbose:
            print('add to pastas project -> {}'.format(o.name))

        if add_metadata:
            meta = _get_metadata_from_obs(o, verbose=verbose)
        else:
            meta = dict()

        series = ps.TimeSeries(o[obs_column], name=o.name, metadata=meta)
        pr.add_series(series, kind=kind)

    return pr
Beispiel #8
0
    def add_recharge(self, ml: ps.Model, rfunc=ps.Gamma) -> None:
        """Add recharge to a pastas model.

        Uses closest precipitation and evaporation timeseries in database.
        These are assumed to be labeled with kind = 'prec' or 'evap'.

        Parameters
        ----------
        ml : pastas.Model
            pastas.Model object
        rfunc : pastas.rfunc, optional
            response function to use for recharge in model,
            by default ps.Gamma (for different response functions, see
            pastas documentation)
        """
        # get nearest prec and evap stns
        names = []
        for var in ("prec", "evap"):
            try:
                name = self.get_nearest_stresses(
                    ml.oseries.name, kind=var).iloc[0, 0]
            except AttributeError:
                msg = "No precipitation or evaporation timeseries found!"
                raise Exception(msg)
            names.append(name)
        if len(names) == 0:
            msg = "No precipitation or evaporation timeseries found!"
            raise Exception(msg)

        # get data
        tsdict = self.conn.get_stresses(names)
        stresses = []
        for k, s in tsdict.items():
            metadata = self.conn.get_metadata("stresses", k, as_frame=False)
            s = self.conn._get_dataframe_values("stresses", k, s,
                                                metadata=metadata)
            stresses.append(ps.TimeSeries(s, name=k, metadata=metadata))

        # add recharge to model
        rch = ps.StressModel2(stresses, rfunc, name="recharge",
                              metadata=[i.metadata for i in stresses],
                              settings=("prec", "evap"))
        ml.add_stressmodel(rch)
Beispiel #9
0
    def create_model(self, name: str, add_recharge: bool = True) -> ps.Model:
        """Create a new pastas Model.

        Parameters
        ----------
        name : str
            name of the oseries to create a model for
        add_recharge : bool, optional
            add recharge to the model by looking for the closest
            precipitation and evaporation timeseries in the stresses
            library, by default True

        Returns
        -------
        pastas.Model
            model for the oseries

        Raises
        ------
        KeyError
            if data is stored as dataframe and no column is provided
        ValueError
            if timeseries is empty
        """
        # get oseries metadata
        meta = self.conn.get_metadata("oseries", name, as_frame=False)
        ts = self.conn.get_oseries(name)
        # get right column of data
        ts = self.conn._get_dataframe_values(
            "oseries", name, ts, metadata=meta)

        # convert to Timeseries and create model
        if not ts.dropna().empty:
            ts = ps.TimeSeries(ts, name=name, settings="oseries",
                               metadata=meta)
            ml = ps.Model(ts, name=name, metadata=meta)

            if add_recharge:
                self.add_recharge(ml)
            return ml
        else:
            raise ValueError("Empty timeseries!")
Beispiel #10
0
H = meny.H['Obsevation well']
ml = ps.Model(H['values'])

# Add precipitation
IN = meny.IN['Precipitation']['values']
IN.index = IN.index.round("D")
IN.name = 'Precipitation'
IN2 = meny.IN['Evaporation']['values']
IN2.index = IN2.index.round("D")
IN2.name = 'Evaporation'
sm = ps.RechargeModel(IN, IN2, ps.Gamma, 'Recharge')
ml.add_stressmodel(sm)

# Add well extraction 2
IN = meny.IN['Extraction 2']
well = ps.TimeSeries(IN["values"], settings="well")
# extraction amount counts for the previous month
sm1 = ps.StressModel(well, ps.Hantush, 'Extraction_2', up=False)

# Add well extraction 3
IN = meny.IN['Extraction 3']
well = ps.TimeSeries(IN["values"], settings="well")
# extraction amount counts for the previous month
sm2 = ps.StressModel(well, ps.Hantush, 'Extraction_3', up=False)

# add_stressmodels also allows addings multiple stressmodels at once
ml.add_stressmodel([sm1, sm2])

# Solve
ml.solve(tmax="1995")
Beispiel #11
0
def load_model(data):
    # Create model
    oseries = ps.TimeSeries(**data["oseries"])

    if "constant" in data.keys():
        constant = data["constant"]
    else:
        constant = False

    if "settings" in data.keys():
        settings = data["settings"]
    else:
        settings = dict()

    if "metadata" in data.keys():
        metadata = data["metadata"]
    else:
        metadata = dict(name="Model")  # Make sure there is a name

    if "name" in data.keys():
        name = data["name"]
    else:
        name = metadata["name"]

    if "noisemodel" in data.keys():
        noise = True
    else:
        noise = False

    ml = ps.Model(oseries, constant=constant, noisemodel=noise, name=name,
                  metadata=metadata, settings=settings)
    if "file_info" in data.keys():
        ml.file_info.update(data["file_info"])

    # Add stressmodels
    for name, ts in data["stressmodels"].items():
        stressmodel = getattr(ps.stressmodels, ts["stressmodel"])
        ts.pop("stressmodel")
        ts["rfunc"] = getattr(ps.rfunc, ts["rfunc"])
        for i, stress in enumerate(ts["stress"]):
            ts["stress"][i] = ps.TimeSeries(**stress)
        stressmodel = stressmodel(**ts)
        ml.add_stressmodel(stressmodel)

    # Add transform
    if "transform" in data.keys():
        transform = getattr(ps.transform, data["transform"]["transform"])
        data["transform"].pop("transform")
        transform = transform(**data["transform"])
        ml.add_transform(transform)

    # Add noisemodel if present
    if "noisemodel" in data.keys():
        n = getattr(ps.noisemodels, data["noisemodel"]["type"])()
        ml.add_noisemodel(n)

    # Add parameters, use update to maintain correct order
    ml.parameters = ml.get_init_parameters(noise=ml.settings["noise"])
    ml.parameters.update(data["parameters"])
    ml.parameters = ml.parameters.apply(pd.to_numeric, errors="ignore")
    return ml
Beispiel #12
0
def _load_model(data):
    """Internal method to create a model from a dictionary."""
    # Create model
    _remove_keyword(data["oseries"])
    oseries = ps.TimeSeries(**data["oseries"])

    if "constant" in data.keys():
        constant = data["constant"]
    else:
        constant = False

    if "metadata" in data.keys():
        metadata = data["metadata"]
    else:
        metadata = None

    if "name" in data.keys():
        name = data["name"]
    else:
        name = None

    if "noisemodel" in data.keys():
        noise = True
    else:
        noise = False

    ml = ps.Model(oseries,
                  constant=constant,
                  noisemodel=noise,
                  name=name,
                  metadata=metadata)

    if "settings" in data.keys():
        ml.settings.update(data["settings"])
    if "file_info" in data.keys():
        ml.file_info.update(data["file_info"])

    # Add stressmodels
    for name, ts in data["stressmodels"].items():
        stressmodel = getattr(ps.stressmodels, ts["stressmodel"])
        ts.pop("stressmodel")
        if "rfunc" in ts.keys():
            ts["rfunc"] = getattr(ps.rfunc, ts["rfunc"])
        if "recharge" in ts.keys():
            ts["recharge"] = getattr(ps.recharge, ts["recharge"])()
        if "stress" in ts.keys():
            for i, stress in enumerate(ts["stress"]):
                _remove_keyword(stress)
                ts["stress"][i] = ps.TimeSeries(**stress)
        if "prec" in ts.keys():
            _remove_keyword(ts["prec"])
            ts["prec"] = ps.TimeSeries(**ts["prec"])
        if "evap" in ts.keys():
            _remove_keyword(ts["evap"])
            ts["evap"] = ps.TimeSeries(**ts["evap"])
        if "temp" in ts.keys() and ts["temp"] is not None:
            ts["temp"] = ps.TimeSeries(**ts["temp"])
        stressmodel = stressmodel(**ts)
        ml.add_stressmodel(stressmodel)

    # Add transform
    if "transform" in data.keys():
        transform = getattr(ps.transform, data["transform"]["transform"])
        data["transform"].pop("transform")
        transform = transform(**data["transform"])
        ml.add_transform(transform)

    # Add noisemodel if present
    if "noisemodel" in data.keys():
        n = getattr(ps.noisemodels, data["noisemodel"]["type"])()
        ml.add_noisemodel(n)

    # Add fit object to the model
    if "fit" in data.keys():
        fit = getattr(ps.solver, data["fit"]["name"])
        data["fit"].pop("name")
        ml.fit = fit(ml=ml, **data["fit"])

    # Add parameters, use update to maintain correct order
    ml.parameters = ml.get_init_parameters(noise=ml.settings["noise"])
    ml.parameters.update(data["parameters"])
    ml.parameters = ml.parameters.apply(to_numeric, errors="ignore")

    # When initial values changed
    for param, value in ml.parameters.loc[:, "initial"].iteritems():
        ml.set_parameter(name=param, initial=value)

    collect()

    return ml
Beispiel #13
0
def load_model(data):
    # Create model
    oseries = ps.TimeSeries(**data["oseries"])

    if "constant" in data.keys():
        constant = data["constant"]
    else:
        constant = False

    if "metadata" in data.keys():
        metadata = data["metadata"]
    else:
        metadata = None

    if "name" in data.keys():
        name = data["name"]
    else:
        name = None

    if "noisemodel" in data.keys():
        noise = True
    else:
        noise = False

    ml = ps.Model(oseries,
                  constant=constant,
                  noisemodel=noise,
                  name=name,
                  metadata=metadata)

    if "settings" in data.keys():
        ml.settings.update(data["settings"])
    if "file_info" in data.keys():
        ml.file_info.update(data["file_info"])
        ml.file_info["version"] = ps.__version__

    # Add stressmodels
    for name, ts in data["stressmodels"].items():
        stressmodel = getattr(ps.stressmodels, ts["stressmodel"])
        ts.pop("stressmodel")
        if "rfunc" in ts.keys():
            ts["rfunc"] = getattr(ps.rfunc, ts["rfunc"])
        if "stress" in ts.keys():
            for i, stress in enumerate(ts["stress"]):
                ts["stress"][i] = ps.TimeSeries(**stress)
        stressmodel = stressmodel(**ts)
        ml.add_stressmodel(stressmodel)

    # Add transform
    if "transform" in data.keys():
        transform = getattr(ps.transform, data["transform"]["transform"])
        data["transform"].pop("transform")
        transform = transform(**data["transform"])
        ml.add_transform(transform)

    # Add noisemodel if present
    if "noisemodel" in data.keys():
        n = getattr(ps.noisemodels, data["noisemodel"]["type"])()
        ml.add_noisemodel(n)

    # Add parameters, use update to maintain correct order
    ml.parameters = ml.get_init_parameters(noise=ml.settings["noise"])
    ml.parameters.update(data["parameters"])
    ml.parameters = ml.parameters.apply(to_numeric, errors="ignore")

    # When initial values changed
    for param, value in ml.parameters.loc[:, "initial"].iteritems():
        ml.set_initial(name=param, value=value)

    gc.collect()

    return ml
Beispiel #14
0
In this example a daily simulation is conducted from 9:00 until 9:00 (dutch standard time)
This is the time at which precipitation is logged in dutch KNMI-stations. 

"""
import pastas as ps
import pandas as pd

# read observations
obs = ps.read_dino('data/B58C0698001_1.csv')

# Create the time series model
ml = ps.Model(obs)

# read weather data
knmi = ps.read.knmi.KnmiStation.fromfile('data/neerslaggeg_HEIBLOEM-L_967-2.txt')
rain = ps.TimeSeries(knmi.data['RD'],settings='prec')

evap = ps.read_knmi('data/etmgeg_380.txt', variables='EV24')
if True:
    # also add 9 hours to the evaporation
    s = evap.series_original
    s.index = s.index+pd.to_timedelta(9,'h')
    evap.series_original = s

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

# set the time-offset of the model. This should be done automatically in the future.
ml._set_time_offset()
Beispiel #15
0
    def load(self, fyaml: str) -> List[ps.Model]:
        """Load Pastas YAML file.

        Note: currently supports RechargeModel, StressModel and WellModel.

        Parameters
        ----------
        fyaml : str
            path to file

        Returns
        -------
        models : list
            list containing pastas model(s)

        Raises
        ------
        ValueError
            if insufficient information is provided to construct pastas model
        NotImplementedError
            if unsupported stressmodel is encountered
        """

        with open(fyaml, "r") as f:
            yml = yaml.load(f, Loader=yaml.CFullLoader)

        models = []

        for mlnam in yml.keys():

            mlyml = yml[mlnam]

            # get oseries + metadata
            if isinstance(mlyml["oseries"], dict):
                onam = str(mlyml["oseries"]["name"])
                settings = mlyml["oseries"].pop("settings", "oseries")
                _ = mlyml.pop("oseries")
            else:
                onam = str(mlyml.pop("oseries"))
                settings = "oseries"

            logger.info(f"Building model '{mlnam}' for oseries '{onam}'")
            o, ometa = self.pstore.get_oseries(
                onam, return_metadata=True)

            # create model to obtain default model settings
            o_ts = ps.TimeSeries(o, metadata=ometa, settings=settings)
            ml = ps.Model(o_ts, name=mlnam, metadata=ometa)
            mldict = ml.to_dict(series=True)

            # update with stored model settings
            if "settings" in mlyml:
                mldict["settings"].update(mlyml["settings"])

            # stressmodels
            for smnam, smyml in mlyml["stressmodels"].items():

                # set name if not provided
                if smyml is not None:
                    name = smyml.get("name", smnam)
                else:
                    name = smnam
                logger.info(f"| parsing stressmodel: '{name}'")

                # check whether smtyp is defined
                if smyml is not None:
                    if "stressmodel" in smyml:
                        smtyp = True
                    else:
                        smtyp = False
                else:
                    smtyp = False

                # check if RechargeModel based on name if smtyp not defined
                if (smnam.lower() in ["rch", "rech", "recharge",
                                      "rechargemodel"]) and not smtyp:
                    logger.info(
                        "| assuming RechargeModel based on stressmodel name.")
                    # check if stressmodel dictionary is empty, create (nearly
                    # empty) dict so defaults are used
                    if smyml is None:
                        mlyml["stressmodels"][smnam] = {"name": "recharge"}
                        smyml = mlyml["stressmodels"][smnam]
                    if "name" not in smyml:
                        smyml["name"] = smnam
                    smtyp = smyml.get("stressmodel", "RechargeModel")
                else:
                    # if no info is provided, raise error,
                    # cannot make any assumptions for non-RechargeModels
                    if smyml is None:
                        raise ValueError("Insufficient information "
                                         f"for stressmodel '{name}'!")
                    # get stressmodel type, with default StressModel
                    if "stressmodel" in smyml:
                        smtyp = smyml["stressmodel"]
                    else:
                        logger.info(
                            "| no 'stressmodel' type provided, "
                            "using 'StressModel'")
                        smtyp = "StressModel"

                # parse dictionary based on smtyp
                if smtyp in ["RechargeModel", "TarsoModel"]:
                    # parse RechargeModel
                    sm = self._parse_rechargemodel_dict(smyml, onam=onam)

                    # turn off constant for TarsoModel
                    if smtyp == "TarsoModel":
                        mldict["constant"] = False
                elif smtyp == "StressModel":
                    # parse StressModel
                    sm = self._parse_stressmodel_dict(smyml, onam=onam)
                elif smtyp == "WellModel":
                    # parse WellModel
                    sm = self._parse_wellmodel_dict(smyml, onam=onam)
                else:
                    raise NotImplementedError(
                        "PastaStore.yaml interface does "
                        f"not (yet) support '{smtyp}'!")

                # add to list
                smyml.update(sm)

            # update model dict w/ default settings with loaded data
            mldict.update(mlyml)

            # add name to dictionary if not already present
            if "name" not in mldict:
                mldict["name"] = mlnam

            # convert warmup and time_offset to panads.Timedelta
            if "warmup" in mldict["settings"]:
                mldict["settings"]["warmup"] = pd.Timedelta(
                    mldict["settings"]["warmup"])
            if "time_offset" in mldict["settings"]:
                mldict["settings"]["time_offset"] = pd.Timedelta(
                    mldict["settings"]["time_offset"])

            # load model
            ml = ps.io.base._load_model(mldict)
            models.append(ml)

        return models
Beispiel #16
0
    def _parse_wellmodel_dict(self, d: Dict,
                              onam: Optional[str] = None) -> Dict:
        """Internal method to parse WellModel dictionary.

        Note: supports 'nearest' or 'nearest <number> <kind>' as input to
        'stress', which will automatically select nearest or <number> of
        nearest stress(es) with kind=<kind>. Requires "x" and "y" locations to
        be present in both oseries and stresses metadata.

        Parameters
        ----------
        d : dict
            dictionary containing WellModel information
        onam : str, optional
            name of oseries used when 'nearest' is provided as stress,
            by default None

        Returns
        -------
        d : dict
            dictionary that can be read by ps.io.base.load(),
            containing stresses obtained from PastaStore, and setting
            defaults if they were not already provided.
        """

        # parse stress
        snames = d.pop("stress")

        # if str, either name of single series or 'nearest <n> <kind>'
        if isinstance(snames, str):
            if snames.startswith("nearest"):
                if len(snames.split()) == 3:
                    n = int(snames.split()[1])
                    kind = snames.split()[2]
                elif len(snames.split()) == 2:
                    try:
                        n = int(snames.split()[1])
                    except ValueError:
                        raise ValueError(
                            f"Could not parse: '{snames}'! "
                            "When using option 'nearest' for WellModel,  "
                            "use 'nearest <n>' or 'nearest <n> <kind>'!")
                    kind = "well"
                elif len(snames.split()) == 1:
                    n = 1
                    kind = "well"
                snames = self.pstore.get_nearest_stresses(
                    onam, kind=kind, n=n).iloc[0].values
                logger.info(
                    f"  | using {n} nearest stress(es) with kind='{kind}': "
                    f"{snames}")
            else:
                snames = [snames]

        # get timeseries
        slist = []
        for snam in snames:
            s, smeta = self.pstore.get_stresses(snam, return_metadata=True)
            slist.append(
                ps.TimeSeries(s, snam, settings="well",
                              metadata=smeta).to_dict()
            )
        d["stress"] = slist

        # get distances
        if "distances" not in d:
            d["distances"] = self.pstore.get_distances(
                oseries=onam, stresses=snames).values.squeeze()

        # use default name if not provided
        if "name" not in d:
            d["name"] = "wells"

        # rfunc
        if "rfunc" not in d:
            logger.info("  | no 'rfunc' provided, using 'HantushWellModel'")
            d["rfunc"] = "HantushWellModel"

        if "up" not in d:
            logger.info("  | no 'up' provided, set to 'False', "
                        "(i.e. pumping rate is positive for extraction).")
            d["up"] = False

        return d
Beispiel #17
0
H = meny.H['Obsevation well']
ml = ps.Model(H['values'])

# Add precipitation
IN = meny.IN['Precipitation']['values']
IN.index = IN.index.round("D")
IN.name = 'Precipitation'
IN2 = meny.IN['Evaporation']['values']
IN2.index = IN2.index.round("D")
IN2.name = 'Evaporation'
sm = ps.StressModel2([IN, IN2], ps.Gamma, 'Recharge')
ml.add_stressmodel(sm)

# Add well extraction 2
IN = meny.IN['Extraction 2']
well = ps.TimeSeries(IN["values"], freq_original="M", settings="well")
# extraction amount counts for the previous month
sm1 = ps.StressModel(well, ps.Hantush, 'Extraction_2', up=False)

# Add well extraction 3
IN = meny.IN['Extraction 3']
well = ps.TimeSeries(IN["values"], freq_original="M", settings="well")
# extraction amount counts for the previous month
sm2 = ps.StressModel(well, ps.Hantush, 'Extraction_3', up=False)

# add_stressmodels also allows addings multiple stressmodels at once
ml.add_stressmodel(sm1, sm2)

# Solve
ml.solve(tmax="1995")
Beispiel #18
0
    def _parse_rechargemodel_dict(self, d: Dict,
                                  onam: Optional[str] = None) -> Dict:
        """Internal method to parse RechargeModel dictionary.

        Note: supports 'nearest' as input to 'prec' and 'evap',
        which will automatically select nearest stress with kind="prec" or
        "evap". Requires "x" and "y" locations to be present in both oseries
        and stresses metadata.

        Parameters
        ----------
        d : dict
            dictionary containing RechargeModel information
        onam : str, optional
            name of oseries used when 'nearest' is provided as prec or evap,
            by default None

        Returns
        -------
        d : dict
            dictionary that can be read by ps.io.base.load(),
            containing stresses obtained from PastaStore, and setting
            defaults if they were not already provided.
        """
        # precipitation
        prec_val = d.get("prec", "nearest")
        if isinstance(prec_val, dict):
            pnam = prec_val["name"]
            prec = self.pstore.get_stresses(pnam)
            prec = ps.TimeSeries(prec, **prec_val)
        elif prec_val.startswith("nearest"):
            if onam is None:
                raise ValueError("Provide oseries name when using nearest!")
            if len(prec_val.split()) > 1:
                kind = prec_val.split()[-1]
            else:
                kind = "prec"
            pnam = self.pstore.get_nearest_stresses(
                onam, kind=kind).iloc[0, 0]
            logger.info(
                f"  | using nearest timeseries with kind='{kind}': '{pnam}'")
            prec, pmeta = self.pstore.get_stresses(pnam, return_metadata=True)
            prec = ps.TimeSeries(prec, pnam, settings="prec", metadata=pmeta)
        elif isinstance(prec_val, str):
            pnam = d["prec"]
            prec, pmeta = self.pstore.get_stresses(pnam, return_metadata=True)
            prec = ps.TimeSeries(prec, pnam, settings="prec", metadata=pmeta)
        else:
            raise NotImplementedError(
                f"Could not parse prec value: '{prec_val}'")
        d["prec"] = prec.to_dict()

        # evaporation
        evap_val = d.get("evap", "nearest")
        if isinstance(evap_val, dict):
            enam = evap_val["name"]
            evap = self.pstore.get_stresses(enam)
            evap = ps.TimeSeries(evap, **evap_val)
        elif evap_val.startswith("nearest"):
            if onam is None:
                raise ValueError("Provide oseries name when using nearest!")
            if len(evap_val.split()) > 1:
                kind = evap_val.split()[-1]
            else:
                kind = "evap"
            enam = self.pstore.get_nearest_stresses(
                onam, kind=kind).iloc[0, 0]
            logger.info(
                f"  | using nearest timeseries with kind='{kind}': '{enam}'")
            evap, emeta = self.pstore.get_stresses(enam, return_metadata=True)
            evap = ps.TimeSeries(evap, enam, metadata=emeta, settings="evap")
        elif isinstance(evap_val, str):
            enam = d["evap"]
            evap, emeta = self.pstore.get_stresses(enam, return_metadata=True)
            evap = ps.TimeSeries(evap, enam, metadata=emeta, settings="evap")
        else:
            raise NotImplementedError(
                f"Could not parse evap value: '{evap_val}'")
        d["evap"] = evap.to_dict()

        # rfunc
        if "rfunc" not in d:
            logger.info("  | no 'rfunc' provided, using 'Exponential'")

        # stressmodel
        if "stressmodel" not in d:
            d["stressmodel"] = "RechargeModel"

        # recharge type (i.e. Linear, FlexModel, etc.)
        if ("recharge" not in d) and (d["stressmodel"] == "RechargeModel"):
            logger.info("  | no 'recharge' type provided, using 'Linear'")

        # tarsomodel logic
        if d["stressmodel"] == "TarsoModel":
            dmin = d.get("dmin", None)
            dmax = d.get("dmin", None)
            oseries = d.get("oseries", None)
            if ((dmin is None) or (dmax is None)) and (oseries is None):
                logger.info("  | no 'dmin/dmax' or 'oseries' provided,"
                            f" filling in 'oseries': '{onam}'")
                d["oseries"] = onam

        if "oseries" in d:
            onam = d["oseries"]
            if isinstance(onam, str):
                o = self.pstore.get_oseries(onam)
                d["oseries"] = o

        return d