Source code for oemof.solph._plumbing

# -*- coding: utf-8 -*-

"""Helpers to fit scalar values into sequences.

SPDX-FileCopyrightText: Uwe Krien <krien@uni-bremen.de>
SPDX-FileCopyrightText: Simon Hilpert
SPDX-FileCopyrightText: Cord Kaldemeyer
SPDX-FileCopyrightText: henhuy

SPDX-License-Identifier: MIT

"""

import warnings
from collections import abc
from itertools import repeat

import numpy as np


[docs] def sequence(iterable_or_scalar): """Checks if an object is iterable (except string) or scalar and returns the an numpy array of the sequence if object is an iterable or an 'emulated' sequence object of class _FakeSequence if object is a scalar. Parameters ---------- iterable_or_scalar : iterable or None or int or float Examples -------- >>> y = sequence([1,2,3,4,5,6,7,8,9,10,11]) >>> y[0] np.int64(1) >>> y[10] np.int64(11) >>> import pandas as pd >>> s1 = sequence(pd.Series([1,5,9])) >>> s1[2] np.int64(9) >>> x = sequence(10) >>> x[0] 10 >>> x[10] 10 """ if len(np.shape(iterable_or_scalar)) > 1: d = len(np.shape(iterable_or_scalar)) raise ValueError( f"Dimension too high ({d} > 1) for {iterable_or_scalar}\n" "The dimension of a number is 0, of a list 1, of a table 2 and so " "on.\nPlease notice that a table with one column is still a table " "with 2 dimensions and not a Series." ) if isinstance(iterable_or_scalar, str): return iterable_or_scalar elif isinstance(iterable_or_scalar, abc.Iterable): return np.array(iterable_or_scalar) else: return _FakeSequence(value=iterable_or_scalar)
[docs] def valid_sequence(sequence, length: int) -> bool: """Checks if an object is a numpy array of at least the given length or an 'emulated' sequence object of class _FakeSequence. If unset, the latter is set to the required lenght. """ if sequence[0] is None: return False if isinstance(sequence, _FakeSequence): if sequence.size is None: sequence.size = length if sequence.size == length: return True else: return False if isinstance(sequence, np.ndarray): if sequence.size == length: return True # --- BEGIN: To be removed for versions >= v0.6 --- elif sequence.size > length: warnings.warn( "Sequence longer than needed" f" ({sequence.size} items instead of {length})." " This will be trated as an error in the future.", FutureWarning, ) return True # --- END --- else: raise ValueError(f"Lentgh of {sequence} should be {length}.") return False
class _FakeSequence: """Emulates a list whose length is not known in advance. Parameters ---------- value : scalar length : integer Examples -------- >>> s = _FakeSequence(value=42, length=5) >>> s [42, 42, 42, 42, 42] >>> s = _FakeSequence(value=42) >>> # undefined lenght, access still works >>> s[1337] 42 """ def __init__(self, value, length=None): self._value = value self._length = length @property def size(self): return self._length @size.setter def size(self, value): self._length = value def __getitem__(self, _): return self._value def __repr__(self): if self._length is not None: return str([i for i in self]) else: return f"[{self._value}, {self._value}, ..., {self._value}]" def __len__(self): return self._length def __iter__(self): return repeat(self._value, self._length) def max(self): return self._value def min(self): return self._value def sum(self): if self._length is None: return np.inf else: return self._length * self._value def to_numpy(self, length=None): if length is not None: return np.full(length, self._value) else: return np.full(len(self), self._value) @property def value(self): return self._value