from pyomo.core import Var, Constraint, Expression
from pyomo.environ import Set, Param, value, NonNegativeReals, quicksum
import numpy as np
import logging
from .models_utils import build_annualization_factor_map, generic_fixed_om_cost_expr_rule, different_fcr_capex_cost_expr_rule, sum_installed_capacity_by_plants_set_expr_rule, add_generic_fixed_costs, add_generation_variables
from ..constants import MW_TO_KW, THERMAL_PROPERTIES_NAMES
[docs]
def initialize_thermal_sets(block, data):
# Initialize THERMAL properties
block.plants_set = Set( initialize = data['thermal_data']['Plant_id'].astype(str).tolist() )
block.properties_set = Set( initialize = THERMAL_PROPERTIES_NAMES )
logging.info(f"Thermal balancing units being considered: {list(block.plants_set)}")
####################################################################################|
# ----------------------------------- Parameters -----------------------------------|
####################################################################################|
def _add_thermal_parameters(block, df):
thermal_dict = df.stack().to_dict()
thermal_tuple_dict = {( prop, name ): thermal_dict[( name, prop )] for prop in THERMAL_PROPERTIES_NAMES for name in block.plants_set}
block.data = Param( block.properties_set, block.plants_set, initialize = thermal_tuple_dict )
# Gas prices (US$/MMBtu)
block.fuel_price = Param(
block.plants_set,
initialize={bu: block.data["FuelCost", bu] for bu in block.plants_set}
)
# Heat rate for gas combined cycle (MMBtu/MWh)
block.heat_rate = Param(
block.plants_set,
initialize = {bu: block.data["HeatRate", bu] for bu in block.plants_set}
)
#block.GasPrice, block.HR, block.FOM_GasCC, block.VOM_GasCC
# Capex for gas combined cycle units (US$/kW)
block.CAPEX_M = Param(
block.plants_set,
initialize ={bu: block.data["Capex", bu] for bu in block.plants_set}
)
# Fixed O&M for gas combined cycle (US$/kW-year)
block.FOM_M = Param(
block.plants_set,
initialize = {bu: block.data["FOM", bu] for bu in block.plants_set}
)
# Variable O&M for gas combined cycle (US$/MWh)
block.VOM_M = Param(
block.plants_set,
initialize = {bu: block.data["VOM", bu] for bu in block.plants_set}
)
block.trans_cap_cost = Param(block.plants_set, initialize=0.0)
[docs]
def add_thermal_parameters(host, data: dict):
df = data["thermal_data"].set_index("Plant_id")
_add_thermal_parameters(host.thermal, df)
r = float(data["scalars"].loc["r"].Value)
host.thermal.r = Param(initialize=r) # Interest rate
lifetimes_by_unit = {bu: df.loc[bu, "Lifetime"] for bu in host.thermal.plants_set}
fcr_values = build_annualization_factor_map(r, lifetimes_by_unit)
host.thermal.FCR = Param(host.thermal.plants_set, initialize=fcr_values) # Capital Recovery Factor - THERMAL
####################################################################################|
# ------------------------------------ Variables -----------------------------------|
####################################################################################|
[docs]
def add_thermal_variables(host):
host.thermal.plant_installed_capacity = Var(host.thermal.plants_set, domain=NonNegativeReals, initialize=0)
add_generation_variables(host.thermal, host.h, host.thermal.plants_set, domain=NonNegativeReals, initialize=0)
# Compute and set the upper bound for CapCC
hours = list(host.h)
demand_vals = np.fromiter((value(host.demand.ts_parameter[h]) for h in hours), dtype=float, count=len(hours))
nuclear_vals = np.fromiter((value(host.nuclear.ts_parameter[h]) for h in hours), dtype=float, count=len(hours))
hydro_vals = np.fromiter((value(host.hydro.ts_parameter[h]) for h in hours), dtype=float, count=len(hours))
other_vals = np.fromiter((value(host.other_renewables.ts_parameter[h]) for h in hours), dtype=float, count=len(hours))
CapCC_upper_bound_value = float(np.max(
demand_vals
- value(host.nuclear.alpha) * nuclear_vals
- value(host.hydro.alpha) * hydro_vals
- value(host.other_renewables.alpha) * other_vals
))
cap_thermal_units = sum(host.thermal.data["MaxCapacity", bu] for bu in host.thermal.plants_set)
if ( len( list(host.thermal.plants_set) ) <= 1 ):
host.thermal.plant_installed_capacity[host.thermal.plants_set[1]].setlb( host.thermal.data["MinCapacity", host.thermal.plants_set[1]] )
if ( CapCC_upper_bound_value > cap_thermal_units ):
host.thermal.plant_installed_capacity[host.thermal.plants_set[1]].setub( CapCC_upper_bound_value )
logging.warning(f"There is only one thermal balancing unit. " \
f"Upper bound for Capacity variable was set to {CapCC_upper_bound_value} instead of the input = {cap_thermal_units} to ensure feasibility.")
else:
host.thermal.plant_installed_capacity[host.thermal.plants_set[1]].setub( host.thermal.data["MaxCapacity", host.thermal.plants_set[1]] )
else:
for bu in host.thermal.plants_set:
host.thermal.plant_installed_capacity[bu].setub( host.thermal.data["MaxCapacity", bu] )
host.thermal.plant_installed_capacity[bu].setlb( host.thermal.data["MinCapacity", bu] )
if ( CapCC_upper_bound_value > cap_thermal_units ):
logging.warning(f"Total allowed capacity for thermal units is {cap_thermal_units}MW. This value might be insufficient to achieve problem feasibility, consider increase it to at least {CapCC_upper_bound_value}MW.")
####################################################################################|
# ----------------------------------- Expressions ----------------------------------|
####################################################################################|
[docs]
def total_thermal_expr_rule(m):
"""
Expression to calculate the total generation from thermal units.
Parameters:
m: The optimization model instance.
h: Time period index.
bu: Balancing unit index.
Returns:
The sum of generation from the specified thermal unit across all time periods.
"""
return sum(m.GenCC[h, bu] for h in m.h for bu in m.thermal.plants_set)
def _add_thermal_expressions(block, set_hours):
block.total_plant_generation = Expression(block.plants_set, rule=lambda m, bu: quicksum(m.generation[h, bu] for h in set_hours))
block.total_generation = Expression(rule=quicksum(block.total_plant_generation[bu] for bu in block.plants_set))
block.total_installed_capacity = Expression( rule = sum_installed_capacity_by_plants_set_expr_rule )
block.fixed_om_cost_expr = Expression( rule = generic_fixed_om_cost_expr_rule )
block.capex_cost_expr = Expression( rule = different_fcr_capex_cost_expr_rule )
block.total_fuel_cost_expr = Expression(
rule = quicksum(
( block.fuel_price[bu] * block.heat_rate[bu] ) * ( block.total_plant_generation[bu] )
for bu in block.plants_set )
)
block.total_vom_cost_expr = Expression(
rule = quicksum(block.VOM_M[bu] * block.total_plant_generation[bu] for bu in block.plants_set)
)
[docs]
def add_thermal_expressions(host):
_add_thermal_expressions(host.thermal, host.h)
####################################################################################|
# ----------------------------------- Constraints ----------------------------------|
####################################################################################|
[docs]
def add_thermal_constraints( host ):
set_hours = host.h
# Capacity of the backup generation
host.thermal.capacity_generation_constraint = Constraint( set_hours, host.thermal.plants_set, rule = lambda m,h,bu: m.plant_installed_capacity[bu] >= m.generation[h,bu] )
####################################################################################|
# -----------------------------------= Add_costs -----------------------------------|
####################################################################################|
[docs]
def add_thermal_fixed_costs(host):
"""
Add cost-related variables for thermal units to the model.
Parameters:
model: The optimization model to which thermal cost variables will be added.
Returns:
Costs sum for each thermal unit, including capital and fixed O&M costs.
"""
return (
add_generic_fixed_costs(host.thermal)
)
[docs]
def add_thermal_variable_costs(host):
"""
Add variable costs (Fuel cost + VOM cost) for thermal units to the model.
Parameters:
model: The optimization model to which thermal variable costs will be added.
Returns:
Variable costs sum for thermal units, including fuel costs.
"""
return (
host.thermal.total_fuel_cost_expr + host.thermal.total_vom_cost_expr
)