def sequences(self): """Sequence declaration lines.""" lines = Lines() lines.add(0, '@cython.final') lines.add(0, 'cdef class Sequences(object):') for subseqs in self.model.sequences: lines.add( 1, 'cdef public %s %s' % (objecttools.classname(subseqs), subseqs.name)) if getattr(self.model.sequences, 'states', None) is not None: lines.add(1, 'cdef public StateSequences old_states') lines.add(1, 'cdef public StateSequences new_states') for subseqs in self.model.sequences: print(' - %s' % subseqs.name) lines.add(0, '@cython.final') lines.add( 0, 'cdef class %s(object):' % objecttools.classname(subseqs)) for seq in subseqs: ctype = 'double' + NDIM2STR[seq.NDIM] if isinstance(subseqs, sequencetools.LinkSequences): if seq.NDIM == 0: lines.add(1, 'cdef double *%s' % seq.name) elif seq.NDIM == 1: lines.add(1, 'cdef double **%s' % seq.name) lines.add(1, 'cdef public int len_%s' % seq.name) else: lines.add(1, 'cdef public %s %s' % (ctype, seq.name)) lines.add(1, 'cdef public int _%s_ndim' % seq.name) lines.add(1, 'cdef public int _%s_length' % seq.name) for idx in range(seq.NDIM): lines.add( 1, 'cdef public int _%s_length_%d' % (seq.name, idx)) if seq.NUMERIC: ctype_numeric = 'double' + NDIM2STR[seq.NDIM + 1] lines.add( 1, 'cdef public %s _%s_points' % (ctype_numeric, seq.name)) lines.add( 1, 'cdef public %s _%s_results' % (ctype_numeric, seq.name)) if isinstance(subseqs, sequencetools.FluxSequences): lines.add( 1, 'cdef public %s _%s_integrals' % (ctype_numeric, seq.name)) lines.add(1, 'cdef public %s _%s_sum' % (ctype, seq.name)) if isinstance(subseqs, sequencetools.IOSequences): lines.extend(self.iosequence(seq)) if isinstance(subseqs, sequencetools.InputSequences): lines.extend(self.load_data(subseqs)) if isinstance(subseqs, sequencetools.IOSequences): lines.extend(self.open_files(subseqs)) lines.extend(self.close_files(subseqs)) if not isinstance(subseqs, sequencetools.InputSequence): lines.extend(self.save_data(subseqs)) if isinstance(subseqs, sequencetools.LinkSequences): lines.extend(self.set_pointer(subseqs)) return lines
def _convert_type(self, value): try: return self.type_(value) except BaseException: raise TypeError( 'The value `%s` of type `%s` could not be converted to type ' '`%s` of the instantaneous unit hydrograph parameter `%s`.' % (value, objecttools.classname(value), objecttools.classname(self.type_), self.name))
def _typeconversion(self, type_): if not self.NDIM: if isinstance(type_, type): return type_(self.value) else: attr = getattr(self.value, type_) try: return attr() except TypeError: return attr else: raise TypeError( 'The %s instance `%s` is %d-dimensional and thus ' 'cannot be converted to a scalar %s value.' % (objecttools.classname(self), self.name, self.NDIM, objecttools.classname(type_)))
def __init__(self, *values): try: self._extractvalues(values) except BaseException: objecttools.augmentexcmessage( 'While trying to initialize a `%s` object' % objecttools.classname(self))
def sequences(self): """Sequence declaration lines.""" lines = Lines() for (name1, subseqs) in self.model.sequences: print(' - %s' % name1) lines.add(0, '@cython.final') lines.add( 0, 'cdef class %s(object):' % objecttools.classname(subseqs)) for (name2, seq) in subseqs: ctype = 'double' + NDIM2STR[seq.NDIM] if isinstance(subseqs, sequencetools.LinkSequences): if seq.NDIM == 0: lines.add(1, 'cdef double *%s' % name2) elif seq.NDIM == 1: lines.add(1, 'cdef double **%s' % name2) lines.add(1, 'cdef public int len_%s' % name2) else: lines.add(1, 'cdef public %s %s' % (ctype, name2)) lines.add(1, 'cdef public int _%s_ndim' % name2) lines.add(1, 'cdef public int _%s_length' % name2) for idx in range(seq.NDIM): lines.add( 1, 'cdef public int _%s_length_%d' % (seq.name, idx)) if isinstance(subseqs, sequencetools.IOSubSequences): lines.extend(self.iosequence(seq)) if isinstance(subseqs, sequencetools.InputSequences): lines.extend(self.loaddata(subseqs)) if isinstance(subseqs, sequencetools.IOSubSequences): lines.extend(self.openfiles(subseqs)) lines.extend(self.closefiles(subseqs)) if not isinstance(subseqs, sequencetools.InputSequence): lines.extend(self.savedata(subseqs)) if isinstance(subseqs, sequencetools.LinkSequences): lines.extend(self.setpointer(subseqs)) return lines
def assignrepr(self, prefix='') -> str: """Return a |repr| string with a prefixed assignment.""" with objecttools.repr_.preserve_strings(True): with hydpy.pub.options.ellipsis(2, optional=True): prefix += '%s(' % objecttools.classname(self) repr_ = objecttools.assignrepr_values(sorted(self.names), prefix, 70) return repr_ + ')'
def _check_duplicate(fn2var, new_var, filename): for (reg_fn, reg_var) in fn2var.items(): if (reg_fn != filename) and (reg_var == new_var): raise ValueError( 'You tried to allocate variable `{0!r}` to ' 'filename `{1}`, but an equal `{2}` object has ' 'already been allocated to filename `{3}`.'.format( new_var, filename, objecttools.classname(new_var), reg_fn))
def assignrepr(self, prefix): """Return a |repr| string with an prefixed assignement. Argument: * prefix(|str|): Usually something like 'x = '. """ with objecttools.repr_.preserve_strings(True): with pub.options.ellipsis(2, optional=True): prefix += '%s(' % objecttools.classname(self) repr_ = objecttools.assignrepr_values(self.names, prefix, 70) return repr_ + ')'
def __repr__(self): parts = [objecttools.classname(self), '('] for (name, primpar) in sorted(self._primary_parameters.items()): value = primpar.__get__(self) if value is not None: parts.extend([name, '=', objecttools.repr_(value), ', ']) if parts[-1] == ', ': parts[-1] = ')' else: parts.append(')') return ''.join(parts)
def parameters(self): """Parameter declaration lines.""" lines = Lines() lines.add(0, '@cython.final') lines.add(0, 'cdef class Parameters(object):') for subpars in self.model.parameters: lines.add( 1, 'cdef public %s %s' % (objecttools.classname(subpars), subpars.name)) for subpars in self.model.parameters: print(' - %s' % subpars.name) lines.add(0, '@cython.final') lines.add( 0, 'cdef class %s(object):' % objecttools.classname(subpars)) for par in subpars: try: ctype = TYPE2STR[par.TYPE] + NDIM2STR[par.NDIM] except KeyError: ctype = par.TYPE + NDIM2STR[par.NDIM] lines.add(1, 'cdef public %s %s' % (ctype, par.name)) return lines
def parameters(self): """Parameter declaration lines.""" lines = Lines() for (name1, subpars) in self.model.parameters: print(' - %s' % name1) lines.add(0, '@cython.final') lines.add( 0, 'cdef class %s(object):' % objecttools.classname(subpars)) for (name2, par) in subpars: ctype = TYPE2STR[par.TYPE] + NDIM2STR[par.NDIM] lines.add(1, 'cdef public %s %s' % (ctype, name2)) return lines
def modeldeclarations(self): """Attribute declarations of the model class.""" lines = Lines() lines.add(0 ,'@cython.final') lines.add(0 ,'cdef class Model(object):') lines.add(1, 'cdef public int idx_sim') for things in (self.model.parameters, self.model.sequences): for (name, thing) in things: lines.add(1, 'cdef public %s %s' % (objecttools.classname(thing), name)) if getattr(self.model.sequences, 'states', None) is not None: lines.add(1, 'cdef public StateSequences old_states') lines.add(1, 'cdef public StateSequences new_states') return lines
def assignrepr(self, prefix: str) -> str: """Return a |repr| string with a prefixed assignment.""" with objecttools.repr_.preserve_strings(True): with hydpy.pub.options.ellipsis(2, optional=True): with objecttools.assignrepr_tuple.always_bracketed(False): classname = objecttools.classname(self) blanks = ' ' * (len(prefix + classname) + 1) nodestr = objecttools.assignrepr_tuple( self.nodes.names, blanks + 'nodes=', 70) elementstr = objecttools.assignrepr_tuple( self.elements.names, blanks + 'elements=', 70) return (f'{prefix}{classname}("{self.name}",\n' f'{nodestr},\n' f'{elementstr})')
def assignrepr(self, prefix): lines = [] prefix += '%s(' % objecttools.classname(self) blanks = ' ' * len(prefix) names = sorted(self.names) for (idx, name) in enumerate(names): device = self[name] if idx == 0: lines.append(device.assignrepr(prefix)) else: lines.append(device.assignrepr(blanks)) lines[-1] += ',' lines[-1] = lines[-1][:-1] + ')' return '\n'.join(lines)
def _setdateformat(self, dateformat): try: dateformat = str(dateformat) except BaseException: raise TypeError( 'The given `dateformat` of type `%s` could not be converted ' 'to a `str` instance.' % objecttools.classname(dateformat)) try: datetime.datetime(2000, 1, 1).strftime(dateformat) except BaseException: raise ValueError( "The given `dateformat` `%s` is not a valid format string " "for `datetime` objects. Please read the documentation " "on module `datetime` of Python's the standard library " "for further information." % dateformat) self._dateformat = dateformat
def set_primary_parameters(self, **kwargs): """Set all primary parameters at once.""" given = sorted(kwargs.keys()) required = sorted(self._primary_parameters) if given == required: for (key, value) in kwargs.items(): setattr(self, key, value) else: raise ValueError( 'When passing primary parameter values as initialization ' 'arguments of the instantaneous unit hydrograph class `%s`, ' 'or when using method `set_primary_parameters, one has to ' 'to define all values at once via keyword arguments. ' 'But instead of the primary parameter names `%s` the ' 'following keywords were given: %s.' % (objecttools.classname(self), ', '.join(required), ', '.join(given)))
def _checkname(self, name): """Raises an :class:`~exceptions.ValueError` if the given name is not a valid Python identifier. """ exc = ValueError('For initializing `%s` objects, `value` is a ' 'necessary function argument. Principally, any ' 'object is allowed that supports the Python build-in ' 'function `str`. But note that `str(value)` must ' 'return a valid Python identifier (that does ' 'not start with a number, that does not contain `-`, ' 'that is not a Python keyword like `for`...). The ' 'given object returned the string `%s`, which is not ' 'a valid Python identifier.' % (objecttools.classname(self), name)) try: exec('%s = None' % name) except SyntaxError: raise exc if name in dir(__builtins__): raise exc
def trim(self, lower=None, upper=None): """Trim the value(s) of a |Variable| instance. One can pass the lower and/or the upper boundary as a function argument. Otherwise, boundary values are taken from the class attribute `SPAN` of the given |Variable| instance, if available. Note that method |trim| works differently on |Variable| instances handling values of different types. For floating point values, an actual trimming is performed. Additionally, a warning message is raised if the trimming results in a change in value exceeding the threshold value defined by function |tolerance|. (This warning message can be suppressed by setting the related option flag to False.) For integer values, instead of a warning an exception is raised. """ span = getattr(self, 'SPAN', (None, None)) if lower is None: lower = span[0] if upper is None: upper = span[1] type_ = getattr(self, 'TYPE', float) if type_ is float: if self.NDIM == 0: _trim_float_0d(self, lower, upper) else: _trim_float_nd(self, lower, upper) elif type_ is int: if self.NDIM == 0: _trim_int_0d(self, lower, upper) else: _trim_int_nd(self, lower, upper) elif type_ is bool: pass else: raise NotImplementedError( 'Method `trim` can only be applied on parameters ' 'handling integer or floating point values, but ' 'value type of parameter `%s` is `%s`.' % (self.name, objecttools.classname(self.TYPE)))
def comparison_function(self, other): try: method = getattr(self.value, method_string) except AttributeError: # in Python 2.7, `int` (but not `float`) defines # `__cmp__` instead of rich comparisons method = getattr(float(self.value), method_string) try: if isinstance(other, abctools.VariableABC): result = method(other.value) else: result = method(other) if result is NotImplemented: return result try: return aggregation_func(result) except TypeError: return result except BaseException: objecttools.augment_excmessage( 'While trying to compare variable `{0!r}` of ' 'element `{1}` with object `{2}` of type `{3}`' .format(self, objecttools.devicename(self), other, objecttools.classname(other)))
def _arithmetic_exception(self, verb, other): objecttools.augment_excmessage( 'While trying to %s %s instance `%s` and %s `%s`' % (verb, objecttools.classname(self), self.name, objecttools.classname(other), other))
def remove(self, *values): """Remove the defined variables. The variables to be removed can be selected in two ways. But the first example shows that passing nothing or an empty iterable to method |Variable2Auxfile.remove| does not remove any variable: >>> from hydpy import dummies >>> v2af = dummies.v2af >>> v2af.remove() >>> v2af.remove([]) >>> from hydpy import print_values >>> print_values(v2af.filenames) file1, file2 >>> print_values(v2af.variables, width=30) eqb(5000.0), eqb(10000.0), eqd1(100.0), eqd2(50.0), eqi1(2000.0), eqi2(1000.0) The first option is to pass auxiliary file names: >>> v2af.remove('file1') >>> print_values(v2af.filenames) file2 >>> print_values(v2af.variables) eqb(10000.0), eqd1(100.0), eqd2(50.0) The second option is, to pass variables of the correct type and value: >>> v2af = dummies.v2af >>> v2af.remove(v2af.eqb[0]) >>> print_values(v2af.filenames) file1, file2 >>> print_values(v2af.variables) eqb(10000.0), eqd1(100.0), eqd2(50.0), eqi1(2000.0), eqi2(1000.0) One can pass multiple variables or iterables containing variables at once: >>> v2af = dummies.v2af >>> v2af.remove(v2af.eqb, v2af.eqd1, v2af.eqd2) >>> print_values(v2af.filenames) file1 >>> print_values(v2af.variables) eqi1(2000.0), eqi2(1000.0) Passing an argument that equals neither a registered file name or a registered variable results in the following exception: >>> v2af.remove('test') Traceback (most recent call last): ... ValueError: While trying to remove the given object `test` of type \ `str` from the actual Variable2AuxFile object, the following error occured: \ `'test'` is neither a registered filename nor a registered variable. """ for value in objecttools.extract(values, (str, abctools.VariableABC)): try: deleted_something = False for fn2var in list(self._type2filename2variable.values()): for fn_, var in list(fn2var.items()): if value in (fn_, var): del fn2var[fn_] deleted_something = True if not deleted_something: raise ValueError( ' `{0!r}` is neither a registered filename nor a ' 'registered variable.'.format(value)) except BaseException: objecttools.augment_excmessage( 'While trying to remove the given object `{0}` of type ' '`{1}` from the actual Variable2AuxFile object'.format( value, objecttools.classname(value)))
def reverse_model_wildcard_import() -> None: """Clear the local namespace from a model wildcard import. Calling this method should remove the critical imports into the local namespace due to the last wildcard import of a particular application model. In this manner, it secures the repeated preparation of different types of models via wildcard imports. See the following example, on how it can be applied. >>> from hydpy import reverse_model_wildcard_import Assume you import the first version of HydPy-L-Land (|lland_v1|): >>> from hydpy.models.lland_v1 import * This import adds, for example, the collection class for handling control parameters of `lland_v1` into the local namespace: >>> print(ControlParameters(None).name) control Calling function |parameterstep| prepares, for example, the control parameter object of class |lland_control.NHRU|: >>> parameterstep('1d') >>> nhru nhru(?) Calling function |reverse_model_wildcard_import| tries to give its best to clear the local namespace (even from unexpected classes as the one we define now): >>> class Test: ... __module__ = 'hydpy.models.lland_v1' >>> test = Test() >>> reverse_model_wildcard_import() >>> ControlParameters Traceback (most recent call last): ... NameError: name 'ControlParameters' is not defined >>> nhru Traceback (most recent call last): ... NameError: name 'nhru' is not defined >>> Test Traceback (most recent call last): ... NameError: name 'Test' is not defined >>> test Traceback (most recent call last): ... NameError: name 'test' is not defined """ namespace = inspect.currentframe().f_back.f_locals model = namespace.get('model') if model is not None: for subpars in model.parameters: for par in subpars: namespace.pop(par.name, None) namespace.pop(objecttools.classname(par), None) namespace.pop(subpars.name, None) namespace.pop(objecttools.classname(subpars), None) for subseqs in model.sequences: for seq in subseqs: namespace.pop(seq.name, None) namespace.pop(objecttools.classname(seq), None) namespace.pop(subseqs.name, None) namespace.pop(objecttools.classname(subseqs), None) for name in ('parameters', 'sequences', 'masks', 'model', 'Parameters', 'Sequences', 'Masks', 'Model', 'cythonizer', 'cymodel', 'cythonmodule'): namespace.pop(name, None) for key in list(namespace.keys()): try: if namespace[key].__module__ == model.__module__: del namespace[key] except AttributeError: pass
def plot(self, filename, width=None, height=None, selected=None, activated=None): """Save a bokeh html file plotting the current test results. (Optional) arguments: * filename: Name of the file. If necessary, the file ending `html` is added automatically. The file is stored in the `html` folder of subpackage `docs`. * width: Width of the plot in screen units. Defaults to 600. * height: Height of the plot in screen units. Defaults to 300. * selected: List of the sequences to be plotted. * activated: List of the sequences to be shown initially. """ if width is None: width = self.plotting_options.width if height is None: height = self.plotting_options.height if not filename.endswith('.html'): filename += '.html' if selected is None: selected = self.plotting_options.selected if selected is None: selected = self.parseqs if activated is None: activated = self.plotting_options.activated if activated is None: activated = self.parseqs activated = tuple(nm_.name if hasattr(nm_, 'name') else nm_.lower() for nm_ in activated) path = os.path.join(docs.__path__[0], 'html', filename) plotting.output_file(path) plot = plotting.figure(x_axis_type="datetime", tools=['pan', 'ywheel_zoom'], toolbar_location=None) plot.toolbar.active_drag = plot.tools[0] plot.toolbar.active_scroll = plot.tools[1] plot.plot_width = width plot.plot_height = height legend_entries = [] viridis = palettes.viridis headers = [header for header in self.raw_header_strings[1:] if header] zipped = zip(selected, viridis(len(selected)), headers) for (seq, col, header) in zipped: series = seq.series.copy() if not seq.NDIM: listofseries = [series] listofsuffixes = [''] else: nmb = seq.shape[0] listofseries = [series[:, idx] for idx in range(nmb)] if nmb == 1: listofsuffixes = [''] else: listofsuffixes = ['-%d' % idx for idx in range(nmb)] for subseries, suffix in zip(listofseries, listofsuffixes): line = plot.line(self._datetimes, subseries, alpha=0.8, muted_alpha=0.0, line_width=2, color=col) line.muted = seq.name not in activated if header.strip() == seq.name: title = objecttools.classname(seq) else: title = header.capitalize() title += suffix legend_entries.append((title, [line])) legend = models.Legend(items=legend_entries, click_policy='mute') legend.border_line_color = None plot.add_layout(legend, 'right') units = self.extract_units(selected) ylabel = objecttools.enumeration(units).replace('and', 'or') plot.yaxis.axis_label = ylabel plot.yaxis.axis_label_text_font_style = 'normal' plotting.save(plot) self._src = filename self._width = width self._height = height
def search_modeltypes(self, *models: ModelTypesArg, name: str = 'modeltypes') -> 'Selection': """Return a |Selection| object containing only the elements currently handling models of the given types. >>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, _ = prepare_full_example_2() You can pass both |Model| objects and names and, as a keyword argument, the name of the newly created |Selection| object: >>> test = pub.selections.complete.copy('test') >>> from hydpy import prepare_model >>> hland_v1 = prepare_model('hland_v1') >>> test.search_modeltypes(hland_v1) Selection("modeltypes", nodes=(), elements=("land_dill", "land_lahn_1", "land_lahn_2", "land_lahn_3")) >>> test.search_modeltypes( ... hland_v1, 'hstream_v1', 'lland_v1', name='MODELTYPES') Selection("MODELTYPES", nodes=(), elements=("land_dill", "land_lahn_1", "land_lahn_2", "land_lahn_3", "stream_dill_lahn_2", "stream_lahn_1_lahn_2", "stream_lahn_2_lahn_3")) Wrong model specifications result in errors like the following: >>> test.search_modeltypes('wrong') Traceback (most recent call last): ... ModuleNotFoundError: While trying to determine the elements of \ selection `test` handling the model defined by the argument(s) `wrong` \ of type(s) `str`, the following error occurred: \ No module named 'hydpy.models.wrong' Method |Selection.select_modeltypes| restricts the current selection to the one determined with the method the |Selection.search_modeltypes|: >>> test.select_modeltypes(hland_v1) Selection("test", nodes=(), elements=("land_dill", "land_lahn_1", "land_lahn_2", "land_lahn_3")) On the contrary, the method |Selection.deselect_upstream| restricts the current selection to all devices not determined by method the |Selection.search_upstream|: >>> pub.selections.complete.deselect_modeltypes(hland_v1) Selection("complete", nodes=(), elements=("stream_dill_lahn_2", "stream_lahn_1_lahn_2", "stream_lahn_2_lahn_3")) """ try: typelist = [] for model in models: if not isinstance(model, modeltools.Model): model = importtools.prepare_model(model) typelist.append(type(model)) typetuple = tuple(typelist) selection = Selection(name) for element in self.elements: if isinstance(element.model, typetuple): selection.elements += element return selection except BaseException: values = objecttools.enumeration(models) classes = objecttools.enumeration( objecttools.classname(model) for model in models) objecttools.augment_excmessage( f'While trying to determine the elements of selection ' f'`{self.name}` handling the model defined by the ' f'argument(s) `{values}` of type(s) `{classes}`')
def reverse_model_wildcard_import(): """Clear the local namespace from a model wildcard import. Calling this method should remove the critical imports into the local namespace due the last wildcard import of a certain application model. It is thought for securing the successive preperation of different types of models via wildcard imports. See the following example, on how it can be applied. >>> from hydpy import reverse_model_wildcard_import Assume you wildcard import the first version of HydPy-L-Land (|lland_v1|): >>> from hydpy.models.lland_v1 import * This for example adds the collection class for handling control parameters of `lland_v1` into the local namespace: >>> print(ControlParameters(None).name) control Calling function |parameterstep| for example prepares the control parameter object |lland_control.NHRU|: >>> parameterstep('1d') >>> nhru nhru(-999999) Calling function |reverse_model_wildcard_import| removes both objects (and many more, but not all) from the local namespace: >>> reverse_model_wildcard_import() >>> ControlParameters Traceback (most recent call last): ... NameError: name 'ControlParameters' is not defined >>> nhru Traceback (most recent call last): ... NameError: name 'nhru' is not defined """ namespace = inspect.currentframe().f_back.f_locals model = namespace.get('model') if model is not None: for subpars in model.parameters: for par in subpars: namespace.pop(par.name, None) namespace.pop(objecttools.classname(par), None) namespace.pop(subpars.name, None) namespace.pop(objecttools.classname(subpars), None) for subseqs in model.sequences: for seq in subseqs: namespace.pop(seq.name, None) namespace.pop(objecttools.classname(seq), None) namespace.pop(subseqs.name, None) namespace.pop(objecttools.classname(subseqs), None) for name in ('parameters', 'sequences', 'model', 'Parameters', 'Sequences', 'Model', 'cythonizer', 'cymodel', 'cythonmodule'): namespace.pop(name, None) for key in list(namespace.keys()): try: if namespace[key].__module__ == model.__module__: del namespace[key] except AttributeError: pass
def _check_variable(self, variable): if self._model and (variable not in self._model.parameters.control): raise TypeError( 'Variable type `{0}` is not handled by model `{1}`.'.format( objecttools.classname(variable), self._model))