def trim(self, lower=None, upper=None): """Trim upper values in accordance with :math:`EQI2 \\leq EQI1`. >>> from hydpy.models.lland import * >>> parameterstep("1d") >>> eqi2.value = 1.0 >>> eqi1(0.0) >>> eqi1 eqi1(1.0) >>> eqi1(1.0) >>> eqi1 eqi1(1.0) >>> eqi1(2.0) >>> eqi1 eqi1(2.0) """ if lower is None: lower = exceptiontools.getattr_( self.subpars.eqi2, "value", None, ) if upper is None: upper = exceptiontools.getattr_( self.subpars.eqb, "value", None, ) super().trim(lower, upper)
def trim(self, lower=None, upper=None): """Trim values in accordance with :math:`PWP \\leq FK \\leq WMax`. >>> from hydpy.models.lland import * >>> parameterstep() >>> nhru(3) >>> lnk(ACKER) >>> wmax(100.0) >>> pwp(-10.0, 50.0, 110.0) >>> pwp pwp(0.0, 50.0, 100.0) >>> fk.values = 80.0 >>> pwp.trim() >>> pwp pwp(0.0, 50.0, 80.0) """ if upper is None: upper = exceptiontools.getattr_( self.subpars.fk, "value", None, ) if upper is None: upper = exceptiontools.getattr_( self.subpars.wmax, "value", None, ) super().trim(lower, upper)
def __call__(self, *args, **kwargs): try: super().__call__(*args, **kwargs) except NotImplementedError as exc: counter = ("khq" in kwargs) + ("hq" in kwargs) if counter == 0: raise ValueError( f"For parameter {objecttools.elementphrase(self)} a " f"value can be set directly or indirectly by using " f"the keyword arguments `khq` and `hq`." ) from exc if counter == 1: raise ValueError( f"For the alternative calculation of parameter " f"{objecttools.elementphrase(self)}, at least the " f"keywords arguments `khq` and `hq` must be given." ) from exc alpha = float( kwargs.get( "alpha", exceptiontools.getattr_(self.subpars.alpha, "value", numpy.nan), ) ) if numpy.isnan(alpha): raise RuntimeError( f"For the alternative calculation of parameter " f"{objecttools.elementphrase(self)}, either the " f"keyword argument `alpha` must be given or the value " f"of parameter `alpha` must be defined beforehand." ) from exc khq = float(kwargs["khq"]) hq = float(kwargs["hq"]) self(hq / ((hq / khq) ** (alpha + 1.0)))
def new(cls, variable: "typingtools.VariableProtocol", **kwargs): """Return a new |IndexMask| object of the same shape as the parameter referenced by |property| |IndexMask.refindices|. Entries are only |True|, if the integer values of the respective entries of the referenced parameter are contained in the |IndexMask| class attribute tuple `RELEVANT_VALUES`. Before calling new (explicitly or implicitely), one must prepare the variable returned by property |IndexMask.refindices|: >>> from hydpy.models.hland import * >>> parameterstep() >>> states.sm.mask Traceback (most recent call last): ... RuntimeError: The mask of parameter `sm` of element `?` cannot be \ determined as long as parameter `zonetype` is not prepared properly. >>> nmbzones(4) >>> zonetype(FIELD, FOREST, ILAKE, GLACIER) >>> states.sm.mask Soil([ True, True, False, False]) """ indices = cls.get_refindices(variable) if numpy.min(exceptiontools.getattr_(indices, "values", 0)) < 1: raise RuntimeError( f"The mask of parameter {objecttools.elementphrase(variable)} " f"cannot be determined as long as parameter `{indices.name}` " f"is not prepared properly." ) return cls.array2mask(numpy.in1d(indices.values, cls.RELEVANT_VALUES), **kwargs)
def __call__(self, *args, **kwargs) -> None: self._keywordarguments = parametertools.KeywordArguments(False) idx = self._find_kwargscombination(args, kwargs, (set(("lag", )), )) if idx is None: super().__call__(*args, **kwargs) else: lag = parametertools.trim_kwarg(self, "lag", kwargs["lag"], lower=0.0) lag /= self.get_timefactor() self.value = int(round(lag)) self._keywordarguments = parametertools.KeywordArguments(lag=lag) shape = self.value model = self.subpars.pars.model model.nmb_segments = shape pars, seqs = model.parameters, model.sequences for subvars in ( pars.control, pars.derived, seqs.factors, seqs.fluxes, seqs.states, ): for variable in (var for var in subvars if var.NDIM == 1): if variable.name == "coefficients": continue oldshape = exceptiontools.getattr_(variable, "shape", None) if variable.name == "discharge": if oldshape != (shape + 1, ): variable.shape = (shape + 1, ) else: if oldshape != (shape, ): variable.shape = (shape, )
def _convertandtest(values, name): try: type_ = float if isinstance(values[0], float) else int array = numpy.array(values, dtype=type_) except BaseException: objecttools.augment_excmessage( f"While trying to assign a new `{name}` " f"index array to an Indexer object" ) if array.ndim != 1: raise ValueError( f"The `{name}` index array of an Indexer object must be " f"1-dimensional. However, the given value has interpreted " f"as a {array.ndim}-dimensional object." ) timegrids = exceptiontools.getattr_(hydpy.pub, "timegrids") if timegrids is not None: if len(array) != len(timegrids.init): raise ValueError( f"The `{name}` index array of an Indexer object must have " f"a number of entries fitting to the initialization time " f"period precisely. However, the given value has been " f"interpreted to be of length `{len(array)}` and the " f"length of the Timegrid object representing the actual " f"initialisation period is `{len(timegrids.init)}`." ) return array
def trim(self, lower=None, upper=None): """Trim values in accordance with :math:`AngstromFactor \\leq 1 - AngstromConstant` or at least in accordance with :math:`AngstromFactor \\leq 1`. >>> from hydpy.models.lland import * >>> parameterstep() >>> angstromconstant(0.4, 0.4, nan, 0.4, 0.4, 0.4, ... 0.6, 0.8, 1.0, 1.0, nan, nan) >>> angstromfactor(-0.2, 0.0, 0.2, 0.4, 0.6, 0.8, ... 1.0, 1.2, 1.4, 1.6, 1.8, 2.0) >>> angstromfactor angstromfactor(jan=0.0, feb=0.0, mar=0.2, apr=0.4, may=0.6, jun=0.6, jul=0.4, aug=0.2, sep=0.0, oct=0.0, nov=1.0, dec=1.0) >>> angstromconstant(None) >>> angstromfactor(0.6) >>> angstromfactor angstromfactor(0.6) """ if upper is None: upper = exceptiontools.getattr_( self.subpars.angstromconstant, "values", None, ) if upper is not None: upper = upper.copy() idxs = numpy.isnan(upper) upper[idxs] = 1.0 idxs = ~idxs upper[idxs] = 1.0 - upper[idxs] super().trim(lower, upper)
def trim(self, lower=None, upper=None): r"""Trim |ThetaS| following :math:`1e^{-6} \leq ThetaS \leq 1.0` and, if |ThetaR| exists for the relevant application model, also following :math:`ThetaR \leq ThetaS`. >>> from hydpy.models.wland import * >>> parameterstep() >>> thetas(0.0) >>> thetas thetas(0.000001) >>> thetar.value = 0.5 >>> thetas(0.4) >>> thetas thetas(0.5) >>> thetas(soil=SANDY_LOAM) >>> thetas thetas(0.5) >>> thetas(1.01) >>> thetas thetas(1.0) """ if lower is None: if exceptiontools.hasattr_(self.subpars, "thetar"): lower = exceptiontools.getattr_(self.subpars.thetar, "value", 1e-6) else: lower = 1e-6 super().trim(lower, upper)
def _get_timegrids(func): timegrids = exceptiontools.getattr_(hydpy.pub, "timegrids", None) if timegrids is None: name = func.__name__[1:] raise exceptiontools.AttributeNotReady( f"An Indexer object has been asked for an `{name}` array. " f"Such an array has neither been determined yet nor can it " f"be determined automatically at the moment. Either define " f"an `{name}` array manually and pass it to the Indexer " f"object, or make a proper Timegrids object available within " f"the pub module." ) return timegrids
def __call__(self, *args, **kwargs): old = exceptiontools.getattr_(self, "value", None) super().__call__(*args, **kwargs) new = self.__hydpy__get_value__() if new != old: for subpars in self.subpars.pars.model.parameters: for par in subpars: if (par.NDIM == 1) and (not isinstance( par, parametertools.MonthParameter)): par.__hydpy__set_shape__(new) for subseqs in self.subpars.pars.model.sequences: for seq in subseqs: if seq.NDIM == 1: seq.__hydpy__set_shape__(new)
def trim(self, lower=None, upper=None): r"""Trim |ThetaR| following :math:`1e^{-6} \leq ThetaR \leq ThetaS`. >>> from hydpy.models.wland import * >>> parameterstep() >>> thetar(0.0) >>> thetar thetar(0.000001) >>> thetas(0.41) >>> thetar(0.42) >>> thetar thetar(0.41) """ if upper is None: upper = exceptiontools.getattr_(self.subpars.thetas, "value", None) super().trim(lower, upper)
def trim(self, lower=None, upper=None): """Trim upper values in accordance with :math:`DMax \\geq DMin`. >>> from hydpy.models.lland import * >>> parameterstep("1d") >>> simulationstep("12h") >>> nhru(3) >>> lnk(ACKER) >>> dmin.values = 2.0 >>> dmax(2.0, 4.0, 6.0) >>> dmax dmax(4.0, 4.0, 6.0) """ if lower is None: lower = exceptiontools.getattr_( self.subpars.dmin, "value", None, ) super().trim(lower, upper)
def trim(self, lower=None, upper=None): """Trim upper values in accordance with :math:`EQD2 \\leq EQD1`. >>> from hydpy.models.lland import * >>> parameterstep("1d") >>> eqd1.value = 3.0 >>> eqd2(2.0) >>> eqd2 eqd2(2.0) >>> eqd2(3.0) >>> eqd2 eqd2(3.0) >>> eqd2(4.0) >>> eqd2 eqd2(3.0) """ if upper is None: upper = exceptiontools.getattr_( self.subpars.eqd1, "value", None, ) super().trim(lower, upper)
def trim(self, lower=None, upper=None): """Trim upper values in accordance with :math:`GSBGrad1 \\leq GSBGrad2`. >>> from hydpy.models.lland import * >>> simulationstep("1h") >>> parameterstep("1d") >>> gsbgrad1(1.0) >>> gsbgrad2(2.0) >>> gsbgrad2 gsbgrad2(2.0) >>> gsbgrad2(1.0) >>> gsbgrad2 gsbgrad2(1.0) >>> gsbgrad2(0.0) >>> gsbgrad2 gsbgrad2(1.0) """ if lower is None: lower = exceptiontools.getattr_( self.subpars.gsbgrad1, "value", None, ) super().trim(lower, upper)
def _fset(self, values): self.values = self._convertandtest(values, self.name) self.timegrids = copy.deepcopy(exceptiontools.getattr_(hydpy.pub, "timegrids"))
def call_fget(self, obj) -> NDArrayFloat: timegrids = exceptiontools.getattr_(hydpy.pub, "timegrids", None) if (self.values is None) or (self.timegrids != timegrids): self.values = self._calcidxs(self.fget(obj)) self.timegrids = copy.deepcopy(timegrids) return self.values
def update(self): """Calculate the number of entries and adjust the shape of all relevant log sequences. The aimed memory duration is one day. Hence, the number of the required log entries depends on the simulation step size: >>> from hydpy.models.lland import * >>> parameterstep() >>> from hydpy import pub >>> pub.timegrids = "2000-01-01", "2000-01-02", "1h" >>> derived.nmblogentries.update() >>> derived.nmblogentries nmblogentries(24) >>> logs wet0(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedteml(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedrelativehumidity(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedsunshineduration(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedpossiblesunshineduration(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedglobalradiation(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) loggedwindspeed2m(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) To prevent from loosing information, updating parameter |NmbLogEntries| resets the shape of the relevant log sequences only when necessary: >>> logs.wet0 = 1.0 >>> logs.loggedteml = 2.0 >>> logs.loggedrelativehumidity.shape = (6,) >>> logs.loggedrelativehumidity = 3.0 >>> derived.nmblogentries.update() >>> logs # doctest: +ELLIPSIS wet0(1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0) loggedteml(2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0) loggedrelativehumidity(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan) ... There is an explicit check for inappropriate simulation step sizes: >>> pub.timegrids = "2000-01-01 00:00", "2000-01-01 10:00", "5h" >>> derived.nmblogentries.update() Traceback (most recent call last): ... ValueError: The value of parameter `nmblogentries` of element `?` cannot be \ determined for a the current simulation step size. The fraction of the memory period \ (1d) and the simulation step size (5h) leaves a remainder. .. testsetup:: >>> del pub.timegrids """ nmb = "1d" / hydpy.pub.options.simulationstep if nmb % 1: raise ValueError( f"The value of parameter {objecttools.elementphrase(self)} cannot be " f"determined for a the current simulation step size. The fraction of " f"the memory period (1d) and the simulation step size " f"({hydpy.pub.timegrids.stepsize}) leaves a remainder.") self(nmb) nmb = int(nmb) logs = self.subpars.pars.model.sequences.logs for seq in logs: shape = exceptiontools.getattr_(seq, "shape", (None, )) if nmb != shape[-1]: seq.shape = nmb