Storage investment¶
Optimize all technologies¶
General description¶
This example shows how to perform a capacity optimization for an energy system with storage. The following energy system is modeled:
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: v1_invest_optimize_all_technologies.py
Click to display code
import logging
import os
import pprint as pp
import warnings
import pandas as pd
from oemof.tools import economics
from oemof.tools import logger
from oemof import solph
def main(optimize=True):
# Read data file
filename = os.path.join(
os.path.dirname(__file__), "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]}
)
number_timesteps = len(data)
##########################################################################
# Initialize the energy system and read/calculate necessary parameters
##########################################################################
logger.define_logging()
logging.info("Initialize the energy system")
date_time_index = solph.create_time_index(2012, number=number_timesteps)
energysystem = solph.EnergySystem(
timeindex=date_time_index, infer_last_interval=False
)
price_gas = 0.04
# If the period is one year the equivalent periodical costs (epc) of an
# investment are equal to the annuity. Use oemof's economic tools.
epc_wind = economics.annuity(capex=1000, n=20, wacc=0.05)
epc_pv = economics.annuity(capex=1000, n=20, wacc=0.05)
# It is assumed that the investment object in storage capacity entails the
# costs for investment into both input and output capacity
epc_storage = economics.annuity(capex=1000, n=20, wacc=0.05)
##########################################################################
# Create oemof objects
##########################################################################
logging.info("Create oemof objects")
# create natural gas bus
bgas = solph.Bus(label="natural_gas")
# create electricity bus
bel = solph.Bus(label="electricity")
energysystem.add(bgas, 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
gas_resource = solph.components.Source(
label="rgas", outputs={bgas: solph.Flow(variable_costs=price_gas)}
)
# create fixed source object representing wind power plants
wind = solph.components.Source(
label="wind",
outputs={
bel: solph.Flow(
fix=data["wind"],
nominal_capacity=solph.Investment(ep_costs=epc_wind),
)
},
)
# create fixed source object representing pv power plants
pv = solph.components.Source(
label="pv",
outputs={
bel: solph.Flow(
fix=data["pv"],
nominal_capacity=solph.Investment(ep_costs=epc_pv),
)
},
)
# create simple sink object representing the electrical demand
demand = solph.components.Sink(
label="demand",
inputs={bel: solph.Flow(fix=data["demand_el"], nominal_capacity=1)},
)
# create simple Converter object representing a gas power plant
pp_gas = solph.components.Converter(
label="pp_gas",
inputs={bgas: solph.Flow()},
outputs={bel: solph.Flow(nominal_capacity=10e10, variable_costs=0)},
conversion_factors={bel: 0.58},
)
# create storage object representing a battery, allow for investment
storage = solph.components.GenericStorage(
label="storage",
inputs={
bel: solph.Flow(
variable_costs=0.0001, nominal_capacity=solph.Investment()
)
},
outputs={bel: solph.Flow(nominal_capacity=solph.Investment())},
loss_rate=0.00,
initial_storage_level=0,
invest_relation_input_capacity=1 / 6, # c-rate of 1/6
invest_relation_output_capacity=1 / 6,
inflow_conversion_factor=1,
outflow_conversion_factor=0.8,
nominal_capacity=solph.Investment(ep_costs=epc_storage),
)
energysystem.add(excess, gas_resource, wind, pv, demand, pp_gas, storage)
##########################################################################
# Optimise the energy system
##########################################################################
if optimize is False:
return energysystem
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)
electricity_bus = solph.views.node(results, "electricity")
meta_results = solph.processing.meta_results(om)
pp.pprint(meta_results)
# returns a pandas Series with all scalar values (investment, total) of
# components connected to the electricity bus
my_results = electricity_bus["scalars"]
# installed capacity of storage in GWh
my_results["storage_invest_GWh"] = (
results[(storage, None)]["scalars"]["invest"] / 1e6
)
# installed capacity of wind power plant in MW
my_results["wind_invest_MW"] = (
results[(wind, bel)]["scalars"]["invest"] / 1e3
)
# resulting renewable energy share
my_results["res_share"] = (
1
- results[(pp_gas, bel)]["sequences"].sum().iloc[0]
/ results[(bel, demand)]["sequences"].sum().iloc[0]
)
pp.pprint(my_results)
if __name__ == "__main__":
main()
Data¶
Download data: storage_investment.csv
Installation requirements¶
This example requires oemof.solph (at least v0.5.0), install by:
pip install oemof.solph>=0.5
License¶
Optimize only gas and storage¶
General description¶
This example shows how to perform a capacity optimization for an energy system with storage. The following energy system is modeled:
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 2:
optimize gas_resource and storage
set installed capacities for wind and pv
set investment cost for storage
set gas price for kWh
Results show a higher renewable energy share than in variation 1 (78% compared to 51%) due to preinstalled renewable capacities. Storage is not installed as the gas resource is cheaper.
Tip
Have a look at different parameter settings. There are four variations of this example in the same folder.
Code¶
Download source code: v2_invest_optimize_only_gas_and_storage.py
Click to display code
import os
import pprint as pp
import warnings
import pandas as pd
from oemof.tools import economics
from oemof.tools import logger
from oemof import solph
def main(optimize=True):
# Read data file
filename = os.path.join(
os.path.dirname(__file__), "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]}
)
number_timesteps = len(data)
##########################################################################
# Initialize the energy system and read/calculate necessary parameters
##########################################################################
logger.define_logging()
logging.info("Initialize the energy system")
date_time_index = solph.create_time_index(2012, number=number_timesteps)
energysystem = solph.EnergySystem(
timeindex=date_time_index, infer_last_interval=False
)
price_gas = 0.04
# If the period is one year the equivalent periodical costs (epc) of an
# investment are equal to the annuity. Use oemof's economic tools.
# It is assumed that the investment object in storage capacity entails the
# costs for investment into both input and output capacity
epc_storage = economics.annuity(capex=1000, n=20, wacc=0.05)
##########################################################################
# Create oemof objects
##########################################################################
logging.info("Create oemof objects")
# create natural gas bus
bgas = solph.Bus(label="natural_gas")
# create electricity bus
bel = solph.Bus(label="electricity")
energysystem.add(bgas, 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
gas_resource = solph.components.Source(
label="rgas", outputs={bgas: solph.Flow(variable_costs=price_gas)}
)
# create fixed source object representing wind power plants
wind = solph.components.Source(
label="wind",
outputs={bel: solph.Flow(fix=data["wind"], nominal_capacity=1000000)},
)
# create fixed source object representing pv power plants
pv = solph.components.Source(
label="pv",
outputs={bel: solph.Flow(fix=data["pv"], nominal_capacity=600000)},
)
# create simple sink object representing the electrical demand
demand = solph.components.Sink(
label="demand",
inputs={bel: solph.Flow(fix=data["demand_el"], nominal_capacity=1)},
)
# create simple Converter object representing a gas power plant
pp_gas = solph.components.Converter(
label="pp_gas",
inputs={bgas: solph.Flow()},
outputs={bel: solph.Flow(nominal_capacity=10e10, variable_costs=0)},
conversion_factors={bel: 0.58},
)
# create storage object representing a battery
storage = solph.components.GenericStorage(
label="storage",
inputs={
bel: solph.Flow(
variable_costs=0.0001, nominal_capacity=solph.Investment()
)
},
outputs={bel: solph.Flow(nominal_capacity=solph.Investment())},
loss_rate=0.00,
initial_storage_level=0,
invest_relation_input_capacity=1 / 6, # c-rate of 1/6
invest_relation_output_capacity=1 / 6,
inflow_conversion_factor=1,
outflow_conversion_factor=0.8,
nominal_capacity=solph.Investment(ep_costs=epc_storage),
)
energysystem.add(excess, gas_resource, wind, pv, demand, pp_gas, storage)
##########################################################################
# Optimise the energy system
##########################################################################
if optimize is False:
return energysystem
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)
electricity_bus = solph.views.node(results, "electricity")
meta_results = solph.processing.meta_results(om)
pp.pprint(meta_results)
# returns a pandas Series with all scalar values (investment, total) of
# components connected to the electricity bus
my_results = electricity_bus["scalars"]
# installed capacity of storage in GWh
my_results["storage_invest_GWh"] = (
results[(storage, None)]["scalars"]["invest"] / 1e6
)
# resulting renewable energy share
my_results["res_share"] = (
1
- results[(pp_gas, bel)]["sequences"].sum().iloc[0]
/ results[(bel, demand)]["sequences"].sum().iloc[0]
)
pp.pprint(my_results)
if __name__ == "__main__":
main()
Data¶
Download data: storage_investment.csv
Installation requirements¶
This example requires oemof.solph (at least v0.5.0), install by:
pip install oemof.solph>=0.5