def read2dict(cls, filename, info): """Read the control parameters from the given path (and its auxiliary paths, where appropriate) and store them in the given |dict| object `info`. Note that the |dict| `info` can be used to feed information into the execution of control files. Use this method only if you are completely sure on how the control parameter import of HydPy works. Otherwise, you should most probably prefer to use |ControlManager.load_file|. """ if not filename.endswith('.py'): filename += '.py' path = os.path.join(cls._workingpath, filename) try: if path not in cls._registry: with open(path) as file_: cls._registry[path] = file_.read() exec(cls._registry[path], {}, info) except BaseException: objecttools.augment_excmessage( 'While trying to load the control file `%s`' % path) if 'model' not in info: raise IOError( 'Model parameters cannot be loaded from control file `%s`. ' 'Please refer to the HydPy documentation on how to prepare ' 'control files properly.' % path)
def load_files(self): """Load nodes and elements from all network files and return them in a |Selections| instance. Each single network file defines a separate |Selection| instance. Additionally, all |Element| and |Node| objects are bundled in a selection named `complete`. """ selections = selectiontools.Selections() for (filename, path) in zip(self.filenames, self.filepaths): # Ensure both `Node` and `Element`start with a `fresh` memory. devicetools.Node.gather_new_nodes() devicetools.Element.gather_new_elements() try: info = runpy.run_path(path) except BaseException: objecttools.augment_excmessage( 'While trying to load the network file `%s`' % path) try: selections += selectiontools.Selection( filename.split('.')[0], info['Node'].gather_new_nodes(), info['Element'].gather_new_elements()) except KeyError as exc: raise KeyError( 'The class `%s` cannot be loaded from the network ' 'file `%s`. Please refer to the HydPy documentation ' 'on how to prepare network files properly.' % (exc.args[0], filename)) selections += selectiontools.Selection( 'complete', info['Node'].registered_nodes(), info['Element'].registered_elements()) return selections
def _apply_method(self, method) -> None: try: method() except BaseException: self._statuscode = 500 objecttools.augment_excmessage( f'While trying to execute method `{method.__name__}`')
def _convertandtest(self, values, name): """Try to convert the given values to a |numpy| |numpy.ndarray| and check if it is plausible. If so, return the array, other raise a |ValueError| or re-raise a |numpy| specific exception. """ try: array = numpy.array(values, dtype=int) except BaseException: objecttools.augment_excmessage( 'While trying to assign a new `%s` ' 'index array to an Indexer object' % name) if array.ndim != 1: raise ValueError( 'The `%s` index array of an Indexer object must be ' '1-dimensional. However, the given value has interpreted ' 'as a %d-dimensional object.' % (name, array.ndim)) if pub.timegrids is not None: if len(array) != len(pub.timegrids.init): raise ValueError( 'The %s` index array of an Indexer object must have a ' 'number of entries fitting to the initialization time ' 'period precisely. However, the given value has been ' 'interpreted to be of length %d and the length of the ' 'Timegrid object representing the actual initialization ' 'time period is %d.' % (name, len(array), len(pub.timegrids.init))) return array
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 __call__(self, *args, **kwargs): self._soil = None try: super().__call__(*args, **kwargs) except NotImplementedError as exc: try: soil = kwargs.pop("soil", None) if soil is None: raise exc if kwargs: raise TypeError( f"It is not allowed to combine keyword `soil` with " f"other keywords, but the following ones are also " f"used: {objecttools.enumeration(kwargs.keys())}." ) from None try: self(self._SOIL2VALUE[soil]) self._soil = soil except KeyError: value2name = wland_constants.CONSTANTS.value2name names = (f"{value2name[value]} ({value})" for value in self._SOIL2VALUE.keys()) raise ValueError( f"The given soil constant `{soil}` is not among the " f"available ones. Please use one of the following " f"constants: {objecttools.enumeration(names)}." ) from None except BaseException: objecttools.augment_excmessage( f"While trying the set the value of parameter " f"{objecttools.elementphrase(self)}")
def save_file(self, filename, text): """Save the given text under the given condition filename and the current path. If the current directory is not defined explicitly, the directory name is constructed with the actual simulation end date. If such an directory does not exist, it is created immediately. """ _defaultdir = self._defaultdir try: if not filename.endswith('.py'): filename += '.py' try: self._defaultdir = ('init_' + pub.timegrids.sim.lastdate.string('os')) except AttributeError: pass path = os.path.join(self.currentpath, filename) with open(path, 'w', encoding="utf-8") as file_: file_.write(text) except BaseException: objecttools.augment_excmessage( 'While trying to write the conditions file `%s`' % filename) finally: self._defaultdir = _defaultdir
def update_variable(self, variable, value) -> None: """Assign the given value(s) to the given target or base variable. If the assignment fails, |ChangeItem.update_variable| raises an error like the following: >>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> item = SetItem("alpha", "hland_v1", "control.alpha", 0) >>> item.collect_variables(pub.selections) >>> item.update_variables() # doctest: +ELLIPSIS Traceback (most recent call last): ... TypeError: When trying to update a target variable of SetItem `alpha` \ with the value(s) `None`, the following error occurred: While trying to set \ the value(s) of variable `alpha` of element `...`, the following error \ occurred: The given value `None` cannot be converted to type `float`. """ try: variable(value) except BaseException: objecttools.augment_excmessage( f"When trying to update a target variable of " f"{type(self).__name__} `{self.name}` " f"with the value(s) `{value}`")
def __call__(self, *args, **kwargs): try: shape = (len(kwargs), self.subpars.xpoints.shape[0]) except exceptiontools.AttributeNotReady: raise RuntimeError( f"The shape of parameter {objecttools.elementphrase(self)} " f"depends on the shape of parameter `xpoints`, which has " f"not been defined so far." ) from None if shape[0] == 0: raise ValueError( f"For parameter {objecttools.elementphrase(self)} " f"no branches are defined. Do this via keyword " f"arguments as explained in the documentation." ) branched = self.subpars.pars.model.sequences.outlets.branched if (branched.shape[0] != 0) and (branched.shape[0] != shape[0]): raise RuntimeError( "The number of branches of the hbranch model should not " "be changed during run time. If you really need to do " "this, first initialize a new `branched` sequence and " "connect it to the respective outlet nodes properly." ) self.shape = shape self.values = numpy.nan for idx, (key, value) in enumerate(sorted(kwargs.items())): if key not in devicetools.Node.query_all(): try: pub.projectname except RuntimeError: pass else: raise RuntimeError( f"Parameter {objecttools.elementphrase(self)} is " f"supposed to branch to node `{key}`, but such a " f"node is not available." ) try: self.values[idx] = value except BaseException: if shape[1] != len(value): raise ValueError( f"Each branch requires the same number of supporting " f"points as given for parameter `xpoints`, which is " f"{shape[1]}, but for branch `{key}` of parameter " f"{objecttools.elementphrase(self)} {len(value)} " f"values are given." ) from None objecttools.augment_excmessage( f"While trying to set the values for branch `{key}` " f"of parameter {objecttools.elementphrase(self)}" ) if branched.shape == (0,): branched.shape = shape[0] self.subpars.pars.model.sequences.fluxes.outputs.shape = shape[0] self.subpars.pars.model.nodenames.clear() for idx, key in enumerate(sorted(kwargs.keys())): setattr(self, key, self.values[idx]) self.subpars.pars.model.nodenames.append(key)
def value(self, value): try: self._value = numpy.full(self.shape, value, dtype=float) except BaseException: objecttools.augment_excmessage( f"When trying to convert the value(s) `{value}` assigned " f"to {type(self).__name__} `{self.name}` to a " f"numpy array of shape `{self.shape}` and type `float`")
def value(self, value): try: self._value = numpy.full(self.shape, value, dtype=float) except BaseException: objecttools.augment_excmessage( f'When trying to convert the value(s) `{value}` assigned ' f'to {objecttools.classname(self)} `{self.name}` to a ' f'numpy array of shape `{self.shape}` and type `float`')
def __delete__(self, obj): try: obj.currentdir = self.value del obj.currentdir except IOError: objecttools.augment_excmessage( 'While trying to delete the input sequence directory') finally: self.value = None
def connect(self): """Connect the link sequences of the actual model.""" try: for group in ('inlets', 'receivers', 'outlets', 'senders'): self._connect_subgroup(group) except BaseException: objecttools.augment_excmessage( 'While trying to build the node connection of the `%s` ' 'sequences of the model handled by element `%s`' % (group[:-1], objecttools.devicename(self)))
def __iadd__(self, values): try: for model in self._get_models(values): self._dict[str(model)] = Variable2Auxfile(_master=self, _model=model) return self except BaseException: objecttools.augment_excmessage( 'While trying to add one ore more models ' 'to the actual auxiliary file handler')
def __exit__(self, exception, message, traceback_): if not self.texts: self.print_('no failures occurred') else: for text in self.texts: self.print_(text) sys.stdout = self.stdout sys.stderr = self.stderr if exception: objecttools.augment_excmessage()
def __call__(self, *args, **kwargs): try: args = [timetools.Period(args[0]).seconds] except BaseException: objecttools.augment_excmessage( 'While trying the set the value of parameter `maxdt` ' 'of the lake model handled by element `%s`' % objecttools.devicename(self), '(An example: set `max dt` to 3600 seconds by writing ' '`maxdt("1h"))') super().__call__(*args, **kwargs)
def __set__(self, obj, directory): obj._inputdir = None try: obj.currentdir = directory self.value = directory except IOError: objecttools.augment_excmessage( 'While trying to set the %s sequence directory' % self.sequence_type) finally: obj._currentdir = None
def __get__(self, obj, type_=None): if obj is None: return self try: obj.currentdir = self.value return obj._currentdir except IOError: objecttools.augment_excmessage( 'While trying to get the %s sequence directory' % self.sequence_type) finally: obj._currentdir = None
def __call__(self, *args, **kwargs): """The prefered way to pass values to |DMax| instances within parameter control files. """ try: lland_parameters.ParameterSoil.__call__(self, *args, **kwargs) except TypeError: args = kwargs.get('r_dmax') if args is not None: self.value = 2.4192 * self.apply_timefactor(numpy.array(args)) self.trim() else: objecttools.augment_excmessage()
def __isub__(self, values): try: for model in self._get_models(values): try: del self._dict[str(model)] except KeyError: raise AttributeError( f'The handler does not contain model `{model}`.') return self except BaseException: objecttools.augment_excmessage( 'While trying to remove one or more models ' 'from the actual auxiliary file handler')
def _get_model(value): if isinstance(value, str): try: value = importlib.import_module('hydpy.models.' + value) except BaseException: objecttools.augment_excmessage( 'While trying to import a model named `%s`' % value) if isinstance(value, types.ModuleType): try: value = importtools.prepare_model(value) except BaseException: objecttools.augment_excmessage( 'While trying to prepare the model defined in' 'module `hydpy.models.%s`' % objecttools.modulename(value)) return value
def __setattr__(self, filename, variables): try: self._check_filename(filename) new_vars = objecttools.extract(variables, (typingtools.VariableProtocol, )) for new_var in new_vars: self._check_variable(new_var) fn2var = self._type2filename2variable.get(type(new_var), {}) self._check_duplicate(fn2var, new_var, filename) fn2var[filename] = copy.deepcopy(new_var) self._type2filename2variable[type(new_var)] = fn2var except BaseException: objecttools.augment_excmessage( 'While trying to extend the range of variables handled by ' 'the actual Variable2AuxFile object')
def __call__(self, *args, **kwargs): """The prefered way to pass values to |DMax| instances within parameter control files. """ try: lland_parameters.ParameterSoil.__call__(self, *args, **kwargs) except TypeError: args = kwargs.get("r_dmax") if args is not None: self.value = (0.1008 * hydpy.pub.timegrids.init.stepsize.hours * numpy.array(args)) self.trim() else: objecttools.augment_excmessage()
def __setattr__(self, key, value): if hasattr(self, key) and not key.startswith('th_'): object.__setattr__(self, key, value) else: std_key = self._standardize_key(key) try: try: self._coefs[std_key] = value.arma.coefs except AttributeError: self._coefs[std_key] = (tuple(float(v) for v in value[0]), tuple(float(v) for v in value[1])) except BaseException: objecttools.augment_excmessage( f'While trying to set a new threshold ({key}) ' f'coefficient pair for parameter `{self.name}` ' f'of element `{objecttools.devicename(self.subpars)}`')
def __setattr__(self, key, value): if self._has_predefined_attr(key): object.__setattr__(self, key, value) else: std_key = self._standardize_key(key) try: try: self._coefs[std_key] = value.arma.coefs except AttributeError: self._coefs[std_key] = (tuple(float(v) for v in value[0]), tuple(float(v) for v in value[1])) except BaseException: objecttools.augment_excmessage( 'While trying to set a new threshold (%s) coefficient ' 'pair for parameter `%s` of element `%s`' % (key, self.name, objecttools.devicename(self.subpars)))
def move_dll(self): """Try to find the resulting dll file and to move it into the `cythons` package. Things to be aware of: * The file extension either `pyd` (Window) or `so` (Linux). * The folder containing the dll file is system dependent, but is always a subfolder of the `cythons` package. * Under Linux, the filename might contain system information, e.g. ...cpython-36m-x86_64-linux-gnu.so. """ dirinfos = os.walk(self.buildpath) next(dirinfos) system_dependent_filename = None for dirinfo in dirinfos: for filename in dirinfo[2]: if (filename.startswith(self.cyname) and filename.endswith(dllextension)): system_dependent_filename = filename break if system_dependent_filename: try: shutil.move( os.path.join(dirinfo[0], system_dependent_filename), os.path.join(self.cydirpath, self.cyname + dllextension)) break except BaseException: prefix = ('After trying to cythonize module %s, when ' 'trying to move the final cython module %s ' 'from directory %s to directory %s' % (self.pyname, system_dependent_filename, self.buildpath, self.cydirpath)) suffix = ('A likely error cause is that the cython module ' '%s does already exist in this directory and is ' 'currently blocked by another Python process. ' 'Maybe it helps to close all Python processes ' 'and restart the cyhonization afterwards.' % self.cyname + dllextension) objecttools.augment_excmessage(prefix, suffix) else: raise IOError('After trying to cythonize module %s, the resulting ' 'file %s could neither be found in directory %s nor ' 'its subdirectories. The distul report should tell ' 'whether the file has been stored somewhere else,' 'is named somehow else, or could not be build at ' 'all.' % self.buildpath)
def __setattr__(self, name: str, value: object) -> None: if name.startswith("toy_"): try: if not isinstance(value, InterpAlgorithm): raise TypeError( f"{objecttools.value_of_type(value).capitalize()} has been " f"given, but an object of type `InterpAlgorithm` is required." ) self._add_toyalgorithpair(name, value) self.refresh() except BaseException: objecttools.augment_excmessage( f"While trying to assign a new interpolator to parameter " f"{objecttools.elementphrase(self)} based on the string `{name}`" ) else: object.__setattr__(self, name, value)
def __delattr__(self, name: str) -> None: if name.startswith("toy_"): try: try: del self._toy2algorithm[timetools.TOY(name)] except KeyError: raise AttributeError( f"No interpolator is registered under a TOY object named " f"`{timetools.TOY(name)}`.") from None self.refresh() except BaseException: objecttools.augment_excmessage( f"While trying to remove an interpolator from parameter " f"{objecttools.elementphrase(self)} based on the string `{name}`" ) else: object.__delattr__(self, name)
def __setattr__(self, key: str, value: object) -> None: if hasattr(self, key) and not key.startswith("th_"): object.__setattr__(self, key, value) else: std_key = self._standardize_key(key) try: try: self._coefs[std_key] = value.arma.coefs except AttributeError: self._coefs[std_key] = ( tuple(float(v) for v in value[0]), tuple(float(v) for v in value[1]), ) except BaseException: objecttools.augment_excmessage( f"While trying to set a new threshold ({key}) coefficient pair " f"for parameter {objecttools.elementphrase(self)}")
def save_files(self, selections): """Save the nodes and elements from each |Selection| object contained within the given |Selections| instance to a separate network file of the same name. """ try: currentpath = self.currentpath selections = selectiontools.Selections(selections) for selection in selections: if selection.name == 'complete': continue path = os.path.join(currentpath, selection.name + '.py') selection.save(path=path, write_nodes=True) except BaseException: objecttools.augment_excmessage( 'While trying to save selections `%s` into network files' % selections)