diff --git a/dynamics/Dynamic.py b/dynamics/Dynamic.py
new file mode 100644
index 0000000000000000000000000000000000000000..b407d2f5bdb21dce9253a0507a1a0d2b8df9ac9f
--- /dev/null
+++ b/dynamics/Dynamic.py
@@ -0,0 +1,629 @@
+"""
+MIT License
+
+Copyright (c) 2023 RWTH Aachen University
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+import pandas as pd
+
+# The root dynamic is
+#   for TrivialDynamic: self
+#   for BackedDynamic: root dynamic of its reference
+#   for PartialDynamic: root dynamic of its reference
+
+# represents a continuus set of time steps relative to the global simulation start t_start
+class Dynamic:
+    # returns the root dynamic of this dynamic
+    def get_root_dynamic(self):
+        pass
+
+    # return true if other is an ancestor of this dynamic
+    def has_ancestor(self, other):
+        pass
+
+    # returns the number of steps in this dynamic
+    def number_of_steps(self):
+        pass
+
+    # returns all time steps in this dynamic
+    def time_steps(self):
+        pass
+
+    # return true if the dynamic contains the given index
+    # index is relative to the root dynamic
+    def has_index(self, index):
+        pass
+
+    # returns the indices between the given start and end positions
+    # p_start and p_end are relative to this dynamic
+    def get_indices_within_p(self, p_start, p_end):
+        pass
+
+    # returns the indices between the given start and end indices
+    # i_start and i_end are relative to the root dynamic
+    def get_indices_within(self, i_start, i_end):
+        pass
+    
+    # returns the index of the given position
+    # position is relative to this dynamic
+    def get_index_of(self, position):
+        pass
+    
+    # returns the position of the given index
+    # index is relative to the root dynamic
+    def get_position_of(self, index):
+        pass
+
+    # returns the length of the time step at the given position
+    # position is relative to this dynamic
+    def step_size_p(self, position):
+        pass
+
+    # returns the length of the time step at the given index
+    # index is relative to the root dynamic
+    def step_size(self, index):
+        pass
+    
+    # returns the length af all time steps in this dynamic
+    def step_sizes(self):
+        pass
+
+    # constructs a sub dynamic containing time steps starting at the time steps at the given positions with the last position representing the end of the last time step
+    # positions are relative to this dynamic
+    def get_sub_dynamic_p(self, positions):
+        pass
+
+    # constructs a sub dynamic containing time steps starting at the time steps at the given indices with the last index representing the end of the last time step
+    # indices are relative to the root dynamic
+    def get_sub_dynamic(self, indices):
+        pass
+
+    # construct a sub dynamic containing time steps between the given positions
+    # p_start and p_end are relative to the root dynamic
+    def get_partial_dynamic_p(self, p_start, p_end):
+        pass
+
+    # construct a sub dynamic containing time steps between the given indices
+    # i_start and i_end are relative to the root dynamic
+    def get_partial_dynamic(self, i_start, i_end):
+        pass
+
+    def to_string(self):
+        to_print = "["
+        for p in range(self.number_of_steps()):
+            to_print += "(" + str(self.get_index_of(p)) + ", " + str(self.step_size_p(p)) + ")"
+            if p != self.number_of_steps() - 1:
+                to_print += ", "
+        to_print += "]"
+        return to_print
+
+# a dynamic defined by the length of each time step
+class TrivialDynamic(Dynamic):
+    # d_step: the length all time_steps
+    def __init__(self, d_steps):
+        self.d_steps = d_steps
+
+    def get_root_dynamic(self):
+        return self
+    
+    def has_ancestor(self, other):
+        return self == other
+
+    def number_of_steps(self):
+        return len(self.d_steps)
+    
+    def time_steps(self):
+        return list(range(len(self.d_steps)))
+    
+    def has_index(self, index):
+        return 0 <= index and index <= len(self.d_steps)
+    
+    def get_indices_within_p(self, p_start, p_end):
+        return range(p_start, p_end)
+    
+    def get_indices_within(self, i_start, i_end):
+        return range(i_start, i_end)
+
+    def get_index_of(self, position):
+        return position
+    
+    def get_position_of(self, index):
+        return index
+    
+    def step_size_p(self, position):
+        return self.d_steps[position]
+    
+    def step_size(self, index):
+        return self.d_steps[index]
+    
+    def step_sizes(self):
+        return self.d_steps
+
+    def get_sub_dynamic_p(self, positions):
+        return BackedDynamic(self, positions)
+
+    def get_sub_dynamic(self, indices):
+        return BackedDynamic(self, indices)
+    
+    def get_partial_dynamic_p(self, p_start, p_end):
+        return PartialDynamic(self, p_start, p_end)
+
+    def get_partial_dynamic(self, i_start, i_end):
+        return PartialDynamic(self, i_start, i_end)
+
+# a dynamic definied by taking certain time steps form a reference dynamic
+class BackedDynamic(Dynamic):
+    # reference: the dynamic that backs this dynamic
+    # indices: the indicies of the time steps contained in this dynamic with the last index representing the end of the last time step
+    # indices are relative to the reference dynamic
+    def __init__(self, reference, indices):
+        self.reference = reference
+        self.indices = indices
+
+    def get_root_dynamic(self):
+        return self.reference.get_root_dynamic()
+    
+    def has_ancestor(self, other):
+        return self == other or self.reference.has_ancestor(other)
+
+    def number_of_steps(self):
+        return len(self.indices) - 1
+    
+    def time_steps(self):
+        return self.indices
+    
+    def has_index(self, index):
+        return index in self.indices
+    
+    def get_indices_within_p(self, p_start, p_end):
+        if p_start < 0 or len(self.indices) <= p_start or p_end < 0 or len(self.indices) <= p_end:
+            raise IndexError("The dynamic does not have indices at the positions!")
+        return self.indices[p_start:p_end]
+    
+    def get_indices_within(self, i_start, i_end):
+        p_start = self.get_position_of(i_start)
+        p_end = self.get_position_of(i_end)
+        return self.indices[p_start:p_end]
+    
+    def get_index_of(self, position):
+        if position < 0 or len(self.indices) <= position:
+            raise IndexError("The dynamic does not have a index for this position!")
+        return self.indices[position]
+    
+    def get_position_of(self, index):
+        if index not in self.indices:
+            raise IndexError('The dynamic does not have a position for this index!')
+        return self.indices.index(index)
+    
+    def step_size_p(self, position):
+        if position < 0 or len(self.indices) - 1 <= position:
+            raise IndexError("The dynamic does not have a time step at this position!")
+        return sum(self.reference.step_size(index) for index in self.reference.get_indices_within(self.indices[position], self.indices[position + 1]))
+    
+    def step_size(self, index):
+        if index not in self.indices[:-1]:
+            raise IndexError("The dynamic does not have a time step at this index!")
+        return self.step_size_p(self.indices.index(index))
+    
+    def step_sizes(self):
+        return [self.step_size_p(position) for position in range(self.number_of_steps)]
+
+    def get_sub_dynamic_p(self, positions):
+        if any(position < 0 or len(self.indices) <= position for position in positions):
+            raise IndexError("The dynamic does not have all requested indices!")
+        return BackedDynamic(self.reference, [self.indices[position] for position in positions])
+
+    def get_sub_dynamic(self, indices):
+        if any(index not in self.indices for index in indices):
+            raise IndexError("The dynamic does not have all requested indices for the sub dynamic!")
+        return BackedDynamic(self.reference, indices)
+    
+    def get_partial_dynamic_p(self, p_start, p_end):
+        if p_start < 0 or len(self.indices) - 1 <= p_start or p_end < 0 or len(self.indices) - 1 <= p_end:
+            raise IndexError("The dynamic does not have all requested positions for the sub dynamic!")
+        return PartialDynamic(self, p_start, p_end)
+
+    def get_partial_dynamic(self, i_start, i_end):
+        if i_start not in self.indices or i_end not in self.indices:
+            raise IndexError("The dynamic does not have all requested indices for the sub dynamic!")
+        return PartialDynamic(self, self.indices.index(i_start), self.indices.index(i_end))
+    
+# a dynamic defined by taking a continuus intervall of time steps from a reference dynamic
+class PartialDynamic(Dynamic):
+    # reference: the dynamic from which the intervall is taken
+    # start: the position in the reference dynamic of the first time step contained in this dynamic
+    # end: the position in the reference dynamic of the end of the last time step contained in this dynamic
+    # start/end are relative to the reference dynamic
+    def __init__(self, reference, start, end):
+        self.reference = reference
+        self.start = start
+        self.end = end
+
+    def get_root_dynamic(self):
+        return self.reference.get_root_dynamic()
+    
+    def has_ancestor(self, other):
+        return self == other or self.reference.has_ancestor(other)
+
+    def number_of_steps(self):
+        return self.end - self.start
+    
+    def time_steps(self):
+        return self.reference.time_steps()[self.start:self.end]
+    
+    def has_index(self, index):
+        reference_position = self.reference.get_position_of(index)
+        return self.start <= reference_position or reference_position <= self.end
+    
+    def get_indices_within_p(self, p_start, p_end):
+        if p_start < self.start or self.end < p_start or p_end < self.start or self.end < p_end:
+            raise IndexError("The dynamic does not have all requested indices!")
+        return self.reference.get_indices_within_p(self.start + p_start, self.start + p_end)
+    
+    def get_indices_within(self, i_start, i_end):
+        p_start = self.reference.get_position_of(i_start)
+        p_end = self.reference.get_position_of(i_end)
+        if p_start < self.start or self.end < p_start or p_end < self.start or self.end < p_end:
+            raise IndexError("The dynamic does not have all requested indices!")
+        return self.reference.get_indices_within_p(self.start + p_start, self.start + p_end)
+    
+    def get_index_of(self, position):
+        if position < 0 or self.end - self.start < position:
+            raise IndexError("The dynamic does not have a index for this position!")
+        return self.reference.get_index_of(self.start + position)
+    
+    def get_position_of(self, index):
+        reference_position = self.reference.get_position_of(index)
+        if reference_position < self.start or self.end < reference_position:
+            raise IndexError('The dynamic does not have a position for this index!')
+        return reference_position - self.start
+    
+    def step_size_p(self, position):
+        if position < 0 or self.end - self.start <= position:
+            raise IndexError("The dynamic does not have a time step at this position!")
+        return self.reference.step_size_p(self.start + position)
+    
+    def step_size(self, index):
+        reference_position = self.reference.get_position_of(index)
+        if reference_position < self.start or self.end <= reference_position:
+            raise IndexError('The dynamic does not have a time step at this index!')
+        return self.step_size_p(reference_position - self.start)
+    
+    def step_sizes(self):
+        return [self.reference.step_size_p(position) for position in range(self.start, self.end)]
+
+    def get_sub_dynamic_p(self, positions):
+        if any(position < 0 or self.end - self.start < position for position in positions):
+            raise IndexError("The dynamic does not have all requested positions for the sub dynamic!")
+        return self.reference.get_sub_dynamic_p([self.start + position for position in positions])
+
+    def get_sub_dynamic(self, indices):
+        def filter(self, index):
+            reference_position = self.reference.get_position_of(index)
+            return reference_position < self.start or self.end < reference_position
+        if any(filter(index) for index in indices):
+            raise IndexError("The does not have all requested indices for the sub dynamic!")
+        return self.reference.get_sub_dynamic(indices)
+    
+    def get_partial_dynamic_p(self, p_start, p_end):
+        if p_start < 0 or self.end - self.start < p_start or p_end < 0 or self.end - self.start < p_end:
+            raise IndexError("The dynamic does not have all requested positions for the sub dynamic!")
+        return PartialDynamic(self.reference, self.start + p_start, self.start + p_end)
+
+    def get_partial_dynamic(self, i_start, i_end):
+        reference_start = self.reference.get_position_of(i_start)
+        reference_end = self.reference.get_position_of(i_end)
+        if reference_start < self.start or self.end < reference_start or reference_end < self.start or self.end < reference_end:
+            raise IndexError("The dynamic does not have all requested indices for the sub dynamic!")
+        return PartialDynamic(self.reference, reference_start, reference_end)
+
+# only works if both dynamics share the same root dynamic
+def resample(values, dynamic, target_dynamic):
+    if dynamic.get_root_dynamic() != target_dynamic.get_root_dynamic():
+        raise ValueError("Both dynamics have to have the same root dynamic!")
+    if target_dynamic.get_index_of(0) < dynamic.get_index_of(0) or dynamic.get_index_of(dynamic.number_of_steps()) < target_dynamic.get_index_of(target_dynamic.number_of_steps()):
+        raise ValueError("The source dynamic does not cover the time steps contained in target_dynamic!")
+    non_partial_dynamic = dynamic
+    while isinstance(non_partial_dynamic, PartialDynamic):
+        non_partial_dynamic = non_partial_dynamic.reference
+    source_p_start = non_partial_dynamic.get_position_of(dynamic.get_index_of(0))
+    source_p_end = non_partial_dynamic.get_position_of(dynamic.get_index_of(dynamic.number_of_steps()))
+    non_partial_target_dynamic = target_dynamic
+    while isinstance(non_partial_target_dynamic, PartialDynamic):
+        non_partial_target_dynamic = non_partial_target_dynamic.reference
+    target_p_start = non_partial_target_dynamic.get_position_of(target_dynamic.get_index_of(0))
+    target_p_end = non_partial_target_dynamic.get_position_of(target_dynamic.get_index_of(target_dynamic.number_of_steps()))
+    if non_partial_dynamic == non_partial_target_dynamic:
+        return resample_same(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end)
+    elif isinstance(non_partial_target_dynamic, BackedDynamic) and non_partial_target_dynamic.has_ancestor(non_partial_dynamic):
+        return resample_to_backed(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end)
+    elif isinstance(non_partial_dynamic, BackedDynamic) and non_partial_dynamic.has_ancestor(non_partial_target_dynamic):
+        return resample_from_backed(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end)
+    else:
+        return resample_common_reference(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end)
+    
+def resample_into(values, dynamic, target_dynamic, target_values):
+    if dynamic.get_root_dynamic() != target_dynamic.get_root_dynamic():
+        raise ValueError("Both dynamics have to have the same root dynamic!")
+    non_partial_dynamic = dynamic
+    while isinstance(non_partial_dynamic, PartialDynamic):
+        non_partial_dynamic = non_partial_dynamic.reference
+    source_p_start = non_partial_dynamic.get_position_of(dynamic.get_index_of(0))
+    source_p_end = non_partial_dynamic.get_position_of(dynamic.get_index_of(dynamic.number_of_steps()))
+    non_partial_target_dynamic = target_dynamic
+    while isinstance(non_partial_target_dynamic, PartialDynamic):
+        non_partial_target_dynamic = non_partial_target_dynamic.reference
+    target_p_start = non_partial_target_dynamic.get_position_of(target_dynamic.get_index_of(0))
+    target_p_end = non_partial_target_dynamic.get_position_of(target_dynamic.get_index_of(target_dynamic.number_of_steps()))
+    if non_partial_dynamic == non_partial_target_dynamic:
+        return resample_into_same(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end, target_values)
+    elif isinstance(non_partial_target_dynamic, BackedDynamic) and non_partial_target_dynamic.has_ancestor(non_partial_dynamic):
+        return resample_into_to_backed(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end, target_values)
+    elif isinstance(non_partial_dynamic, BackedDynamic) and non_partial_dynamic.has_ancestor(non_partial_target_dynamic):
+        return resample_into_from_backed(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end, target_values)
+    else:
+        return resample_into_common_reference(values, non_partial_dynamic, source_p_start, source_p_end, non_partial_target_dynamic, target_p_start, target_p_end, target_values)
+
+# source_dynamic and target_dynamic are the same dynamic
+def resample_same(values, dynamic, source_p_start, source_p_end, target_dnymiac, target_p_start, target_p_end):
+    return values.loc[target_dnymiac.get_indices_within_p(target_p_start, target_p_end)]
+
+def resample_into_same(values, dynamic, source_p_start, source_p_end, target_dnymiac, target_p_start, target_p_end, target_values):
+    i_start = max(dynamic.get_index_of(source_p_start), target_dnymiac.get_index_of(target_p_start))
+    i_end = min(dynamic.get_index_of(source_p_end), target_dnymiac.get_index_of(target_p_end))
+    temp_values = values.loc[dynamic.get_indices_within(i_start, i_end)]
+    if i_start == target_dnymiac.get_index_of(target_p_start) and i_end == target_dnymiac.get_index_of(target_p_end):
+        target_values.loc[:] = temp_values
+    else:
+        target_values.loc[target_dnymiac.get_indices_within(i_start, i_end)] = temp_values
+    return target_values
+
+# target_dynamic is BackedDynamic and has dynamic as an ancestor
+def resample_to_backed(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end):
+    target_values = values.loc[target_dynamic.indices[target_p_start:target_p_end]]
+    for target_position in range(target_p_start, target_p_end):
+        acc = 0
+        sum = 0
+        for source_index in dynamic.get_indices_within(target_dynamic.indices[target_position], target_dynamic.indices[target_position + 1]):
+            acc += values.loc[source_index] * dynamic.step_size(source_index)
+            sum += dynamic.step_size(source_index)
+        target_values.loc[target_dynamic.indices[target_position]] = acc / float(sum)
+    return target_values
+
+def resample_into_to_backed(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end, target_values):
+    source_i_start = dynamic.get_index_of(source_p_start)
+    target_i_start = target_dynamic.indices[target_p_start]
+    if source_i_start == target_i_start:
+        target_p_start = target_dynamic.get_position_of(source_i_start)
+    # elif source_i_start < target_i_start: # target_p_start assigned here is the same as the input target_p_start so skip this branch
+    #     target_p_start = target_dynamic.get_position_of(target_i_start)
+    elif source_i_start > target_i_start:
+        while source_i_start not in target_dynamic.indices:
+            source_p_start += 1
+            source_i_start = dynamic.get_index_of(source_p_start)
+        target_p_start = target_dynamic.get_position_of(source_i_start)
+    source_i_end = dynamic.get_index_of(source_p_end)
+    target_i_end = target_dynamic.indices[target_p_end]
+    if source_i_end == target_i_end:
+        target_p_end = target_dynamic.get_position_of(source_i_end)
+    # elif source_i_end > target_i_end: # target_p_end assigned here is the same as the input target_p_end so skip this branch
+    #     target_p_end = target_dynamic.get_position_of(target_i_end)
+    elif source_i_end < target_i_end:
+        while source_i_end not in target_dynamic.indices:
+            source_p_end -= 1
+            source_i_end = dynamic.get_index_of(source_p_end)
+        target_p_end = target_dynamic.get_position_of(source_i_end)
+    for target_position in range(target_p_start, target_p_end):
+        acc = 0
+        sum = 0
+        for source_index in dynamic.get_indices_within(target_dynamic.indices[target_position], target_dynamic.indices[target_position + 1]):
+            acc += values.loc[source_index] * dynamic.step_size(source_index)
+            sum += dynamic.step_size(source_index)
+        target_values.loc[target_dynamic.indices[target_position]] = acc / float(sum)
+    return target_values
+
+# dynamic is BackedDynamic and has target_dynamic as an ancestor
+def resample_from_backed(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end):
+    target_values = pd.Series(dtype = values.dtype, index = (target_dynamic.get_index_of(position) for position in range(target_p_start, target_p_end)))
+    source_i_start = dynamic.indices[source_p_start]
+    target_i_start = target_dynamic.get_index_of(target_p_start)
+    if source_i_start < target_i_start:
+        if target_i_start not in dynamic.indices:
+            target_p_start_succ = target_p_start + 1
+            target_i_start_succ = target_dynamic.get_index_of(target_p_start_succ)
+            while target_i_start_succ not in dynamic.indices:
+                target_p_start_succ += 1
+                target_i_start_succ = target_dynamic.get_index_of(target_p_start_succ)
+            source_p_start = dynamic.get_position_of(target_i_start_succ)
+            target_indices = target_dynamic.get_indices_within(target_i_start, target_i_start_succ)
+            target_values.loc[target_indices] = values[dynamic.indices[source_p_start - 1]]
+        else:
+            source_p_start = dynamic.get_position_of(target_i_start)
+    source_i_end = dynamic.indices[source_p_end]
+    target_i_end = target_dynamic.get_index_of(target_p_end)
+    if target_i_end < source_i_end:
+        if target_i_end not in dynamic.indices:
+            target_p_end_prev = target_p_end - 1
+            target_i_end_prev = target_dynamic.get_index_of(target_p_end_prev)
+            while target_i_end_prev not in dynamic.indices:
+                target_p_end_prev -= 1
+                target_i_end_prev = target_dynamic.get_index_of(target_p_end_prev)
+            source_p_end = dynamic.get_position_of(target_i_end_prev)
+            target_indices = target_dynamic.get_indices_within(target_i_end_prev, target_i_end)
+            target_values.loc[target_indices] = values[dynamic.indices[source_p_end]]            
+    for source_position in range(source_p_start, source_p_end):
+        target_indices = target_dynamic.get_indices_within(dynamic.indices[source_position], dynamic.indices[source_position + 1])
+        target_values.loc[target_indices] = values[dynamic.indices[source_position]]
+    return target_values
+
+def resample_into_from_backed(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end, target_values):
+    source_i_start = dynamic.indices[source_p_start]
+    target_i_start = target_dynamic.get_index_of(target_p_start)
+    if source_i_start < target_i_start:
+        if target_i_start not in dynamic.indices:
+            target_p_start_succ = target_p_start + 1
+            target_i_start_succ = target_dynamic.get_index_of(target_p_start_succ)
+            while target_i_start_succ not in dynamic.indices:
+                target_p_start_succ += 1
+                target_i_start_succ = target_dynamic.get_index_of(target_p_start_succ)
+            source_p_start = dynamic.get_position_of(target_i_start_succ)
+            target_indices = target_dynamic.get_indices_within(target_i_start, target_i_start_succ)
+            target_values.loc[target_indices] = values[dynamic.indices[source_p_start - 1]]
+        else:
+            source_p_start = dynamic.get_position_of(target_i_start)
+    source_i_end = dynamic.indices[source_p_end]
+    target_i_end = target_dynamic.get_index_of(target_p_end)
+    if target_i_end < source_i_end:
+        if target_i_end not in dynamic.indices:
+            target_p_end_prev = target_p_end - 1
+            target_i_end_prev = target_dynamic.get_index_of(target_p_end_prev)
+            while target_i_end_prev not in dynamic.indices:
+                target_p_end_prev -= 1
+                target_i_end_prev = target_dynamic.get_index_of(target_p_end_prev)
+            source_p_end = dynamic.get_position_of(target_i_end_prev)
+            target_indices = target_dynamic.get_indices_within(target_i_end_prev, target_i_end)
+            target_values.loc[target_indices] = values[dynamic.indices[source_p_end]]            
+    for source_position in range(source_p_start, source_p_end):
+        target_indices = target_dynamic.get_indices_within(dynamic.indices[source_position], dynamic.indices[source_position + 1])
+        target_values.loc[target_indices] = values[dynamic.indices[source_position]]
+    return target_values
+
+# dynamic and target_dynamic share the same root dynamic
+def resample_common_reference(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end):
+    target_values = pd.Series(dtype = values.dtype, index = (target_dynamic.get_index_of(position) for position in range(target_p_start, target_p_end)))
+    target_i_start = target_dynamic.get_index_of(target_p_start)
+    root_dynamic = dynamic.get_root_dynamic()
+    root_p_start = root_dynamic.get_position_of(target_i_start)
+    root_i_start = target_i_start
+    length = 0
+    while not dynamic.has_index(root_i_start):
+        root_p_start -= 1
+        root_i_start = root_dynamic.get_index_of(root_p_start)
+        length += root_dynamic.step_size(root_i_start)
+    source_position = dynamic.get_position_of(root_i_start)
+    target_position = target_p_start
+    remaining_length = dynamic.step_size_p(source_position) - length
+    while target_position < target_p_end:
+        remaining_target_length = target_dynamic.step_size_p(target_position)
+        acc = 0
+        while remaining_target_length > 0:
+            if remaining_length == 0:
+                source_position += 1
+                remaining_length = dynamic.step_size_p(source_position)
+            if remaining_target_length <= remaining_length:
+                acc += values[dynamic.get_index_of(source_position)] * remaining_target_length
+                remaining_length -= remaining_target_length
+                remaining_target_length -= remaining_target_length
+            else:
+                acc += values[dynamic.get_index_of(source_position)] * remaining_length
+                remaining_target_length -= remaining_length
+                remaining_length -= remaining_length
+        target_values[target_dynamic.get_index_of(target_position)] = acc / target_dynamic.step_size_p(target_position)
+        target_position += 1
+    return target_values
+
+def resample_into_common_reference(values, dynamic, source_p_start, source_p_end, target_dynamic, target_p_start, target_p_end, target_values):
+    source_i_start = dynamic.get_index_of(source_p_start)
+    target_i_start = target_dynamic.get_index_of(target_p_start)
+    if source_i_start > target_i_start:
+        root_dynamic = dynamic.get_root_dynamic()
+        root_p_start = root_dynamic.get_position_of(source_i_start)
+        root_i_start = source_i_start
+        length = 0
+        while not target_dynamic.has_index(root_i_start):
+            length += root_dynamic.step_size(root_i_start)
+            root_p_start += 1
+            root_i_start = root_dynamic.get_index_of(root_p_start)
+        source_position = source_p_start
+        target_position = target_dynamic.get_position_of(root_i_start)
+    else:
+        root_dynamic = dynamic.get_root_dynamic()
+        root_p_start = root_dynamic.get_position_of(target_i_start)
+        root_i_start = target_i_start
+        length = 0
+        while not dynamic.has_index(root_i_start):
+            root_p_start -= 1
+            root_i_start = root_dynamic.get_index_of(root_p_start)
+            length += root_dynamic.step_size(root_i_start)
+        source_position = dynamic.get_position_of(root_i_start)
+        target_position = target_p_start
+    remaining_length = dynamic.step_size_p(source_position) - length
+    while target_position < target_p_end:
+        remaining_target_length = target_dynamic.step_size_p(target_position)
+        acc = 0
+        while remaining_target_length > 0:
+            if remaining_length == 0:
+                source_position += 1
+                if source_position >= source_p_end:
+                    return target_values
+                remaining_length = dynamic.step_size_p(source_position)
+            if remaining_target_length <= remaining_length:
+                acc += values[dynamic.get_index_of(source_position)] * remaining_target_length
+                remaining_length -= remaining_target_length
+                remaining_target_length -= remaining_target_length
+            else:
+                acc += values[dynamic.get_index_of(source_position)] * remaining_length
+                remaining_target_length -= remaining_length
+                remaining_length -= remaining_length
+        target_values[target_dynamic.get_index_of(target_position)] = acc / target_dynamic.step_size_p(target_position)
+        target_position += 1
+    return target_values
+
+def test_single_resampling(dynamic, target_dynamic):
+    index = [dynamic.get_index_of(position) for position in range(dynamic.number_of_steps())]
+    values = pd.Series(data = [float(i) for i in range(len(index))], index = index)
+    try:
+        print(resample(values, dynamic, target_dynamic))
+    except:
+        print('hey')
+    target_index = [target_dynamic.get_index_of(position) for position in range(target_dynamic.number_of_steps())]
+    target_values = pd.Series(dtype = float, index = target_index)
+    print(resample_into(values, dynamic, target_dynamic, target_values))
+
+def test_resampling():
+    dynamics = {}
+    dynamics['d'] = TrivialDynamic([1 for i in range(36)])
+    dynamics['d_1'] = PartialDynamic(dynamics['d'], 0, 36)
+    dynamics['d_2'] = PartialDynamic(dynamics['d'], 1, 35)
+    dynamics['d_3'] = BackedDynamic(dynamics['d'], [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36])
+    dynamics['d_3_1'] = PartialDynamic(dynamics['d_3'], 0, 18)
+    dynamics['d_3_2'] = PartialDynamic(dynamics['d_3'], 1, 17)
+    dynamics['d_3_3'] = BackedDynamic(dynamics['d_3'], [0, 4, 8, 12, 16, 20, 24, 28, 32, 36])
+    dynamics['d_3_3_1'] = PartialDynamic(dynamics['d_3_3'], 0, 9)
+    dynamics['d_3_3_2'] = PartialDynamic(dynamics['d_3_3'], 1, 8)
+    dynamics['d_3_4'] = BackedDynamic(dynamics['d_3'], [2, 6, 10, 14, 18, 22, 26, 30, 34])
+    dynamics['d_3_4_1'] = PartialDynamic(dynamics['d_3_4'], 0, 8)
+    dynamics['d_3_4_2'] = PartialDynamic(dynamics['d_3_4'], 1, 7)
+    dynamics['d_4'] = BackedDynamic(dynamics['d'], [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33])
+    dynamics['d_4_1'] = PartialDynamic(dynamics['d_4'], 0, 10)
+    dynamics['d_4_2'] = PartialDynamic(dynamics['d_4'], 1, 9)
+    dynamics['d_4_3'] = BackedDynamic(dynamics['d_4'], [3, 9, 15, 21, 27, 33])
+    dynamics['d_4_3_1'] = PartialDynamic(dynamics['d_4_3'], 0, 5)
+    dynamics['d_4_3_2'] = PartialDynamic(dynamics['d_4_3'], 1, 4)
+    dynamics['d_4_4'] = BackedDynamic(dynamics['d_4'], [6, 12, 18, 24, 30])
+    dynamics['d_4_4_1'] = PartialDynamic(dynamics['d_4_4'], 0, 4)
+    dynamics['d_4_4_2'] = PartialDynamic(dynamics['d_4_4'], 1, 3)
+
+    test_resampling(dynamics['d_1'], dynamics['d_2'])
+    test_resampling(dynamics['d_3_4'], dynamics['d_4'])
+    test_resampling(dynamics['d_3_4_2'], dynamics['d_4'])
+    test_resampling(dynamics['d_3_4'], dynamics['d_4_2'])
+    test_resampling(dynamics['d_3_4_2'], dynamics['d_4_2'])
diff --git a/input_profile_processor/calc_irradiance.py b/input_profile_processor/calc_irradiance.py
index 4e8fd9eb02dd492d687c37eb025250ff7ce7d754..d0d47eab7f5b2e86a2a621bae71d522e6bd30448 100644
--- a/input_profile_processor/calc_irradiance.py
+++ b/input_profile_processor/calc_irradiance.py
@@ -118,8 +118,8 @@ def calc_total_irradiance(irradiance, timer, beta, psi_f, phi, lambda_st, lambda
     g_total = g_diffuse + g_reflected + g_beam
     return g_total
 
-def generate_g_t_series(irradiance, beta, psi_f, phi, lambda_st, lambda_1, t_start, t_horizon, t_step):
-    T = list(pd.date_range(pd.Timestamp(t_start), pd.Timestamp(t_start) + timedelta(hours=t_horizon * t_step - t_step), freq=str(t_step) + 'H'))
+def generate_g_t_series(irradiance, beta, psi_f, phi, lambda_st, lambda_1, t_start, dynamic):
+    T = list(irradiance.index)
     g_total_lst = []
     for t in T:
         g_total_lst.append(calc_total_irradiance(irradiance.loc[t], t, beta, psi_f, phi, lambda_st, lambda_1))
diff --git a/input_profile_processor/input_profile_processor.py b/input_profile_processor/input_profile_processor.py
index 4b18450fad7b04675df8c58c02d7a70686861fe2..4c48e2f8fea6d7af04f6a229d447dfa7a53bdbef 100644
--- a/input_profile_processor/input_profile_processor.py
+++ b/input_profile_processor/input_profile_processor.py
@@ -30,23 +30,39 @@ from Tooling.input_profile_processor.calc_irradiance import generate_g_t_series
 from Tooling.modifier import Modifier
 
 
-def process_input_profiles(input_profile_dict, t_start, t_horizon, t_step):
-    # This entire process assumes three things:
-    # 1. t_step devides a day into an integer amount of steps
-    # 2. t_step devides the intervall from YYYY.MM.DD 00:00:00 of the day containing t_start to t_start into an integer amount of steps
+def process_input_profiles(input_profile_dict, t_start, dynamic):
+    d_step_min = min(dynamic.step_size_p(position) for position in range(dynamic.number_of_steps()))
+    # This entire process assumes four things:
+    # 1. d_step_min devides a day into an integer amount of steps
+    # 2. d_step_min devides the intervall from YYYY.MM.DD 00:00:00 of the day containing t_start to t_start into an integer amount of steps
     # 3. The timestamps of the desired timeseries either line up the with timestamps provided by the profiles in the files or the other way around
+    # 4. The sizes of all time steps in the given dynamic are devisible by 
     input_profiles = {}
     for input_profile_name, input_profile_config in input_profile_dict.items():
         if 'file' in input_profile_config:
-            profile = load_profile(input_profile_name, input_profile_config['file'], t_start, t_horizon, t_step)
+            profile = load_profile(input_profile_name, input_profile_config['file'], t_start, dynamic, d_step_min)
         elif 'generate' in input_profile_config:
-            profile = generate_profile(input_profile_name, input_profile_config['type'], input_profile_config['generate'], input_profiles, t_start, t_horizon, t_step)
+            profile = generate_profile(input_profile_name, input_profile_config['type'], input_profile_config['generate'], input_profiles, t_start, dynamic, d_step_min)
         elif 'modify' in input_profile_config:
-            profile = modify_profile(input_profile_name, input_profile_config['type'], input_profile_config['modify'], input_profiles, t_start, t_horizon, t_step)
+            profile = modify_profile(input_profile_name, input_profile_config['type'], input_profile_config['modify'], input_profiles, t_start, dynamic, d_step_min)
         input_profiles[input_profile_name] = (input_profile_config['type'], profile)
 
     for input_profile_name, (input_profile_type, input_profile) in input_profiles.items():
-        input_profile = input_profile[t_start:t_start + pd.Timedelta(hours = t_horizon * t_step - t_step)]
+        input_profile.drop(pd.date_range(input_profile.index[0], t_start - timedelta(hours = d_step_min), freq = str(d_step_min) + 'H'), inplace = True)
+        input_profile.drop(pd.date_range(t_start + pd.Timedelta(hours = sum(dynamic.step_size_p(position) for position in range(dynamic.number_of_steps()))), input_profile.index[-1], freq = str(d_step_min) + 'H'), inplace = True)
+
+        if len(input_profile) != dynamic.number_of_steps():
+            index = 0
+            drop = []
+            for position in range(dynamic.number_of_steps()):
+                if dynamic.step_size_p(position) != d_step_min:
+                    number_of_steps = int(dynamic.step_size_p(position) / d_step_min)
+                    input_profile.iloc[index] = sum(d_step_min * input_profile.iloc[index + i] for i in range(number_of_steps)) / dynamic.step_size_p(position)
+                    drop.extend(index + i + 1 for i in range(number_of_steps - 1))
+                    index += number_of_steps
+                else:
+                    index += 1
+            input_profile.drop((input_profile.index[i] for i in drop), inplace = True)
 
         if input_profile_type == 'irradiance':
             lambda_1 = 14.122
@@ -54,21 +70,21 @@ def process_input_profiles(input_profile_dict, t_start, t_horizon, t_step):
             phi = 52.21
             psi_f = 0
             beta = 30
-            input_profile = generate_g_t_series(input_profile, beta, psi_f, phi, lambda_st, lambda_1, t_start, t_horizon, t_step)
+            input_profile = generate_g_t_series(input_profile, beta, psi_f, phi, lambda_st, lambda_1, t_start, dynamic)
 
         input_profile = input_profile.squeeze()
-        input_profile.set_axis(list(range(t_horizon)), inplace = True)
+        input_profile.set_axis(list(range(dynamic.number_of_steps())), inplace = True)
         input_profiles[input_profile_name] = input_profile
     return input_profiles
 
-def resample_profile(name, profile, t_start, t_horizon, t_step, t_last):
-    profile = profile.resample(str(t_step) + 'H').mean().interpolate('linear')
+def resample_profile(name, profile, t_start, dynamic, d_step_min, t_last):
+    profile = profile.resample(str(d_step_min) + 'H').mean().interpolate('linear')
     if t_start < profile.index[0]:
-        missing_indices = pd.date_range(t_start, profile.index[0] - timedelta(hours = t_step), freq = str(t_step) + 'H')
+        missing_indices = pd.date_range(t_start, profile.index[0] - timedelta(hours = d_step_min), freq = str(d_step_min) + 'H')
         print(f'For input profile {name} {len(missing_indices)} {"values are" if len(missing_indices) > 1 else "value is"} extrapolated to the beginning of the profile!')
         profile = pd.concat([pd.DataFrame([profile.iloc[0]], index = missing_indices), profile])
     if profile.index[-1] < t_last:
-        missing_indices = pd.date_range(profile.index[-1] + timedelta(hours = t_step), t_last, freq = str(t_step) + 'H')
+        missing_indices = pd.date_range(profile.index[-1] + timedelta(hours = d_step_min), t_last, freq = str(d_step_min) + 'H')
         print(f'For input profile {name} {len(missing_indices)} {"values are" if len(missing_indices) > 1 else "value is"} extrapolated to the end of the profile!')
         profile = pd.concat([profile, pd.DataFrame([profile.iloc[-1]], index = missing_indices)])
     return profile
@@ -82,8 +98,8 @@ def expand_profile_to_year(profile, year, t_step):
         profile = pd.concat([profile, pd.DataFrame([profile.iloc[-1]], index = missing_indices)])
     return profile
 
-def load_profile(name, file, t_start, t_horizon, t_step):
-    t_last = t_start + pd.Timedelta(hours = t_horizon * t_step - t_step)
+def load_profile(name, file, t_start, dynamic, d_step_min):
+    t_last = t_start + pd.Timedelta(hours = sum(dynamic.step_size_p(position) for position in range(dynamic.number_of_steps() - 1)))
     profile = pd.read_csv(file, index_col = 0)
     try:
         file_start = pd.to_datetime(profile.index[0], format = '%d-%m-%Y %H:%M:%S')
@@ -132,14 +148,14 @@ def load_profile(name, file, t_start, t_horizon, t_step):
     #         left_index = middle_index
     #     middle_index = left_index + int((right_index - left_index) / 2)
     #     middle_time = pd.to_datetime(profile.index[middle_index], format = format)
-    first_index = 0
-    last_index = len(profile) - 1
-    profile = profile[first_index:last_index + 1]
+    # first_index = 0
+    # last_index = len(profile) - 1
+    # profile = profile[first_index:last_index + 1]
     profile.index = pd.to_datetime(profile.index, format = format)
-    return resample_profile(name, profile, t_start, t_horizon, t_step, t_last)
+    return resample_profile(name, profile, t_start, dynamic, d_step_min, t_last)
 
-def generate_profile(name, profile_type, parameters, input_profiles, t_start, t_horizon, t_step):
-    t_last = t_start + pd.Timedelta(hours = t_horizon * t_step - t_step)
+def generate_profile(name, profile_type, parameters, input_profiles, t_start, dynamic, d_step_min):
+    t_last = t_start + pd.Timedelta(hours = sum(dynamic.step_size_p(position) for position in range(dynamic.number_of_steps() - 1)))
     years = range(t_start.year, t_last.year + 1)
 
     if profile_type == 'elec_demand':
@@ -148,20 +164,20 @@ def generate_profile(name, profile_type, parameters, input_profiles, t_start, t_
             year_profile = ElectricalDemand(year).get_profile(parameters['yearly_demand'], 'h0', True)  # True: with smoothing function for household profiles, False: no smoothing function for household profiles
             profiles.append(year_profile)
         profile = pd.concat(profiles)
-        return resample_profile(name, profile, t_start, t_horizon, t_step, t_last)
+        return resample_profile(name, profile, t_start, dynamic, d_step_min, t_last)
     elif profile_type == 'therm_demand':
         profiles = []
         for year in years:
             demand_df = pd.DataFrame(index=pd.date_range(datetime(year, 1, 1, 0),
-                                                         periods=8760 / t_step,
-                                                         freq=str(t_step) + 'H'))
+                                                         periods=8760 / d_step_min,
+                                                         freq=str(d_step_min) + 'H'))
             # Fixme: non-residential building only have building_class = 0
             # residential building could varies from 1 to 11
             # same problem for hot water demand.
             # temporary fix
             if len(input_profiles[parameters['temperature']][1]) < len(demand_df.index):
                 temp = list(input_profiles[parameters['temperature']])
-                temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, t_step)
+                temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, d_step_min)
                 input_profiles[parameters['temperature']] = tuple(temp)
             year_profile = ThermalDemand(demand_df.index,
                                          shlp_type='EFH',
@@ -178,12 +194,12 @@ def generate_profile(name, profile_type, parameters, input_profiles, t_start, t_
         profiles = []
         for year in years:
             demand_df = pd.DataFrame(index=pd.date_range(datetime(year, 1, 1, 0),
-                                                         periods=8760 / t_step,
-                                                         freq=str(t_step) + 'H'))
+                                                         periods=8760 / d_step_min,
+                                                         freq=str(d_step_min) + 'H'))
             # temporary fix
             if len(input_profiles[parameters['temperature']][1]) < len(demand_df.index):
                 temp = list(input_profiles[parameters['temperature']])
-                temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, t_step)
+                temp[1] = expand_profile_to_year(input_profiles[parameters['temperature']][1], year, d_step_min)
                 input_profiles[parameters['temperature']] = tuple(temp)
             year_profile = ThermalDemand(demand_df.index,
                                          shlp_type='EFH',
@@ -200,7 +216,7 @@ def generate_profile(name, profile_type, parameters, input_profiles, t_start, t_
         raise Exception("Generator for profile type " + str(profile_type) + " is not implemented!")
 
 
-def modify_profile(name, mod_type: str, parameters, input_profiles, t_start, t_horizon, t_step):
+def modify_profile(name, mod_type: str, parameters, input_profiles, t_start, dynamic, d_step_min):
     # Example of the parameters for use in the runme
     # ------------------------------------------------------------------------------------------------------------------
     # 'temperature_1': ['prophet', 'modify', 'input_files/data/temperature/temperature.csv', 'temperature',
diff --git a/predictor/Predictor.py b/predictor/Predictor.py
index 448a0cb9b6d23f2a367f21db4caff906c83daee7..3de6ebc8bd1253d87c4fd6a446a4b39ea4203822 100644
--- a/predictor/Predictor.py
+++ b/predictor/Predictor.py
@@ -1,7 +1,7 @@
 import numpy as np
 import pandas as pd
 import math
-
+from Tooling.dynamics.Dynamic import Dynamic
 
 # ----------------------------------------------------------------------------------------------------------------------
 # Functions which can be used as predictors for rolling horizon steps in timeseries
@@ -22,11 +22,11 @@ class Predictor:
         Name of the prediction method to be used. The default method is "same_as_last_day".
     """
 
-    def __init__(self, profile, type: str, method: str, t_step: float):
+    def __init__(self, profile, type: str, method: str, dynamic: Dynamic):
         self.profile = profile
         self.type = type
         self.method = method
-        self.t_step = t_step
+        self.dynamic = dynamic
     
     def predict(self, time_steps):
         if self.method == "perfect_foresight":
@@ -36,6 +36,7 @@ class Predictor:
                 print('Requested time forward prediction for time steps that include the first time step! Using original data for the first time step.')
                 return pd.Series(self.profile[time_steps[0]], index = time_steps)
             else:
+                raise("Requested a prediction, this feature is currently not supported, so use original data!")
                 return pd.Series(self.profile[time_steps[0]] - 1, index = time_steps)
         elif self.method == "same_as_last_day":
             time_steps_per_day = int(24 / self.t_step)
@@ -45,6 +46,7 @@ class Predictor:
                 previous_day_data[:time_steps_per_day - time_steps[0]] = self.profile[time_steps[0]:time_steps_per_day]
                 previous_day_data[time_steps_per_day - time_steps[0]:] = self.profile[:time_steps[0]]
             else:
+                raise("Requested a prediction, this feature is currently not supported, so use original data!")
                 previous_day_data = np.array(self.profile[time_steps[0] - len(time_steps_per_day):time_steps[0]])
             days_in_prediction = [(t * time_steps_per_day, (t + 1) * time_steps_per_day) for t in range(math.ceil(len(time_steps) / time_steps_per_day))]
             days_in_prediction[-1] = (days_in_prediction[-1][0], time_steps[-1] - time_steps[0] + 1)
@@ -61,6 +63,7 @@ class Predictor:
                 previous_week_data[:time_steps_per_week - time_steps[0]] = self.profile[time_steps[0]:time_steps_per_week]
                 previous_week_data[time_steps_per_week - time_steps[0]:] = self.profile[:time_steps[0]]
             else:
+                raise("Requested a prediction, this feature is currently not supported, so use original data!")
                 previous_week_data = np.array(self.profile[time_steps[0] - len(time_steps_per_week):time_steps[0]])
             weeks_in_prediction = [(t * time_steps_per_week, (t + 1) * time_steps_per_week) for t in range(math.ceil(len(time_steps) / time_steps_per_week))]
             weeks_in_prediction[-1] = (weeks_in_prediction[-1][0], time_steps[-1] - time_steps[0] + 1)