Source code for NaivePyDECOMP.PDDDMergeModels

"""
EELT 7030 — Operation and Expansion Planning of Electric Power Systems  
Federal University of Paraná (UFPR)

Utility: PDDD Synthetic Model Generator

Author
------
Augusto Mathias Adams <augusto.adams@ufpr.br>

Description
-----------

This utility provides tools to reconstruct a deterministic representation 
of a multistage dual dynamic programming (PDDD) solution as a static Pyomo 
model. It enables post-optimization analysis by aggregating decision 
variables, dual prices, volumes, and cost structures from multiple stages 
into a single structured object.

Main Functionality
------------------
The primary function exposed by this package is:

- `generate_dummy_model(pddd_solution, yaml_data)`: Constructs a 
  fully populated `ConcreteModel` that mirrors the original optimization 
  trajectory. It fixes all relevant decision variables and duals for each 
  unit (hydro, thermal, renewable, storage), and reassembles cost 
  expressions for reporting, visualization, and interpretation.

Intended Use
------------
This package is not designed to perform optimization itself. Instead, it 
serves as a post-processing tool for exporting or inspecting results from 
a PDDD optimization workflow — for example, to be used in scenario analysis, 
LaTeX export, sensitivity evaluation, or policy verification.

Dependencies
------------
- Pyomo
- A compatible solver (e.g., GLPK, CPLEX, IPOPT)
- Auxiliary model-building functions such as:
  - `add_hydraulic_cost_expression`
  - `add_thermal_cost_expression`
  - `add_storage_cost_expression`
  - `add_renewable_cost_expression`

Examples
--------
>>> from naivepydecomp.pddd import generate_dummy_model
>>> model = generate_dummy_model(pddd_solution, yaml_data)
>>> print(value(model.OBJ))

See Also
--------
solve_pddd : The iterative algorithm that produces the input data for `generate_dummy_model`.
solve_stage_pddd : Solves a single stage of the PDDD problem and stores intermediate results.

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 typing import Any, Dict, List

from pyomo.environ import (
    ConcreteModel,
    Constraint,
    Objective,
    Set,
    value,
    minimize
)

from pyomo.contrib.latex_printer import latex_printer

from .ModelCheck import (
    has_hydro_model,
    has_thermal_model,
    has_renewable_model,
    has_storage_model
)

from .Builder import build_model_from_data as build_model

from NaivePyDECOMP.HydraulicGenerator.HydraulicEquations import (
    add_hydraulic_cost_expression
)

from NaivePyDECOMP.ThermalGenerator.ThermalEquations import (
    add_thermal_cost_expression
)

from NaivePyDECOMP.RenewableGenerator.RenewableEquations import (
    add_renewable_cost_expression
)

from NaivePyDECOMP.Storage.StorageEquations import (
    add_storage_cost_expression
)


[docs] def generate_dummy_model(pddd_solution: List[Any], yaml_data: Dict) -> ConcreteModel: """ Generates a synthetic Pyomo model representing the structure and results of a full PDDD (Deterministic Dual Dynamic Programming) optimization process. This function aggregates relevant decision variables and dual information across all stages and stores them as fixed model components. It is useful for analyzing and exporting the solution in a structured and interpretable Pyomo `ConcreteModel`, without performing any further optimization. Parameters ---------- pddd_solution : List List containing the results of each stage of the PDDD algorithm, including model objects, decision variables, shadow prices, volumes, and costs for each time stage. yaml_data : dict Dictionary parsed from the YAML configuration file, containing system metadata, unit definitions (hydro, thermal, renewable, storage), cost parameters, and other structural data used during model construction. Returns ------- model : ConcreteModel A fully populated Pyomo `ConcreteModel` object that encapsulates: - Fixed decision variables from hydro, thermal, renewable, and storage units. - Cost terms and expressions used in the original optimization. - Final values of market price (CMO), deficit penalty, and alpha values. - Sets for all units and time stages. Notes ----- - The returned model is not intended to be solved again, but rather to serve as a reference for results visualization, report generation, or post-analysis. - The cost components are reassembled using the same structure as in the original model, using the `add_*_cost_expression` helper functions. - The model object stores one time step ahead (nstages + 1) for correct alignment with stage-based formulations. Raises ------ KeyError If expected keys or values are missing from `pddd_solution` or `yaml_data`. See Also -------- solve_pddd : Function that produces the input `pddd_solution` dictionary. """ model, _ = build_model(root=yaml_data) for constraint in model.component_objects(Constraint, active=True): constraint.deactivate() start_model = pddd_solution[0]['model'] for t in model.T: model.D[t] = value(pddd_solution[t-1]['model'].D[1]) if has_hydro_model(start_model): for t in model.T: for h in model.HG: model.hydro_Q[h, t] = value( pddd_solution[t-1]['model'].hydro_Q[h, 1]) model.hydro_V[h, t] = value( pddd_solution[t-1]['model'].hydro_V[h, 1]) model.hydro_S[h, t] = value( pddd_solution[t-1]['model'].hydro_S[h, 1]) model.hydro_G[h, t] = value( pddd_solution[t-1]['model'].hydro_G[h, 1]) model.CMA = {(h, t): -value( pddd_solution[t-1]['cma'][h]) for h in model.HG for t in model.T} model.FC = {t: value(pddd_solution[t-1]['alpha']) for t in model.T} model.CMO = {t: value(pddd_solution[t-1]['cmo']) for t in model.T} model.alpha = {t: value(pddd_solution[t-1]['alpha']) for t in model.T} if has_thermal_model(start_model): for t in model.T: for g in model.TG: model.thermal_p[g, t] = value( pddd_solution[t-1]['model'].thermal_p[g, 1]) if has_renewable_model(start_model): for t in model.T: for r in model.RU: model.renewable_gen[r, t] = value( pddd_solution[t-1]['model'].renewable_gen[r, 1]) if has_storage_model(start_model): for t in model.T: for s in model.SU: model.storage_E[s, t] = value( pddd_solution[t-1]['model'].storage_E[s, 1]) model.storage_ch[s, t] = value( pddd_solution[t-1]['model'].storage_ch[s, 1]) model.storage_dis[s, t] = value( pddd_solution[t-1]['model'].storage_dis[s, 1]) return model