Source code for hyveopt.pdata

from abc import ABC, abstractmethod
from dataclasses import dataclass
from shilps.components import Component, Entity, PVGenerator, TSParameter, InvData, TimeConfig, TSParameter
from typing import Union, List, Self, Dict

import numpy as np


[docs] class CustomAttribute(ABC):
[docs] @abstractmethod def copy(self) -> Self: pass
[docs] @abstractmethod def to_dict(self) -> dict: pass
[docs] @classmethod @abstractmethod def from_dict(cls, input): pass
[docs] @dataclass class Range(CustomAttribute): min: float max: float
[docs] def to_dict(self) -> dict: return {'min': self.min, 'max': self.max}
[docs] @classmethod def from_dict(cls, input: Dict[str, float]): return cls(**input)
[docs] def copy(self) -> Self: return Range(self.min, self.max)
[docs] class Gain(CustomAttribute): def __init__(self, breakpoints: List[float], slopes: List[float], intercepts: List[float]) -> None: self.breakpoints = breakpoints self.slopes = slopes self.intercepts = intercepts
[docs] def to_dict(self) -> Dict: return {'breakpoints': self.breakpoints, 'slopes': self.slopes, 'intercepts': self.intercepts}
[docs] def from_dict(cls, input: Dict[str, List[float]]): return cls(**input)
[docs] def copy(self) -> Self: return Gain(self.breakpoints.copy(), self.slopes.copy(), self.intercepts.copy())
[docs] def piecewise_linear(self, x): """ Evaluate a piecewise linear function. Parameters: x : array_like Input array. breakpoints : list or array_like Breakpoints of the piecewise function. slopes : list or array_like Slopes of the linear pieces. intercepts : list or array_like Intercepts of the linear pieces. Returns: np.ndarray Output array where each element is the result of the piecewise linear function. """ x = np.asarray(x) conditions = [np.logical_and(self.breakpoints[i] <= x, x < self.breakpoints[i + 1]) for i in range(len(self.breakpoints) - 1)] conditions.append(x >= self.breakpoints[-1]) # Handle the last interval including the right endpoint functions = [lambda x, i=i: self.slopes[i] * x + self.intercepts[i] for i in range(len(self.slopes))] functions.append(lambda x: self.slopes[-1] * x + self.intercepts[-1]) # Handle the last interval return np.piecewise(x, conditions, functions)
@property def domain(self): return (self.breakpoints[0], self.breakpoints[-1]) def __call__(self, x): return self.piecewise_linear(x)
[docs] @dataclass(slots=True) class GainComponent(Component): gain: Gain = None
[docs] @dataclass(slots=True) class Electrolizer(GainComponent): """ Represents a hydrogen electrolizer. Attributes: ----------- pnom_kW: float Nominal pmin_pu: float Minimum power consumption in per unit. pmax_pu: float Maximum power consumption in per unit. """ pnom_kW: float = None pmin_pu: float = None pmax_pu: float = None
[docs] @dataclass class TemplateElectrolizer(Electrolizer): """ Represents a hydrogen electrolizer with fixed efficiency. Attributes: ----------- pnom_kW: float Nominal pmin_pu: float Minimum power consumption in per unit. pmax_pu: float Maximum power consumption in per unit. """ pnom_kW: Union[float, List[float]] = None inv_cost_per_kw: float = None
[docs] @dataclass class ESS(GainComponent): """ Energy storage system component. It considers a fixed rate of charge and fixed efficiency. Attributes: ----------- pnom_kW: float Nominal active power (kW). soc_nom_kWh: float Nominal state of charge (kWh). """ pnom_kW: float = None soc_nom_kWh: float = None
[docs] @dataclass class TemplateESS(ESS): """ Template storage system component. It considers a fixed rate of charge and fixed efficiency. Attributes: ----------- pnom_kW: float Nominal active power (kW). soc_nom_kWh: float Nominal state of charge (kWh). efficiency: float Round-trip efficiency. """ pnom_kW: Range = None soc_nom_kWh: Range = None inv_cost_per_kw: float = None bat_kw_to_kwh: float = None
[docs] @dataclass class PVSystem(GainComponent): snom_KVA: float = None solar_irradiance: TSParameter = None
[docs] @dataclass class TemplatePVSystem(PVSystem): """ Template for a PVSystem component. """ snom_MVA: Range = None inv_cost_per_kw: float = None
[docs] @dataclass class EconomicData(Component): """ Economic data for a hydrogen plant. """ inv_cost_per_kw: float = None
[docs] class PlantTemplate(Entity): """ Template for a hydrogen plant. """ @classmethod def _initialize_class(cls): cls.register_serializable_component("batteries", TemplateESS) cls.register_serializable_component("electrolizers", TemplateElectrolizer) cls.register_serializable_component("pv_systems", TemplatePVSystem) cls.register_serializable_parameter("name", str) cls.register_serializable_parameter("location", tuple) def __init__(self, name: str = None, batteries: Dict[int, TemplateESS]=None, electrolizers: Dict[int, TemplateElectrolizer]=None, pv_systems: Dict[int, TemplatePVSystem]=None, location: tuple = None ) -> None: super().__init__() self.name = name self.locations = location self.batteries = batteries if batteries is not None else {} self.electrolizers = electrolizers if electrolizers is not None else {} self.pv_systems = pv_systems if pv_systems is not None else {}
[docs] def tsnames(self) -> List[str]: """Return the time series names of the problem data. Returns: -------- - List[str], the time series names. """ tsnames = [] for pv_system in self.pv_systems.values(): tsnames.append(pv_system.solar_irradiance.tsname) return tsnames