diff --git a/dynamics.py b/dynamics.py
index 096a6b0ce2c4264adee5fdbb94999020aea14f5c..11c84166dd0289ec931bd08a92e2333c1ad20c56 100644
--- a/dynamics.py
+++ b/dynamics.py
@@ -1247,10 +1247,26 @@ class AssignmentCommon(Assignment):
         return target_variable
 
 
-class AggregatedDynamic(Dynamic):
-    # dynamic: The dynamic of the time series that was aggregated
+# hold information about the architecture of a model or result
+class Architecture(abc.ABC):
+    # returns the dynamic that a model constructed using this architecture should hold
+    @abc.abstractmethod
+    def model_dynamic(self) -> Dynamic:
+        pass
+
+
+class TrivialArchitecture(Architecture):
+    def __init__(self, dynamic):
+        self.dynamic = dynamic
+
+    def model_dynamic(self) -> Dynamic:
+        return self.dynamic
+
+
+class PeriodAggregation(Architecture):
+    # dynamic: the dynamic that is aggregated
     # periods: the segment lengths of all periods
-    # period_order: the order the periods have arranged to reconstruct the original time series
+    # period_order: the order the periods have to be arranged in order to reconstruct the original dynamic
     def __init__(
         self, dynamic: Dynamic, periods: List[List[int]], period_order: List[int]
     ):
@@ -1277,14 +1293,13 @@ class AggregatedDynamic(Dynamic):
             )
 
         self.dynamic = dynamic
-        self.dynamic_tree = DynamicTree(
+        root_dynamic = DynamicTree(
             np.full(number_of_time_steps_per_period, step_length, dtype=int)
-        )
-        root_dynamic = self.dynamic_tree.root()
+        ).root()
         self.period_dynamics = []
         self.n_p = []
         running_index = 0
-        self.offsets = []
+        offsets = []
         for i, period in enumerate(periods):
             index = 0
             period_indices = [0]
@@ -1299,12 +1314,13 @@ class AggregatedDynamic(Dynamic):
             self.n_p.append(
                 sum(1 for period_index in period_order if period_index == i)
             )
-            self.offsets.append((running_index, running_index + len(period)))
+            offsets.append((running_index, running_index + len(period)))
             running_index += len(period)
-        self.period_order = period_order
         self.n = len(period_order)
-        self.shape_ = running_index
-        self.frist_state_shape_ = len(periods)
+
+        self.value_dynamic = AggregatedDynamic(
+            self, len(periods), running_index, offsets
+        )
 
         # sanity check 5: all elements in period order are valid period indices (above n_p[i] is the number of times that period index i appears in period order -> sum(n_p) == len(period_order))
         if sum(n_p for n_p in self.n_p) != len(period_order):
@@ -1315,17 +1331,31 @@ class AggregatedDynamic(Dynamic):
     def number_of_periods(self) -> int:
         return len(self.period_dynamics)
 
-    def offset(self, period_index) -> Tuple[int]:
-        return self.offsets[period_index]
+    def model_dynamic(self) -> Dynamic:
+        return self.dynamic
+
+
+class AggregatedDynamic(Dynamic):
+    def __init__(
+        self,
+        period_aggregation: PeriodAggregation,
+        number_of_periods: int,
+        length: int,
+        offsets: List[Tuple[int, int]],
+    ):
+        self.period_aggregation = period_aggregation
+        self.number_of_periods = number_of_periods
+        self.length = length
+        self.offsets = offsets
 
     def number_of_steps(self) -> int:
-        return self.dynamic.number_of_steps()
+        raise NotImplementedError
 
     def shape(self) -> int:
-        return self.shape_
+        return self.length
 
     def first_state_shape(self) -> int:
-        return self.frist_state_shape_
+        return self.number_of_periods
 
     def pandas_index(self) -> pd.Index:
         first_level = np.empty(self.shape(), dtype=int)
@@ -1336,31 +1366,31 @@ class AggregatedDynamic(Dynamic):
         return pd.MultiIndex.from_arrays([first_level, second_level])
 
     def first_state_pandas_index(self) -> pd.Index:
-        first_level = np.arange(0, self.number_of_periods(), dtype=int)
+        first_level = np.arange(0, self.first_state_shape(), dtype=int)
         second_level = np.full(self.first_state_shape(), -1, dtype=int)
         return pd.MultiIndex.from_arrays([first_level, second_level])
 
     def step_size(self, position) -> float:
-        return self.dynamic.step_size(position)
+        raise NotImplementedError
 
     def step_lengths(self):  # type hinting a np-array of ints
-        return self.dynamic.step_lengths()
+        raise NotImplementedError
 
     def _all_indices(self):  # type hinting a np-array of ints
-        return self.dynamic._all_indices()
+        raise NotImplementedError
 
 
 class PeriodDynamic(Dynamic):
-    # aggregated_dynamic: The aggregated dynamic that is period originates from
-    # period_index: The index in the period aggregation that this period represents
-    # dynamic: The dynamic of this period
+    # period_aggregation: the period aggregation that is period originates from
+    # period_index: the index of this period in the period aggregation
+    # dynamic: the dynamic of this period
     def __init__(
         self,
-        aggregated_dynamic: AggregatedDynamic,
+        period_aggregation: PeriodAggregation,
         period_index: int,
         dynamic: BackedDynamic,
     ):
-        self.aggregated_dynamic = aggregated_dynamic
+        self.period_aggregation = period_aggregation
         self.period_index = period_index
         self.dynamic = dynamic
 
@@ -1396,7 +1426,7 @@ def resample(
         return values
     elif isinstance(dynamic, TreeDynamic) and isinstance(target_dynamic, TreeDynamic):
         if dynamic.root() != target_dynamic.root():
-            raise ValueError("Both dynamics have to have the same root dynamic!")
+            raise ValueError("Both dynamics have to be part of the same dynamic tree!")
         (
             assignment,
             source_start,
@@ -1410,11 +1440,11 @@ def resample(
     elif isinstance(dynamic, AggregatedDynamic) and isinstance(
         target_dynamic, PeriodDynamic
     ):
-        if target_dynamic.aggregated_dynamic != dynamic:
+        if dynamic.period_aggregation != target_dynamic.period_aggregation:
             raise ValueError(
-                f"The period dynamic has to be part of the aggregated dynamic!"
+                f"The aggregated dynamic and the period dynamic have to be part of the same period aggregation!"
             )
-        offset = dynamic.offset(target_dynamic.period_index)
+        offset = dynamic.offsets[target_dynamic.period_index]
         return values[:, offset[0] : offset[1]]
     else:
         raise ValueError(
@@ -1429,7 +1459,7 @@ def resample_into(
         target_values[:] = values
     elif isinstance(dynamic, TreeDynamic) and isinstance(target_dynamic, TreeDynamic):
         if dynamic.root() != target_dynamic.root():
-            raise ValueError("Both dynamics have to have the same root dynamic!")
+            raise ValueError("Both dynamics have to be part of the same dynamic tree!")
         (
             assignment,
             source_start,
@@ -1443,11 +1473,11 @@ def resample_into(
     elif isinstance(dynamic, PeriodDynamic) and isinstance(
         target_dynamic, AggregatedDynamic
     ):
-        if dynamic.aggregated_dynamic != target_dynamic:
+        if dynamic.period_aggregation != target_dynamic.period_aggregation:
             raise ValueError(
-                f"The period dynamic has to be part of the aggregated dynamic!"
+                f"The period dynamic and the aggregated dynamic have to be part of the same period aggregation!"
             )
-        offset = target_dynamic.offset(dynamic.period_index)
+        offset = target_dynamic.offsets[dynamic.period_index]
         target_values[:, offset[0] : offset[1]] = values
     else:
         raise ValueError(
@@ -1462,14 +1492,14 @@ def resample_first_state_into(
         target_values[:] = values
     elif isinstance(dynamic, TreeDynamic) and isinstance(target_dynamic, TreeDynamic):
         if dynamic.root() != target_dynamic.root():
-            raise ValueError("Both dynamics have to have the same root dynamic!")
+            raise ValueError("Both dynamics have to be part of the same dynamic tree!")
         target_values[:] = values
     elif isinstance(dynamic, PeriodDynamic) and isinstance(
         target_dynamic, AggregatedDynamic
     ):
-        if dynamic.aggregated_dynamic != target_dynamic:
+        if dynamic.period_aggregation != target_dynamic.period_aggregation:
             raise ValueError(
-                f"The period dynamic has to be part of the aggregated dynamic!"
+                f"The period dynamic and the aggregated dynamic have to be part of the same period aggregation!"
             )
         target_values[:, dynamic.period_index] = values
 
@@ -1514,13 +1544,15 @@ class Profile:
             }
         elif isinstance(dynamic, AggregatedDynamic):
             df = pd.read_csv(path, index_col=[0, 1])
-            if len(df.index.levels[0]) != dynamic.number_of_periods():
+            if len(df.index.levels[0]) != dynamic.number_of_periods:
                 raise ValueError(
                     f"The number of periods in the csv file and the number of periods in the dynamic have the be the same!"
                 )
             if any(
                 len(df.loc[period_index]) != period_dynamic.number_of_steps()
-                for period_index, period_dynamic in enumerate(dynamic.period_dynamics)
+                for period_index, period_dynamic in enumerate(
+                    dynamic.period_aggregation.period_dynamics
+                )
             ):
                 raise ValueError(
                     f"The number of rows in the csv file for a period and the number of segments in that period have the be the same!"
diff --git a/optimization_model.py b/optimization_model.py
index 9638c68c1153fc26891c62312094dd2e62639762..9108e29103d32da5eb90a403a1e15ef61db08116 100644
--- a/optimization_model.py
+++ b/optimization_model.py
@@ -134,7 +134,7 @@ class OptimizationModel(OptimizationBlock):
         )
 
 class EntityResult:
-    def __init__(self, var_names, dynamic):
+    def __init__(self, var_names, architecture, dynamic):
         n_u_vars = 0
         n_i_vars = 0
         n_i_prime_vars = 0
@@ -160,6 +160,7 @@ class EntityResult:
         self.i_prime_first_result = np.empty((n_i_prime_vars, dynamic.first_state_shape()), dtype=float)
         self.i_prime_result = np.empty((n_i_prime_vars, dynamic.shape()), dtype=float)
 
+        self.architecture = architecture
         self.dynamic = dynamic
 
         self.sub_results = dict()
diff --git a/topology.py b/topology.py
index 798bda3ae2fcc7b25fffd069b1399fa9ad12096b..e942c3ccb16daf9a5d047affe66d6c72b3870717 100644
--- a/topology.py
+++ b/topology.py
@@ -23,7 +23,7 @@ THE SOFTWARE.
 """
 
 from Model_Library.component.core import ComponentCommodity, ComponentKind
-from Model_Library.dynamics import AggregatedDynamic, TreeDynamic, TrivialDynamic
+from Model_Library.dynamics import PeriodAggregation, TrivialArchitecture
 from Model_Library.optimization_model import (
     EntityResult,
     OptimizationBlock,
@@ -239,8 +239,8 @@ class Topology:
             if component.match(kind=kind, commodity=commodity)
         )
 
-    def optimize(self, key, dynamic, strategy):
-        model = self.build_model(dynamic, strategy)
+    def optimize(self, key, architecture, strategy):
+        model = self.build_model(architecture, strategy)
 
         options = dict()
         options["MIPGap"] = 0.01
@@ -252,24 +252,24 @@ class Topology:
         if not model.is_ok():
             raise RuntimeError("Model is infeasible or unbounded!")
 
-        self.create_empty_entity_result(key, dynamic)
+        self.create_empty_entity_result(key, architecture)
 
         self.extract_result(model, key)
 
-    def build_model(self, dynamic, strategy):
-        model = OptimizationModel(self._name, dynamic)
+    def build_model(self, architecture, strategy):
+        model = OptimizationModel(self._name, architecture.model_dynamic())
 
-        self.fill_block(model, dynamic, strategy)
+        self.fill_block(model, architecture, strategy)
 
         model.collect_objectives()
 
         return model
 
-    def fill_block(self, block, dynamic, strategy):
+    def fill_block(self, block, architecture, strategy):
         for asset in self._assets.values():
-            asset_block = OptimizationBlock(asset._name, dynamic)
+            asset_block = OptimizationBlock(asset._name, architecture.model_dynamic())
             block.add(asset._name, asset_block)
-            asset.fill_block(asset_block, dynamic, strategy)
+            asset.fill_block(asset_block, architecture, strategy)
 
         if strategy is None:
             objectives = {"objective": []}
@@ -280,7 +280,7 @@ class Topology:
         else:
             raise ValueError(f"Invalid strategy type!")
 
-        if isinstance(block.dynamic, (TrivialDynamic, TreeDynamic)):
+        if isinstance(architecture, TrivialArchitecture):
             design_objectives = self.fill_design_block(block, objectives)
 
             operational_objectives = self.fill_operational_block(
@@ -302,14 +302,12 @@ class Topology:
                 )
                 block.add_objective(name, objective)
 
-        elif isinstance(block.dynamic, AggregatedDynamic):
+        elif isinstance(architecture, PeriodAggregation):
             design_objectives = self.fill_design_block(block, objectives)
 
             period_blocks = []
             operational_objectives = []
-            for period_index, period_dynamic in enumerate(
-                block.dynamic.period_dynamics
-            ):
+            for period_index, period_dynamic in enumerate(architecture.period_dynamics):
                 period_block = OptimizationBlock(str(period_index), period_dynamic)
                 block.add(str(period_index), period_block)
                 period_blocks.append(period_block)
@@ -323,12 +321,12 @@ class Topology:
             for name in objectives:
                 scaled_expression = 0.0
                 one_time_expression = 0.0
-                for period_index in range(block.dynamic.number_of_periods()):
+                for period_index in range(architecture.number_of_periods()):
                     (
                         period_scaled_expression,
                         period_one_time_expression,
                     ) = operational_objectives[period_index][name]
-                    scaled_expression += block.dynamic.n_p[period_index] * (
+                    scaled_expression += architecture.n_p[period_index] * (
                         period_scaled_expression
                         + pyo.quicksum(
                             term
@@ -338,15 +336,15 @@ class Topology:
                         )
                     )
                     one_time_expression += (
-                        block.dynamic.n_p[period_index] * period_one_time_expression
+                        architecture.n_p[period_index] * period_one_time_expression
                     )
                 objective = design_objectives[name] + w * (
-                    scaled_expression + (1.0 / block.dynamic.n) * one_time_expression
+                    scaled_expression + (1.0 / architecture.n) * one_time_expression
                 )
                 block.add_objective(name, objective)
 
         else:
-            raise ValueError(f"Invalid dynamic type {type(block.dynamic)}")
+            raise ValueError(f"Invalid architecture type {type(architecture)}")
 
     def fill_design_block(self, d_block, objectives):
         for component in self._components:
@@ -496,11 +494,11 @@ class Topology:
 
         return operational_objectives
 
-    def create_empty_entity_result(self, key, dynamic):
+    def create_empty_entity_result(self, key, architecture):
         for asset in self._assets.values():
-            asset.create_empty_entity_result(key, dynamic)
+            asset.create_empty_entity_result(key, architecture)
 
-        if isinstance(dynamic, (TrivialDynamic, TreeDynamic)):
+        if isinstance(architecture, TrivialArchitecture):
             base_variable_names = []
             for component in self._components:
                 base_variable_names.extend(
@@ -513,16 +511,20 @@ class Topology:
             for flow in self._flows:
                 base_variable_names.append((flow, VariableKind.INDEXED))
 
-            result = EntityResult(base_variable_names, dynamic)
+            result = EntityResult(
+                base_variable_names, architecture, architecture.dynamic
+            )
 
-        elif isinstance(dynamic, (AggregatedDynamic)):
+        elif isinstance(architecture, PeriodAggregation):
             design_base_variable_names = []
             for component in self._components:
                 design_base_variable_names.extend(
                     self._components[component].design_base_variable_names()
                 )
 
-            result = EntityResult(design_base_variable_names, dynamic)
+            result = EntityResult(
+                design_base_variable_names, architecture, architecture.dynamic
+            )
 
             operational_base_variable_names = []
             for component in self._components:
@@ -533,15 +535,15 @@ class Topology:
             for flow in self._flows:
                 operational_base_variable_names.append((flow, VariableKind.INDEXED))
 
-            for period_index, period_dynamic in enumerate(dynamic.period_dynamics):
+            for period_index, period_dynamic in enumerate(architecture.period_dynamics):
                 period_result = EntityResult(
-                    operational_base_variable_names, period_dynamic
+                    operational_base_variable_names, None, period_dynamic
                 )
 
                 result.add_sub_result(period_index, period_result)
 
         else:
-            raise ValueError(f"Invalid dynamic type {type(dynamic)}")
+            raise ValueError(f"Invalid architecture type {type(architecture)}")
 
         self._results[key] = result
         self._last_result_key = key
@@ -551,21 +553,21 @@ class Topology:
             asset_block = block.blocks[asset._name]
             asset.extract_result(asset_block, key)
 
-        if isinstance(block.dynamic, (TrivialDynamic, TreeDynamic)):
-            self._results[key].extract_result(block)
+        result = self._results[key]
 
-        elif isinstance(block.dynamic, (AggregatedDynamic)):
-            self._results[key].extract_result(block)
+        if isinstance(result.architecture, TrivialArchitecture):
+            result.extract_result(block)
 
-            for period_index in range(block.dynamic.number_of_periods()):
+        elif isinstance(result.architecture, PeriodAggregation):
+            result.extract_result(block)
+
+            for period_index in range(result.architecture.number_of_periods()):
                 period_block = block.blocks[str(period_index)]
 
-                self._results[key].sub_results[period_index].extract_result(
-                    period_block
-                )
+                result.sub_results[period_index].extract_result(period_block)
 
         else:
-            raise ValueError(f"Invalid dynamic type {type(block.dynamic)}")
+            raise ValueError(f"Invalid architecture type {type(result.architecture)}")
 
     def save_results(self, path, keys=None):
         for asset in self._assets.values():