From 24a6ed1c17df57946feb5f593df531a089b1cae0 Mon Sep 17 00:00:00 2001
From: "christoph.von.oy" <christoph.von.oy@rwth-aachen.de>
Date: Thu, 2 May 2024 16:00:42 +0200
Subject: [PATCH] Moved VariableKind into base variable names

Is necessary for constructing an EntityResult withoud a constructed
OptimizationModel
---
 component/adapter.py     | 11 +++++++++--
 component/core.py        | 27 +++++++++++++++++++--------
 component/electricity.py |  3 ++-
 component/heat.py        |  3 ++-
 optimization_model.py    | 28 ++++++----------------------
 topology.py              | 10 +++++++---
 6 files changed, 45 insertions(+), 37 deletions(-)

diff --git a/component/adapter.py b/component/adapter.py
index 2446819..b629746 100644
--- a/component/adapter.py
+++ b/component/adapter.py
@@ -29,6 +29,7 @@ from Model_Library.component.core import (
     ComponentKind,
 )
 from Model_Library.dynamics import Profile, resample, resample_variable
+from Model_Library.optimization_model import VariableKind
 
 import pyomo.environ as pyo
 
@@ -61,7 +62,10 @@ class AssetAdapter(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1", self.name + ".output_1"]
+        return [
+            (self.name + ".input_1", VariableKind.INDEXED),
+            (self.name + ".output_1", VariableKind.INDEXED),
+        ]
 
     def _build_operational_model(self, d_block, o_block):
         input = pyo.Var(o_block.T, bounds=(0, None))
@@ -123,7 +127,10 @@ class MemberAdapter(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1", self.name + ".output_1"]
+        return [
+            (self.name + ".input_1", VariableKind.INDEXED),
+            (self.name + ".output_1", VariableKind.INDEXED),
+        ]
 
     def _build_operational_model(self, d_block, o_block):
         input = pyo.Var(o_block.T, bounds=(0, None))
diff --git a/component/core.py b/component/core.py
index a67777a..7fc3987 100644
--- a/component/core.py
+++ b/component/core.py
@@ -22,8 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
 
-from Model_Library.utility import design_annuity, operational_annuity
 from Model_Library.dynamics import resample
+from Model_Library.utility import design_annuity, operational_annuity
+from Model_Library.optimization_model import VariableKind
 
 from enum import Enum
 import json
@@ -132,7 +133,7 @@ class AbstractComponent:
         if self.capacity is None:
             return []
         else:
-            return [self.name + ".capacity"]
+            return [(self.name + ".capacity", VariableKind.UNINDEXED)]
 
     def operational_base_variable_names(self):
         pass
@@ -325,7 +326,10 @@ class BaseBusBar(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1", self.name + ".output_1"]
+        return [
+            (self.name + ".input_1", VariableKind.INDEXED),
+            (self.name + ".output_1", VariableKind.INDEXED),
+        ]
 
     def _build_operational_model(self, d_block, o_block):
         input = pyo.Var(o_block.T, bounds=(0, None))
@@ -379,7 +383,7 @@ class BaseComponent(AbstractComponent):
         dep_var_2 = self.conversion_2[1] if self.conversion_2 is not None else None
 
         self.operational_variables = [
-            self.name + "." + var
+            (self.name + "." + var, VariableKind.INDEXED)
             for var in ["input_1", "input_2", "output_1", "output_2"]
             if var in [self.indep_var_1, self.indep_var_2, dep_var_1, dep_var_2]
         ]
@@ -543,7 +547,7 @@ class BaseConsumption(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1"]
+        return [(self.name + ".input_1", VariableKind.INDEXED)]
 
     def _build_operational_model(self, d_block, o_block):
         input = pyo.Var(o_block.T, bounds=(0, None))
@@ -586,7 +590,7 @@ class BaseGeneration(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".output_1"]
+        return [(self.name + ".output_1", VariableKind.INDEXED)]
 
     def _build_operational_model(self, d_block, o_block):
         output = pyo.Var(o_block.T, bounds=(0, None))
@@ -655,7 +659,10 @@ class BaseGrid(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1", self.name + ".output_1"]
+        return [
+            (self.name + ".input_1", VariableKind.INDEXED),
+            (self.name + ".output_1", VariableKind.INDEXED),
+        ]
 
     def _build_operational_model(self, d_block, o_block):
         input = pyo.Var(o_block.T, bounds=(0, None))
@@ -784,7 +791,11 @@ class BaseStorage(AbstractComponent):
         return match_kind and match_commodity
 
     def operational_base_variable_names(self):
-        return [self.name + ".input_1", self.name + ".output_1", self.name + ".energy"]
+        return [
+            (self.name + ".input_1", VariableKind.INDEXED),
+            (self.name + ".output_1", VariableKind.INDEXED),
+            (self.name + ".energy", VariableKind.EXTENDED_INDEXED),
+        ]
 
     def _build_operational_model(self, d_block, o_block):
         capacity = d_block.component_dict[self.name + ".capacity"]
diff --git a/component/electricity.py b/component/electricity.py
index c738895..15202c8 100644
--- a/component/electricity.py
+++ b/component/electricity.py
@@ -32,6 +32,7 @@ from Model_Library.component.core import (
     ComponentCommodity,
 )
 from Model_Library.dynamics import resample
+from Model_Library.optimization_model import VariableKind
 
 import json
 import pyomo.environ as pyo
@@ -128,7 +129,7 @@ class PVGenerator(BaseComponent):
         self.noct = model["NOCT"]
         self.temperature_coefficient = model["temperature_coefficient"]
 
-        self.operational_variables = [self.name + ".output_1"]
+        self.operational_variables = [(self.name + ".output_1", VariableKind.INDEXED)]
 
     def _build_operational_model(self, d_block, o_block):
         if hasattr(self, "power_factors"):
diff --git a/component/heat.py b/component/heat.py
index ea51bd1..b841a5c 100644
--- a/component/heat.py
+++ b/component/heat.py
@@ -30,6 +30,7 @@ from Model_Library.component.core import (
     ComponentCommodity,
 )
 from Model_Library.dynamics import resample
+from Model_Library.optimization_model import VariableKind
 
 import pyomo.environ as pyo
 
@@ -126,7 +127,7 @@ class SolarThermalCollector(BaseComponent):
         self.irradiance = resample(configuration["irradiance"], dynamic)
 
     def _setup_conversions(self, configuration):
-        self.operational_variables = [self.name + ".output_1"]
+        self.operational_variables = [(self.name + ".output_1", VariableKind.INDEXED)]
 
     def _build_operational_model_new(self, d_block, o_block):
         irradiance = resample(self.irradiance, o_block.dynamic).values
diff --git a/optimization_model.py b/optimization_model.py
index ecdc6e9..31f2db6 100644
--- a/optimization_model.py
+++ b/optimization_model.py
@@ -56,7 +56,6 @@ class OptimizationBlock:
         self.T_prime = self.block.T_prime
 
         self.component_dict = dict()
-        self.var_kinds = dict()
         self.objectives = dict()
         self.general_scaled_expression = []
         self.blocks = dict()
@@ -72,23 +71,9 @@ class OptimizationBlock:
             self.block.add_component(name, component.block)
         if isinstance(component, pyo.Var):
             self.component_dict[name] = component
-            if component.is_indexed() and component.index_set() == self.T:
-                self.var_kinds[name] = VariableKind.INDEXED
-            elif not component.is_indexed():
-                self.var_kinds[name] = VariableKind.UNINDEXED
-            elif component.is_indexed() and component.index_set() == self.T_prime:
-                self.var_kinds[name] = VariableKind.EXTENDED_INDEXED
-            else:
-                raise ValueError(
-                    "A variable to be added can only be unindexed or indexed over T or T_prime!"
-                )
         elif isinstance(component, pyo.Expression):
             self.component_dict[name] = component
         elif isinstance(component, pyo.Param):
-            if (
-                not component.is_indexed()
-            ):  # Capacity can be a parameter -> fix is to add all pyo.Param and pyo.Expression to u_vars/i_vars/i_prime_vars
-                self.var_kinds[name] = VariableKind.UNINDEXED
             self.component_dict[name] = component
 
     def add_general_scaled_expression_term(self, term):
@@ -110,7 +95,7 @@ class OptimizationBlock:
         return objectives
 
     def extract_result(self, var_names):
-        result = EntityResult(var_names, self.var_kinds, self.dynamic)
+        result = EntityResult(var_names, self.dynamic)
 
         result.extract_result(self)
 
@@ -154,20 +139,19 @@ class OptimizationModel(OptimizationBlock):
         )
 
 class EntityResult:
-    def __init__(self, var_names, var_kinds, dynamic):
+    def __init__(self, var_names, dynamic):
         n_u_vars = 0
         n_i_vars = 0
         n_i_prime_vars = 0
         self.var_kinds = dict()
-        for var_name in var_names:
-            kind = var_kinds[var_name]
-            if kind == VariableKind.INDEXED:
+        for var_name, var_kind in var_names:
+            if var_kind == VariableKind.INDEXED:
                 self.var_kinds[var_name] = ('i_result', n_i_vars)
                 n_i_vars += 1
-            elif kind == VariableKind.UNINDEXED:
+            elif var_kind == VariableKind.UNINDEXED:
                 self.var_kinds[var_name] = ('u_result', n_u_vars)
                 n_u_vars += 1
-            elif kind == VariableKind.EXTENDED_INDEXED:
+            elif var_kind == VariableKind.EXTENDED_INDEXED:
                 self.var_kinds[var_name] = ('i_prime_result', n_i_prime_vars)
                 n_i_prime_vars += 1
                 
diff --git a/topology.py b/topology.py
index cf6408e..550ce08 100644
--- a/topology.py
+++ b/topology.py
@@ -24,7 +24,11 @@ THE SOFTWARE.
 
 from Model_Library.component.core import ComponentCommodity, ComponentKind
 from Model_Library.dynamics import AggregatedDynamic, TreeDynamic, TrivialDynamic
-from Model_Library.optimization_model import OptimizationBlock, OptimizationModel
+from Model_Library.optimization_model import (
+    OptimizationBlock,
+    OptimizationModel,
+    VariableKind,
+)
 
 from enum import Enum
 import importlib
@@ -507,7 +511,7 @@ class Topology:
                 )
 
             for flow in self._flows:
-                base_variable_names.append(flow)
+                base_variable_names.append((flow, VariableKind.INDEXED))
 
             result = block.extract_result(base_variable_names)
 
@@ -527,7 +531,7 @@ class Topology:
                 )
 
             for flow in self._flows:
-                operational_base_variable_names.append(flow)
+                operational_base_variable_names.append((flow, VariableKind.INDEXED))
 
             for period_index in range(len(block.dynamic.period_dynamics)):
                 period_block = block.blocks[str(period_index)]
-- 
GitLab