Skip to content

Commit

Permalink
added periods of time indices
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxHiDLR committed Jan 22, 2025
1 parent 6d2db50 commit fab9cfa
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 139 deletions.
45 changes: 31 additions & 14 deletions src/oemof/solph/_energy_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,21 +195,29 @@ def __init__(

if isinstance(tsa_parameters, dict):
# Set up tsa_parameters for single period:
tsa_parameters = tsa_parameters

tsa_parameters["occurrences"] = collections.Counter(
tsa_parameters["order"]
)
tsa_parameters = [tsa_parameters]

# Construct occurrences of typical periods
if periods is not None:
for p in range(len(periods)):
tsa_parameters[p]["occurrences"] = collections.Counter(
tsa_parameters[p]["order"]
)
else:
tsa_parameters[0]["occurrences"] = collections.Counter(
tsa_parameters[0]["order"]
)

# If segmentation is used, timesteps is set to number of
# segmentations per period.
# Otherwise, default timesteps_per_period is used.
if "segments" in tsa_parameters:
tsa_parameters["timesteps"] = int(
len(tsa_parameters["segments"]) / len(tsa_parameters["occurrences"])
)
else:
tsa_parameters["timesteps"] = tsa_parameters["timesteps_per_period"]
for params in tsa_parameters:
if "segments" in params:
params["timesteps"] = int(
len(params["segments"]) / len(params["occurrences"])
)
else:
params["timesteps"] = params["timesteps_per_period"]
self.tsa_parameters = tsa_parameters

timeincrement = self._init_timeincrement(
Expand Down Expand Up @@ -314,14 +322,23 @@ def _init_timeincrement(timeincrement, timeindex, periods, tsa_parameters):
"TSAM will define timeincrement itself."
)
raise AttributeError(msg)

if tsa_parameters is not None and any(
if (
tsa_parameters is not None
and any("segments" in params for params in tsa_parameters)
and not all("segments" in params for params in tsa_parameters)
):
msg = (
"You have to set up segmentation in all periods, "
"if you want to use segmentation in TSAM mode"
)
raise AttributeError(msg)
if tsa_parameters is not None and all(
"segments" in params for params in tsa_parameters
):
# Concatenate segments from TSAM parameters to get timeincrement
return list(
itertools.chain(
*[tsa_parameters["segments"].values()]
*[params["segments"].values() for params in tsa_parameters]
)
)

Expand Down
34 changes: 22 additions & 12 deletions src/oemof/solph/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,32 +274,40 @@ def _add_parent_block_sets(self):

# Construct weighting from occurrences and order
self.tsam_weighting = list(
self.es.tsa_parameters["occurrences"][k]
for k in range(len(self.es.tsa_parameters["occurrences"]))
self.es.tsa_parameters[p]["occurrences"][k]
for p in self.PERIODS
for k in range(len(self.es.tsa_parameters[p]["occurrences"]))
for _ in range(
self.es.tsa_parameters["timesteps"]
self.es.tsa_parameters[p]["timesteps"]
)
)
self.CLUSTERS = po.Set(
initialize=list(
range(
len(self.es.tsa_parameters["order"])
sum(
len(self.es.tsa_parameters[p]["order"])
for p in self.PERIODS
)
)
)
)
self.CLUSTERS_OFFSET = po.Set(
initialize=list(
range(
len(self.es.tsa_parameters["order"])
sum(
len(self.es.tsa_parameters[p]["order"])
for p in self.PERIODS
)
+ 1
)
)
)
self.TYPICAL_CLUSTERS = po.Set(
initialize=[
(i)
(p, i)
for p in self.PERIODS
for i in range(
len(self.es.tsa_parameters["occurrences"])
len(self.es.tsa_parameters[p]["occurrences"])
)
]
)
Expand Down Expand Up @@ -489,10 +497,11 @@ def relax_problem(self):

return self

def get_timestep_from_tsam_timestep(self, ik, g):
def get_timestep_from_tsam_timestep(self, p, ik, g):
"""Return original timestep from cluster-based timestep"""
t = (
+ ik * self.es.tsa_parameters["timesteps"]
p * len(self.TIMESTEPS_IN_PERIOD[p])
+ ik * self.es.tsa_parameters[p]["timesteps"]
+ g
)
return t
Expand All @@ -503,9 +512,10 @@ def get_cluster_index(self, cluster_type, offset):
without offset
"""
return [
(k, t)
for k in range(len(self.es.tsa_parameters[cluster_type]))
(p, k, t)
for p in range(len(self.es.tsa_parameters))
for k in range(len(self.es.tsa_parameters[p][cluster_type]))
for t in range(
self.es.tsa_parameters["timesteps"] + offset
self.es.tsa_parameters[p]["timesteps"] + offset
)
]
70 changes: 40 additions & 30 deletions src/oemof/solph/components/_generic_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,9 @@ def _storage_content_bound_rule(block, n, t):
# set the initial intra storage content
# first timestep in intra storage is always zero
for n in group:
for k in m.TYPICAL_CLUSTERS:
self.storage_content_intra[n, k, 0] = 0
self.storage_content_intra[n, k, 0].fix()
for p, k in m.TYPICAL_CLUSTERS:
self.storage_content_intra[n, p, k, 0] = 0
self.storage_content_intra[n, p, k, 0].fix()
if n.initial_storage_level is not None:
self.storage_content_inter[n, 0] = (
n.initial_storage_level * n.nominal_storage_capacity
Expand All @@ -532,20 +532,25 @@ def _storage_inter_minimum_level_rule(block):
# https://github.com/FZJ-IEK3-VSA/FINE/blob/
# 57ec32561fb95e746c505760bd0d61c97d2fd2fb/FINE/storage.py#L1329
for n in self.STORAGES:
for i, g in m.TIMEINDEX_CLUSTER:
t = m.get_timestep_from_tsam_timestep(i, g)
for p, i, g in m.TIMEINDEX_CLUSTER:
t = m.get_timestep_from_tsam_timestep(p, i, g)
lhs = n.nominal_storage_capacity * n.min_storage_level[t]
k = m.es.tsa_parameters["order"][i]
tk = m.get_timestep_from_tsam_timestep(k, g)
inter_i = len(m.es.tsa_parameters["order"] + i)

k = m.es.tsa_parameters[p]["order"][i]
tk = m.get_timestep_from_tsam_timestep(p, k, g)
inter_i = (
sum(
len(m.es.tsa_parameters[ip]["order"])
for ip in range(p)
)
+ i
)
rhs = (
self.storage_content_inter[n, inter_i]
* (1 - n.loss_rate[t]) ** (g * m.timeincrement[tk])
+ self.storage_content_intra[n, k, g]
+ self.storage_content_intra[n, p, k, g]
)
self.storage_inter_minimum_level.add(
(n, i, g), lhs <= rhs
(n, p, i, g), lhs <= rhs
)

if m.TSAM_MODE:
Expand All @@ -559,20 +564,25 @@ def _storage_inter_minimum_level_rule(block):

def _storage_inter_maximum_level_rule(block):
for n in self.STORAGES:
for i, g in m.TIMEINDEX_CLUSTER:
t = m.get_timestep_from_tsam_timestep(i, g)
k = m.es.tsa_parameters["order"][i]
tk = m.get_timestep_from_tsam_timestep(k, g)
inter_i = len(m.es.tsa_parameters["order"] + i
for p, i, g in m.TIMEINDEX_CLUSTER:
t = m.get_timestep_from_tsam_timestep(p, i, g)
k = m.es.tsa_parameters[p]["order"][i]
tk = m.get_timestep_from_tsam_timestep(p, k, g)
inter_i = (
sum(
len(m.es.tsa_parameters[ip]["order"])
for ip in range(p)
)
+ i
)
lhs = (
self.storage_content_inter[n, inter_i]
* (1 - n.loss_rate[t]) ** (g * m.timeincrement[tk])
+ self.storage_content_intra[n, k, g]
+ self.storage_content_intra[n, p, k, g]
)
rhs = n.nominal_storage_capacity * n.max_storage_level[t]
self.storage_inter_maximum_level.add(
(n, i, g), lhs <= rhs
(n, p, i, g), lhs <= rhs
)

if m.TSAM_MODE:
Expand Down Expand Up @@ -617,16 +627,16 @@ def _storage_balance_rule(block, n, t):
) * m.timeincrement[t]
return expr == block.storage_content[n, t + 1]

def _intra_storage_balance_rule(block, n, k, g):
def _intra_storage_balance_rule(block, n, p, k, g):
"""
Rule definition for the storage balance of every storage n and
every timestep.
"""
t = m.get_timestep_from_tsam_timestep(k, g)
t = m.get_timestep_from_tsam_timestep(p, k, g)
expr = 0
expr += block.storage_content_intra[n, k, g + 1]
expr += block.storage_content_intra[n, p, k, g + 1]
expr += (
-block.storage_content_intra[n, k, g]
-block.storage_content_intra[n, p, k, g]
* (1 - n.loss_rate[t]) ** m.timeincrement[t]
)
expr += (
Expand Down Expand Up @@ -661,29 +671,29 @@ def _inter_storage_balance_rule(block, n, i):
"""
ii = 0
for p in m.PERIODS:
ii += len(m.es.tsa_parameters["order"])
ii += len(m.es.tsa_parameters[p]["order"])
if ii > i:
ii -= len(m.es.tsa_parameters["order"])
ii -= len(m.es.tsa_parameters[p]["order"])
ii = i - ii
break

k = m.es.tsa_parameters["order"][ii]
k = m.es.tsa_parameters[p]["order"][ii]

# Calculate inter losses over whole typical period
t0 = m.get_timestep_from_tsam_timestep(k, 0)
t0 = m.get_timestep_from_tsam_timestep(p, k, 0)
losses = math.prod(
(1 - n.loss_rate[t0 + s])
** m.es.tsa_parameters["segments"][(k, s)]
if "segments" in m.es.tsa_parameters
** m.es.tsa_parameters[p]["segments"][(k, s)]
if "segments" in m.es.tsa_parameters[p]
else 1 - n.loss_rate[t0 + s]
for s in range(m.es.tsa_parameters["timesteps"])
for s in range(m.es.tsa_parameters[p]["timesteps"])
)

expr = 0
expr += block.storage_content_inter[n, i + 1]
expr += -block.storage_content_inter[n, i] * losses
expr += -self.storage_content_intra[
n, k, m.es.tsa_parameters["timesteps"]
n, p, k, m.es.tsa_parameters[p]["timesteps"]
]
return expr == 0

Expand Down
Loading

0 comments on commit fab9cfa

Please sign in to comment.