def build(self): """Build the model. Args: None Returns: None """ # Setup model build logger model_log = idaeslog.getModelLogger(self.name, tag="unit") # Call UnitModel.build to setup dynamics super(CondenserData, self).build() # Check config arguments if self.config.temperature_spec is None: raise ConfigurationError("temperature_spec config argument " "has not been specified. Please select " "a valid option.") if (self.config.condenser_type == CondenserType.partialCondenser) and \ (self.config.temperature_spec == TemperatureSpec.atBubblePoint): raise ConfigurationError("condenser_type set to partial but " "temperature_spec set to atBubblePoint. " "Select customTemperature and specify " "outlet temperature.") # Add Control Volume for the condenser self.control_volume = ControlVolume0DBlock( default={ "dynamic": self.config.dynamic, "has_holdup": self.config.has_holdup, "property_package": self.config.property_package, "property_package_args": self.config.property_package_args }) self.control_volume.add_state_blocks(has_phase_equilibrium=True) self.control_volume.add_material_balances( balance_type=self.config.material_balance_type, has_phase_equilibrium=True) self.control_volume.add_energy_balances( balance_type=self.config.energy_balance_type, has_heat_transfer=True) self.control_volume.add_momentum_balances( balance_type=self.config.momentum_balance_type, has_pressure_change=self.config.has_pressure_change) # Get liquid and vapor phase objects from the property package # to be used below. Avoids repition. _liquid_list = [] _vapor_list = [] for p in self.config.property_package.phase_list: pobj = self.config.property_package.get_phase(p) if pobj.is_vapor_phase(): _vapor_list.append(p) elif pobj.is_liquid_phase(): _liquid_list.append(p) else: _liquid_list.append(p) model_log.warning( "A non-liquid/non-vapor phase was detected but will " "be treated as a liquid.") # Create a pyomo set for indexing purposes. This set is appended to # model otherwise results in an abstract set. self._liquid_set = Set(initialize=_liquid_list) self._vapor_set = Set(initialize=_vapor_list) self._make_ports() if self.config.condenser_type == CondenserType.totalCondenser: self._make_splits_total_condenser() if (self.config.temperature_spec == TemperatureSpec.atBubblePoint): # Option 1: if true, condition for total condenser # (T_cond = T_bubble) # Option 2: if this is false, then user has selected # custom temperature spec and needs to fix an outlet # temperature. def rule_total_cond(self, t): return self.control_volume.properties_out[t].\ temperature == self.control_volume.properties_out[t].\ temperature_bubble self.eq_total_cond_spec = Constraint(self.flowsheet().time, rule=rule_total_cond) else: self._make_splits_partial_condenser() # Add object reference to variables of the control volume # Reference to the heat duty add_object_reference(self, "heat_duty", self.control_volume.heat) # Reference to the pressure drop (if set to True) if self.config.has_pressure_change: add_object_reference(self, "deltaP", self.control_volume.deltaP)
def _create(self, group=None): """ """ m = self.parent_block() if group is None: return None # ########################## SETS ##################################### self.INVESTSTORAGES = Set(initialize=[n for n in group]) self.INVEST_REL_CAP_IN = Set(initialize=[ n for n in group if n.invest_relation_input_capacity is not None ]) self.INVEST_REL_CAP_OUT = Set(initialize=[ n for n in group if n.invest_relation_output_capacity is not None ]) self.INVEST_REL_IN_OUT = Set(initialize=[ n for n in group if n.invest_relation_input_output is not None ]) self.INITIAL_CAPACITY = Set( initialize=[n for n in group if n.initial_capacity is not None]) # The capacity is set as a non-negative variable, therefore it makes no # sense to create an additional constraint if the lower bound is zero # for all time steps. self.MIN_INVESTSTORAGES = Set(initialize=[ n for n in group if sum([n.capacity_min[t] for t in m.TIMESTEPS]) > 0 ]) # ######################### Variables ################################ self.capacity = Var(self.INVESTSTORAGES, m.TIMESTEPS, within=NonNegativeReals) def _storage_investvar_bound_rule(block, n): """Rule definition to bound the invested storage capacity `invest`. """ return 0, n.investment.maximum self.invest = Var(self.INVESTSTORAGES, within=NonNegativeReals, bounds=_storage_investvar_bound_rule) # ######################### CONSTRAINTS ############################### i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} def _storage_balance_rule(block, n, t): """Rule definition for the storage energy balance. """ expr = 0 expr += block.capacity[n, t] expr += -block.capacity[n, m.previous_timesteps[t]] * ( 1 - n.capacity_loss[t]) expr += (-m.flow[i[n], n, t] * n.inflow_conversion_factor[t]) * m.timeincrement[t] expr += (m.flow[n, o[n], t] / n.outflow_conversion_factor[t]) * m.timeincrement[t] return expr == 0 self.balance = Constraint(self.INVESTSTORAGES, m.TIMESTEPS, rule=_storage_balance_rule) def _initial_capacity_invest_rule(block, n): """Rule definition for constraint to connect initial storage capacity with capacity of last timesteps. """ expr = (self.capacity[n, m.TIMESTEPS[-1]] == (n.investment.existing + self.invest[n]) * n.initial_capacity) return expr self.initial_capacity = Constraint(self.INITIAL_CAPACITY, rule=_initial_capacity_invest_rule) def _power_coupled(block, n): """Rule definition for constraint to connect the input power and output power """ expr = ((m.InvestmentFlow.invest[n, o[n]] + m.flows[n, o[n]].investment.existing) * n.invest_relation_input_output == ( m.InvestmentFlow.invest[i[n], n] + m.flows[i[n], n].investment.existing)) return expr self.power_coupled = Constraint(self.INVEST_REL_IN_OUT, rule=_power_coupled) def _storage_capacity_inflow_invest_rule(block, n): """Rule definition of constraint connecting the inflow `InvestmentFlow.invest of storage with invested capacity `invest` by nominal_capacity__inflow_ratio """ expr = ((m.InvestmentFlow.invest[i[n], n] + m.flows[i[n], n].investment.existing ) == (n.investment.existing + self.invest[n]) * n.invest_relation_input_capacity) return expr self.storage_capacity_inflow = Constraint( self.INVEST_REL_CAP_IN, rule=_storage_capacity_inflow_invest_rule) def _storage_capacity_outflow_invest_rule(block, n): """Rule definition of constraint connecting outflow `InvestmentFlow.invest` of storage and invested capacity `invest` by nominal_capacity__outflow_ratio """ expr = ((m.InvestmentFlow.invest[n, o[n]] + m.flows[n, o[n]].investment.existing ) == (n.investment.existing + self.invest[n]) * n.invest_relation_output_capacity) return expr self.storage_capacity_outflow = Constraint( self.INVEST_REL_CAP_OUT, rule=_storage_capacity_outflow_invest_rule) def _max_capacity_invest_rule(block, n, t): """Rule definition for upper bound constraint for the storage cap. """ expr = ( self.capacity[n, t] <= (n.investment.existing + self.invest[n]) * n.capacity_max[t]) return expr self.max_capacity = Constraint(self.INVESTSTORAGES, m.TIMESTEPS, rule=_max_capacity_invest_rule) def _min_capacity_invest_rule(block, n, t): """Rule definition of lower bound constraint for the storage cap. """ expr = ( self.capacity[n, t] >= (n.investment.existing + self.invest[n]) * n.capacity_min[t]) return expr # Set the lower bound of the storage capacity if the attribute exists self.min_capacity = Constraint(self.MIN_INVESTSTORAGES, m.TIMESTEPS, rule=_min_capacity_invest_rule)
def add_model_components(m, d, scenario_directory, subproblem, stage): """ Before adding any components, this module will go through each relevant capacity type and add the module components for that capacity type. Then the following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`TX_OPR_PRDS` | | | | Two-dimensional set of the transmission lines and their operational | | periods (capacity exists and is available). | +-------------------------------------------------------------------------+ | | :code:`TX_LINES_OPR_IN_PRD` | | | *Defined over*: :code:`PERIODS` | | | | Indexed set of transmission lines operational in each period. | +-------------------------------------------------------------------------+ | | :code:`OPR_PRDS_BY_TX_LINE` | | | *Defined over*: :code:`TX_LINES` | | | | Indexed set of operational period for each transmission line. | +-------------------------------------------------------------------------+ | | :code:`TX_OPR_TMPS` | | | | Two-dimensional set of the transmission lines and their operational | | timepoints, derived from :code:`TX_OPR_PRDS` and the timepoitns in each | | period. | +-------------------------------------------------------------------------+ | | :code:`TX_LINES_OPR_IN_TMP` | | | *Defined over*: :code:`TIMEPOINTS` | | | | Indexed set of transmission lines operatoinal in each timepoint. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Expressions | +=========================================================================+ | | :code:`Tx_Min_Capacity_MW` | | | *Defined over*: :code:`TX_OPR_PRDS` | | | | The transmission line's minimum flow in MW (negative number indicates | | flow in the opposite direction of the line's defined flow direction). | | Depending on the capacity type, this can be a pre-specified amount or | | a decision variable (with an associated cost). | +-------------------------------------------------------------------------+ | | :code:`Tx_Max_Capacity_MW` | | | *Defined over*: :code:`TX_OPR_PRDS` | | | | The transmission line's maximum flow in MW (negative number indicates | | flow in the opposite direction of the line's defined flow direction). | | Depending on the capacity type, this can be a pre-specified amount or | | a decision variable (with an associated cost). | +-------------------------------------------------------------------------+ | | :code:`Tx_Capacity_Cost_in_Prd` | | | *Defined over*: :code:`TX_OPR_PRDS` | | | | The cost to have the transmission capacity available in the period. | | Depending on the capacity type, this could be zero. | | If the subproblem is less than a full year (e.g. in production- | | cost mode with 365 daily subproblems), the costs are scaled down | | proportionally. | +-------------------------------------------------------------------------+ | | :code:`Total_Tx_Capacity_Costs` | | | | The total cost of the system's transmission capacity across all periods.| +-------------------------------------------------------------------------+ """ # Dynamic Inputs ########################################################################### df = pd.read_csv(os.path.join(scenario_directory, str(subproblem), str(stage), "inputs", "transmission_lines.tab"), sep="\t", usecols=[ "TRANSMISSION_LINES", "tx_capacity_type", "tx_operational_type" ]) # Required capacity modules are the unique set of tx capacity types # This list will be used to know which capacity modules to load required_tx_capacity_modules = df.tx_capacity_type.unique() # Import needed transmission capacity type modules for expression rules imported_tx_capacity_modules = load_tx_capacity_type_modules( required_tx_capacity_modules) # Add model components for each of the transmission capacity modules for op_m in required_tx_capacity_modules: imp_op_m = imported_tx_capacity_modules[op_m] if hasattr(imp_op_m, "add_model_components"): imp_op_m.add_model_components(m, d, scenario_directory, subproblem, stage) # Sets ########################################################################### m.TX_OPR_PRDS = Set( dimen=2, within=m.TX_LINES * m.PERIODS, initialize=lambda mod: join_sets( mod, getattr(d, tx_capacity_type_operational_period_sets), ), ) # assumes capacity types model components are already added! m.TX_LINES_OPR_IN_PRD = Set(m.PERIODS, initialize=lambda mod, period: list( set(tx for (tx, p) in mod.TX_OPR_PRDS if p == period))) m.OPR_PRDS_BY_TX_LINE = Set(m.TX_LINES, initialize=lambda mod, tx: list( set(p for (l, p) in mod.TX_OPR_PRDS if l == tx))) m.TX_OPR_TMPS = Set( dimen=2, initialize=lambda mod: [(tx, tmp) for tx in mod.TX_LINES for p in mod.OPR_PRDS_BY_TX_LINE[tx] for tmp in mod.TMPS_IN_PRD[p]]) m.TX_LINES_OPR_IN_TMP = Set(m.TMPS, initialize=lambda mod, tmp: list( set(tx for (tx, t) in mod.TX_OPR_TMPS if t == tmp))) # Expressions ########################################################################### def tx_min_capacity_rule(mod, tx, p): tx_cap_type = mod.tx_capacity_type[tx] return imported_tx_capacity_modules[tx_cap_type]. \ min_transmission_capacity_rule(mod, tx, p) def tx_max_capacity_rule(mod, tx, p): tx_cap_type = mod.tx_capacity_type[tx] return imported_tx_capacity_modules[tx_cap_type]. \ max_transmission_capacity_rule(mod, tx, p) def tx_capacity_cost_rule(mod, tx, p): tx_cap_type = mod.tx_capacity_type[tx] return imported_tx_capacity_modules[tx_cap_type].\ tx_capacity_cost_rule(mod, tx, p) \ * mod.hours_in_subproblem_period[p] \ / mod.hours_in_full_period[p] m.Tx_Min_Capacity_MW = Expression(m.TX_OPR_PRDS, rule=tx_min_capacity_rule) m.Tx_Max_Capacity_MW = Expression(m.TX_OPR_PRDS, rule=tx_max_capacity_rule) m.Tx_Capacity_Cost_in_Prd = Expression(m.TX_OPR_PRDS, rule=tx_capacity_cost_rule)
def build(self): super(TurbineMultistageData, self).build() config = self.config unit_cfg = { # general unit model config "dynamic": config.dynamic, "has_holdup": config.has_holdup, "has_phase_equilibrium": config.has_phase_equilibrium, "material_balance_type": config.material_balance_type, "property_package": config.property_package, "property_package_args": config.property_package_args, } ni = self.config.num_parallel_inlet_stages inlet_idx = self.inlet_stage_idx = RangeSet(ni) # Adding unit models # ------------------------ # Splitter to inlet that splits main flow into parallel flows for # paritial arc admission to the turbine self.inlet_split = Separator(default=self._split_cfg(unit_cfg, ni)) self.throttle_valve = SteamValve(inlet_idx, default=unit_cfg) self.inlet_stage = TurbineInletStage(inlet_idx, default=unit_cfg) # mixer to combine the parallel flows back together self.inlet_mix = Mixer(default=self._mix_cfg(unit_cfg, ni)) # add turbine sections. # inlet stage -> hp stages -> ip stages -> lp stages -> outlet stage self.hp_stages = TurbineStage(RangeSet(config.num_hp), default=unit_cfg) self.ip_stages = TurbineStage(RangeSet(config.num_ip), default=unit_cfg) self.lp_stages = TurbineStage(RangeSet(config.num_lp), default=unit_cfg) self.outlet_stage = TurbineOutletStage(default=unit_cfg) for i in self.hp_stages: self.hp_stages[i].ratioP.fix() self.hp_stages[i].efficiency_isentropic[:].fix() for i in self.ip_stages: self.ip_stages[i].ratioP.fix() self.ip_stages[i].efficiency_isentropic[:].fix() for i in self.lp_stages: self.lp_stages[i].ratioP.fix() self.lp_stages[i].efficiency_isentropic[:].fix() # Then make splitter config. If number of outlets is specified # make a specific config, otherwise use default with 2 outlets s_sfg_default = self._split_cfg(unit_cfg, 2) hp_splt_cfg = {} ip_splt_cfg = {} lp_splt_cfg = {} # Now to finish up if there are more than two outlets, set that for i, v in config.hp_split_num_outlets.items(): hp_splt_cfg[i] = self._split_cfg(unit_cfg, v) for i, v in config.ip_split_num_outlets.items(): ip_splt_cfg[i] = self._split_cfg(unit_cfg, v) for i, v in config.lp_split_num_outlets.items(): lp_splt_cfg[i] = self._split_cfg(unit_cfg, v) # put in splitters for turbine steam extractions if config.hp_split_locations: self.hp_split = Separator( config.hp_split_locations, default=s_sfg_default, initialize=hp_splt_cfg ) if config.ip_split_locations: self.ip_split = Separator( config.ip_split_locations, default=s_sfg_default, initialize=ip_splt_cfg ) if config.lp_split_locations: self.lp_split = Separator( config.lp_split_locations, default=s_sfg_default, initialize=lp_splt_cfg ) # Done with unit models. Adding Arcs (streams). # ------------------------------------------------ # First up add streams in the inlet section def _split_to_rule(b, i): return { "source": getattr(self.inlet_split, "outlet_{}".format(i)), "destination": self.throttle_valve[i].inlet, } def _valve_to_rule(b, i): return { "source": self.throttle_valve[i].outlet, "destination": self.inlet_stage[i].inlet, } def _inlet_to_rule(b, i): return { "source": self.inlet_stage[i].outlet, "destination": getattr(self.inlet_mix, "inlet_{}".format(i)), } self.split_to_valve_stream = Arc(inlet_idx, rule=_split_to_rule) self.valve_to_inlet_stage_stream = Arc(inlet_idx, rule=_valve_to_rule) self.inlet_stage_to_mix = Arc(inlet_idx, rule=_inlet_to_rule) # There are three sections HP, IP, and LP which all have the same sort # of internal connctions, so the functions below provide some generic # capcbilities for adding the internal Arcs (streams). def _arc_indexes(nstages, index_set, discon, splits): """ This takes the index set of all possible streams in a turbine section and throws out arc indexes for stages that are disconnected and arc indexes that are not needed because there is no splitter after a stage. Args: nstages (int): Number of stages in section index_set (Set): Index set for arcs in the section discon (list): Disconnected stages in the section splits (list): Spliter locations """ sr = set() # set of things to remove from the Arc index set for i in index_set: if (i[0] in discon or i[0] == nstages) and i[0] in splits: # don't connect stage i to next remove stream after split sr.add((i[0], 2)) elif (i[0] in discon or i[0] == nstages) and i[0] not in splits: # no splitter and disconnect so remove both streams sr.add((i[0], 1)) sr.add((i[0], 2)) elif i[0] not in splits: # no splitter and not disconnected so just second stream sr.add((i[0], 2)) else: # has splitter so need both streams don't remove anything pass for i in sr: # remove the unneeded Arc indexes index_set.remove(i) def _arc_rule(turbines, splitters): """ This creates a rule function for arcs in a turbine section. When this is used the indexes for nonexistant stream will have already been removed, so any indexes the rule will get should have a stream associated. Args: turbines (TurbineStage): Indexed block with turbine section stages splitters (Separator): Indexed block of splitters """ def _rule(b, i, j): if i in splitters and j == 1: return { "source": turbines[i].outlet, "destination": splitters[i].inlet, } elif j == 2: return { "source": splitters[i].outlet_1, "destination": turbines[i + 1].inlet, } else: return { "source": turbines[i].outlet, "destination": turbines[i + 1].inlet, } return _rule # Create initial arcs index sets with all possible streams self.hp_stream_idx = Set(initialize=self.hp_stages.index_set() * [1, 2]) self.ip_stream_idx = Set(initialize=self.ip_stages.index_set() * [1, 2]) self.lp_stream_idx = Set(initialize=self.lp_stages.index_set() * [1, 2]) # Throw out unneeded streams _arc_indexes( config.num_hp, self.hp_stream_idx, config.hp_disconnect, config.hp_split_locations, ) _arc_indexes( config.num_ip, self.ip_stream_idx, config.ip_disconnect, config.ip_split_locations, ) _arc_indexes( config.num_lp, self.lp_stream_idx, config.lp_disconnect, config.lp_split_locations, ) # Create connections internal to each turbine section (hp, ip, and lp) self.hp_stream = Arc( self.hp_stream_idx, rule=_arc_rule(self.hp_stages, self.hp_split) ) self.ip_stream = Arc( self.ip_stream_idx, rule=_arc_rule(self.ip_stages, self.ip_split) ) self.lp_stream = Arc( self.lp_stream_idx, rule=_arc_rule(self.lp_stages, self.lp_split) ) # Connect hp section to ip section unless its a disconnect location last_hp = config.num_hp if 0 not in config.ip_disconnect and last_hp not in config.hp_disconnect: if last_hp in config.hp_split_locations: # connect splitter to ip self.hp_to_ip_stream = Arc( source=self.hp_split[last_hp].outlet_1, destination=self.ip_stages[1].inlet, ) else: # connect last hp to ip self.hp_to_ip_stream = Arc( source=self.hp_stages[last_hp].outlet, destination=self.ip_stages[1].inlet, ) # Connect ip section to lp section unless its a disconnect location last_ip = config.num_ip if 0 not in config.lp_disconnect and last_ip not in config.ip_disconnect: if last_ip in config.ip_split_locations: # connect splitter to ip self.ip_to_lp_stream = Arc( source=self.ip_split[last_ip].outlet_1, destination=self.lp_stages[1].inlet, ) else: # connect last hp to ip self.ip_to_lp_stream = Arc( source=self.ip_stages[last_ip].outlet, destination=self.lp_stages[1].inlet, ) # Connect inlet stage to hp section # not allowing disconnection of inlet and first regular hp stage if 0 in config.hp_split_locations: # connect inlet mix to splitter and splitter to hp section self.inlet_to_splitter_stream = Arc( source=self.inlet_mix.outlet, destination=self.hp_split[0].inlet ) self.splitter_to_hp_stream = Arc( source=self.hp_split[0].outlet_1, destination=self.hp_stages[1].inlet ) else: # connect mixer to first hp turbine stage self.inlet_to_hp_stream = Arc( source=self.inlet_mix.outlet, destination=self.hp_stages[1].inlet ) @self.Expression(self.flowsheet().config.time) def power(b, t): return ( sum(b.inlet_stage[i].power_shaft[t] for i in b.inlet_stage) + b.outlet_stage.power_shaft[t] + sum(b.hp_stages[i].power_shaft[t] for i in b.hp_stages) + sum(b.ip_stages[i].power_shaft[t] for i in b.ip_stages) + sum(b.lp_stages[i].power_shaft[t] for i in b.lp_stages) ) # Connect inlet stage to hp section # not allowing disconnection of inlet and first regular hp stage last_lp = config.num_lp if last_lp in config.lp_split_locations: # connect splitter to outlet self.lp_to_outlet_stream = Arc( source=self.lp_split[last_lp].outlet_1, destination=self.outlet_stage.inlet, ) else: # connect last lpstage to outlet self.lp_to_outlet_stream = Arc( source=self.lp_stages[last_lp].outlet, destination=self.outlet_stage.inlet, ) TransformationFactory("network.expand_arcs").apply_to(self)
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`GEN_HYDRO_MUST_TAKE` | | | | The set of generators of the :code:`gen_hydro_must_take` operational | | type. | +-------------------------------------------------------------------------+ | | :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | | Two-dimensional set with generators of the :code:`gen_hydro_must_take` | | operational type and their operational horizons. | +-------------------------------------------------------------------------+ | | :code:`GEN_HYDRO_MUST_TAKE_OPR_TMPS` | | | | Two-dimensional set with generators of the :code:`gen_hydro_must_take` | | operational type and their operational timepoints. | +-------------------------------------------------------------------------+ | | :code:`GEN_HYDRO_MUST_TAKE_LINKED_TMPS` | | | | Two-dimensional set with generators of the :code:`gen_hydro_must_take` | | operational type and their linked timepoints. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Required Input Params | +=========================================================================+ | | :code:`gen_hydro_must_take_max_power_fraction` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | *Within*: :code:`PercentFraction` | | | | The project's maximum power output in each operational horizon as a | | fraction of its available capacity. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_min_power_fraction` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | *Within*: :code:`PercentFraction` | | | | The project's minimum power output in each operational horizon as a | | fraction of its available capacity. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_average_power_fraction` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | *Within*: :code:`PercentFraction` | | | | The project's avarage power output in each operational horizon as a | | fraction of its available capacity. This can be interpreted as the | | project's average capacity factor or plant load factor. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Optional Input Params | +=========================================================================+ | | :code:`gen_hydro_must_take_ramp_up_when_on_rate` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE` | | | *Within*: :code:`PercentFraction` | | | *Default*: :code:`1` | | | | The project's upward ramp rate limit during operations, defined as a | | fraction of its capacity per minute. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_ramp_down_when_on_rate` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE` | | | *Within*: :code:`PercentFraction` | | | *Default*: :code:`1` | | | | The project's downward ramp rate limit during operations, defined as a | | fraction of its capacity per minute. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_aux_consumption_frac_capacity` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE` | | | *Within*: :code:`PercentFraction` | | | *Default*: :code:`0` | | | | Auxiliary consumption as a fraction of capacity. This would be | | incurred in all timepoints when capacity is available. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_aux_consumption_frac_power` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE` | | | *Within*: :code:`PercentFraction` | | | *Default*: :code:`0` | | | | Auxiliary consumption as a fraction of gross power output. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Linked Input Params | +=========================================================================+ | | :code:`gen_hydro_must_take_linked_power` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's power provision in the linked timepoints. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_linked_upwards_reserves` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's upward reserve provision in the linked timepoints. | +-------------------------------------------------------------------------+ | | :code:`gen_hydro_must_take_linked_downwards_reserves` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's downward reserve provision in the linked timepoints. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Variables | +=========================================================================+ | | :code:`GenHydroMustTake_Gross_Power_MW` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | Power provision in MW from this project in each timepoint in which the | | project is operational (capacity exists and the project is available). | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Expressions | +=========================================================================+ | | :code:`GenHydroMustTake_Auxiliary_Consumption_MW` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_TMPS` | | | | The project's auxiliary consumption (power consumed on-site and not | | sent to the grid) in each timepoint. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Constraints | +=========================================================================+ | Power | +-------------------------------------------------------------------------+ | | :code:`GenHydroMustTake_Max_Power_Constraint` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | | Limits the power plus upward reserves based on the | | :code:`gen_hydro_must_take_max_power_fraction` and the available | | capacity. | +-------------------------------------------------------------------------+ | | :code:`GenHydroMustTake_Min_Power_Constraint` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | | Power provision minus downward reserves should exceed a certain level | | based on the :code:`gen_hydro_must_take_min_power_fraction` and the | | available capacity. | +-------------------------------------------------------------------------+ | | :code:`GenHydroMustTake_Energy_Budget_Constraint` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_HRZS` | | | | The project's average capacity factor in each operational horizon, | | should match the specified | | :code:`gen_hydro_must_take_average_power_fraction`. | +-------------------------------------------------------------------------+ | Ramps | +-------------------------------------------------------------------------+ | | :code:`GenHydroMustTake_Ramp_Up_Constraint` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_TMPS` | | | | Limits the allowed project upward ramp based on the | | :code:`gen_hydro_must_take_ramp_up_when_on_rate`. | +-------------------------------------------------------------------------+ | | :code:`GenHydroMustTake_Ramp_Down_Constraint` | | | *Defined over*: :code:`GEN_HYDRO_MUST_TAKE_OPR_TMPS` | | | | Limits the allowed project downward ramp based on the | | :code:`gen_hydro_must_take_ramp_down_when_on_rate`. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.GEN_HYDRO_MUST_TAKE = Set( within=m.PROJECTS, initialize=lambda mod: subset_init_by_param_value( mod, "PROJECTS", "operational_type", "gen_hydro_must_take")) m.GEN_HYDRO_MUST_TAKE_OPR_HRZS = Set(dimen=2) m.GEN_HYDRO_MUST_TAKE_OPR_TMPS = Set( dimen=2, within=m.PRJ_OPR_TMPS, initialize=lambda mod: list( set((g, tmp) for (g, tmp) in mod.PRJ_OPR_TMPS if g in mod.GEN_HYDRO_MUST_TAKE))) m.GEN_HYDRO_MUST_TAKE_LINKED_TMPS = Set(dimen=2) # Required Params ########################################################################### m.gen_hydro_must_take_max_power_fraction = Param( m.GEN_HYDRO_MUST_TAKE_OPR_HRZS, within=PercentFraction) m.gen_hydro_must_take_min_power_fraction = Param( m.GEN_HYDRO_MUST_TAKE_OPR_HRZS, within=PercentFraction) m.gen_hydro_must_take_average_power_fraction = Param( m.GEN_HYDRO_MUST_TAKE_OPR_HRZS, within=PercentFraction) # Optional Params ########################################################################### m.gen_hydro_must_take_ramp_up_when_on_rate = Param(m.GEN_HYDRO_MUST_TAKE, within=PercentFraction, default=1) m.gen_hydro_must_take_ramp_down_when_on_rate = Param( m.GEN_HYDRO_MUST_TAKE, within=PercentFraction, default=1) m.gen_hydro_must_take_aux_consumption_frac_capacity = Param( m.GEN_HYDRO_MUST_TAKE, within=PercentFraction, default=0) m.gen_hydro_must_take_aux_consumption_frac_power = Param( m.GEN_HYDRO_MUST_TAKE, within=PercentFraction, default=0) # Linked Params ########################################################################### m.gen_hydro_must_take_linked_power = Param( m.GEN_HYDRO_MUST_TAKE_LINKED_TMPS, within=NonNegativeReals) m.gen_hydro_must_take_linked_upwards_reserves = Param( m.GEN_HYDRO_MUST_TAKE_LINKED_TMPS, within=NonNegativeReals) m.gen_hydro_must_take_linked_downwards_reserves = Param( m.GEN_HYDRO_MUST_TAKE_LINKED_TMPS, within=NonNegativeReals) # Variables ########################################################################### m.GenHydroMustTake_Gross_Power_MW = Var(m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, within=NonNegativeReals) # Expressions ########################################################################### def upwards_reserve_rule(mod, g, tmp): return sum( getattr(mod, c)[g, tmp] for c in getattr(d, headroom_variables)[g]) m.GenHydroMustTake_Upwards_Reserves_MW = Expression( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=upwards_reserve_rule) def downwards_reserve_rule(mod, g, tmp): return sum( getattr(mod, c)[g, tmp] for c in getattr(d, footroom_variables)[g]) m.GenHydroMustTake_Downwards_Reserves_MW = Expression( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=downwards_reserve_rule) def auxiliary_consumption_rule(mod, g, tmp): """ **Expression Name**: GenHydroMustTake_Auxiliary_Consumption_MW **Defined Over**: GEN_HYDRO_MUST_TAKE_OPR_TMPS """ return mod.Capacity_MW[g, mod.period[tmp]] \ * mod.Availability_Derate[g, tmp] \ * mod.gen_hydro_must_take_aux_consumption_frac_capacity[g] \ + mod.GenHydroMustTake_Gross_Power_MW[g, tmp] \ * mod.gen_hydro_must_take_aux_consumption_frac_power[g] m.GenHydroMustTake_Auxiliary_Consumption_MW = Expression( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=auxiliary_consumption_rule) # Constraints ########################################################################### m.GenHydroMustTake_Max_Power_Constraint = Constraint( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=max_power_rule) m.GenHydroMustTake_Min_Power_Constraint = Constraint( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=min_power_rule) m.GenHydroMustTake_Energy_Budget_Constraint = Constraint( m.GEN_HYDRO_MUST_TAKE_OPR_HRZS, rule=energy_budget_rule) m.GenHydroMustTake_Ramp_Up_Constraint = Constraint( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=ramp_up_rule) m.GenHydroMustTake_Ramp_Down_Constraint = Constraint( m.GEN_HYDRO_MUST_TAKE_OPR_TMPS, rule=ramp_down_rule)
def create_model(): m = ConcreteModel() m.tf = Param(initialize=20) m.t = ContinuousSet(bounds=(0,m.tf)) m.i = Set(initialize=[0,1,2,3,4,5],ordered=True) m.j = Set(initialize=[0,1],ordered=True) m.ij = Set(initialize=[(0,0),(0,1),(1,0),(1,1),(2,0),(2,1),(3,0),(4,0)], ordered=True) #Set Parameters m.eps = Param(initialize = 0.75, mutable=True) m.sig = Param(initialize = 0.15, mutable=True) m.xi = Param(initialize = 0.983, mutable=True) m.omeg = Param(initialize = 1.0/10, mutable=True) m.Y0 = Param(initialize = 55816.0, mutable=True) m.phi0 = Param(initialize = 0.493, mutable=True) d={} d[(0,0)] = 0.0222 d[(0,1)] = 0.0222 d[(1,0)] = 1/7.1 d[(1,1)] = 1/7.1 d[(2,0)] = 1/8.1 d[(2,1)] = 1/(8.1+5) d[(3,0)] = 1/2.7 d[(4,0)] = 1/2.1 m.dd = Param(m.ij, initialize=d, mutable=True) d_inv={} d_inv[(1,0)] = 7.1 d_inv[(2,0)] = 8.1 d_inv[(3,0)] = 2.7 d_inv[(4,0)] = 2.1 m.dd_inv = Param(m.ij, initialize=d_inv, default=0, mutable=True) I={} I[(0,0)] = 0.9*m.dd[(0,0)]*m.Y0 I[(1,0)] = 0.04*m.dd[(0,0)]*m.Y0 I[(2,0)] = 0.04*m.dd[(0,0)]*m.Y0 I[(3,0)] = 0.02*m.dd[(0,0)]*m.Y0 m.II=Param(m.ij, initialize=I, default=0, mutable=True) p={} p[(4,0)] = 0.667 m.pp = Param(m.ij, initialize=p, default=2, mutable=True) b={} b[(1,0)] = 0.066 b[(1,1)] = 0.066 b[(2,0)] = 0.066 b[(2,1)] = 0.066*(1-0.25) b[(3,0)] = 0.147 b[(4,0)] = 0.147 m.bb = Param(m.ij,initialize=b, default=0, mutable=True) eta00={} eta00[(0,0)] = 0.505 eta00[(0,1)] = 0.505 eta00[(1,0)] = 0.505 eta00[(1,1)] = 0.505 eta00[(2,0)] = 0.307 eta00[(2,1)] = 0.4803 eta00[(3,0)] = 0.235 eta00[(4,0)] = 0.235 m.eta00 = Param(m.ij, initialize=eta00, mutable=True) eta01={} eta01[(0,0)] = 0.505 eta01[(0,1)] = 0.6287 eta01[(1,0)] = 0.505 eta01[(1,1)] = 0.6287 eta01[(2,0)] = 0.307 eta01[(2,1)] = 0.4803 eta01[(3,0)] = 0.235 eta01[(4,0)] = 0.235 m.eta01 = Param(m.ij, initialize=eta01, mutable=True) m.kp = Param(initialize = 1000.0, mutable=True) m.kt = Param(initialize = 1000.0, mutable=True) m.rr = Param(initialize = 0.05, mutable=True) c={} c[(0,0)] = 3307 c[(0,1)] = 3307 c[(1,0)] = 5467 c[(1,1)] = 5467 c[(2,0)] = 5467 c[(2,1)] = 5467 c[(3,0)] = 12586 c[(4,0)] = 35394 m.cc = Param(m.ij, initialize=c, mutable=True) q={} q[(0,0)] = 1 q[(0,1)] = 1 q[(1,0)] = 1 q[(1,1)] = 1 q[(2,0)] = 0.83 q[(2,1)] = 0.83 q[(3,0)] = 0.42 q[(4,0)] = 0.17 m.qq = Param(m.ij, initialize=q, mutable=True) m.aa = Param(initialize = 0.0001, mutable=True) #Set Variables m.yy = Var(m.t,m.ij) m.L = Var(m.t) m.vp = Var(m.t, initialize=0.75, bounds=(0,0.75)) m.vt = Var(m.t, initialize=0.75, bounds=(0,0.75)) m.dyy = DerivativeVar(m.yy, wrt=m.t) m.dL = DerivativeVar(m.L, wrt=m.t) def CostFunc(m): return m.L[m.tf] m.cf = Objective(rule=CostFunc) def _initDistConst(m): return (m.phi0*m.Y0)/sum(m.dd_inv[kk] for kk in m.ij) m.idc = Expression(rule=_initDistConst) m.yy[0,(0,0)].fix(value((1-m.phi0)*m.Y0)) m.yy[0,(0,1)].fix(0) m.yy[0,(1,0)].fix(value(m.dd_inv[(1,0)]*m.idc)) m.yy[0,(1,1)].fix(0) m.yy[0,(2,0)].fix(value(m.dd_inv[(2,0)]*m.idc)) m.yy[0,(2,1)].fix(0) m.yy[0,(3,0)].fix(value(m.dd_inv[(3,0)]*m.idc)) m.yy[0,(4,0)].fix(value(m.dd_inv[(4,0)]*m.idc)) m.L[0].fix(0) #ODEs def _yy00(m, t): return sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.dyy[t,(0,0)] == \ sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*(m.II[(0,0)]-\ (m.vp[t]+m.dd[(0,0)])*m.yy[t,(0,0)]+m.omeg*m.yy[t,(0,1)])-\ m.pp[(0,0)]*sum(m.bb[kk]*m.eta00[kk]*m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.yy[t,(0,0)] m.yy00DiffCon = Constraint(m.t, rule=_yy00) def _yy01(m, t): return sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.dyy[t,(0,1)] == \ sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*(m.vp[t]*m.yy[t,(0,0)]- (m.dd[(0,0)]+m.omeg)*m.yy[t,(0,1)])-\ m.pp[(0,1)]*(1-m.eps)*sum(m.bb[kk]*m.eta01[kk]* m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.yy[t,(0,1)] m.yy01DiffCon = Constraint(m.t, rule=_yy01) def _yy10(m, t): return sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.dyy[t,(1,0)] == \ sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*\ (m.II[(1,0)]-((m.sig*m.xi)+m.vp[t]+m.dd[(1,0)]+m.dd[(0,0)])* m.yy[t,(1,0)]+m.omeg*m.yy[t,(1,1)] )+m.pp[(0,0)]*sum(m.bb[kk]*m.eta00[kk]* m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.yy[t,(0,0)] m.yy10DiffCon = Constraint(m.t, rule=_yy10) def _yy11(m, t): return sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.dyy[t,(1,1)] == \ sum(m.pp[kk]*m.yy[t,kk] for kk in m.ij)*(m.vp[t]*m.yy[t,(1,0)]-\ (m.omeg+(m.sig*m.xi)+m.dd[(1,1)]+m.dd[(0,0)])*m.yy[t,(1,1)])+\ m.pp[(0,1)]*(1-m.eps)*sum(m.bb[kk]*m.eta01[kk]* m.pp[kk]*m.yy[t,kk] for kk in m.ij)*m.yy[t,(0,1)] m.yy11DiffCon = Constraint(m.t, rule=_yy11) def _yy20(m, t): return m.dyy[t,(2,0)] == \ m.II[(2,0)]+m.sig*m.xi*(m.yy[t,(1,0)]+m.yy[t,(1,1)])-\ (m.vt[t]+m.dd[(2,0)]+m.dd[(0,0)])*m.yy[t,(2,0)] m.yy20DiffCon = Constraint(m.t, rule=_yy20) def _yy21(m, t): return m.dyy[t,(2,1)] == \ m.vt[t]*m.yy[t,(2,0)]-(m.dd[(2,1)]+m.dd[(0,0)])*m.yy[t,(2,1)] m.yy21DiffCon = Constraint(m.t, rule=_yy21) def _yy30(m, t): return m.dyy[t,(3,0)] == \ m.II[(3,0)]+m.dd[(1,0)]*m.yy[t,(1,0)]+\ m.dd[(1,1)]*m.yy[t,(1,1)]+m.dd[(2,0)]*m.yy[t,(2,0)]+\ m.dd[(2,1)]*m.yy[t,(2,1)]-\ (m.dd[(3,0)]+m.dd[(0,0)])*m.yy[t,(3,0)] m.yy30DiffCon = Constraint(m.t, rule=_yy30) def _yy40(m, t): return m.dyy[t, (4,0)] == \ m.dd[(3,0)]*m.yy[t,(3,0)]-(m.dd[(4,0)]+\ m.dd[(0,0)])*m.yy[t,(4,0)] m.yy40DiffCon = Constraint(m.t, rule=_yy40) def _L(m, t): return m.dL[t] == \ exp(-m.rr*t)*(m.aa*(m.kp*m.vp[t]*(m.yy[t,(0,0)]+m.yy[t,(1,0)]) \ +(m.kt*m.vt[t]*m.yy[t,(2,0)])+sum(m.cc[kk]*m.yy[t,kk] for kk in m.ij)) \ -(1-m.aa)*sum(m.qq[kk]*m.yy[t,kk] for kk in m.ij)) m.LDiffCon = Constraint(m.t, rule=_L) return m
def create_model(namestr, unslim=False, emitlimit=False, nem_ret_ratio=False, nem_ret_gwh=False, region_ret_ratio=False, nem_disp_ratio=False, nem_re_disp_ratio=False): """Creates an instance of the pyomo definition of openCEM""" m = AbstractModel(name=namestr) # Sets m.regions = Set(initialize=cemo.const.REGION.keys()) # Set of NEM regions m.zones = Set( initialize=cemo.const.ZONE.keys()) # Set of NTNDP planning zones m.all_tech = Set( initialize=cemo.const.ALL_TECH) # Set of generation technologies # set of fuel based gen technologies m.fuel_gen_tech = Set(initialize=cemo.const.FUEL_TECH) & m.all_tech # set of gen techs that obey linearised unit commitment constraints m.commit_gen_tech = Set(initialize=cemo.const.COMMIT_TECH) & m.all_tech # set of retireable technologies m.retire_gen_tech = Set(initialize=cemo.const.RETIRE_TECH) & m.all_tech # set of retireable technologies m.nobuild_gen_tech = Set(initialize=cemo.const.NOBUILD_TECH) & m.all_tech # Set of storage technologies m.stor_tech = Set(initialize=cemo.const.STOR_TECH) & m.all_tech # set of hybrid (gen+storage) technologies m.hyb_tech = Set(initialize=cemo.const.HYB_TECH) & m.all_tech # Set of dispatch intervals m.t = Set(ordered=True) # Sparse set of zones per region m.zones_in_regions = Set(dimen=2, initialize=init_zones_in_regions) # Set listing technologies avaialable per zone (like a sparsity pattern) m.gen_tech_in_zones = Set(dimen=2) # Retirable technologies avaialable per zone (like a sparsity pattern) m.retire_gen_tech_in_zones = Set(dimen=2) # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern) m.fuel_gen_tech_in_zones = Set(dimen=2) # Fuel/emmitting technologies avaialable per zone (like a sparsity pattern) m.commit_gen_tech_in_zones = Set(dimen=2) # Renewable technologies avaialable per zone (like a sparsity pattern) m.re_gen_tech_in_zones = Set(dimen=2) # Dispatchable technologies avaialable per zone (like a sparsity pattern) m.disp_gen_tech_in_zones = Set(dimen=2) # Renewable Dispatchable technologies avaialable per zone (like a sparsity pattern) m.re_disp_gen_tech_in_zones = Set(dimen=2) # Set listing storage avaialable per zone (like a sparsity pattern) m.hyb_tech_in_zones = Set(dimen=2) # Set listing storage avaialable per zone (like a sparsity pattern) m.stor_tech_in_zones = Set(dimen=2) # Set listing transmission lines to other regions in each region m.intercons_in_zones = Set(dimen=2, initialize=init_intercons_in_zones) # sparse sets built by build actions # Returns a list of planning zones for each region in R m.zones_per_region = Set(m.regions, within=m.zones, initialize=[]) # Returns a tuple with generating techs in each zone m.gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) # Returns a tuple with emitting techs in each zone m.fuel_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) # tuple for techs that obey linearised unit commitment constraints m.commit_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) m.re_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) m.disp_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) m.re_disp_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) # Returns a tuple with retirable techs in each zone m.retire_gen_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) # Returns a tuple with storage techs in each zone m.stor_tech_per_zone = Set(m.zones, within=m.stor_tech, initialize=[]) # Returns a tuple with emitting techs in each zone m.hyb_tech_per_zone = Set(m.zones, within=m.all_tech, initialize=[]) # returns a tuple with transmission links in each region m.intercon_per_zone = Set(m.zones, initialize=[]) # @@ Build actions # Scan TechinZones and populate ?_gen_tech_per_zone m.TpZ_build = BuildAction(rule=ScanForTechperZone) # Scan HybTechinZones and populate hyb_tech_per_zone m.HpZ_build = BuildAction(rule=ScanForHybridperZone) # Scan ZinR and populate ZperR m.ZpR_build = BuildAction(rule=ScanForZoneperRegion) # Scan TransLines and populate intercon_per_zone m.intercon_build = BuildAction(rule=build_intercon_per_zone) # Scan StorinZones and populate stor_tech_per_zone m.SpZ_build = BuildAction(rule=ScanForStorageperZone) # @@ Parameters # Capital costs generators # Build costs for generators m.cost_gen_build = Param(m.gen_tech_in_zones, default=9e7) m.cost_stor_build = Param(m.stor_tech_in_zones) # Capital costs storage m.cost_hyb_build = Param(m.hyb_tech_in_zones) # Capital costs hybrid # Capital costs $/MW/km trans m.cost_intercon_build = Param(m.intercons_in_zones, initialize=init_intercon_build_cost) m.cost_fuel = Param(m.fuel_gen_tech_in_zones, initialize=init_default_fuel_price) # Fuel cost # Fixed operating costs generators m.cost_gen_fom = Param(m.all_tech) # Variable operating costs generators m.cost_gen_vom = Param(m.all_tech) # Fixed operating costs storage m.cost_stor_fom = Param(m.stor_tech) # Variable operating costs storage m.cost_stor_vom = Param(m.stor_tech) # Fixed operating costs hybrid m.cost_hyb_fom = Param(m.hyb_tech) # Variable operating costs hybrid m.cost_hyb_vom = Param(m.hyb_tech) # Technology lifetime in years m.all_tech_lifetime = Param(m.all_tech, initialize=init_default_lifetime) # Project discount rate m.all_tech_discount_rate = Param(default=0.05) # Generator tech fixed charge rate m.fixed_charge_rate = Param(m.all_tech, initialize=init_fcr) # Transmission tech fixed charge rate m.intercon_fixed_charge_rate = Param(initialize=init_intercon_fcr) # Per year cost adjustment for sims shorter than 1 year of dispatch m.year_correction_factor = Param(initialize=init_year_correction_factor) m.cost_retire = Param(m.retire_gen_tech, initialize=init_cost_retire) m.cost_unserved = Param(initialize=cemo.const.DEFAULT_COSTS["unserved"] ) # cost of unserved power # cost in $/kg of total emissions m.cost_emit = Param(initialize=cemo.const.DEFAULT_COSTS["emit"]) m.cost_trans = Param( initialize=cemo.const.DEFAULT_COSTS["trans"]) # cost of transmission # Round trip efficiency of storage technology m.stor_rt_eff = Param(m.stor_tech, initialize=init_stor_rt_eff) # Number of hours of storage technology m.stor_charge_hours = Param(m.stor_tech, initialize=init_stor_charge_hours) # Collector multiple of hybrid technology m.hyb_col_mult = Param(m.hyb_tech, initialize=init_hyb_col_mult) # Number of hours of storage technology m.hyb_charge_hours = Param(m.hyb_tech, initialize=init_hyb_charge_hours) m.fuel_heat_rate = Param(m.fuel_gen_tech_in_zones, initialize=init_default_heat_rate) # Emission rates m.fuel_emit_rate = Param(m.fuel_gen_tech, initialize=init_default_fuel_emit_rate) # proportioning factors for notional interconnectors m.intercon_loss_factor = Param(m.intercons_in_zones, initialize=init_intercon_loss_factor) m.gen_cap_factor = Param( m.gen_tech_in_zones, m.t, initialize=init_cap_factor) # Capacity factors for generators m.hyb_cap_factor = Param( m.hyb_tech_in_zones, m.t, initialize=init_cap_factor) # Capacity factors for generators # Maximum capacity per generating technology per zone m.gen_build_limit = Param(m.gen_tech_in_zones, initialize=init_gen_build_limit) m.gen_cap_initial = Param(m.gen_tech_in_zones, default=0) # operating capacity m.stor_cap_initial = Param(m.stor_tech_in_zones, default=0) # operating capacity m.hyb_cap_initial = Param(m.hyb_tech_in_zones, default=0) # operating capacity m.intercon_cap_initial = Param( m.intercons_in_zones, initialize=init_intercon_cap_initial) # operating capacity # exogenous new capacity m.gen_cap_exo = Param(m.gen_tech_in_zones, default=0) # exogenous new storage capacity m.stor_cap_exo = Param(m.stor_tech_in_zones, default=0) # exogenous new hybrid capacity m.hyb_cap_exo = Param(m.hyb_tech_in_zones, default=0) # exogenous transmission capacity m.intercon_cap_exo = Param(m.intercons_in_zones, default=0) m.ret_gen_cap_exo = Param(m.retire_gen_tech_in_zones, default=0) # Net Electrical load (may include rooftop and EV) m.region_net_demand = Param(m.regions, m.t) # Zone load distribution factors as a pct of region demand m.zone_demand_factor = Param(m.zones, m.t, initialize=init_zone_demand_factors) # NEM operating reserve margin m.nem_operating_reserve = Param(default=0.075) # carry forward capital costs calculated m.cost_cap_carry_forward_sim = Param(m.zones, default=0) # carry forward capital costs NEM historical estimate m.cost_cap_carry_forward_hist = Param(m.zones, default=0) # carry forward capital costs total m.cost_cap_carry_forward = Param(m.zones, mutable=True) # Build action to Compute carry forward costs m.cost_carry_forward_build = BuildAction( rule=build_carry_fwd_cost_per_zone) # @@ Variables m.gen_cap_new = Var(m.gen_tech_in_zones, within=NonNegativeReals) # New capacity m.gen_cap_op = Var(m.gen_tech_in_zones, within=NonNegativeReals) # Total generation capacity m.stor_cap_new = Var(m.stor_tech_in_zones, within=NonNegativeReals) # New storage capacity m.stor_cap_op = Var(m.stor_tech_in_zones, within=NonNegativeReals) # Total storage capacity m.hyb_cap_new = Var(m.hyb_tech_in_zones, within=NonNegativeReals) m.hyb_cap_op = Var(m.hyb_tech_in_zones, within=NonNegativeReals) m.intercon_cap_new = Var(m.intercons_in_zones, within=NonNegativeReals) m.intercon_cap_op = Var(m.intercons_in_zones, within=NonNegativeReals) m.gen_cap_ret = Var(m.retire_gen_tech_in_zones, within=NonNegativeReals) # retireable capacity m.gen_cap_ret_neg = Var(m.retire_gen_tech_in_zones, within=NonNegativeReals ) # slack for exogenous retires beyond gen_op_cap m.gen_cap_exo_neg = Var( m.gen_tech_in_zones, within=NonNegativeReals ) # slack for exogenous builds exceeding gen_build_limit m.gen_disp = Var(m.gen_tech_in_zones, m.t, within=NonNegativeReals) # dispatched power # Variables for committed power constraints m.gen_disp_com = Var(m.commit_gen_tech_in_zones, m.t, within=NonNegativeReals) m.gen_disp_com_p = Var(m.commit_gen_tech_in_zones, m.t, within=NonNegativeReals) m.gen_disp_com_m = Var(m.commit_gen_tech_in_zones, m.t, within=NonNegativeReals) m.gen_disp_com_s = Var(m.commit_gen_tech_in_zones, m.t, within=NonNegativeReals) m.stor_disp = Var(m.stor_tech_in_zones, m.t, within=NonNegativeReals) # dispatched power from storage m.stor_reserve = Var( m.stor_tech_in_zones, m.t, within=NonNegativeReals) # dispatched power from storage m.stor_charge = Var(m.stor_tech_in_zones, m.t, within=NonNegativeReals) # power to charge storage m.hyb_disp = Var(m.hyb_tech_in_zones, m.t, within=NonNegativeReals) # dispatched power from hybrid m.hyb_reserve = Var( m.hyb_tech_in_zones, m.t, within=NonNegativeReals) # reserve capacity for hybrids m.hyb_charge = Var(m.hyb_tech_in_zones, m.t, within=NonNegativeReals) # charging power from hybrid m.stor_level = Var(m.stor_tech_in_zones, m.t, within=NonNegativeReals) # Charge level for storage m.hyb_level = Var(m.hyb_tech_in_zones, m.t, within=NonNegativeReals) # Charge level for storage m.unserved = Var(m.zones, m.t, within=NonNegativeReals) # unserved power m.surplus = Var(m.zones, m.t, within=NonNegativeReals) # surplus power (if any) # Interconnector flow m.intercon_disp = Var(m.intercons_in_zones, m.t, within=NonNegativeReals) # @@ Constraints # Transmission limits m.con_max_trans = Constraint(m.intercons_in_zones, m.t, rule=con_max_trans) # Transmission capacity balance m.con_intercon_cap = Constraint(m.intercons_in_zones, rule=con_intercon_cap) # Load balance m.ldbal = Constraint(m.zones, m.t, rule=con_ldbal) # Dispatch to be within capacity, RE have variable capacity factors m.caplim = Constraint(m.gen_tech_in_zones, m.t, rule=con_caplim) # Limit maximum capacity to be built in each region and each technology m.maxcap = Constraint(m.gen_tech_in_zones, rule=con_maxcap) # gen_cap_op in existing period is previous gen_cap_op plus gen_cap_new m.con_gen_cap = Constraint(m.gen_tech_in_zones, rule=con_gen_cap) # MaxMWh limit m.con_max_mwh_per_zone = Constraint(m.gen_tech_in_zones, rule=con_max_mhw_per_zone) # MaxMWh limit (currently only for hydro) m.con_max_mwh_nem_wide = Constraint(m.all_tech, rule=con_max_mwh_nem_wide) # Slack constraint on exogenous retirement to prevent it to go nevative m.con_slackretire = Constraint(m.retire_gen_tech_in_zones, rule=con_slackretire) # Slack constraint on exogenous retirement to prevent it to go nevative m.con_slackbuild = Constraint(m.gen_tech_in_zones, rule=con_slackbuild) # linearised unit commitment constraints m.con_min_load_commit = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_min_load_commit) m.con_disp_ramp_down = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_disp_ramp_down) m.con_disp_ramp_up = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_disp_ramp_up) m.con_ramp_down_uptime = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_ramp_down_uptime) m.con_uptime_commitment = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_uptime_commitment) m.con_committed_cap = Constraint(m.commit_gen_tech_in_zones, m.t, rule=con_committed_cap) # NEM operating reserve constraint m.con_operating_reserve = Constraint(m.regions, m.t, rule=con_operating_reserve) # Hard constraint on unserved energy if unslim: m.con_uns = Constraint(m.regions, rule=con_uns) # Emmissions constraint if emitlimit: m.con_emissions = Constraint(rule=con_emissions) # maximum kg/MWh rate of total emissions m.nem_year_emit_limit = Param() # NEM wide RET constraint as a ratio if nem_ret_ratio: # NEM wide renewable energy target for current year m.nem_ret_ratio = Param(default=0) # NEM wide renewable energy constraint m.con_nem_ret_ratio = Constraint(rule=con_nem_ret_ratio) # NEM wide RET constraint as a ratio if nem_ret_gwh: # NEM wide renewable energy target for current year m.nem_ret_gwh = Param(default=0) # NEM wide renewable energy constraint m.con_nem_ret_gwh = Constraint(rule=con_nem_ret_gwh) if region_ret_ratio: # Regional RET targets for current year m.region_ret_ratio = Param(m.regions, default=0) # Regional RET constraint m.con_region_ret = Constraint(m.regions, rule=con_region_ret_ratio) if nem_re_disp_ratio: # NEM wide minimum hour by our generation from "dispatchable" sources m.nem_re_disp_ratio = Param(default=0) # NEM wide minimum hourly dispatch from dispatchable sources constraint m.con_nem_re_disp_ratio = Constraint(m.regions, m.t, rule=con_nem_re_disp_ratio) # Storage charge/discharge dynamic m.StCharDis = Constraint(m.stor_tech_in_zones, m.t, rule=con_storcharge) # Maxiumum rate of storage charge m.con_stor_flow_lim = Constraint(m.stor_tech_in_zones, m.t, rule=con_stor_flow_lim) # Maxiumum rate of storage discharge m.con_stor_reserve_lim = Constraint(m.stor_tech_in_zones, m.t, rule=con_stor_reserve_lim) # Maxiumum charge capacity of storage m.MaxCharge = Constraint(m.stor_tech_in_zones, m.t, rule=con_maxcharge) # StCap in existing period is previous stor_cap_op plus stor_cap_new m.con_stor_cap = Constraint(m.stor_tech_in_zones, rule=con_stor_cap) # Hybrid charge/discharge dynamic m.HybCharDis = Constraint(m.hyb_tech_in_zones, m.t, rule=con_hybcharge) # Maxiumum level of hybrid storage discharge m.con_hyb_level_max = Constraint(m.hyb_tech_in_zones, m.t, rule=con_hyb_level_max) # Maxiumum rate of hybrid storage charge/discharge m.con_hyb_flow_lim = Constraint(m.hyb_tech_in_zones, m.t, rule=con_hyb_flow_lim) # Limit hybrid reserve capacity to be within storage level m.con_hyb_reserve_lim = Constraint(m.hyb_tech_in_zones, m.t, rule=con_hyb_reserve_lim) # Maxiumum charge capacity of storage m.MaxChargehy = Constraint(m.hyb_tech_in_zones, m.t, rule=con_maxchargehy) # HyCap in existing period is previous stor_cap_op plus stor_cap_new m.con_hyb_cap = Constraint(m.hyb_tech_in_zones, rule=con_hyb_cap) # @@ Objective # Minimise capital, variable and fixed costs of system m.FSCost = Expression(expr=0) m.SSCost = Expression(rule=obj_cost) # objective: minimise all other objectives m.Obj = Objective(expr=m.FSCost + m.SSCost) # Short run marginal prices m.dual = Suffix(direction=Suffix.IMPORT) return m
def simple_recycle_model(self): m = ConcreteModel() m.comps = Set(initialize=["A", "B", "C"]) # Feed m.feed = Block() m.feed.flow_out = Var(m.comps) m.feed.temperature_out = Var() m.feed.pressure_out = Var() m.feed.expr_var_idx_out = Var(m.comps) @m.feed.Expression(m.comps) def expr_idx_out(b, i): return -b.expr_var_idx_out[i] m.feed.expr_var_out = Var() m.feed.expr_out = -m.feed.expr_var_out @m.feed.Port() def outlet(b): return dict(flow=b.flow_out, temperature=b.temperature_out, pressure=b.pressure_out, expr_idx=b.expr_idx_out, expr=b.expr_out) def initialize_feed(self): pass m.feed.initialize = MethodType(initialize_feed, m.feed) # Mixer m.mixer = Block() m.mixer.flow_in_side_1 = Var(m.comps) m.mixer.temperature_in_side_1 = Var() m.mixer.pressure_in_side_1 = Var() m.mixer.expr_var_idx_in_side_1 = Var(m.comps) @m.mixer.Expression(m.comps) def expr_idx_in_side_1(b, i): return -b.expr_var_idx_in_side_1[i] m.mixer.expr_var_in_side_1 = Var() m.mixer.expr_in_side_1 = -m.mixer.expr_var_in_side_1 m.mixer.flow_in_side_2 = Var(m.comps) m.mixer.temperature_in_side_2 = Var() m.mixer.pressure_in_side_2 = Var() m.mixer.expr_var_idx_in_side_2 = Var(m.comps) @m.mixer.Expression(m.comps) def expr_idx_in_side_2(b, i): return -b.expr_var_idx_in_side_2[i] m.mixer.expr_var_in_side_2 = Var() m.mixer.expr_in_side_2 = -m.mixer.expr_var_in_side_2 m.mixer.flow_out = Var(m.comps) m.mixer.temperature_out = Var() m.mixer.pressure_out = Var() m.mixer.expr_var_idx_out = Var(m.comps) @m.mixer.Expression(m.comps) def expr_idx_out(b, i): return -b.expr_var_idx_out[i] m.mixer.expr_var_out = Var() m.mixer.expr_out = -m.mixer.expr_var_out @m.mixer.Port() def inlet_side_1(b): return dict(flow=b.flow_in_side_1, temperature=b.temperature_in_side_1, pressure=b.pressure_in_side_1, expr_idx=b.expr_idx_in_side_1, expr=b.expr_in_side_1) @m.mixer.Port() def inlet_side_2(b): return dict(flow=b.flow_in_side_2, temperature=b.temperature_in_side_2, pressure=b.pressure_in_side_2, expr_idx=b.expr_idx_in_side_2, expr=b.expr_in_side_2) @m.mixer.Port() def outlet(b): return dict(flow=b.flow_out, temperature=b.temperature_out, pressure=b.pressure_out, expr_idx=b.expr_idx_out, expr=b.expr_out) def initialize_mixer(self): for i in self.flow_out: self.flow_out[i].value = \ value(self.flow_in_side_1[i] + self.flow_in_side_2[i]) for i in self.expr_var_idx_out: self.expr_var_idx_out[i].value = \ value(self.expr_var_idx_in_side_1[i] + self.expr_var_idx_in_side_2[i]) self.expr_var_out.value = \ value(self.expr_var_in_side_1 + self.expr_var_in_side_2) assert self.temperature_in_side_1 == self.temperature_in_side_2 self.temperature_out.value = value(self.temperature_in_side_1) assert self.pressure_in_side_1 == self.pressure_in_side_2 self.pressure_out.value = value(self.pressure_in_side_1) m.mixer.initialize = MethodType(initialize_mixer, m.mixer) # Pass through m.unit = Block() m.unit.flow_in = Var(m.comps) m.unit.temperature_in = Var() m.unit.pressure_in = Var() m.unit.expr_var_idx_in = Var(m.comps) @m.unit.Expression(m.comps) def expr_idx_in(b, i): return -b.expr_var_idx_in[i] m.unit.expr_var_in = Var() m.unit.expr_in = -m.unit.expr_var_in m.unit.flow_out = Var(m.comps) m.unit.temperature_out = Var() m.unit.pressure_out = Var() m.unit.expr_var_idx_out = Var(m.comps) @m.unit.Expression(m.comps) def expr_idx_out(b, i): return -b.expr_var_idx_out[i] m.unit.expr_var_out = Var() m.unit.expr_out = -m.unit.expr_var_out @m.unit.Port() def inlet(b): return dict(flow=b.flow_in, temperature=b.temperature_in, pressure=b.pressure_in, expr_idx=b.expr_idx_in, expr=b.expr_in) @m.unit.Port() def outlet(b): return dict(flow=b.flow_out, temperature=b.temperature_out, pressure=b.pressure_out, expr_idx=b.expr_idx_out, expr=b.expr_out) def initialize_unit(self): for i in self.flow_out: self.flow_out[i].value = value(self.flow_in[i]) for i in self.expr_var_idx_out: self.expr_var_idx_out[i].value = value(self.expr_var_idx_in[i]) self.expr_var_out.value = value(self.expr_var_in) self.temperature_out.value = value(self.temperature_in) self.pressure_out.value = value(self.pressure_in) m.unit.initialize = MethodType(initialize_unit, m.unit) # Splitter m.splitter = Block() @m.splitter.Block(m.comps) def flow_in(b, i): b.flow = Var() m.splitter.temperature_in = Var() m.splitter.pressure_in = Var() m.splitter.expr_var_idx_in = Var(m.comps) @m.splitter.Expression(m.comps) def expr_idx_in(b, i): return -b.expr_var_idx_in[i] m.splitter.expr_var_in = Var() m.splitter.expr_in = -m.splitter.expr_var_in m.splitter.flow_out_side_1 = Var(m.comps) m.splitter.temperature_out_side_1 = Var() m.splitter.pressure_out_side_1 = Var() m.splitter.expr_var_idx_out_side_1 = Var(m.comps) @m.splitter.Expression(m.comps) def expr_idx_out_side_1(b, i): return -b.expr_var_idx_out_side_1[i] m.splitter.expr_var_out_side_1 = Var() m.splitter.expr_out_side_1 = -m.splitter.expr_var_out_side_1 m.splitter.flow_out_side_2 = Var(m.comps) m.splitter.temperature_out_side_2 = Var() m.splitter.pressure_out_side_2 = Var() m.splitter.expr_var_idx_out_side_2 = Var(m.comps) @m.splitter.Expression(m.comps) def expr_idx_out_side_2(b, i): return -b.expr_var_idx_out_side_2[i] m.splitter.expr_var_out_side_2 = Var() m.splitter.expr_out_side_2 = -m.splitter.expr_var_out_side_2 @m.splitter.Port() def inlet(b): return dict(flow=Reference(b.flow_in[:].flow), temperature=b.temperature_in, pressure=b.pressure_in, expr_idx=b.expr_idx_in, expr=b.expr_in) @m.splitter.Port() def outlet_side_1(b): return dict(flow=b.flow_out_side_1, temperature=b.temperature_out_side_1, pressure=b.pressure_out_side_1, expr_idx=b.expr_idx_out_side_1, expr=b.expr_out_side_1) @m.splitter.Port() def outlet_side_2(b): return dict(flow=b.flow_out_side_2, temperature=b.temperature_out_side_2, pressure=b.pressure_out_side_2, expr_idx=b.expr_idx_out_side_2, expr=b.expr_out_side_2) def initialize_splitter(self): recycle = 0.1 prod = 1 - recycle for i in self.flow_in: self.flow_out_side_1[i].value \ = prod * value(self.flow_in[i].flow) self.flow_out_side_2[i].value \ = recycle * value(self.flow_in[i].flow) for i in self.expr_var_idx_in: self.expr_var_idx_out_side_1[i].value = \ prod * value(self.expr_var_idx_in[i]) self.expr_var_idx_out_side_2[i].value = \ recycle * value(self.expr_var_idx_in[i]) self.expr_var_out_side_1.value = prod * value(self.expr_var_in) self.expr_var_out_side_2.value = recycle * value(self.expr_var_in) self.temperature_out_side_1.value = value(self.temperature_in) self.temperature_out_side_2.value = value(self.temperature_in) self.pressure_out_side_1.value = value(self.pressure_in) self.pressure_out_side_2.value = value(self.pressure_in) m.splitter.initialize = MethodType(initialize_splitter, m.splitter) # Prod m.prod = Block() m.prod.flow_in = Var(m.comps) m.prod.temperature_in = Var() m.prod.pressure_in = Var() m.prod.actual_var_idx_in = Var(m.comps) m.prod.actual_var_in = Var() @m.prod.Port() def inlet(b): return dict(flow=b.flow_in, temperature=b.temperature_in, pressure=b.pressure_in, expr_idx=b.actual_var_idx_in, expr=b.actual_var_in) def initialize_prod(self): pass m.prod.initialize = MethodType(initialize_prod, m.prod) # Arcs @m.Arc(directed=True) def stream_feed_to_mixer(m): return (m.feed.outlet, m.mixer.inlet_side_1) @m.Arc(directed=True) def stream_mixer_to_unit(m): return (m.mixer.outlet, m.unit.inlet) @m.Arc(directed=True) def stream_unit_to_splitter(m): return (m.unit.outlet, m.splitter.inlet) @m.Arc(directed=True) def stream_splitter_to_mixer(m): return (m.splitter.outlet_side_2, m.mixer.inlet_side_2) @m.Arc(directed=True) def stream_splitter_to_prod(m): return (m.splitter.outlet_side_1, m.prod.inlet) # Expand Arcs TransformationFactory("network.expand_arcs").apply_to(m) # Fix Feed m.feed.flow_out['A'].fix(100) m.feed.flow_out['B'].fix(200) m.feed.flow_out['C'].fix(300) m.feed.expr_var_idx_out['A'].fix(10) m.feed.expr_var_idx_out['B'].fix(20) m.feed.expr_var_idx_out['C'].fix(30) m.feed.expr_var_out.fix(40) m.feed.temperature_out.fix(450) m.feed.pressure_out.fix(128) return m
def build(self): ''' Callable method for Block construction. ''' super(BTParameterData, self).build() self.cubic_type = CubicEoS.PR # Add Component objects self.benzene = Component() self.toluene = Component() # List of phase equilibrium index self.phase_equilibrium_idx = Set(initialize=[1, 2]) self.phase_equilibrium_list = \ {1: ["benzene", ("Vap", "Liq")], 2: ["toluene", ("Vap", "Liq")]} # Thermodynamic reference state self.pressure_ref = Param(mutable=True, default=101325, doc='Reference pressure [Pa]', units=pyunits.Pa) self.temperature_ref = Param(mutable=True, default=298.15, doc='Reference temperature [K]', units=pyunits.K) # Critical Properties pressure_crit_data = {'benzene': 48.9e5, 'toluene': 41.0e5} self.pressure_crit = Param(self.component_list, within=NonNegativeReals, mutable=False, initialize=extract_data(pressure_crit_data), doc='Critical pressure [Pa]', units=pyunits.Pa) temperature_crit_data = {'benzene': 562.2, 'toluene': 591.8} self.temperature_crit = Param( self.component_list, within=NonNegativeReals, mutable=False, initialize=extract_data(temperature_crit_data), doc='Critical temperature [K]', units=pyunits.K) # Pitzer acentricity factor omega_data = {'benzene': 0.212, 'toluene': 0.263} self.omega = Param(self.component_list, within=Reals, mutable=False, initialize=extract_data(omega_data), doc='Acentricity Factor') # Peng-Robinson binary interaction parameters kappa_data = { ('benzene', 'benzene'): 0.0000, ('benzene', 'toluene'): 0.0000, ('toluene', 'benzene'): 0.0000, ('toluene', 'toluene'): 0.0000 } self.kappa = Param(self.component_list, self.component_list, within=Reals, mutable=False, initialize=extract_data(kappa_data), doc='Peng-Robinson binary interaction parameters') # Molecular Weights mw_comp_data = {'benzene': 78.1136E-3, 'toluene': 92.1405E-3} self.mw_comp = Param(self.component_list, mutable=False, initialize=extract_data(mw_comp_data), doc="molecular weight kg/mol", units=pyunits.kg / pyunits.mol) # Constants for specific heat capacity, enthalpy and entropy self.cp_mol_ig_comp_coeff_1 = Param( self.component_list, mutable=False, initialize={ 'benzene': -3.392E1, 'toluene': -2.435E1 }, doc="Parameter 1 to compute cp_mol_comp", units=pyunits.J / pyunits.mol / pyunits.K) self.cp_mol_ig_comp_coeff_2 = Param( self.component_list, mutable=False, initialize={ 'benzene': 4.739E-1, 'toluene': 5.125E-1 }, doc="Parameter 2 to compute cp_mol_comp", units=pyunits.J / pyunits.mol / pyunits.K**2) self.cp_mol_ig_comp_coeff_3 = Param( self.component_list, mutable=False, initialize={ 'benzene': -3.017E-4, 'toluene': -2.765E-4 }, doc="Parameter 3 to compute cp_mol_comp", units=pyunits.J / pyunits.mol / pyunits.K**3) self.cp_mol_ig_comp_coeff_4 = Param( self.component_list, mutable=False, initialize={ 'benzene': 7.130E-8, 'toluene': 4.911E-8 }, doc="Parameter 4 to compute cp_mol_comp", units=pyunits.J / pyunits.mol / pyunits.K**4) # Standard heats of formation # Source: NIST Webbook, https://webbook.nist.gov # Retrieved 25th September 2019 dh_form_data = {'benzene': 82.9e3, 'toluene': 50.1e3} self.enth_mol_form_ref = Param(self.component_list, mutable=False, initialize=extract_data(dh_form_data), doc="Standard heats of formation", units=pyunits.J / pyunits.mol) # Standard entropy of formation # Source: Engineering Toolbox, https://www.engineeringtoolbox.com # Retrieved 25th September, 2019 ds_form_data = {'benzene': -269, 'toluene': -321} self.entr_mol_form_ref = Param(self.component_list, mutable=False, initialize=extract_data(ds_form_data), doc="Standard entropy of formation", units=pyunits.J / pyunits.mol / pyunits.K) # Antoine coefficients for ideal vapour (units: bar, K) # This is needed for initial guesses of bubble and dew points self.antoine_coeff_A = Param( self.component_list, mutable=False, initialize={ 'benzene': 4.202, 'toluene': 4.216 }, doc="Antoine A Parameter to calculate pressure_sat", units=pyunits.dimensionless) self.antoine_coeff_B = Param( self.component_list, mutable=False, initialize={ 'benzene': 1322, 'toluene': 1435 }, doc="Antoine B Parameter to calculate pressure_sat", units=pyunits.K) self.antoine_coeff_C = Param( self.component_list, mutable=False, initialize={ 'benzene': -38.56, 'toluene': -43.33 }, doc="Antoine C Parameter to calculate pressure_sat", units=pyunits.K)
def _setup_mccormick(b, nsegs, indx): if not hasattr(b, 'mccormick_cuts'): b.mccormick_cuts = Set(initialize=['lb1', 'lb2', 'ub1', 'ub2']) if not hasattr(b, 'sets'): if indx is None: b.sets = Set() elif isinstance(indx, tuple): b.sets = Set(dimen=len(indx)) else: b.sets = Set(dimen=1) if indx is not None and indx not in b.sets: # Presumably I would need to add indices to this set, but # apparently it's complaining when I do, so I'm not. # b.sets.add(indx) pass if not hasattr(b, 'constr'): if indx is not None: b.constr = Constraint(b.sets, b.mccormick_cuts) else: b.constr = Constraint(b.mccormick_cuts) if not hasattr(b, 'segs'): b.segs = RangeSet(nsegs) else: if nsegs != len(b.segs): raise ValueError( 'We do not currently support different segment counts within the same set of McCormick relaxations.' ) if not hasattr(b, 'xbar'): if indx is not None: b.xbar = Var(b.sets, initialize=0) else: b.xbar = Var(initialize=0) if not hasattr(b, 'delta_y'): if indx is not None: b.delta_y = Var(b.sets, b.segs, domain=NonNegativeReals, initialize=0) else: b.delta_y = Var(b.segs, domain=NonNegativeReals, initialize=0) if not hasattr(b, 'lbda'): if indx is not None: b.lbda = Var(b.sets, b.segs, domain=Binary) else: b.lbda = Var(b.segs, domain=Binary) if not hasattr(b, 'x_defn'): if indx is not None: b.x_defn = Constraint(b.sets, ['lb', 'ub']) else: b.x_defn = Constraint(['lb', 'ub']) if not hasattr(b, 'xbar_defn'): if indx is not None: b.xbar_defn = Constraint(b.sets, ['lb1', 'lb2', 'ub1', 'ub2']) else: b.xbar_defn = Constraint(['lb1', 'lb2', 'ub1', 'ub2']) if not hasattr(b, 'y_defn'): if indx is not None: b.y_defn = Constraint(b.sets, ['lb', 'ub']) else: b.y_defn = Constraint(['lb', 'ub']) if not hasattr(b, 'dy_ub'): if indx is not None: b.dy_ub = Constraint(b.sets, b.segs) else: b.dy_ub = Constraint(b.segs) if not hasattr(b, 'lbda_defn'): if indx is not None: b.lbda_defn = Constraint(b.sets) else: b.lbda_defn = Constraint() b.xbar._index.add(indx) for s in b.segs: # b.delta_y._index.add((indx, s)) b.delta_y.add(squish(_concat(indx, s))) # b.lbda._index.add(indx) b.lbda.add(squish(_concat(indx, s)))
def extensive_recycle_model(self): def build_in_out(b): b.flow_in = Var(m.comps) b.mass_in = Var() b.temperature_in = Var() b.pressure_in = Var() b.expr_var_idx_in = Var(m.comps) @b.Expression(m.comps) def expr_idx_in(b, i): return -b.expr_var_idx_in[i] b.expr_var_in = Var() b.expr_in = -b.expr_var_in b.flow_out = Var(m.comps) b.mass_out = Var() b.temperature_out = Var() b.pressure_out = Var() b.expr_var_idx_out = Var(m.comps) @b.Expression(m.comps) def expr_idx_out(b, i): return -b.expr_var_idx_out[i] b.expr_var_out = Var() b.expr_out = -b.expr_var_out b.inlet = Port(rule=inlet) b.outlet = Port(rule=outlet) b.initialize = MethodType(initialize, b) def inlet(b): return dict(flow=(b.flow_in, Port.Extensive), mass=(b.mass_in, Port.Extensive), temperature=b.temperature_in, pressure=b.pressure_in, expr_idx=(b.expr_idx_in, Port.Extensive), expr=(b.expr_in, Port.Extensive)) def outlet(b): return dict(flow=(b.flow_out, Port.Extensive), mass=(b.mass_out, Port.Extensive), temperature=b.temperature_out, pressure=b.pressure_out, expr_idx=(b.expr_idx_out, Port.Extensive), expr=(b.expr_out, Port.Extensive)) def initialize(self): for i in self.flow_out: self.flow_out[i].value = value(self.flow_in[i]) self.mass_out.value = value(self.mass_in) for i in self.expr_var_idx_out: self.expr_var_idx_out[i].value = value(self.expr_var_idx_in[i]) self.expr_var_out.value = value(self.expr_var_in) self.temperature_out.value = value(self.temperature_in) self.pressure_out.value = value(self.pressure_in) def nop(self): pass m = ConcreteModel() m.comps = Set(initialize=["A", "B", "C"]) # Feed m.feed = Block() m.feed.flow_out = Var(m.comps) m.feed.mass_out = Var() m.feed.temperature_out = Var() m.feed.pressure_out = Var() m.feed.expr_var_idx_out = Var(m.comps) @m.feed.Expression(m.comps) def expr_idx_out(b, i): return -b.expr_var_idx_out[i] m.feed.expr_var_out = Var() m.feed.expr_out = -m.feed.expr_var_out m.feed.outlet = Port(rule=outlet) m.feed.initialize = MethodType(nop, m.feed) # Mixer m.mixer = Block() build_in_out(m.mixer) # Pass through m.unit = Block() build_in_out(m.unit) # Splitter m.splitter = Block() build_in_out(m.splitter) # Prod m.prod = Block() m.prod.flow_in = Var(m.comps) m.prod.mass_in = Var() m.prod.temperature_in = Var() m.prod.pressure_in = Var() m.prod.actual_var_idx_in = Var(m.comps) m.prod.actual_var_in = Var() @m.prod.Port() def inlet(b): return dict(flow=(b.flow_in, Port.Extensive), mass=(b.mass_in, Port.Extensive), temperature=b.temperature_in, pressure=b.pressure_in, expr_idx=(b.actual_var_idx_in, Port.Extensive), expr=(b.actual_var_in, Port.Extensive)) m.prod.initialize = MethodType(nop, m.prod) # Arcs @m.Arc(directed=True) def stream_feed_to_mixer(m): return (m.feed.outlet, m.mixer.inlet) @m.Arc(directed=True) def stream_mixer_to_unit(m): return (m.mixer.outlet, m.unit.inlet) @m.Arc(directed=True) def stream_unit_to_splitter(m): return (m.unit.outlet, m.splitter.inlet) @m.Arc(directed=True) def stream_splitter_to_mixer(m): return (m.splitter.outlet, m.mixer.inlet) @m.Arc(directed=True) def stream_splitter_to_prod(m): return (m.splitter.outlet, m.prod.inlet) # Split Fraction rec = 0.1 prod = 1 - rec m.splitter.outlet.set_split_fraction(m.stream_splitter_to_mixer, rec) m.splitter.outlet.set_split_fraction(m.stream_splitter_to_prod, prod) # Expand Arcs TransformationFactory("network.expand_arcs").apply_to(m) # Fix Feed m.feed.flow_out['A'].fix(100) m.feed.flow_out['B'].fix(200) m.feed.flow_out['C'].fix(300) m.feed.mass_out.fix(400) m.feed.expr_var_idx_out['A'].fix(10) m.feed.expr_var_idx_out['B'].fix(20) m.feed.expr_var_idx_out['C'].fix(30) m.feed.expr_var_out.fix(40) m.feed.temperature_out.fix(450) m.feed.pressure_out.fix(128) return m
def build_rect_strip_packing_model(): """Build the strip packing model.""" model = ConcreteModel(name="Rectangles strip packing") model.rectangles = Set(ordered=True, initialize=[0, 1, 2, 3, 4, 5, 6, 7]) # Width and Length of each rectangle model.rect_width = Param( model.rectangles, initialize={0: 3, 1: 3, 2: 2, 3: 2, 4: 3, 5: 5, 6: 7, 7: 7}) # parameter indexed by each rectangle # same as height? model.rect_length = Param( model.rectangles, initialize={0: 4, 1: 3, 2: 2, 3: 2, 4: 3, 5: 3, 6: 4, 7: 4}) model.strip_width = Param( initialize=10, doc="Available width of the strip") # upperbound on length (default is sum of lengths of rectangles) model.max_length = Param( initialize=sum(model.rect_length[i] for i in model.rectangles), doc="maximum length of the strip (if all rectangles were arranged " "lengthwise)") # x (length) and y (width) coordinates of each of the rectangles model.x = Var(model.rectangles, bounds=(0, model.max_length), doc="rectangle corner x-position (position across length)") def w_bounds(m, i): return (0, m.strip_width - m.rect_width[i]) model.y = Var(model.rectangles, bounds=w_bounds, doc="rectangle corner y-position (position down width)") model.strip_length = Var( within=NonNegativeReals, doc="Length of strip required.") def rec_pairs_filter(model, i, j): return i < j model.overlap_pairs = Set( initialize=model.rectangles * model.rectangles, dimen=2, filter=rec_pairs_filter, doc="set of possible rectangle conflicts") @model.Constraint(model.rectangles) def strip_ends_after_last_rec(model, i): return model.strip_length >= model.x[i] + model.rect_length[i] model.total_length = Objective(expr=model.strip_length, doc="Minimize length") @model.Disjunction( model.overlap_pairs, doc="Make sure that none of the rectangles on the strip overlap in " "either the x or y dimensions.") def no_overlap(m, i, j): return [ m.x[i] + m.rect_length[i] <= m.x[j], m.x[j] + m.rect_length[j] <= m.x[i], m.y[i] + m.rect_width[i] <= m.y[j], m.y[j] + m.rect_width[j] <= m.y[i], ] return model
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`STOR_SPEC_OPR_PRDS` | | | | Two-dimensional set of project-period combinations that helps describe | | the project capacity available in a given period. This set is added to | | the list of sets to join to get the final :code:`PRJ_OPR_PRDS` set | | defined in **gridpath.project.capacity.capacity**. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Required Input Params | +=========================================================================+ | | :code:`stor_spec_power_capacity_mw` | | | *Defined over*: :code:`STOR_SPEC_OPR_PRDS` | | | *Within*: :code:`NonNegativeReals` | | | | The storage project's specified power capacity (in MW) in each | | operational period. | +-------------------------------------------------------------------------+ | | :code:`stor_spec_energy_capacity_mwh` | | | *Defined over*: :code:`STOR_SPEC_OPR_PRDS` | | | *Within*: :code:`NonNegativeReals` | | | | The storage project's specified energy capacity (in MWh) in each | | operational period. | +-------------------------------------------------------------------------+ | | :code:`stor_spec_fixed_cost_per_mw_yr` | | | *Defined over*: :code:`STOR_SPEC_OPR_PRDS` | | | *Within*: :code:`NonNegativeReals` | | | | The storage project's fixed cost for the power components (in $ per | | MW-yr.) in each operational period. This cost will be added to the | | objective function but will not affect optimization decisions. | +-------------------------------------------------------------------------+ | | :code:`stor_spec_fixed_cost_per_mwh_yr` | | | *Defined over*: :code:`STOR_SPEC_OPR_PRDS` | | | *Within*: :code:`NonNegativeReals` | | | | The storage project's fixed cost for the energy components (in $ per | | MWh-yr.) in each operational period. This cost will be added to the | | objective function but will not affect optimization decisions. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.STOR_SPEC_OPR_PRDS = Set(dimen=2) # Required Params ########################################################################### m.stor_spec_power_capacity_mw = Param(m.STOR_SPEC_OPR_PRDS, within=NonNegativeReals) m.stor_spec_energy_capacity_mwh = Param(m.STOR_SPEC_OPR_PRDS, within=NonNegativeReals) m.stor_spec_fixed_cost_per_mw_yr = Param(m.STOR_SPEC_OPR_PRDS, within=NonNegativeReals) m.stor_spec_fixed_cost_per_mwh_yr = Param(m.STOR_SPEC_OPR_PRDS, within=NonNegativeReals) # Dynamic Components ########################################################################### # Add to list of sets we'll join to get the final # PRJ_OPR_PRDS set getattr(d, capacity_type_operational_period_sets).append( "STOR_SPEC_OPR_PRDS", ) # Add to list of sets we'll join to get the final # STOR_OPR_PRDS set getattr(d, storage_only_capacity_type_operational_period_sets).append( "STOR_SPEC_OPR_PRDS", )
def __init__(self, y, x, orient): """DEA model Args: y (float): output variable. x (float): input variables. orient (String): ORIENT_IO (input orientation) or ORIENT_OO (output orientation) """ # TODO(error/warning handling): Check the configuration of the model exist self.x = x.tolist() self.y = y.tolist() self.orient = orient if type(self.x[0]) != list: self.x = [] for x_value in x.tolist(): self.x.append([x_value]) if type(self.y[0]) != list: self.y = [] for y_value in y.tolist(): self.y.append([y_value]) # Initialize DEA model self.__model__ = ConcreteModel() # Initialize sets self.__model__.I = Set(initialize=range(len(self.y))) self.__model__.J = Set(initialize=range(len(self.x[0]))) self.__model__.K = Set(initialize=range(len(self.y[0]))) # Initialize variable self.__model__.theta = Var(self.__model__.I, doc='efficiency') self.__model__.lamda = Var(self.__model__.I, self.__model__.I, within=Binary, doc='intensity variables') # Setup the objective function and constraints if self.orient == ORIENT_IO: self.__model__.objective = Objective(rule=self.__objective_rule(), sense=minimize, doc='objective function') else: self.__model__.objective = Objective(rule=self.__objective_rule(), sense=maximize, doc='objective function') self.__model__.input = Constraint(self.__model__.I, self.__model__.J, rule=self.__input_rule(), doc='input constraint') self.__model__.output = Constraint(self.__model__.I, self.__model__.K, rule=self.__output_rule(), doc='output constraint') self.__model__.vrs = Constraint(self.__model__.I, rule=self.__vrs_rule(), doc='various return to scale rule') # Optimize model self.optimization_status = 0 self.problem_status = 0
def Xtest_pyomo_Set_domain_duplicates(self): with self.assertRaises(ValueError) as cm: self.model = ConcreteModel() self.model.s = Set(initialize=[1.7, 2, 3]) self.model.y = Var([1, 2], within=self.model.s)
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`GEN_NEW_BIN` | | | | Two-dimensional set of project-vintage combinations to describe all | | possible project-vintage combinations for projects with a cumulative | | minimum build capacity specified. | +-------------------------------------------------------------------------+ | | :code:`GEN_NEW_BIN_VNTS` | | | | A two-dimensional set of project-vintage combinations to describe the | | periods in time when project capacity can be built in the optimization. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Required Input Params | +=========================================================================+ | | :code:`gen_new_bin_lifetime_yrs_by_vintage` | | | *Defined over*: :code:`GEN_NEW_BIN_VNTS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's lifetime, i.e. how long project capacity of a particular | | vintage remains operational. | +-------------------------------------------------------------------------+ | | :code:`gen_new_bin_annualized_real_cost_per_mw_yr` | | | *Defined over*: :code:`GEN_NEW_BIN_VNTS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's cost to build new capacity in annualized real dollars per | | MW. | +-------------------------------------------------------------------------+ | | :code:`gen_new_bin_build_size_mw` | | | *Defined over*: :code:`GEN_NEW_BIN` | | | *Within*: :code:`NonNegativeReals` | | | | The project's specified build size in MW. The model can only build the | | project in this pre-specified size. | +-------------------------------------------------------------------------+ .. note:: The cost input to the model is a levelized cost per unit capacity. This annualized cost is incurred in each period of the study (and multiplied by the number of years the period represents) for the duration of the project's lifetime. It is up to the user to ensure that the :code:`gen_new_bin_lifetime_yrs_by_vintage` and :code:`gen_new_bin_annualized_real_cost_per_mw_yr` parameters are consistent. +-------------------------------------------------------------------------+ | Derived Sets | +=========================================================================+ | | :code:`OPR_PRDS_BY_GEN_NEW_BIN_VINTAGE` | | | *Defined over*: :code:`GEN_NEW_BIN_VNTS` | | | | Indexed set that describes the operational periods for each possible | | project-vintage combination, based on the | | :code:`gen_new_bin_lifetime_yrs_by_vintage`. For instance, capacity of | | the 2020 vintage with lifetime of 30 years will be assumed operational | | starting Jan 1, 2020 and through Dec 31, 2049, but will *not* be | | operational in 2050. | +-------------------------------------------------------------------------+ | | :code:`GEN_NEW_BIN_OPR_PRDS` | | | | Two-dimensional set that includes the periods when project capacity of | | any vintage *could* be operational if built. This set is added to the | | list of sets to join to get the final :code:`PRJ_OPR_PRDS` set defined | | in **gridpath.project.capacity.capacity**. | +-------------------------------------------------------------------------+ | | :code:`GEN_NEW_BIN_VNTS_OPR_IN_PERIOD` | | | *Defined over*: :code:`PERIODS` | | | | Indexed set that describes the project-vintages that could be | | operational in each period based on the | | :code:`gen_new_bin_lifetime_yrs_by_vintage`. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Variables | +=========================================================================+ | | :code:`GenNewBin_Build` | | | *Defined over*: :code:`GEN_NEW_BIN_VNTS` | | | *Within*: :code:`Binary ` | | | | Binary build decision for each project-vintage combination (1=build). | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Constraints | +=========================================================================+ | | :code:`GenNewBin_Only_Build_Once_Constraint` | | | *Defined over*: :code:`GEN_NEW_BIN_OPR_PRDS` | | | | Once a project is built, it cannot be built again in another vintage | | until its lifetime is expired. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.GEN_NEW_BIN = Set( within=m.PROJECTS ) m.GEN_NEW_BIN_VNTS = Set( dimen=2, within=m.PROJECTS*m.PERIODS ) # Required Params ########################################################################### m.gen_new_bin_lifetime_yrs_by_vintage = Param( m.GEN_NEW_BIN_VNTS, within=NonNegativeReals ) m.gen_new_bin_annualized_real_cost_per_mw_yr = Param( m.GEN_NEW_BIN_VNTS, within=NonNegativeReals ) m.gen_new_bin_build_size_mw = Param( m.GEN_NEW_BIN, within=NonNegativeReals ) # Derived Sets ########################################################################### m.OPR_PRDS_BY_GEN_NEW_BIN_VINTAGE = Set( m.GEN_NEW_BIN_VNTS, initialize=operational_periods_by_generator_vintage ) m.GEN_NEW_BIN_OPR_PRDS = Set( dimen=2, initialize=gen_new_bin_operational_periods ) m.GEN_NEW_BIN_VNTS_OPR_IN_PERIOD = Set( m.PERIODS, dimen=2, initialize=gen_new_bin_vintages_operational_in_period ) # Variables ########################################################################### m.GenNewBin_Build = Var( m.GEN_NEW_BIN_VNTS, within=Binary ) # Constraints ########################################################################### m.GenNewBin_Only_Build_Once_Constraint = Constraint( m.GEN_NEW_BIN_OPR_PRDS, rule=only_build_once_rule ) # Dynamic Components ########################################################################### # Add to list of sets we'll join to get the final # PRJ_OPR_PRDS set getattr(d, capacity_type_operational_period_sets).append( "GEN_NEW_BIN_OPR_PRDS", )
def build(self): ''' Callable method for Block construction. ''' super(PhysicalParameterData, self).build() self.state_block_class = IdealStateBlock # List of valid phases in property package # List of valid phases in property package if self.config.valid_phase == ('Liq', 'Vap') or \ self.config.valid_phase == ('Vap', 'Liq'): self.phase_list = Set(initialize=['Liq', 'Vap'], ordered=True) elif self.config.valid_phase == 'Liq': self.phase_list = Set(initialize=['Liq']) else: self.phase_list = Set(initialize=['Vap']) self.component_list = Set(initialize=['CH4', 'CO', 'H2', 'CH3OH']) # List of components in each phase (optional) self.phase_comp = { "Liq": self.component_list, "Vap": self.component_list } self.phase_equilibrium_idx = Set(initialize=[1, 2, 3, 4]) self.phase_equilibrium_list = \ {1: ["CH4", ("Vap", "Liq")], 2: ["CO", ("Vap", "Liq")], 3: ["H2", ("Vap", "Liq")], 4: ["CH3OH", ("Vap", "Liq")]} # Gas Constant self.gas_constant = Param(within=NonNegativeReals, mutable=False, default=0.008314, doc='Gas Constant [MJ/(kgmol.K)]') self.vapor_pressure_coeff = { ('CH4', 'A'): 15.2243, ('CH4', 'B'): 897.84, ('CH4', 'C'): -7.16, ('CO', 'A'): 14.3686, ('CO', 'B'): 530.22, ('CO', 'C'): -13.15, ('H2', 'A'): 13.6333, ('H2', 'B'): 164.9, ('H2', 'C'): 3.19, ('CH3OH', 'A'): 18.5875, ('CH3OH', 'B'): 3626.55, ('CH3OH', 'C'): -34.29 } Cp = self.config.Cp Cv = Cp - self.gas_constant.value gamma = Cp / Cv self.gamma = Param(within=NonNegativeReals, mutable=True, default=gamma, doc='Ratio of Cp to Cv') self.Cp = Param(within=NonNegativeReals, mutable=True, default=Cp, doc='Constant pressure heat capacity [MJ/(kgmol K)]')
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`BLN_TYPE_HRZS` | | | | Two dimensional set of balancing types and their associated horizons. | | Balancing types are strings, e.g. year, month, week, day, whereas | | horizons must be non-negative integers. | +-------------------------------------------------------------------------+ | | :code:`TMPS_BY_BLN_TYPE_HRZ` | | | *Defined over*: :code:`BLN_TYPE_HRZS` | | | | Ordered, indexed set that describes the the horizons associated with | | each balancing type. The timepoins within a horizon-balancing type | | are ordered. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Derived Sets | +=========================================================================+ | | :code:`BLN_TYPES` | | | | The list of all balancing types. | +-------------------------------------------------------------------------+ | | :code:`HRZS_BY_BLN_TYPE` | | | *Defined over*: :code:`BLN_TYPES` | | | | Ordered, indexed set that describes the horizons associated with | | each balancing type. The horizons within a balancing type are ordered. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Required Input Params | +=========================================================================+ | | :code:`horizon` | | | *Defined over*: :code:`TMPS x BLN_TYPES` | | | | Describes the horizon that each timeoint belongs to for a given | | balancing type. Depending on the balancing type, timepoints can be | | grouped in different horizons. | +-------------------------------------------------------------------------+ | | :code:`boundary` | | | *Defined over*: :code:`BLN_TYPE_HRZS` | | | *Within*: :code:`['circular', 'linear']` | | | | The boundary for each horizon. If the boundary is 'circular,' then the | | last timepoint of the horizon is treated as the 'previous' timepoint | | for the first timepoint of the horizon (e.g. for ramping constraints | | or tracking storage state of charge). | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Derived Input Params | +=========================================================================+ | | :code:`first_hrz_tmp` | | | *Defined over*: :code:`BLN_TYPE_HRZS` | | | | Derived parameter describing the first timepoint in each horizon for a | | given balancing type. Note: this relies on :code:`TMPS_BY_BLN_TYPE_HRZ` | | being an ordered (indexed) set. | +-------------------------------------------------------------------------+ | | :code:`last_hrz_tmp` | | | *Defined over*: :code:`BLN_TYPE_HRZS` | | | | Derived parameter describing the last timepoint in each horizon for a | | given balancing type. Note: this relies on :code:`TMPS_BY_BLN_TYPE_HRZ` | | being an ordered (indexed) set. | +-------------------------------------------------------------------------+ | | :code:`prev_tmp` | | | *Defined over*: :code:`TMPS x BLN_TYPES` | | | *Within*: :code: `m.TMPS | {None}` | | | | Derived parameter describing the previous timepoint for each timepoint | | in each balancing type; depends on whether horizon is circular or | | linear and relies on having ordered :code:`TIMEPOINTS`. | +-------------------------------------------------------------------------+ | | :code:`next_tmp` | | | *Defined over*: :code:`TMPS x BLN_TYPES` | | | *Within*: :code: `m.TMPS | {None}` | | | | Derived parameter describing the next timepoint for each timepoint in | | each balancing type; depends on whether horizon is circular or linear | | and relies on having ordered :code:`TIMEPOINTS`. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.BLN_TYPE_HRZS = Set(dimen=2, ordered=True) m.TMPS_BY_BLN_TYPE_HRZ = Set(m.BLN_TYPE_HRZS, within=PositiveIntegers, ordered=True) # Derived Sets ########################################################################### m.BLN_TYPES = Set(initialize=balancing_types_init) m.HRZS_BY_BLN_TYPE = Set(m.BLN_TYPES, within=PositiveIntegers, initialize=horizons_by_balancing_type_init) # Required Params ########################################################################### # TODO: can create here instead of upstream in data (i.e. we can get the # balancing type index from the horizon of the timepoint) m.horizon = Param(m.TMPS, m.BLN_TYPES) m.boundary = Param(m.BLN_TYPE_HRZS, within=['circular', 'linear', 'linked']) # Derived Params ########################################################################### m.first_hrz_tmp = Param( m.BLN_TYPE_HRZS, within=PositiveIntegers, initialize=lambda mod, b, h: list(mod.TMPS_BY_BLN_TYPE_HRZ[b, h])[0]) m.last_hrz_tmp = Param( m.BLN_TYPE_HRZS, within=PositiveIntegers, initialize=lambda mod, b, h: list(mod.TMPS_BY_BLN_TYPE_HRZ[b, h])[-1]) m.prev_tmp = Param(m.TMPS, m.BLN_TYPES, within=m.TMPS | {None}, initialize=prev_tmp_init) m.next_tmp = Param(m.TMPS, m.BLN_TYPES, within=m.TMPS | {None}, initialize=next_tmp_init)
def test_get_check_units_on_all_expressions(self): # this method is going to test all the expression types that should work # to be defensive, we will also test that we actually have the expected expression type # therefore, if the expression system changes and we get a different expression type, # we will know we need to change these tests uc = units kg = uc.kg m = uc.m model = ConcreteModel() model.x = Var() model.y = Var() model.z = Var() model.p = Param(initialize=42.0, mutable=True) model.xkg = Var(units=kg) model.ym = Var(units=m) # test equality self._get_check_units_ok(3.0 * kg == 1.0 * kg, uc, 'kg', EXPR.EqualityExpression) self._get_check_units_fail(3.0 * kg == 2.0 * m, uc, EXPR.EqualityExpression) # test inequality self._get_check_units_ok(3.0 * kg <= 1.0 * kg, uc, 'kg', EXPR.InequalityExpression) self._get_check_units_fail(3.0 * kg <= 2.0 * m, uc, EXPR.InequalityExpression) self._get_check_units_ok(3.0 * kg >= 1.0 * kg, uc, 'kg', EXPR.InequalityExpression) self._get_check_units_fail(3.0 * kg >= 2.0 * m, uc, EXPR.InequalityExpression) # test RangedExpression self._get_check_units_ok(inequality(3.0 * kg, 4.0 * kg, 5.0 * kg), uc, 'kg', EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0 * m, 4.0 * kg, 5.0 * kg), uc, EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0 * kg, 4.0 * m, 5.0 * kg), uc, EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0 * kg, 4.0 * kg, 5.0 * m), uc, EXPR.RangedExpression) # test SumExpression, NPV_SumExpression self._get_check_units_ok( 3.0 * model.x * kg + 1.0 * model.y * kg + 3.65 * model.z * kg, uc, 'kg', EXPR.SumExpression) self._get_check_units_fail( 3.0 * model.x * kg + 1.0 * model.y * m + 3.65 * model.z * kg, uc, EXPR.SumExpression) self._get_check_units_ok(3.0 * kg + 1.0 * kg + 2.0 * kg, uc, 'kg', EXPR.NPV_SumExpression) self._get_check_units_fail(3.0 * kg + 1.0 * kg + 2.0 * m, uc, EXPR.NPV_SumExpression) # test ProductExpression, NPV_ProductExpression self._get_check_units_ok(model.x * kg * model.y * m, uc, 'kg*m', EXPR.ProductExpression) self._get_check_units_ok(3.0 * kg * 1.0 * m, uc, 'kg*m', EXPR.NPV_ProductExpression) self._get_check_units_ok(3.0 * kg * m, uc, 'kg*m', EXPR.NPV_ProductExpression) # I don't think that there are combinations that can "fail" for products # test MonomialTermExpression self._get_check_units_ok(model.x * kg, uc, 'kg', EXPR.MonomialTermExpression) # test DivisionExpression, NPV_DivisionExpression self._get_check_units_ok(1.0 / (model.x * kg), uc, '1/kg', EXPR.DivisionExpression) self._get_check_units_ok(2.0 / kg, uc, '1/kg', EXPR.NPV_DivisionExpression) self._get_check_units_ok((model.x * kg) / 1.0, uc, 'kg', EXPR.MonomialTermExpression) self._get_check_units_ok(kg / 2.0, uc, 'kg', EXPR.NPV_DivisionExpression) self._get_check_units_ok(model.y * m / (model.x * kg), uc, 'm/kg', EXPR.DivisionExpression) self._get_check_units_ok(m / kg, uc, 'm/kg', EXPR.NPV_DivisionExpression) # I don't think that there are combinations that can "fail" for products # test PowExpression, NPV_PowExpression # ToDo: fix the str representation to combine the powers or the expression system self._get_check_units_ok( (model.x * kg**2)**3, uc, 'kg**6', EXPR.PowExpression) # would want this to be kg**6 self._get_check_units_fail(kg**model.x, uc, EXPR.PowExpression, UnitsError) self._get_check_units_fail(model.x**kg, uc, EXPR.PowExpression, UnitsError) self._get_check_units_ok(kg**2, uc, 'kg**2', EXPR.NPV_PowExpression) self._get_check_units_fail(3.0**kg, uc, EXPR.NPV_PowExpression, UnitsError) # test NegationExpression, NPV_NegationExpression self._get_check_units_ok(-(kg * model.x * model.y), uc, 'kg', EXPR.NegationExpression) self._get_check_units_ok(-kg, uc, 'kg', EXPR.NPV_NegationExpression) # don't think there are combinations that fan "fail" for negation # test AbsExpression, NPV_AbsExpression self._get_check_units_ok(abs(kg * model.x), uc, 'kg', EXPR.AbsExpression) self._get_check_units_ok(abs(kg), uc, 'kg', EXPR.NPV_AbsExpression) # don't think there are combinations that fan "fail" for abs # test the different UnaryFunctionExpression / NPV_UnaryFunctionExpression types # log self._get_check_units_ok(log(3.0 * model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(log(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(log(3.0 * model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(log(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # log10 self._get_check_units_ok(log10(3.0 * model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(log10(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(log10(3.0 * model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(log10(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sin self._get_check_units_ok(sin(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(sin(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(sin(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(sin(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(sin(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # cos self._get_check_units_ok(cos(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(cos(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(cos(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(cos(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(cos(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # tan self._get_check_units_ok(tan(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(tan(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(tan(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(tan(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(tan(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sin self._get_check_units_ok(sinh(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(sinh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(sinh(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(sinh(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(sinh(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # cos self._get_check_units_ok(cosh(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(cosh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(cosh(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(cosh(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(cosh(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # tan self._get_check_units_ok(tanh(3.0 * model.x * uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(tanh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(tanh(3.0 * kg * model.x * uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(tanh(3.0 * model.p * uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(tanh(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # asin self._get_check_units_ok(asin(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(asin(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(asin(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(asin(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # acos self._get_check_units_ok(acos(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(acos(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(acos(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(acos(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # atan self._get_check_units_ok(atan(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(atan(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(atan(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(atan(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # exp self._get_check_units_ok(exp(3.0 * model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(exp(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(exp(3.0 * model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(exp(3.0 * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sqrt self._get_check_units_ok(sqrt(3.0 * model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0 * model.x * kg**2), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0 * model.x * kg), uc, 'kg**0.5', EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0 * model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0 * model.p * kg**2), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0 * model.p * kg), uc, 'kg**0.5', EXPR.NPV_UnaryFunctionExpression) # asinh self._get_check_units_ok(asinh(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(asinh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(asinh(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(asinh(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # acosh self._get_check_units_ok(acosh(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(acosh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(acosh(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(acosh(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # atanh self._get_check_units_ok(atanh(3.0 * model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(atanh(3.0 * kg * model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(atanh(3.0 * model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(atanh(3.0 * model.p * kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # ceil self._get_check_units_ok(ceil(kg * model.x), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(ceil(kg), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) # don't think there are combinations that fan "fail" for ceil # floor self._get_check_units_ok(floor(kg * model.x), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(floor(kg), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) # don't think there are combinations that fan "fail" for floor # test Expr_ifExpression # consistent if, consistent then/else self._get_check_units_ok( EXPR.Expr_if(IF=model.x * kg + kg >= 2.0 * kg, THEN=model.x * kg, ELSE=model.y * kg), uc, 'kg', EXPR.Expr_ifExpression) # unitless if, consistent then/else self._get_check_units_ok( EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.x * kg, ELSE=model.y * kg), uc, 'kg', EXPR.Expr_ifExpression) # consistent if, unitless then/else self._get_check_units_ok( EXPR.Expr_if(IF=model.x * kg + kg >= 2.0 * kg, THEN=model.x, ELSE=model.x), uc, None, EXPR.Expr_ifExpression) # inconsistent then/else self._get_check_units_fail( EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.x * m, ELSE=model.y * kg), uc, EXPR.Expr_ifExpression) # inconsistent then/else NPV self._get_check_units_fail( EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.p * m, ELSE=model.p * kg), uc, EXPR.Expr_ifExpression) # inconsistent then/else NPV units only self._get_check_units_fail( EXPR.Expr_if(IF=model.x >= 2.0, THEN=m, ELSE=kg), uc, EXPR.Expr_ifExpression) # test EXPR.IndexTemplate and GetItemExpression model.S = Set() i = EXPR.IndexTemplate(model.S) j = EXPR.IndexTemplate(model.S) self._get_check_units_ok(i, uc, None, EXPR.IndexTemplate) model.mat = Var(model.S, model.S) self._get_check_units_ok(model.mat[i, j + 1], uc, None, EXPR.GetItemExpression) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef = ExternalFunction(python_callback_function) self._get_check_units_ok(model.ef(model.x, model.y), uc, None, EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef(1.0, 2.0), uc, None, EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef(model.x * kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef(2.0 * kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef2 = ExternalFunction(python_callback_function, units=uc.kg) self._get_check_units_ok(model.ef2(model.x, model.y), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef2(1.0, 2.0), uc, 'kg', EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef2(model.x * kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef2(2.0 * kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef3 = ExternalFunction(python_callback_function, units=uc.kg, arg_units=[uc.kg, uc.m]) self._get_check_units_fail(model.ef3(model.x, model.y), uc, EXPR.ExternalFunctionExpression) self._get_check_units_fail(model.ef3(1.0, 2.0), uc, EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef3(model.x * kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef3(2.0 * kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) self._get_check_units_ok(model.ef3(2.0 * kg, 1.0 * uc.m), uc, 'kg', EXPR.NPV_ExternalFunctionExpression) self._get_check_units_ok(model.ef3(model.x * kg, model.y * m), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef3(model.xkg, model.ym), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_fail(model.ef3(model.ym, model.xkg), uc, EXPR.ExternalFunctionExpression, InconsistentUnitsError)
def test_nonnegativity_transformation_2(self): self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals) self.model.y3 = Var(self.model.S, domain=NegativeIntegers) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1 or args[0] == 0: return NonNegativeReals elif args[0] == 1: return NonNegativeIntegers elif args[0] == 2: return NonPositiveReals elif args[0] == 3: return NonPositiveIntegers elif args[0] == 4: return NegativeReals elif args[0] == 5: return NegativeIntegers elif args[0] == 6: return PositiveReals elif args[0] == 7: return PositiveIntegers elif args[0] == 8: return Reals elif args[0] == 9: return Integers elif args[0] == 10: return Binary else: return NonNegativeReals self.model.x4 = Var(domain=domainRule) self.model.y4 = Var(self.model.S, domain=domainRule) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule) instance = self.model.create_instance() xfrm = TransformationFactory('core.nonnegative_vars') transformed = xfrm.create_using(instance) # Make sure everything is nonnegative for c in ('x', 'y', 'z'): for n in ('1', '2', '3', '4'): var = transformed.__getattribute__(c + n) for ndx in var._index: self.assertTrue(self.nonnegativeBounds(var[ndx]))
def __init__(self, y, x, cutactive, cet=CET_ADDI, fun=FUN_PROD, rts=RTS_VRS): """CNLS+G model 1 Args: y (float): output variable. x (float): input variables. cutactive (float): active concavity constraint. cet (String, optional): CET_ADDI (additive composite error term) or CET_MULT (multiplicative composite error term). Defaults to CET_ADDI. fun (String, optional): FUN_PROD (production frontier) or FUN_COST (cost frontier). Defaults to FUN_PROD. rts (String, optional): RTS_VRS (variable returns to scale) or RTS_CRS (constant returns to scale). Defaults to RTS_VRS. """ # TODO(error/warning handling): Check the configuration of the model exist self.x = x self.y = y self.cet = cet self.fun = fun self.rts = rts self.cutactive = cutactive # Initialize the CNLS model self.__model__ = ConcreteModel() # Initialize the sets self.__model__.I = Set(initialize=range(len(self.y))) self.__model__.J = Set(initialize=range(len(self.x[0]))) # Initialize the variables self.__model__.alpha = Var(self.__model__.I, doc='alpha') self.__model__.beta = Var(self.__model__.I, self.__model__.J, bounds=(0.0, None), doc='beta') self.__model__.epsilon = Var(self.__model__.I, doc='residual') self.__model__.frontier = Var(self.__model__.I, bounds=(0.0, None), doc='estimated frontier') # Setup the objective function and constraints self.__model__.objective = Objective(rule=self.__objective_rule(), sense=minimize, doc='objective function') self.__model__.regression_rule = Constraint( self.__model__.I, rule=self.__regression_rule(), doc='regression equation') if self.cet == CET_MULT: self.__model__.log_rule = Constraint( self.__model__.I, rule=self.__log_rule(), doc='log-transformed regression equation') self.__model__.afriat_rule = Constraint( self.__model__.I, rule=self.__afriat_rule(), doc='elementary Afriat approach') self.__model__.sweet_rule = Constraint(self.__model__.I, self.__model__.I, rule=self.__sweet_rule(), doc='sweet spot approach') # Optimize model self.optimization_status = 0 self.problem_status = 0
def test_nonnegative_transform_3(self): self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals, bounds=(-10, 10)) self.model.y3 = Var(self.model.S, domain=NegativeIntegers, bounds=(-10, 10)) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals, bounds=(-10, 10)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1: arg = 0 else: arg = args[1] if len(args) == 1 or arg == 0: return NonNegativeReals elif arg == 1: return NonNegativeIntegers elif arg == 2: return NonPositiveReals elif arg == 3: return NonPositiveIntegers elif arg == 4: return NegativeReals elif arg == 5: return NegativeIntegers elif arg == 6: return PositiveReals elif arg == 7: return PositiveIntegers elif arg == 8: return Reals elif arg == 9: return Integers elif arg == 10: return Binary else: return Reals self.model.x4 = Var(domain=domainRule, bounds=(-10, 10)) self.model.y4 = Var(self.model.S, domain=domainRule, bounds=(-10, 10)) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule, bounds=(-10, 10)) def objRule(model): return sum(5*sum_product(model.__getattribute__(c+n)) \ for c in ('x', 'y', 'z') for n in ('1', '2', '3', '4')) self.model.obj = Objective(rule=objRule) transform = TransformationFactory('core.nonnegative_vars') instance = self.model.create_instance() transformed = transform.create_using(instance) opt = SolverFactory("glpk") instance_sol = opt.solve(instance) transformed_sol = opt.solve(transformed) self.assertEqual( instance_sol["Solution"][0]["Objective"]['obj']["value"], transformed_sol["Solution"][0]["Objective"]['obj']["value"])
def add_model_components(m, d, scenario_directory, subproblem, stage): """ The following Pyomo model components are defined in this module: +-------------------------------------------------------------------------+ | Sets | +=========================================================================+ | | :code:`STOR` | | | | The set of projects of the :code:`stor` operational type. | +-------------------------------------------------------------------------+ | | :code:`STOR_OPR_TMPS` | | | | Two-dimensional set with projects of the :code:`stor` | | operational type and their operational timepoints. | +-------------------------------------------------------------------------+ | | :code:`STOR_LINKED_TMPS` | | | | Two-dimensional set with generators of the :code:`stor` | | operational type and their linked timepoints. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Required Input Params | +=========================================================================+ | | :code:`stor_charging_efficiency` | | | *Defined over*: :code:`STOR` | | | *Within*: :code:`PercentFraction` | | | | The storage project's charging efficiency (1 = 100% efficient). | +-------------------------------------------------------------------------+ | | :code:`stor_discharging_efficiency` | | | *Defined over*: :code:`STOR` | | | *Within*: :code:`PercentFraction` | | | | The storage project's discharging efficiency (1 = 100% efficient). | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Optional Input Params | +=========================================================================+ | | :code:`stor_losses_factor_in_rps` | | | *Within*: :code:`PercentFraction` | | | *Default*: :code:`1` | | | | The fraction of storage losses that count against the RPS target. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Linked Input Params | +=========================================================================+ | | :code:`stor_linked_starting_energy_in_storage` | | | *Defined over*: :code:`STOR_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's starting energy in storage in the linked timepoints. | +-------------------------------------------------------------------------+ | | :code:`stor_linked_discharge` | | | *Defined over*: :code:`STOR_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's dicharging in the linked timepoints. | +-------------------------------------------------------------------------+ | | :code:`stor_linked_charge` | | | *Defined over*: :code:`STOR_LINKED_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The project's charging in the linked timepoints. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Variables | +=========================================================================+ | | :code:`Stor_Charge_MW` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | Charging power in MW from this project in each timepoint in which the | | project is operational (capacity exists and the project is available). | +-------------------------------------------------------------------------+ | | :code:`Stor_Discharge_MW` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | Discharging power in MW from this project in each timepoint in which the| | project is operational (capacity exists and the project is available). | +-------------------------------------------------------------------------+ | | :code:`Stor_Starting_Energy_in_Storage_MWh` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | *Within*: :code:`NonNegativeReals` | | | | The state of charge of the storage project at the start of each | | timepoint, in MWh of energy stored. | +-------------------------------------------------------------------------+ | +-------------------------------------------------------------------------+ | Constraints | +=========================================================================+ | Power and Stage of Charge | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Charge_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Limits the project's charging power to the available capacity. | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Discharge_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Limits the project's discharging power to the available capacity. | +-------------------------------------------------------------------------+ | | :code:`Stor_Energy_Tracking_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Tracks the amount of energy stored in each timepoint based on the | | previous timepoint's energy stored and the charge and discharge | | decisions. | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Energy_in_Storage_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Limits the project's total energy stored to the available energy | | capacity. | +-------------------------------------------------------------------------+ | Reserves | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Headroom_Power_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Limits the project's upward reserves based on available headroom. | | Going from charging to non-charging also counts as headroom, doubling | | the maximum amount of potential headroom. | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Footroom_Power_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Limits the project's downward reserves based on available footroom. | | Going from non-charging to charging also counts as footroom, doubling | | the maximum amount of potential footroom. | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Headroom_Energy_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Can't provide more upward reserves (times sustained duration required) | |than available energy in storage in that timepoint. | +-------------------------------------------------------------------------+ | | :code:`Stor_Max_Footroom_Energy_Constraint` | | | *Defined over*: :code:`STOR_OPR_TMPS` | | | | Can't provide more downard reserves (times sustained duration required) | | than available capacity to store energy in that timepoint. | +-------------------------------------------------------------------------+ """ # Sets ########################################################################### m.STOR = Set(within=m.PROJECTS, initialize=lambda mod: subset_init_by_param_value( mod, "PROJECTS", "operational_type", "stor")) m.STOR_OPR_TMPS = Set(dimen=2, within=m.PRJ_OPR_TMPS, initialize=lambda mod: list( set((g, tmp) for (g, tmp) in mod.PRJ_OPR_TMPS if g in mod.STOR))) m.STOR_LINKED_TMPS = Set(dimen=2) # Required Params ########################################################################### m.stor_charging_efficiency = Param(m.STOR, within=PercentFraction) m.stor_discharging_efficiency = Param(m.STOR, within=PercentFraction) # Optional Params ########################################################################### m.stor_losses_factor_in_rps = Param(default=1) # Linked Params ########################################################################### m.stor_linked_starting_energy_in_storage = Param(m.STOR_LINKED_TMPS, within=NonNegativeReals) m.stor_linked_discharge = Param(m.STOR_LINKED_TMPS, within=NonNegativeReals) m.stor_linked_charge = Param(m.STOR_LINKED_TMPS, within=NonNegativeReals) # Variables ########################################################################### m.Stor_Charge_MW = Var(m.STOR_OPR_TMPS, within=NonNegativeReals) m.Stor_Discharge_MW = Var(m.STOR_OPR_TMPS, within=NonNegativeReals) m.Stor_Starting_Energy_in_Storage_MWh = Var(m.STOR_OPR_TMPS, within=NonNegativeReals) # Expressions ########################################################################### def upward_reserve_rule(mod, g, tmp): return sum( getattr(mod, c)[g, tmp] for c in getattr(d, headroom_variables)[g]) m.Stor_Upward_Reserves_MW = Expression(m.STOR_OPR_TMPS, rule=upward_reserve_rule) def downward_reserve_rule(mod, g, tmp): return sum( getattr(mod, c)[g, tmp] for c in getattr(d, footroom_variables)[g]) m.Stor_Downward_Reserves_MW = Expression(m.STOR_OPR_TMPS, rule=downward_reserve_rule) # Constraints ########################################################################### # Power and State of Charge m.Stor_Max_Charge_Constraint = Constraint(m.STOR_OPR_TMPS, rule=max_charge_rule) m.Stor_Max_Discharge_Constraint = Constraint(m.STOR_OPR_TMPS, rule=max_discharge_rule) m.Stor_Energy_Tracking_Constraint = Constraint(m.STOR_OPR_TMPS, rule=energy_tracking_rule) m.Stor_Max_Energy_in_Storage_Constraint = Constraint( m.STOR_OPR_TMPS, rule=max_energy_in_storage_rule) # Reserves m.Stor_Max_Headroom_Power_Constraint = Constraint( m.STOR_OPR_TMPS, rule=max_headroom_power_rule) m.Stor_Max_Footroom_Power_Constraint = Constraint( m.STOR_OPR_TMPS, rule=max_footroom_power_rule) m.Stor_Max_Headroom_Energy_Constraint = Constraint( m.STOR_OPR_TMPS, rule=max_headroom_energy_rule) m.Stor_Max_Footroom_Energy_Constraint = Constraint( m.STOR_OPR_TMPS, rule=max_footroom_energy_rule)
def test_standard_form_transform_2(self): """ Same as #1, but adds constraints """ self.model.S = RangeSet(0, 10) self.model.T = Set(initialize=["foo", "bar"]) # Unindexed, singly indexed, and doubly indexed variables with # explicit bounds self.model.x1 = Var(bounds=(-3, 3)) self.model.y1 = Var(self.model.S, bounds=(-3, 3)) self.model.z1 = Var(self.model.S, self.model.T, bounds=(-3, 3)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined bounds def boundsRule(*args): return (-4, 4) self.model.x2 = Var(bounds=boundsRule) self.model.y2 = Var(self.model.S, bounds=boundsRule) self.model.z2 = Var(self.model.S, self.model.T, bounds=boundsRule) # Unindexed, singly indexed, and doubly indexed variables with # explicit domains self.model.x3 = Var(domain=NegativeReals, bounds=(-10, 10)) self.model.y3 = Var(self.model.S, domain=NegativeIntegers, bounds=(-10, 10)) self.model.z3 = Var(self.model.S, self.model.T, domain=Reals, bounds=(-10, 10)) # Unindexed, singly indexed, and doubly indexed variables with # rule-defined domains def domainRule(*args): if len(args) == 1: arg = 0 else: arg = args[1] if len(args) == 1 or arg == 0: return NonNegativeReals elif arg == 1: return NonNegativeIntegers elif arg == 2: return NonPositiveReals elif arg == 3: return NonPositiveIntegers elif arg == 4: return NegativeReals elif arg == 5: return NegativeIntegers elif arg == 6: return PositiveReals elif arg == 7: return PositiveIntegers elif arg == 8: return Reals elif arg == 9: return Integers elif arg == 10: return Binary else: return Reals self.model.x4 = Var(domain=domainRule, bounds=(-10, 10)) self.model.y4 = Var(self.model.S, domain=domainRule, bounds=(-10, 10)) self.model.z4 = Var(self.model.S, self.model.T, domain=domainRule, bounds=(-10, 10)) # Add some constraints def makeXConRule(var): def xConRule(model, var): return (-1, var, 1) def makeYConRule(var): def yConRule(model, var, s): return (-1, var[s], 1) def makeZConRule(var): def zConRule(model, var, s, t): return (-1, var[s, t], 1) for n in ('1', '2', '3', '4'): self.model.__setattr__( "x" + n + "_constraint", Constraint( rule=makeXConRule(self.model.__getattribute__("x" + n)))) self.model.__setattr__( "y" + n + "_constraint", Constraint( rule=makeYConRule(self.model.__getattribute__("y" + n)))) self.model.__setattr__( "z" + n + "_constraint", Constraint( rule=makeZConRule(self.model.__getattribute__("z" + n)))) def objRule(model): return sum(5*sum_product(model.__getattribute__(c+n)) \ for c in ('x', 'y', 'z') for n in ('1', '2', '3', '4')) self.model.obj = Objective(rule=objRule) transform = StandardForm() instance = self.model.create_instance() transformed = transform(instance) opt = SolverFactory("glpk") instance_sol = opt.solve(instance) transformed_sol = opt.solve(transformed) self.assertEqual( instance_sol["Solution"][0]["Objective"]['obj']["value"], transformed_sol["Solution"][0]["Objective"]['obj']["value"])
def _create(self, group=None): """ Parameters ---------- group : list List containing storage objects. e.g. groups=[storage1, storage2,..] """ m = self.parent_block() if group is None: return None i = {n: [i for i in n.inputs][0] for n in group} o = {n: [o for o in n.outputs][0] for n in group} self.STORAGES = Set(initialize=[n for n in group]) self.STORAGES_WITH_INVEST_FLOW_REL = Set(initialize=[ n for n in group if n.invest_relation_input_output is not None ]) def _storage_capacity_bound_rule(block, n, t): """Rule definition for bounds of capacity variable of storage n in timestep t """ bounds = (n.nominal_capacity * n.capacity_min[t], n.nominal_capacity * n.capacity_max[t]) return bounds self.capacity = Var(self.STORAGES, m.TIMESTEPS, bounds=_storage_capacity_bound_rule) # set the initial capacity of the storage for n in group: if n.initial_capacity is not None: self.capacity[n, m.TIMESTEPS[-1]] = (n.initial_capacity * n.nominal_capacity) self.capacity[n, m.TIMESTEPS[-1]].fix() # storage balance constraint def _storage_balance_rule(block, n, t): """Rule definition for the storage balance of every storage n and timestep t """ expr = 0 expr += block.capacity[n, t] expr += -block.capacity[n, m.previous_timesteps[t]] * ( 1 - n.capacity_loss[t]) expr += (-m.flow[i[n], n, t] * n.inflow_conversion_factor[t]) * m.timeincrement[t] expr += (m.flow[n, o[n], t] / n.outflow_conversion_factor[t]) * m.timeincrement[t] return expr == 0 self.balance = Constraint(self.STORAGES, m.TIMESTEPS, rule=_storage_balance_rule) def _power_coupled(block, n): """Rule definition for constraint to connect the input power and output power """ expr = ((m.InvestmentFlow.invest[n, o[n]] + m.flows[n, o[n]].investment.existing) * n.invest_relation_input_output == ( m.InvestmentFlow.invest[i[n], n] + m.flows[i[n], n].investment.existing)) return expr self.power_coupled = Constraint(self.STORAGES_WITH_INVEST_FLOW_REL, rule=_power_coupled)
def test_sim_initialization_multi_index2(self): m = self.m m.s2 = Set(initialize=[(1, 1), (2, 2)]) m.w1 = Var(m.t, m.s2) m.dw1 = DerivativeVar(m.w1) m.w2 = Var(m.s2, m.t) m.dw2 = DerivativeVar(m.w2) m.w3 = Var([0, 1], m.t, m.s2) m.dw3 = DerivativeVar(m.w3) t = IndexTemplate(m.t) def _deq1(m, t, i, j): return m.dw1[t, i, j] == m.w1[t, i, j] m.deq1 = Constraint(m.t, m.s2, rule=_deq1) def _deq2(m, *idx): return m.dw2[idx] == m.w2[idx] m.deq2 = Constraint(m.s2, m.t, rule=_deq2) def _deq3(m, i, t, j, k): return m.dw3[i, t, j, k] == m.w1[t, j, k] + m.w2[j, k, t] m.deq3 = Constraint([0, 1], m.t, m.s2, rule=_deq3) mysim = Simulator(m) self.assertIs(mysim._contset, m.t) self.assertEqual(len(mysim._diffvars), 8) self.assertTrue(_GetItemIndexer(m.w1[t, 1, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w1[t, 2, 2]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[1, 1, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w2[2, 2, t]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[0, t, 1, 1]) in mysim._diffvars) self.assertTrue(_GetItemIndexer(m.w3[1, t, 2, 2]) in mysim._diffvars) self.assertEqual(len(mysim._derivlist), 8) self.assertTrue(_GetItemIndexer(m.dw1[t, 1, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw1[t, 2, 2]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[1, 1, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw2[2, 2, t]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[0, t, 1, 1]) in mysim._derivlist) self.assertTrue(_GetItemIndexer(m.dw3[1, t, 2, 2]) in mysim._derivlist) self.assertEqual(len(mysim._templatemap), 4) self.assertTrue(_GetItemIndexer(m.w1[t, 1, 1]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w1[t, 2, 2]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[1, 1, t]) in mysim._templatemap) self.assertTrue(_GetItemIndexer(m.w2[2, 2, t]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[0, t, 1, 1]) in mysim._templatemap) self.assertFalse(_GetItemIndexer(m.w3[1, t, 2, 2]) in mysim._templatemap) self.assertEqual(len(mysim._rhsdict), 8) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1, 1])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw1[t, 2, 2])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw2[1, 1, t])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw2[2, 2, t])], Param)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw3[0, t, 1, 1])], EXPR.SumExpression)) self.assertTrue(isinstance( mysim._rhsdict[_GetItemIndexer(m.dw3[1, t, 2, 2])], EXPR.SumExpression)) self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 1, 1])].name, 'w1[{t},1,1]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw1[t, 2, 2])].name, 'w1[{t},2,2]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw2[1, 1, t])].name, 'w2[1,1,{t}]') self.assertEqual(mysim._rhsdict[_GetItemIndexer(m.dw2[2, 2, t])].name, 'w2[2,2,{t}]') self.assertEqual(len(mysim._rhsfun(0, [0] * 8)), 8) self.assertIsNone(mysim._tsim) self.assertIsNone(mysim._simsolution) m.del_component('deq1') m.del_component('deq1_index') m.del_component('deq2') m.del_component('deq2_index') m.del_component('deq3') m.del_component('deq3_index')
def _create(self, group=None): """ Create constraints for GenericCHPBlock. Parameters ---------- group : list List containing `GenericCHP` objects. e.g. groups=[ghcp1, gchp2,..] """ m = self.parent_block() if group is None: return None self.GENERICCHPS = Set(initialize=[n for n in group]) # variables self.H_F = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.H_L_FG_max = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.H_L_FG_min = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.P_woDH = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.P = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.Q = Var(self.GENERICCHPS, m.TIMESTEPS, within=NonNegativeReals) self.Y = Var(self.GENERICCHPS, m.TIMESTEPS, within=Binary) # constraint rules def _H_flow_rule(block, n, t): """Link fuel consumption to component inflow.""" expr = 0 expr += self.H_F[n, t] expr += -m.flow[list(n.fuel_input.keys())[0], n, t] return expr == 0 self.H_flow = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_flow_rule) def _Q_flow_rule(block, n, t): """Link heat flow to component outflow.""" expr = 0 expr += self.Q[n, t] expr += -m.flow[n, list(n.heat_output.keys())[0], t] return expr == 0 self.Q_flow = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_Q_flow_rule) def _P_flow_rule(block, n, t): """Link power flow to component outflow.""" expr = 0 expr += self.P[n, t] expr += -m.flow[n, list(n.electrical_output.keys())[0], t] return expr == 0 self.P_flow = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_P_flow_rule) def _H_F_1_rule(block, n, t): """Set P_woDH depending on H_F.""" expr = 0 expr += -self.H_F[n, t] expr += n.alphas[0][t] * self.Y[n, t] expr += n.alphas[1][t] * self.P_woDH[n, t] return expr == 0 self.H_F_1 = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_F_1_rule) def _H_F_2_rule(block, n, t): """Determine relation between H_F, P and Q.""" expr = 0 expr += -self.H_F[n, t] expr += n.alphas[0][t] * self.Y[n, t] expr += n.alphas[1][t] * (self.P[n, t] + n.Beta[t] * self.Q[n, t]) return expr == 0 self.H_F_2 = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_F_2_rule) def _H_F_3_rule(block, n, t): """Set upper value of operating range via H_F.""" expr = 0 expr += self.H_F[n, t] expr += - self.Y[n, t] * \ (list(n.electrical_output.values())[0].P_max_woDH[t] / list(n.electrical_output.values())[0].Eta_el_max_woDH[t]) return expr <= 0 self.H_F_3 = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_F_3_rule) def _H_F_4_rule(block, n, t): """Set lower value of operating range via H_F.""" expr = 0 expr += self.H_F[n, t] expr += - self.Y[n, t] * \ (list(n.electrical_output.values())[0].P_min_woDH[t] / list(n.electrical_output.values())[0].Eta_el_min_woDH[t]) return expr >= 0 self.H_F_4 = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_F_4_rule) def _H_L_FG_max_rule(block, n, t): """Set max. flue gas loss as share fuel flow share.""" expr = 0 expr += -self.H_L_FG_max[n, t] expr += self.H_F[n, t] * \ list(n.fuel_input.values())[0].H_L_FG_share_max[t] return expr == 0 self.H_L_FG_max_def = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_L_FG_max_rule) def _Q_max_res_rule(block, n, t): """Set maximum Q depending on fuel and electrical flow.""" expr = 0 expr += self.P[n, t] + self.Q[n, t] + self.H_L_FG_max[n, t] expr += list(n.heat_output.values())[0].Q_CW_min[t] * self.Y[n, t] expr += -self.H_F[n, t] # back-pressure characteristics or one-segment model if n.back_pressure is True: return expr == 0 else: return expr <= 0 self.Q_max_res = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_Q_max_res_rule) def _H_L_FG_min_rule(block, n, t): """Set min. flue gas loss as fuel flow share.""" # minimum flue gas losses e.g. for motoric CHPs if getattr( list(n.fuel_input.values())[0], 'H_L_FG_share_min', None): expr = 0 expr += -self.H_L_FG_min[n, t] expr += self.H_F[n, t] * \ list(n.fuel_input.values())[0].H_L_FG_share_min[t] return expr == 0 else: return Constraint.Skip self.H_L_FG_min_def = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_H_L_FG_min_rule) def _Q_min_res_rule(block, n, t): """Set minimum Q depending on fuel and eletrical flow.""" # minimum restriction for heat flows e.g. for motoric CHPs if getattr( list(n.fuel_input.values())[0], 'H_L_FG_share_min', None): expr = 0 expr += self.P[n, t] + self.Q[n, t] + self.H_L_FG_min[n, t] expr += list(n.heat_output.values())[0].Q_CW_min[t] \ * self.Y[n, t] expr += -self.H_F[n, t] return expr >= 0 else: return Constraint.Skip self.Q_min_res = Constraint(self.GENERICCHPS, m.TIMESTEPS, rule=_Q_min_res_rule)
def test_abstract_index(self): model = AbstractModel() model.A = Set() model.B = Set() model.C = model.A | model.B model.x = Expression(model.C)
def __init__(self, y, x, cet='addi', fun='prod', rts='vrs'): """ y : Output variable x : Input variables cet = "addi" : Additive composite error term = "mult" : Multiplicative composite error term fun = "prod" : Production frontier = "cost" : Cost frontier rts = "vrs" : Variable returns to scale = "crs" : Constant returns to scale """ # TODO(error/warning handling): Check the configuration of the model exist self.x = x.tolist() self.y = y.tolist() self.cet = cet self.fun = fun self.rts = rts if type(self.x[0]) != list: self.x = [] for x_value in x.tolist(): self.x.append([x_value]) # Initialize the CNLS model self.__model__ = ConcreteModel() # Initialize the sets self.__model__.I = Set(initialize=range(len(self.y))) self.__model__.J = Set(initialize=range(len(self.x[0]))) # Initialize the variables self.__model__.alpha = Var(self.__model__.I, doc='alpha') self.__model__.beta = Var(self.__model__.I, self.__model__.J, bounds=(0.0, None), doc='beta') self.__model__.epsilon = Var(self.__model__.I, doc='residual') self.__model__.frontier = Var(self.__model__.I, bounds=(0.0, None), doc='estimated frontier') # Setup the objective function and constraints self.__model__.objective = Objective(rule=self.__objective_rule(), sense=minimize, doc='objective function') self.__model__.regression_rule = Constraint( self.__model__.I, rule=self.__regression_rule(), doc='regression equation') if self.cet == "mult": self.__model__.log_rule = Constraint( self.__model__.I, rule=self.__log_rule(), doc='log-transformed regression equation') self.__model__.afriat_rule = Constraint(self.__model__.I, self.__model__.I, rule=self.__afriat_rule(), doc='afriat inequality') # Optimize model self.optimization_status = 0 self.problem_status = 0
def __init__(self, y, x, b=None, gy=[1], gx=[1], gb=None, fun='prod', tau=0.9): """ y : Output variables x : Input variables b : Undesirable output variables gy : Output directional vector gx : Input directional vector gb : Undesirable output directional vector fun = "prod" : Production frontier = "cost" : Cost frontier """ # TODO(error/warning handling): Check the configuration of the model exist self.x = x.tolist() self.y = y.tolist() self.b = b self.tau = tau self.fun = fun self.gy = self._CNLSDDF__to_1d_list(gy) self.gx = self._CNLSDDF__to_1d_list(gx) self.gb = self._CNLSDDF__to_1d_list(gb) if type(self.x[0]) != list: self.x = [] for x_value in x.tolist(): self.x.append([x_value]) if type(self.y[0]) != list: self.y = [] for y_value in y.tolist(): self.y.append([y_value]) self.__model__ = ConcreteModel() # Initialize the sets self.__model__.I = Set(initialize=range(len(self.y))) self.__model__.J = Set(initialize=range(len(self.x[0]))) self.__model__.K = Set(initialize=range(len(self.y[0]))) # Initialize the variables self.__model__.alpha = Var(self.__model__.I, doc='alpha') self.__model__.beta = Var( self.__model__.I, self.__model__.J, bounds=(0.0, None), doc='beta') self.__model__.gamma = Var( self.__model__.I, self.__model__.K, bounds=(0.0, None), doc='gamma') self.__model__.epsilon = Var(self.__model__.I, doc='residuals') self.__model__.epsilon_plus = Var( self.__model__.I, bounds=(0.0, None), doc='positive error term') self.__model__.epsilon_minus = Var( self.__model__.I, bounds=(0.0, None), doc='negative error term') if type(self.b) != type(None): self.b = b.tolist() self.gb = self._CNLSDDF__to_1d_list(gb) if type(self.b[0]) != list: self.b = [] for b_value in b.tolist(): self.b.append([b_value]) self.__model__.L = Set(initialize=range(len(self.b[0]))) self.__model__.delta = Var( self.__model__.I, self.__model__.L, bounds=(0.0, None), doc='delta') self.__model__.objective = Objective(rule=self._CQR__objective_rule(), sense=minimize, doc='objective function') self.__model__.error_decomposition = Constraint(self.__model__.I, rule=self._CQR__error_decomposition(), doc='decompose error term') self.__model__.regression_rule = Constraint(self.__model__.I, rule=self.__regression_rule(), doc='regression equation') self.__model__.translation_rule = Constraint(self.__model__.I, rule=self._CNLSDDF__translation_property(), doc='translation property') self.__model__.afriat_rule = Constraint(self.__model__.I, self.__model__.I, rule=self.__afriat_rule(), doc='afriat inequality') # Optimize model self.optimization_status = 0 self.problem_status = 0