Пример #1
0
 def model(self, request):
     """Return model class."""
     models = list(set(request.inputs.keys()).intersection(self.tuple_inputs.keys()))
     if len(models) > 1:
         raise NotImplementedError("Multi-model simulations are not supported. ")
     name = models.pop()
     params = self.parse_tuple(request.inputs.pop(name)[0])
     model = get_model(name)(workdir=self.workdir)
     model.config.update("params", params)
     return model
Пример #2
0
def perform_forecasting_step(rvc, model, workdir=None, **kwds):
    """
    Function that might be useful eventually to do a forecast from a model setup.
    """
    # kwds includes 'ts', the forecast timeseries data
    # Setup the model
    m = get_model(model)(workdir=workdir)

    # Force the initial conditions
    m.resume(rvc)

    # Set the parameters, start dates, etc. required to run the model and run
    m(overwrite=True, **kwds)

    return m.q_sim
Пример #3
0
def param(model):
    """Return a parameter coordinate.

    Parameters
    ----------
    model : str
      Model name.
    """
    model_cls = models.get_model(model)
    return xr.IndexVariable(
        "param",
        data=np.array([f.name for f in fields(model_cls.Params)]),
        attrs={
            "standard_name": "parameter",
            "long_name": f"{model} model parameter name",
        },
    )
Пример #4
0
def param(model):
    """Return a parameter coordinate.

    Parameters
    ----------
    model : str
      Model name.
    """
    model = models.get_model(model)
    return xr.IndexVariable(
        "param",
        data=np.array(model.params._fields),
        attrs={
            "standard_name": "parameter",
            "long_name": "{} model parameter name".format(model),
        },
    )
Пример #5
0
def get_raven_states(model, workdir=None, **kwds):
    """Get the RAVEN states file (.rvc file) after a model run.

    Parameters
    ----------
    model : {'HMETS', 'GR4JCN', 'MOHYSE', 'HBVEC'}
      Model name.
    kwds : {}
      Model configuration parameters, including the forcing files (ts).

    Returns
    -------
    rvc : {}
      Raven model forcing file

    """
    # Run the model and get the rvc file for future hotstart.
    m = get_model(model)(workdir=workdir)
    m(overwrite=True, **kwds)
    rvc = m.outputs["solution"]

    return rvc
Пример #6
0
def regionalize(
    method,
    model,
    nash,
    params=None,
    props=None,
    target_props=None,
    size=5,
    min_NSE=0.6,
    **kwds,
):
    """Perform regionalization for catchment whose outlet is defined by coordinates.

    Parameters
    ----------
    method : {'MLR', 'SP', 'PS', 'SP_IDW', 'PS_IDW', 'SP_IDW_RA', 'PS_IDW_RA'}
      Name of the regionalization method to use.
    model : {'HMETS', 'GR4JCN', 'MOHYSE'}
      Model name.
    nash : pd.Series
      NSE values for the parameters of gauged catchments.
    params : pd.DataFrame
      Model parameters of gauged catchments. Needed for all but MRL method.
    props : pd.DataFrame
      Properties of gauged catchments to be analyzed for the regionalization. Needed for MLR and RA methods.
    target_props : pd.Series or dict
      Properties of ungauged catchment. Needed for MLR and RA methods.
    size : int
      Number of catchments to use in the regionalization.
    min_NSE : float
      Minimum calibration NSE value required to be considered as a donor.
    kwds : {}
      Model configuration parameters, including the forcing files (ts).

    Returns
    -------
    (qsim, ensemble)
    qsim : DataArray (time, )
      Multi-donor averaged predicted streamflow.
    ensemble : Dataset
      q_sim : DataArray  (realization, time)
        Ensemble of members based on number of donors.
      parameter : DataArray (realization, param)
        Parameters used to run the model.
    """
    # TODO: Include list of available properties in docstring.
    # TODO: Add error checking for source, target stuff wrt method chosen.

    # Select properties based on those available in the ungauged properties DataFrame.
    if isinstance(target_props, dict):
        ungauged_properties = pd.Series(target_props)
    elif isinstance(target_props, pd.Series):
        ungauged_properties = target_props
    elif isinstance(target_props, pd.DataFrame):
        ungauged_properties = target_props.to_series()
    else:
        raise ValueError

    cr = coords.realization(1 if method == "MLR" else size)
    cp = coords.param(model)

    # Filter on NSE
    valid = nash > min_NSE
    filtered_params = params.where(valid).dropna()
    filtered_prop = props.where(valid).dropna()

    # Check to see if we have enough data, otherwise raise error
    if len(filtered_prop) < size and method != "MLR":
        raise ValueError("Hydrological_model and minimum NSE threshold \
                         combination is too strict for the number of donor \
                         basins. Please reduce the number of donor basins OR \
                         reduce the minimum NSE threshold.")

    # Rank the matrix according to the similarity or distance.
    if method in ["PS", "PS_IDW", "PS_IDW_RA"]:  # Physical similarity
        dist = similarity(filtered_prop, ungauged_properties)
    else:  # Geographical distance.
        dist = distance(filtered_prop, ungauged_properties)

    # Series of distances for the first `size` best donors
    sdist = dist.sort_values().iloc[:size]

    # Pick the donors' model parameters and catchment properties
    sparams = filtered_params.loc[sdist.index]
    sprop = filtered_prop.loc[sdist.index]

    # Get the list of parameters to run
    reg_params = regionalization_params(method, sparams, sprop,
                                        ungauged_properties, filtered_params,
                                        filtered_prop)

    # Run the model over all parameters and create ensemble DataArray
    m = models.get_model(model)()
    qsims = list()

    for i, params in enumerate(reg_params):
        kwds["params"] = params
        kwds["run_name"] = f"reg_{i}"
        m(**kwds)
        qsims.append(m.q_sim.copy(deep=True))

    qsims = xr.concat(qsims, dim=cr)

    # 3. Aggregate runs into a single result -> dataset
    if method in [
            "MLR",
            "SP",
            "PS",
    ]:  # Average (one realization for MLR, so no effect).
        qsim = qsims.mean(dim="realization", keep_attrs=True)
    elif (
            "IDW" in method
    ):  # Here we are replacing the mean by the IDW average, keeping attributes and dimensions.
        qsim = IDW(qsims, sdist)
    else:
        raise ValueError("No matching algorithm for {}".format(method))

    # Metadata handling
    # TODO: Store the basin_name

    # Create a DataArray for the parameters used in the regionalization
    param_da = xr.DataArray(
        reg_params,
        dims=("realization", "param"),
        coords={
            "param": cp,
            "realization": cr
        },
        attrs={"long_name": "Model parameters used in the regionalization."},
    )

    ens = xr.Dataset(
        data_vars={
            "q_sim": qsims,
            "parameter": param_da
        },
        attrs={
            "title": "Regionalization ensemble",
            "institution": "",
            "source": "RAVEN V.{} - {}".format(m.version, model),
            "history": "Created by raven regionalize.",
            "references": "",
            "comment": "Regionalization method: {}".format(method),
        },
    )

    # TODO: Add global attributes (model name, date, version, etc)
    return qsim, ens
Пример #7
0
def perform_climatology_esp(model_name,
                            forecast_date,
                            forecast_duration,
                            workdir=None,
                            **kwds):
    """
    This function takes the model setup and name as well as forecast data and duration and returns
    an ESP forecast netcdf. The data comes from the climatology data and thus there is a mechanism
    to get the correct data from the time series and exclude the current year.

    Parameters
    ----------
    model_name : {'HMETS', 'MOHYSE', 'GR4JCN', 'HBVEC'}
      Model name to instantiate Raven model.
    forecast_date : datetime.datetime
      Date of the forecast issue.
    forecast_duration : int
      Number of days of forecast, forward looking.
    kwds : dict
      Raven model configuration parameters.

    Returns
    -------
    qsims
      Array of streamflow values from the ESP method along with list of member years

    """
    # Get the timeseries
    tsnc = xr.open_dataset(kwds["ts"])

    # Prepare model instance
    m = get_model(model_name)(workdir=workdir)

    # Now find the periods of time for warm-up and forecast and add to the model keywords as the defaults are failing
    # (nanoseconds datetimes do not like the year 0001...)
    start_date = pd.to_datetime(tsnc["time"][0].values)
    start_date = start_date.to_pydatetime()

    kwds["start_date"] = start_date

    # Forecasting from Feb 29th is not ideal, we will replace with Feb 28th.
    # Should not change much in a climatological forecast.
    if forecast_date.month == 2 and forecast_date.day == 29:
        forecast_date.replace(day=28)

    # Check to make sure forecast date is not in the first year as we need model warm-up.
    # We cannot use timedelta because if the dataset happens to start on a leap
    # year, then the timedelta=365 days will not be robust. (and we cannot use timedelta(years=1)...)
    dateLimit = start_date.replace(year=start_date.year + 1)
    if dateLimit > forecast_date:
        msg = (
            "Forecast date is within the warm-up period. Select another forecast date."
        )
        warnings.warn(msg)

    # initialize the array of forecast variables
    qsims = []

    # list of unique years in the dataset:
    avail_years = list(np.unique(tsnc["time.year"].data))

    # Take a copy of the forecast initial date before overwriting in the forecast step.
    forecast_date_main = forecast_date

    # Remove the year that we are forecasting. Or else it's cheating!
    avail_years.remove(forecast_date.year)

    # Update the forecast end-date, which will be the day prior to the forecast date.
    # So forecasts warm-up will be from day 1 in the dataset to the forecast date.
    kwds["end_date"] = forecast_date - dt.timedelta(days=1)

    # Get RVC file if it exists, else compute it.
    if len(kwds['rvc']) > 0:
        rvc = kwds.pop('rvc')
    else:
        # Run model to get rvc file after warm-up using base meteo
        rvc = get_raven_states(model_name, workdir=workdir, **kwds)

    # We need to check which years are long enough (ex: wrapping years, 365-day forecast starting in
    # September 2015 will need data up to August 2016 at least)
    for years in avail_years:
        if forecast_date.replace(year=years) + dt.timedelta(
                days=forecast_duration - 1) > pd.to_datetime(
                    tsnc["time"][-1].values):
            avail_years.remove(years)
            msg = (
                f"Year {years} has been removed because it is the last year in the dataset and does not cover the "
                f"forecast duration.")
            warnings.warn(msg)

    # We will iterate this for all forecast years
    for years in avail_years:

        # Replace the forecast period start and end dates with the climatological ESP dates for the
        # current member (year)
        forecast_date = forecast_date.replace(year=years)
        kwds["start_date"] = forecast_date
        kwds["end_date"] = forecast_date + dt.timedelta(
            days=forecast_duration - 1)

        # Setup the initial states from the warm-up and run the model.
        # Note that info on start/end dates and timeseries are in the kwds.
        m.resume(rvc)
        m(run_name=f"run_{years}", **kwds)

        # Add member to the ensemble and retag the dates to the real forecast dates
        # (or else we will get dates from the climate dataset that cover all years)
        new_member = m.q_sim.copy(deep=True)
        new_member["time"] = pd.date_range(forecast_date_main,
                                           periods=forecast_duration)
        qsims.append(new_member)

    # Concatenate the members through a new dimension for the members and remove unused dims.
    qsims = xr.concat(qsims, dim="member")
    qsims = qsims.squeeze()

    # Add the number of the forecast year as member ID
    qsims["member"] = (["member"], avail_years)

    return qsims