Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate objective_weighting into TSAM module #1000

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
ce1618c
Add tsam_parameters to ES
henhuy Sep 22, 2023
c199f60
Add test for TSAM integration with storage investment
henhuy Sep 22, 2023
c0d92e0
Add indexes and sets fr TSAM integration in model
henhuy Sep 22, 2023
f3b16d2
Refactor TSAM indexes
henhuy Sep 22, 2023
245e489
Add inter and intra storages in TSAM mode
henhuy Sep 22, 2023
3bb86c9
Change cluster and timeindex indexes for TSAM mode
henhuy Sep 26, 2023
58a4c22
Disaggregate TSAM mode flows and storage SOCs
henhuy Sep 26, 2023
d0b007a
Integrate new cluster and timeindexes in storage model
henhuy Sep 26, 2023
3ebb8b1
Fix timeindex in inter storage balance rule
henhuy Sep 27, 2023
60ab4f9
Fix SOC generation from intra SOC
henhuy Sep 27, 2023
fbc98c3
Fix timeincrement for min and max inter storage levels
henhuy Sep 28, 2023
fa21917
Fix SOC calculation in postprocessing by adding self-discharge to int…
henhuy Sep 28, 2023
fef5305
Add timeindex to aggregation setup in example
henhuy Sep 28, 2023
02edc39
Minor change
henhuy Sep 28, 2023
9470465
Add flow repsentation in v5 storage example
henhuy Sep 28, 2023
1565b7f
Ttry to add extreme periods to TSAM aggregation for storage example v5
henhuy Sep 28, 2023
2c3f2aa
Fix line lengths
henhuy Sep 28, 2023
4593a55
Fix F401
henhuy Sep 28, 2023
7684cde
Fix import order
henhuy Sep 28, 2023
fc0de6f
add example for tsam use
MaxHiDLR Oct 4, 2023
dded16f
add aggregate time series in _helpers
MaxHiDLR Oct 4, 2023
eb8afa8
add basic_example csv
MaxHiDLR Oct 4, 2023
ebfd8fe
add example for tsam use
MaxHiDLR Oct 4, 2023
e96f553
add aggregate time series in _helpers
MaxHiDLR Oct 4, 2023
3dbdd73
add basic_example csv
MaxHiDLR Oct 4, 2023
1807259
Merge remote-tracking branch 'origin/feature/integrate_tsam_seg_objwe…
MaxHiDLR Oct 6, 2023
f571759
functions to generate correct objective weighting for cost function a…
MaxHiDLR Oct 9, 2023
69c018b
added objetive weighting fo model
MaxHiDLR Oct 9, 2023
f26b122
add plot SOC for example5
MaxHiDLR Oct 9, 2023
71f1557
add no investment storage example for simpler debugging
MaxHiDLR Oct 9, 2023
d04da8b
correct inter_storage_level equation
MaxHiDLR Oct 9, 2023
80d9ef6
revert "correction" inter_storage_level equation
MaxHiDLR Oct 10, 2023
575b75b
add initial inter storage level = 0
MaxHiDLR Oct 11, 2023
15db7e8
updated no investment example, so loadprofiles force use of long term…
MaxHiDLR Oct 12, 2023
4801ac7
extended timeindex to delete manual initializing of timeincrement
MaxHiDLR Oct 13, 2023
19abc81
v7 added as reference example with new tsam structure
MaxHiDLR Oct 30, 2023
07400e4
tsam weighting and functions added to energy system
MaxHiDLR Oct 30, 2023
1f4c41b
tsam weighting added to models
MaxHiDLR Oct 30, 2023
d8586bb
tsam_weighting_objective_weighting and time_increment added to cost f…
MaxHiDLR Oct 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
# -*- coding: utf-8 -*-

"""
General description
-------------------
This example shows how to perform a capacity optimization for
an energy system with storage. The following energy system is modeled:

.. code-block:: text

input/output bgas bel
| | |
| | |
wind(FixedSource) |------------------>|
| | |
pv(FixedSource) |------------------>|
| | |
gas_resource |--------->| |
(Commodity) | | |
| | |
demand(Sink) |<------------------|
| | |
| | |
pp_gas(Converter) |<---------| |
|------------------>|
| | |
storage(Storage) |<------------------|
|------------------>|

The example exists in four variations. The following parameters describe
the main setting for the optimization variation 1:

- optimize wind, pv, gas_resource and storage
- set investment cost for wind, pv and storage
- set gas price for kWh

Results show an installation of wind and the use of the gas resource.
A renewable energy share of 51% is achieved.

.. tip::

Have a look at different parameter settings. There are four variations
of this example in the same folder.

Code
----
Download source code: :download:`v1_invest_optimize_all_technologies.py </../examples/storage_investment/v1_invest_optimize_all_technologies.py>`

.. dropdown:: Click to display code

.. literalinclude:: /../examples/storage_investment/v1_invest_optimize_all_technologies.py
:language: python
:lines: 80-

Data
----
Download data: :download:`storage_investment.csv </../examples/storage_investment/storage_investment.csv>`


Installation requirements
-------------------------

This example requires oemof.solph (v0.5.x), install by:

.. code:: bash

pip install oemof.solph[examples]


License
-------
`MIT license <https://github.com/oemof/oemof-solph/blob/dev/LICENSE>`_

"""

###############################################################################
# Imports
###############################################################################

import logging
import os
import pprint as pp
import warnings
import tsam.timeseriesaggregation as tsam
import matplotlib.pyplot as plt

import pandas as pd
from oemof.tools import economics
from oemof.tools import logger
from oemof.solph import views

from oemof import solph


def main():
# Read data file
filename = os.path.join(os.getcwd(), "storage_investment.csv")
try:
data = pd.read_csv(filename)
except FileNotFoundError:
msg = "Data file not found: {0}. Only one value used!"
warnings.warn(msg.format(filename), UserWarning)
data = pd.DataFrame(
{"pv": [0.3, 0.5], "wind": [0.6, 0.8], "demand_el": [500, 600]}
)

data = pd.concat([data, data], ignore_index=True)
data["wind"].iloc[8760 - 24 : 8760] = 0
data["wind"].iloc[8760 * 2 - 24 : 8760] = 0
data["pv"].iloc[8760 - 24 : 8760] = 0
data["pv"].iloc[8760 * 2 - 24 : 8760] = 0

# add a season without electricity production to simulate the possible advantage using a seasonal storages
# for the first perido
data["wind"].iloc[2920: 2 * 2920 + 1] = 0
data["pv"].iloc[2920:2 * 2920 + 1] = 0

##########################################################################
# Initialize the energy system and read/calculate necessary parameters
##########################################################################

logger.define_logging()
logging.info("Initialize the energy system")
#todo: right now, tsam only determines the timeincrement right, when you pick the
#first periods last timestamp next to the second periods first timestep
#2022-31-12-23:00 --> 2023-01-01-00:00 , than timeincrement in between is equal to 1
#todo add initial storage level in new periods is equal to zero?
t1 = pd.date_range("2022-01-01", periods=8760, freq="H")
t2 = pd.date_range("2023-01-01", periods=8760, freq="H")
tindex = t1.append(t2)

data.index = tindex
del data["timestep"]

typical_periods = 10
hours_per_period = 24
segmentation = False
if segmentation:
print("segmentation hasn't been added so far")


else:
aggregation1 = tsam.TimeSeriesAggregation(
timeSeries=data.iloc[:8760],
noTypicalPeriods=typical_periods,
hoursPerPeriod=hours_per_period,
clusterMethod="hierarchical",
sortValues=True,
segmentation=False,
rescaleClusterPeriods=False,
extremePeriodMethod="replace_cluster_center",
addPeakMin=["wind", "pv"],
representationMethod="durationRepresentation",
)

aggregation2 = tsam.TimeSeriesAggregation(
timeSeries=data.iloc[8760:],
noTypicalPeriods=typical_periods,
hoursPerPeriod=hours_per_period,
clusterMethod="hierarchical",
sortValues=True,
segmentation=False,
rescaleClusterPeriods=False,
extremePeriodMethod="replace_cluster_center",
addPeakMin=["wind", "pv"],
representationMethod="durationRepresentation",
)

energysystem = solph.EnergySystem(
tsam_aggregations=[aggregation1, aggregation2],
infer_last_interval=False,
)

electricity_price = 100

##########################################################################
# Create oemof objects
##########################################################################

logging.info("Create oemof objects")


# create electricity bus
bel = solph.Bus(label="electricity")

energysystem.add( bel)

# create excess component for the electricity bus to allow overproduction
excess = solph.components.Sink(
label="excess_bel", inputs={bel: solph.Flow()}
)

# create source object representing the gas commodity (annual limit)
elect_resource = solph.components.Source(
label="electricity_source", outputs={bel: solph.Flow(variable_costs=electricity_price)}
)

wind_profile = pd.concat(
[
aggregation1.typicalPeriods["wind"],
aggregation2.typicalPeriods["wind"],
],
ignore_index=True,
)
wind_profile.iloc[-24:] = 0

# create fixed source object representing wind power plants
wind = solph.components.Source(
label="wind",
outputs={
bel: solph.Flow(
fix=wind_profile,
nominal_value=1500000
)
},
)

pv_profile = pd.concat(
[aggregation1.typicalPeriods["pv"],
aggregation2.typicalPeriods["pv"]
],
ignore_index=True,
)
pv_profile.iloc[-24:] = 0

# create fixed source object representing pv power plants
if False:
pv = solph.components.Source(
label="pv",
outputs={
bel: solph.Flow(
fix=pd.concat(
[
aggregation1.typicalPeriods["pv"],
aggregation2.typicalPeriods["pv"],
],
ignore_index=True,
),
nominal_value=900000
)
},
)

# create simple sink object representing the electrical demand
demand = solph.components.Sink(
label="demand",
inputs={
bel: solph.Flow(
fix=pd.concat(
[
aggregation1.typicalPeriods["demand_el"],
aggregation2.typicalPeriods["demand_el"],
],
ignore_index=True,
),
nominal_value=0.05,
)
},
)

# create storage object representing a battery
storage = solph.components.GenericStorage(
label="storage",
nominal_storage_capacity=3000000,
initial_storage_level=0,
inputs={bel: solph.Flow(variable_costs=0.0, nominal_value=2000)},
outputs={bel: solph.Flow(nominal_value=2000)},
loss_rate=0.001,
inflow_conversion_factor=1,
outflow_conversion_factor=1,
)
if False:
energysystem.add(excess, elect_resource, wind, pv, demand, storage)
else:
energysystem.add(excess, elect_resource, wind, demand, storage)

##########################################################################
# Optimise the energy system
##########################################################################

logging.info("Optimise the energy system")

# initialise the operational model
om = solph.Model(energysystem
)

# if tee_switch is true solver messages will be displayed
logging.info("Solve the optimization problem")
om.solve(solver="cbc", solve_kwargs={"tee": True})

##########################################################################
# Check and plot the results
##########################################################################

# check if the new result object is working for custom components
results = solph.processing.results(om)
print(results)

# Concatenate flows:
flows = pd.concat([flow["sequences"] for flow in results.values()], axis=1)
flows.columns = [
f"{oemof_tuple[0]}-{oemof_tuple[1]}" for oemof_tuple in results.keys()
]
print(flows)

electricity_bus = solph.views.node(results, "electricity")

meta_results = solph.processing.meta_results(om)
pp.pprint(meta_results)

fig, ax = plt.subplots(figsize=(10, 5))
storage_results = results[(storage, None)]["sequences"] / storage.nominal_storage_capacity
storage_results .plot(
ax=ax, kind="line", drawstyle="steps-post"
)
plt.show()

fig, ax = plt.subplots(figsize=(10, 5))
storage_results = results[(wind, bel)]["sequences"]
storage_results .plot(
ax=ax, kind="line", drawstyle="steps-post"
)
ax.set_title("Elect. from Wind")
plt.show()
if False:
fig, ax = plt.subplots(figsize=(10, 5))
storage_results = results[(pv, bel)]["sequences"]
storage_results .plot(
ax=ax, kind="line", drawstyle="steps-post"
)
ax.set_title("Elect. from PV")
plt.show()

fig, ax = plt.subplots(figsize=(10, 5))
storage_results = results[(bel, demand)]["sequences"]
storage_results .plot(
ax=ax, kind="line", drawstyle="steps-post"
)
ax.set_title("Demand")
plt.show()

fig, ax = plt.subplots(figsize=(10, 5))
storage_results = results[(elect_resource, bel)]["sequences"]
storage_results .plot(
ax=ax, kind="line", drawstyle="steps-post"
)
ax.set_title("Elect. from Grid")
plt.show()
my_results = electricity_bus["period_scalars"]


pp.pprint(my_results)


if __name__ == "__main__":
main()
Loading