def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.from_bus = kwargs.get('from_bus') self.to_bus = kwargs.get('to_bus') self.reactance = sequence(kwargs.get('reactance', 0.00001)) self.capacity = kwargs.get('capacity') self.capacity_cost = kwargs.get('capacity_cost') # oemof related attribute setting of 'Flow-object' self.input = self.from_bus self.output = self.to_bus self.bidirectional = True self.nominal_value = self.capacity self.min = sequence(-1) self.investment = self._investment()
def __init__(self, energysystem, **kwargs): super().__init__() # ######################## Arguments ################################# self.name = kwargs.get('name', type(self).__name__) self.es = energysystem try: self.timeincrement = sequence(self.es.timeindex.freq.nanos / 3.6e12) except AttributeError: logging.warning( 'Could not get timeincrement from pd.DateTimeIndex! ' + 'To avoid this warning, make sure the `freq` attribute of ' + 'your timeindex is not None. Setting timeincrement to 1...') self.timeincrement = sequence(1) self.objective_weighting = kwargs.get('objective_weighting', self.timeincrement) self._constraint_groups = (type(self).CONSTRAINT_GROUPS + kwargs.get('constraint_groups', [])) self._constraint_groups += [i for i in self.es.groups if hasattr(i, 'CONSTRAINT_GROUP') and i not in self._constraint_groups] self.flows = self.es.flows() if kwargs.get("auto_construct", True): self._construct()
def build_solph_components(self): """ """ if self.expandable: raise NotImplementedError( "Investment for solar thermal collector facade has not been implemented yet." ) inflow = Source( label=self.label + "-inflow", outputs={ self: Flow(nominal_value=self.aperture_area, max=self.collectors_heat) }, ) self.conversion_factors.update({ self.electricity_in_bus: sequence(self.electrical_consumption * (1 - self.peripheral_losses)), self.heat_out_bus: sequence(1 - self.peripheral_losses), inflow: sequence(1) }) self.inputs.update({self.electricity_in_bus: Flow()}) self.outputs.update({self.heat_out_bus: Flow()}) self.subnodes = (inflow, )
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs, _facade_requires_=['from_bus', 'to_bus']) self.capacity = kwargs.get('capacity') self.efficiency = kwargs.get('efficiency', 1) self.marginal_cost = kwargs.get('marginal_cost', 0) self.capacity_cost = kwargs.get('capacity_cost') self.input_edge_parameters = kwargs.get('input_edge_parameters', {}) self.output_edge_parameters = kwargs.get('output_edge_parameters', {}) self.conversion_factors.update({ self.from_bus: sequence(1), self.to_bus: sequence(self.efficiency) }) self.inputs.update({self.from_bus: Flow(**self.input_edge_parameters)}) self.outputs.update({ self.to_bus: Flow(nominal_value=self.capacity, variable_costs=self.marginal_cost, investment=self._investment(), **self.output_edge_parameters) })
def __init__(self, energysystem, **kwargs): super().__init__() # ######################## Arguments ################################# self.name = kwargs.get('name', type(self).__name__) self.es = energysystem self.timeincrement = sequence(kwargs.get('timeincrement', None)) if self.timeincrement[0] is None: try: self.timeincrement = sequence(self.es.timeindex.freq.nanos / 3.6e12) except AttributeError: msg = ("No valid time increment found. Please pass a valid " "timeincremet parameter or pass an EnergySystem with " "a valid time index. Please note that a valid time" "index need to have a 'freq' attribute.") raise AttributeError(msg) self.objective_weighting = kwargs.get('objective_weighting', self.timeincrement) self._constraint_groups = (type(self).CONSTRAINT_GROUPS + kwargs.get('constraint_groups', [])) self._constraint_groups += [ i for i in self.es.groups if hasattr(i, 'CONSTRAINT_GROUP') and i not in self._constraint_groups ] self.flows = self.es.flows() if kwargs.get("auto_construct", True): self._construct()
def build_solph_components(self): """ """ self.conversion_factors.update({ self.fuel_bus: sequence(1), self.electricity_bus: sequence(self.electric_efficiency), self.heat_bus: sequence(self.thermal_efficiency), }) self.inputs.update({ self.fuel_bus: Flow(variable_costs=self.carrier_cost, **self.input_parameters) }) self.outputs.update({ self.electricity_bus: Flow( nominal_value=self._nominal_value(), investment=self._investment(), ), self.heat_bus: Flow(), })
def build_solph_components(self): """ """ if self.expandable: raise NotImplementedError( "Investment for reservoir class is not implemented.") inflow = Source( label=self.label + "-inflow", outputs={ self: Flow(nominal_value=self.aperture_area, max=self.collectors_heat) }, ) self.conversion_factors.update({ self.electrical_bus: sequence(self.electrical_consumption * (1 - self.additional_losses)), self.heat_bus: sequence(1 - self.additional_losses), inflow: sequence(1) }) self.inputs.update({self.electrical_bus: Flow()}) self.outputs.update({self.heat_bus: Flow()}) self.subnodes = (inflow, )
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs, _facade_requires_=['from_bus', 'to_bus']) self.capacity = kwargs.get('capacity') self.loss = kwargs.get('loss', 0) self.capacity_cost = kwargs.get('capacity_cost') investment = self._investment() self.inputs.update({self.from_bus: Flow(), self.to_bus: Flow()}) self.outputs.update({ self.from_bus: Flow(nominal_value=self.capacity, investment=investment), self.to_bus: Flow(nominal_value=self.capacity, investment=investment) }) self.conversion_factors.update({ (self.from_bus, self.to_bus): sequence((1 - self.loss)), (self.to_bus, self.from_bus): sequence((1 - self.loss)) })
def __init__(self, **kwargs): # TODO: Check if we can inherit from pyomo.core.base.var _VarData # then we need to create the var object with # pyomo.core.base.IndexedVarWithDomain before any Flow is created. # E.g. create the variable in the energy system and populate with # information afterwards when creating objects. super().__init__() scalars = [ 'nominal_value', 'summed_max', 'summed_min', 'investment', 'nonconvex', 'integer', 'fixed' ] sequences = ['actual_value', 'variable_costs', 'min', 'max'] dictionaries = ['positive_gradient', 'negative_gradient'] defaults = { 'fixed': False, 'min': 0, 'max': 1, 'variable_costs': 0, 'positive_gradient': { 'ub': None, 'costs': 0 }, 'negative_gradient': { 'ub': None, 'costs': 0 }, } keys = [k for k in kwargs if k != 'label'] for attribute in set(scalars + sequences + dictionaries + keys): value = kwargs.get(attribute, defaults.get(attribute)) if attribute in dictionaries: setattr(self, attribute, { 'ub': sequence(value['ub']), 'costs': value['costs'] }) elif 'fixed_costs' in attribute: raise AttributeError( "The `fixed_costs` attribute has been removed" " with v0.2!") else: setattr(self, attribute, sequence(value) if attribute in sequences else value) # Checking for impossible attribute combinations if self.fixed and self.actual_value[0] is None: raise ValueError("Cannot fix flow value to None.\n Please " "set the actual_value attribute of the flow") if self.investment and self.nominal_value is not None: raise ValueError("Using the investment object the nominal_value" " has to be set to None.") if self.investment and self.nonconvex: raise ValueError("Investment flows cannot be combined with " + "nonconvex flows!")
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.conversion_factors = { k: sequence(v) for k, v in kwargs.get('conversion_factors', {}).items() } missing_conversion_factor_keys = ( (set(self.outputs) | set(self.inputs)) - set(self.conversion_factors)) for cf in missing_conversion_factor_keys: self.conversion_factors[cf] = sequence(1)
def __init__(self, *args, **kwargs): super().__init__(conversion_factor_full_condensation={}, *args, **kwargs, _facade_requires_=[ 'carrier', 'electricity_bus', 'heat_bus', 'thermal_efficiency', 'electric_efficiency', 'condensing_efficiency' ]) self.carrier = kwargs.get('carrier') self.carrier_cost = kwargs.get('carrier_cost', 0) self.capacity = kwargs.get('capacity') self.condensing_efficiency = sequence(self.condensing_efficiency) self.marginal_cost = kwargs.get('marginal_cost', 0) self.capacity_cost = kwargs.get('capacity_cost') self.electricity_bus = kwargs.get('electricity_bus') self.heat_bus = kwargs.get('heat_bus') self.conversion_factors.update({ self.carrier: sequence(1), self.electricity_bus: sequence(self.electric_efficiency), self.heat_bus: sequence(self.thermal_efficiency) }) self.inputs.update( {self.carrier: Flow(variable_cost=self.carrier_cost)}) self.outputs.update({ self.electricity_bus: Flow(nominal_value=self.capacity, variable_costs=self.marginal_cost, investment=self._investment()), self.heat_bus: Flow() }) self.conversion_factor_full_condensation.update( {self.electricity_bus: self.condensing_efficiency})
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.heat_loss_factor = sequence(kwargs.get('heat_loss_factor', 0)) self.heat_loss_factor_fix = sequence( kwargs.get('heat_loss_factor_fix', 0)) self._invest_group = False self._nonconvex_group = False self._demand_group = False if len(self.inputs) > 1 or len(self.outputs) > 2: if len(self.outputs) == 2: self._demand_group = True else: raise ValueError("Heatpipe must not have more than" " one input and two outputs!") for f in self.inputs.values(): if f.nonconvex is not None: raise ValueError( "Inputflow must not be of type NonConvexFlow!") for f in self.outputs.values(): if f.nonconvex is not None: self._nonconvex_group = True self._check_flows_invest() if (self._nonconvex_group is True) and (self._invest_group is True): raise ValueError( "Either an investment OR a switchable heatloss can be set" " (NonConvexFlow)." " Remove the NonConvexFlow or drop " "the Investment attribute.") if self._invest_group is True: self._set_flows() o = list(self.outputs.keys())[0] if (self.heat_loss_factor_fix[0] > 0) \ and (self.outputs[o].investment.nonconvex is False): warnings.warn( "Be careful! In case of a convex Investment " "(Investment.nonconvex is False), the " "'heat_loss_factor_fix' is considered, even though the " "investment might be zero! => A simple sink could be " "the results. Hopefully, you know what you are doing.") else: self._set_nominal_value()
def calculate_timeincrement(timeindex, fill_value=None): """ Calculates timeincrement for `timeindex` Parameters ---------- timeindex: pd.DatetimeIndex timeindex of energysystem fill_value: numerical timeincrement for first timestep in hours """ if isinstance(timeindex, pd.DatetimeIndex) and \ (fill_value and isinstance(fill_value, pd.Timedelta) or fill_value is None): if len(set(timeindex)) != len(timeindex): raise IndexError("No equal DatetimeIndex allowed!") timeindex = timeindex.to_series() timeindex_sorted = timeindex.sort_values() if fill_value: timeincrement = timeindex_sorted.diff().fillna(value=fill_value) else: timeincrement = timeindex_sorted.diff().fillna(method='bfill') timeincrement_sec = timeincrement.map(dt.timedelta.total_seconds) timeincrement_hourly = list(timeincrement_sec.map(lambda x: x / 3600)) timeincrement = sequence(timeincrement_hourly) return timeincrement else: raise AttributeError( "'timeindex' must be of type 'DatetimeIndex' and " + "'fill_value' of type 'Timedelta'.")
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.length = kwargs.get('length') self.heat_loss_factor = sequence(kwargs.get('heat_loss_factor')) self._invest_group = False if len(self.inputs) > 1 or len(self.outputs) > 1: raise ValueError("Heatpipe must not have more than \ one input and one output!") for f in self.inputs.values(): if f.nonconvex is not None: raise ValueError( "Attribute `nonconvex` of component `HeatPipeline`" + "has not been tested yet.") for f in self.outputs.values(): if f.nonconvex is not None: raise ValueError( "Attribute `nonconvex` of component `HeatPipeline`" + "has not been tested yet.") self._check_flows()
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.reactance = sequence(kwargs.get('reactance', 0.00001)) if len(self.inputs) > 1 or len(self.outputs) > 1: raise ValueError("Component ElectricLine must not have more than \ one input and one output!") self.input = self._input() self.output = self._output() # set input / output flow values to -1 by default if not set by user for f in self.inputs.values(): if f.nonconvex is not None: raise ValueError("Attribute `nonconvex` must be None for" + " inflows of component `ElectricalLine`!") if f.min is None: f.min = -1 # to be used in grouping for all bidi flows f.bidirectional = True for f in self.outputs.values(): if f.nonconvex is not None: raise ValueError("Attribute `nonconvex` must be None for" + " outflows of component `ElectricalLine`!") if f.min is None: f.min = -1 # to be used in grouping for all bidi flows f.bidirectional = True
def build_solph_components(self): """ """ self.nominal_storage_capacity = self.storage_capacity self.inflow_conversion_factor = sequence(self.efficiency) self.outflow_conversion_factor = sequence(self.efficiency) # make it investment but don't set costs (set below for flow (power)) self.investment = self._investment() if self.investment: self.invest_relation_input_output = 1 for attr in ["invest_relation_input_output"]: if getattr(self, attr) is None: raise AttributeError( ("You need to set attr " "`{}` " "for component {}").format(attr, self.label)) # set capacity costs at one of the flows fi = Flow(investment=Investment( ep_costs=self.capacity_cost, maximum=self.capacity_potential, existing=self.capacity, ), **self.input_parameters) # set investment, but no costs (as relation input / output = 1) fo = Flow(investment=Investment(), variable_costs=self.marginal_cost, **self.output_parameters) # required for correct grouping in oemof.solph.components self._invest_group = True else: fi = Flow(nominal_value=self._nominal_value(), **self.input_parameters) fo = Flow(nominal_value=self._nominal_value(), variable_costs=self.marginal_cost, **self.output_parameters) self.inputs.update({self.bus: fi}) self.outputs.update({self.bus: fo}) self._set_flows()
def build_solph_components(self): """ """ self.conversion_factors.update({ self.from_bus: sequence(1), self.to_bus: sequence(self.efficiency), }) self.inputs.update({self.from_bus: Flow(**self.input_parameters)}) self.outputs.update({ self.to_bus: Flow(nominal_value=self._nominal_value(), variable_costs=self.marginal_cost, investment=self._investment(), **self.output_parameters) })
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if len(self.inputs) > 2 or len(self.outputs) > 2: raise ValueError("Component `Link` must not have more than \ 2 inputs and 2 outputs!") self.conversion_factors = { k: sequence(v) for k, v in kwargs.get('conversion_factors', {}).items()}
def __init__(self, **kwargs): scalars = ['minimum_uptime', 'minimum_downtime', 'initial_status'] sequences = ['startup_costs', 'shutdown_costs'] defaults = {'initial_status': 0} for attribute in set(scalars + sequences + list(kwargs)): value = kwargs.get(attribute, defaults.get(attribute)) setattr(self, attribute, sequence(value) if attribute in sequences else value) self._max_up_down = None
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not self.inputs: msg = "`Transformer` '{0}' constructed without `inputs`." warn(msg.format(self), debugging.SuspiciousUsageWarning) if not self.outputs: msg = "`Transformer` '{0}' constructed without `outputs`." warn(msg.format(self), debugging.SuspiciousUsageWarning) self.conversion_factors = { k: sequence(v) for k, v in kwargs.get('conversion_factors', {}).items() } missing_conversion_factor_keys = ( (set(self.outputs) | set(self.inputs)) - set(self.conversion_factors)) for cf in missing_conversion_factor_keys: self.conversion_factors[cf] = sequence(1)
def constraint_optimization_against_two_values(om: solph.Model, limit: float) -> solph.Model: """ Function for optimization against two parameters (e.g. monetary, emissions) :param om: oemof solph model to which the constraints will be \ added :type om: oemof.solph.Model :param limit: maximum value for the second parameter for the \ whole energysystem :type limit: int :return: - **om** (oemof.solph.Model) - oemof solph Model within \ the constraints """ import pyomo.environ as po from oemof.solph.plumbing import sequence invest_flows = {} for (i, o) in om.flows: if hasattr(om.flows[i, o].investment, "periodical_constraint_costs"): invest_flows[(i, o)] = om.flows[i, o].investment limit_name = "invest_limit_" + "space" setattr(om, limit_name, po.Expression( expr=sum(om.InvestmentFlow.invest[inflow, outflow] * getattr(invest_flows[inflow, outflow], "periodical_constraint_costs") for (inflow, outflow) in invest_flows ))) ############ flows = {} for (i, o) in om.flows: if hasattr(om.flows[i, o], "emission_factor"): flows[(i, o)] = om.flows[i, o] limit_name1 = "integral_limit_" + "emission_factor" setattr(om, limit_name1, po.Expression( expr=sum(om.flow[inflow, outflow, t] * om.timeincrement[t] * sequence(getattr(flows[inflow, outflow], "emission_factor"))[t] for (inflow, outflow) in flows for t in om.TIMESTEPS))) setattr(om, limit_name + "_constraint", po.Constraint( expr=((getattr(om, limit_name) + getattr(om, limit_name1)) <= limit))) return om
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.reactance = sequence(kwargs.get('reactance', 0.00001)) # set input / output flow values to -1 by default if not set by user if self.nonconvex is not None: raise ValueError( ("Attribute `nonconvex` must be None for " + "component `ElectricalLine` from {} to {}!").format( self.input, self.output)) if self.min is None: self.min = -1 # to be used in grouping for all bidi flows self.bidirectional = True
def build_solph_components(self): """ """ investment = self._investment() self.inputs.update({self.from_bus: Flow(), self.to_bus: Flow()}) self.outputs.update({ self.from_bus: Flow( variable_costs=self.marginal_cost, nominal_value=self._nominal_value(), investment=investment, ), self.to_bus: Flow(nominal_value=self._nominal_value(), investment=investment), }) self.conversion_factors.update({ (self.from_bus, self.to_bus): sequence((1 - self.loss)), (self.to_bus, self.from_bus): sequence((1 - self.loss)), })
def __init__(self, *args, **kwargs): kwargs.update({"_facade_requires_": ["bus", "carrier", "tech"]}) super().__init__(*args, **kwargs) self.profile = kwargs.get("profile", sequence(1)) self.capacity = kwargs.get("capacity") self.capacity_potential = kwargs.get("capacity_potential") self.marginal_cost = kwargs.get("marginal_cost", 0) self.capacity_cost = kwargs.get("capacity_cost") self.expandable = bool(kwargs.get("expandable", False)) self.output_parameters = kwargs.get("output_parameters", {}) self.build_solph_components()
def __init__(self, *args, **kwargs): kwargs.update({ "_facade_requires_": [ "fuel_bus", "carrier", "tech", "electricity_bus", "heat_bus", "thermal_efficiency", "electric_efficiency", "condensing_efficiency", ] }) super().__init__(conversion_factor_full_condensation={}, *args, **kwargs) self.fuel_bus = kwargs.get("fuel_bus") self.electricity_bus = kwargs.get("electricity_bus") self.heat_bus = kwargs.get("heat_bus") self.carrier = kwargs.get("carrier") self.carrier_cost = kwargs.get("carrier_cost", 0) self.capacity = kwargs.get("capacity") self.condensing_efficiency = sequence(self.condensing_efficiency) self.marginal_cost = kwargs.get("marginal_cost", 0) self.capacity_cost = kwargs.get("capacity_cost") self.expandable = bool(kwargs.get("expandable", False)) self.input_parameters = kwargs.get("input_parameters", {}) self.build_solph_components()
def __init__(self, energysystem, **kwargs): super().__init__() # ######################## Arguments ################################# self.name = kwargs.get('name', type(self).__name__) self.es = energysystem self.timeincrement = sequence(self.es.timeindex.freq.nanos / 3.6e12) self._constraint_groups = (type(self).CONSTRAINT_GROUPS + kwargs.get('constraint_groups', [])) self._constraint_groups += [ i for i in self.es.groups if hasattr(i, 'CONSTRAINT_GROUP') and i not in self._constraint_groups ] self.flows = self.es.flows() if kwargs.get("auto_construct", True): self._construct()
def build_solph_components(self): """ """ self.nominal_storage_capacity = self.storage_capacity self.outflow_conversion_factor = sequence(self.efficiency) if self.expandable: raise NotImplementedError( "Investment for reservoir class is not implemented.") inflow = Source( label=self.label + "-inflow", outputs={ self: Flow(nominal_value=1, max=self.profile, fixed=False) }, ) self.outputs.update({ self.bus: Flow(nominal_value=self.capacity, **self.output_parameters) }) self.subnodes = (inflow, )
def build_solph_components(self): """ """ self.inflow_conversion_factor = sequence(self.efficiency) self.outflow_conversion_factor = sequence(self.efficiency) self.loss_rate = sequence(self.loss_rate) self.fixed_losses_relative = sequence(self.fixed_losses_relative) self.fixed_losses_absolute = sequence(self.fixed_losses_absolute) # make it investment but don't set costs (set below for flow (power)) self.investment = self._investment() if self.investment: self.invest_relation_input_output = 1 for attr in ["invest_relation_input_output"]: if getattr(self, attr) is None: raise AttributeError( ("You need to set attr " "`{}` " "for component {}").format(attr, self.label)) # set capacity costs at one of the flows fi = Flow(investment=Investment( ep_costs=self.capacity_cost, maximum=self.capacity_potential, existing=self.capacity, ), **self.input_parameters) # set investment, but no costs (as relation input / output = 1) fo = Flow(investment=Investment(), variable_costs=self.marginal_cost, **self.output_parameters) # required for correct grouping in oemof.solph.components self._invest_group = True else: self.volume = calculate_storage_dimensions(self.height, self.diameter)[0] self.nominal_storage_capacity = calculate_capacities( self.volume, self.temp_h, self.temp_c, **{ key: value for key, value in self.water_properties.items() if value is not None }) fi = Flow(nominal_value=self._nominal_value(), **self.input_parameters) fo = Flow(nominal_value=self._nominal_value(), variable_costs=self.marginal_cost, **self.output_parameters) self.inputs.update({self.bus: fi}) self.outputs.update({self.bus: fo}) self._set_flows()
def generic_integral_limit(om, keyword, flows=None, limit=None): """Set a global limit for flows weighted by attribute called keyword. The attribute named by keyword has to be added to every flow you want to take into account. Total value of keyword attributes after optimization can be retrieved calling the :attr:`om.oemof.solph.Model.integral_limit_${keyword}()`. Parameters ---------- om : oemof.solph.Model Model to which constraints are added. flows : dict Dictionary holding the flows that should be considered in constraint. Keys are (source, target) objects of the Flow. If no dictionary is given all flows containing the keyword attribute will be used. keyword : attribute to consider limit : numeric Absolute limit of keyword attribute for the energy system. Note ---- Flow objects required an attribute named like keyword! **Constraint:** .. math:: \sum_{i \in F_E} \sum_{t \in T} P_i(t) \cdot w_i(t) \cdot \tau(t) \leq M With `F_I` being the set of flows considered for the integral limit and `T` being the set of time steps. The symbols used are defined as follows (with Variables (V) and Parameters (P)): ================ ==== ===================================================== math. symbol type explanation ================ ==== ===================================================== :math:`P_n(t)` V power flow :math:`n` at time step :math:`t` :math:`w_N(t)` P weight given to Flow named according to `keyword` :math:`\tau(t)` P width of time step :math:`t` :math:`L` P global limit given by keyword `limit` """ if flows is None: flows = {} for (i, o) in om.flows: if hasattr(om.flows[i, o], keyword): flows[(i, o)] = om.flows[i, o] else: for (i, o) in flows: if not hasattr(flows[i, o], keyword): raise AttributeError( ('Flow with source: {0} and target: {1} ' 'has no attribute {2}.').format(i.label, o.label, keyword)) limit_name = "integral_limit_" + keyword setattr( om, limit_name, po.Expression(expr=sum( om.flow[inflow, outflow, t] * om.timeincrement[t] * sequence(getattr(flows[inflow, outflow], keyword))[t] for (inflow, outflow) in flows for t in om.TIMESTEPS))) setattr(om, limit_name + "_constraint", po.Constraint(expr=(getattr(om, limit_name) <= limit))) return om
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs, _facade_requires_=['bus']) self.storage_capacity = kwargs.get('storage_capacity') self.capacity = kwargs.get('capacity') self.nominal_capacity = self.storage_capacity self.capacity_cost = kwargs.get('capacity_cost') self.loss = sequence(kwargs.get('loss', 0)) self.inflow_conversion_factor = sequence(kwargs.get('efficiency', 1)) self.outflow_conversion_factor = sequence(kwargs.get('efficiency', 1)) # make it investment but don't set costs (set below for flow (power)) self.investment = self._investment() self.input_edge_parameters = kwargs.get('input_edge_parameters', {}) self.output_edge_parameters = kwargs.get('output_edge_parameters', {}) if self.investment: if kwargs.get('capacity_ratio') is None: raise AttributeError( ("You need to set attr `capacity_ratio` for " "component {}").format(self.label)) else: self.invest_relation_input_capacity = kwargs.get( 'capacity_ratio') self.invest_relation_output_capacity = kwargs.get( 'capacity_ratio') self.invest_relation_input_output = 1 # set capacity costs at one of the flows fi = Flow(investment=Investment(ep_costs=self.capacity_cost, maximum=getattr( self, 'capacity_potential', float('+inf'))), **self.input_edge_parameters) # set investment, but no costs (as relation input / output = 1) fo = Flow(investment=Investment(), **self.output_edge_parameters) # required for correct grouping in oemof.solph.components self._invest_group = True else: investment = None fi = Flow(nominal_value=self.capacity, **self.input_edge_parameters) fo = Flow(nominal_value=self.capacity, **self.output_edge_parameters) self.inputs.update({self.bus: fi}) self.outputs.update({self.bus: fo}) # annoying oemof stuff, that requires _set_flows() to provide a # drepreciated warning self._set_flows('Ignore this warning...')