"""
EELT 7030 — Operation and Expansion Planning of Electric Power Systems
Federal University of Paraná (UFPR)
Utility: Post-Solve Dispatch Summary and Cost Reports
Author
------
Augusto Mathias Adams <augusto.adams@ufpr.br>
Description
-----------
This module contains functions to summarize dispatch results after solving
a Pyomo-based optimization problem. It prints total generation, cost breakdowns,
and unit-level summaries for hydropower, thermal, renewable, and storage technologies.
Features:
- Total cost, demand, deficit and thermal cost components.
- Per-unit dispatch summaries with color-enhanced output (via `colorama`).
- Compatible with modular NaivePyDECOMP subsystem architecture.
References
----------
[1] CEPEL, DESSEM. Manual de Metodologia, 2023
[2] Unsihuay Vila, C. Introdução aos Sistemas de Energia Elétrica, Lecture Notes, EELT7030/UFPR, 2023.
"""
from pyomo.environ import ConcreteModel, value
from typing import Any, Tuple, Dict
from colorama import Fore, Style
from .Formatters import format_brl
from .ModelCheck import *
def __compute_total_generation(model: ConcreteModel) -> float:
"""
Compute total system generation across all technologies.
Parameters
----------
model : ConcreteModel
Solved Pyomo model instance.
Returns
-------
float
Total generated energy [MWmed] across hydro, thermal, renewable, and storage.
"""
total_generation = 0.0
if has_hydro_model(model):
total_generation += sum(value(model.hydro_G[h, t])
for h in model.HG for t in model.T)
if has_thermal_model(model):
total_generation += sum(value(model.thermal_p[g, t])
for g in model.TG for t in model.T)
if has_renewable_model(model):
total_generation += sum(value(model.renewable_gen[r, t])
for r in model.RU for t in model.T)
if has_storage_model(model):
total_generation += sum(value(model.storage_dis[s, t]
- model.storage_ch[s, t])
for s in model.SU for t in model.T)
return total_generation
def __compute_thermal_generation_cost(model: ConcreteModel) -> float:
"""
Compute total thermal generation cost based on quadratic cost function.
Parameters
----------
model : ConcreteModel
Solved Pyomo model with thermal components.
Returns
-------
float
Total generation cost [$] for thermal units.
"""
generation_cost = 0.0
if has_thermal_model(model):
generation_cost = sum(
value(model.thermal_Cost[g] * model.thermal_p[g, t])
for g in model.TG for t in model.T
)
return generation_cost
[docs]
def dispatch_summary(model: ConcreteModel) -> None:
"""
Print a complete dispatch and cost summary including:
- Total generation and demand.
- Deficit and its monetary cost.
- Thermal cost breakdown (start-up, generation).
- Overall total cost from the model objective.
Parameters
----------
model : ConcreteModel
Solved Pyomo model instance.
"""
print(f"\n{Fore.MAGENTA}{Style.BRIGHT}==================== DISPATCH SUMMARY ===================={Style.RESET_ALL}")
total_generation = __compute_total_generation(model)
Cdef = value(model.Cdef) if hasattr(model, 'Cdef') else 1000.0
print(f" {Fore.CYAN}Total Generation{Style.RESET_ALL}: {Fore.RED}{total_generation:.2f} MWmed")
required = [
'D', 'd', 'OBJ'
]
if all(hasattr(model, attr) for attr in required):
demand = sum(value(model.d[t]) for t in model.T)
print(
f" {Fore.CYAN}Total Demand{Style.RESET_ALL}: {Fore.RED}{demand:.2f} MWmed")
deficit = sum(value(model.D[t]) for t in model.T)
print(
f" {Fore.CYAN}Total Deficit{Style.RESET_ALL}: {Fore.RED}{deficit:.2f} MWmed")
cost_deficit = deficit * Cdef
print(
f" {Fore.CYAN}Total Deficil Cost{Style.RESET_ALL}: {Fore.RED} $ {format_brl(cost_deficit)}")
if has_thermal_model(model):
generation_cost = __compute_thermal_generation_cost(model)
total_cost = generation_cost
print(
f" {Fore.CYAN}Total Thermal Cost{Style.RESET_ALL}: {Fore.RED} $ {format_brl(total_cost)}")
print(
f" {Fore.CYAN}Thermal Generation Cost{Style.RESET_ALL}: {Fore.RED} $ {format_brl(generation_cost)}")
total_cost = value(model.OBJ)
print(
f" {Fore.CYAN}Total Cost{Style.RESET_ALL}: {Fore.RED} $ {format_brl(total_cost)}")
[docs]
def hydro_dispatch_summary(model: ConcreteModel) -> None:
"""
Print unit-level hydropower generation summary in MWmed.
Parameters
----------
model : ConcreteModel
Solved Pyomo model with hydropower subsystem.
"""
if has_hydro_model(model):
print(f"\n{Fore.YELLOW}Hydropower Generation:{Style.RESET_ALL}")
for h in model.HG:
dispatch = sum(value(model.hydro_G[h, t]) for t in model.T)
print(
f" {Fore.CYAN}{h}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")
[docs]
def thermal_dispatch_summary(model: ConcreteModel) -> None:
"""
Print unit-level thermal generation summary in MWmed.
Parameters
----------
model : ConcreteModel
Solved Pyomo model with thermal subsystem.
"""
if has_thermal_model(model):
print(f"{Fore.YELLOW}Thermal Generation:{Style.RESET_ALL}")
for g in model.TG:
dispatch = sum(value(model.thermal_p[g, t]) for t in model.T)
print(
f" {Fore.BLUE}{g}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")
[docs]
def renewable_dispatch_summary(model: ConcreteModel) -> None:
"""
Print unit-level renewable generation summary in MWmed.
Parameters
----------
model : ConcreteModel
Solved Pyomo model with renewable subsystem.
"""
if has_renewable_model(model):
print(f"\n{Fore.YELLOW}Renewable Generation:{Style.RESET_ALL}")
for r in model.RU:
dispatch = sum(value(model.renewable_gen[r, t]) for t in model.T)
print(
f" {Fore.GREEN}{r}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")
[docs]
def storage_dispatch_summary(model: ConcreteModel) -> None:
"""
Print unit-level storage discharge summary in MWmed.
Parameters
----------
model : ConcreteModel
Solved Pyomo model with storage subsystem.
"""
if has_storage_model(model):
print(f"\n{Fore.YELLOW}Storage Discharge:{Style.RESET_ALL}")
for s in model.SU:
dispatch = sum(value(model.storage_dis[s, t]) for t in model.T)
print(
f" {Fore.MAGENTA}{s}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")
print(f"\n{Fore.YELLOW}Storage Charge:{Style.RESET_ALL}")
for s in model.SU:
dispatch = sum(value(model.storage_ch[s, t]) for t in model.T)
print(
f" {Fore.MAGENTA}{s}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")
print(f"\n{Fore.YELLOW}Storage Delta:{Style.RESET_ALL}")
for s in model.SU:
dispatch = sum(value(model.storage_dis[s, t] - model.storage_ch[s, t]) for t in model.T)
print(f" {Fore.MAGENTA}{s}{Style.RESET_ALL}: {Fore.RED}{dispatch:.2f} MWmed")