Skip to content

Commit

Permalink
Merge pull request #1040 from oemof/revision/basic_example
Browse files Browse the repository at this point in the history
Deactivate dump/restore in basic example
  • Loading branch information
p-snft committed Apr 2, 2024
2 parents 124f886 + 3f1bb5d commit 0986292
Showing 1 changed file with 116 additions and 81 deletions.
197 changes: 116 additions & 81 deletions examples/basic_example/basic_example.py
Expand Up @@ -71,28 +71,54 @@
from oemof.solph import EnergySystem
from oemof.solph import Model
from oemof.solph import buses
from oemof.solph import components as cmp
from oemof.solph import components
from oemof.solph import create_time_index
from oemof.solph import flows
from oemof.solph import helpers
from oemof.solph import processing
from oemof.solph import views

STORAGE_LABEL = "battery_storage"


def get_data_from_file_path(file_path: str) -> pd.DataFrame:
try:
data = pd.read_csv(file_path)
except FileNotFoundError:
warn_msg = f"Data file not found: {file_path}."
"Values for one timestep created!"
warnings.warn(warn_msg, UserWarning)
data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]})
return data


def plot_figures_for(element: dict) -> None:
figure, axes = plt.subplots(figsize=(10, 5))
element["sequences"].plot(ax=axes, kind="line", drawstyle="steps-post")
plt.legend(
loc="upper center",
prop={"size": 8},
bbox_to_anchor=(0.5, 1.25),
ncol=2,
)
figure.subplots_adjust(top=0.8)
plt.show()


def main(dump_and_restore=False):
# For models that need a long time to optimise, saving and loading the
# EnergySystem might be advised. By default, we do not do this here. Feel
# free to experiment with this once you understood the rest of the code.
dump_results = restore_results = dump_and_restore

def main():
# *************************************************************************
# ********** PART 1 - Define and optimise the energy system ***************
# *************************************************************************

# Read data file

filename = os.path.join(os.getcwd(), "basic_example.csv")
try:
data = pd.read_csv(filename)
except FileNotFoundError:
msg = "Data file not found: {0}. Values for one timestep created!"
warnings.warn(msg.format(filename), UserWarning)
data = pd.DataFrame({"pv": [0.3], "wind": [0.6], "demand_el": [500]})
file_name = "basic_example.csv"
file_path = os.path.join(os.getcwd(), file_name)
data = get_data_from_file_path(file_path)

solver = "cbc" # 'glpk', 'gurobi',....
debug = False # Set number_of_timesteps to 3 to get a readable lp-file.
Expand Down Expand Up @@ -124,75 +150,101 @@ def main():
# connect components to these buses (see below).

# create natural gas bus
bgas = buses.Bus(label="natural_gas")
bus_gas = buses.Bus(label="natural_gas")

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

# adding the buses to the energy system
energysystem.add(bgas, bel)
energysystem.add(bus_gas, bus_electricity)

# create excess component for the electricity bus to allow overproduction
energysystem.add(cmp.Sink(label="excess_bel", inputs={bel: flows.Flow()}))
energysystem.add(
solver_components.Sink(
label="excess_bus_electricity",
inputs={bus_electricity: flows.Flow()},
)
)

# create source object representing the gas commodity
energysystem.add(
cmp.Source(
solver_components.Source(
label="rgas",
outputs={bgas: flows.Flow()},
outputs={bus_gas: flows.Flow()},
)
)

# create fixed source object representing wind power plants
energysystem.add(
cmp.Source(
solver_components.Source(
label="wind",
outputs={bel: flows.Flow(fix=data["wind"], nominal_value=1000000)},
outputs={
bus_electricity: flows.Flow(
fix=data["wind"], nominal_value=1000000
)
},
)
)

# create fixed source object representing pv power plants
energysystem.add(
cmp.Source(
solver_components.Source(
label="pv",
outputs={bel: flows.Flow(fix=data["pv"], nominal_value=582000)},
outputs={
bus_electricity: flows.Flow(
fix=data["pv"], nominal_value=582000
)
},
)
)

# create simple sink object representing the electrical demand
# nominal_value is set to 1 because demand_el is not a normalised series
energysystem.add(
cmp.Sink(
solver_components.Sink(
label="demand",
inputs={bel: flows.Flow(fix=data["demand_el"], nominal_value=1)},
inputs={
bus_electricity: flows.Flow(
fix=data["demand_el"], nominal_value=1
)
},
)
)

# create simple converter object representing a gas power plant
energysystem.add(
cmp.Converter(
solver_components.Converter(
label="pp_gas",
inputs={bgas: flows.Flow()},
outputs={bel: flows.Flow(nominal_value=10e10, variable_costs=50)},
conversion_factors={bel: 0.58},
inputs={bus_gas: flows.Flow()},
outputs={
bus_electricity: flows.Flow(
nominal_value=10e10, variable_costs=50
)
},
conversion_factors={bus_electricity: 0.58},
)
)

# create storage object representing a battery
storage = cmp.GenericStorage(
nominal_storage_capacity=10077997,
label="storage",
inputs={bel: flows.Flow(nominal_value=10077997 / 6)},
nominal_capacity = 10077997
nominal_value = nominal_capacity / 6

battery_storage = solver_components.GenericStorage(
nominal_storage_capacity=nominal_capacity,
label=BATTERY_STORAGE,
inputs={bus_electricity: flows.Flow(nominal_value=nominal_value)},
outputs={
bel: flows.Flow(nominal_value=10077997 / 6, variable_costs=0.001)
bus_electricity: flows.Flow(
nominal_value=nominal_value, variable_costs=0.001
)
},
loss_rate=0.00,
initial_storage_level=None,
inflow_conversion_factor=1,
outflow_conversion_factor=0.8,
)

energysystem.add(storage)
energysystem.add(battery_storage)

##########################################################################
# Optimise the energy system and plot the results
Expand All @@ -201,97 +253,80 @@ def main():
logging.info("Optimise the energy system")

# initialise the operational model
model = Model(energysystem)
energysystem_model = Model(energysystem)

# This is for debugging only. It is not(!) necessary to solve the problem
# and should be set to False to save time and disc space in normal use. For
# debugging the timesteps should be set to 3, to increase the readability
# of the lp-file.
if debug:
filename = os.path.join(
file_path = os.path.join(
helpers.extend_basic_path("lp_files"), "basic_example.lp"
)
logging.info("Store lp-file in {0}.".format(filename))
model.write(filename, io_options={"symbolic_solver_labels": True})
logging.info(f"Store lp-file in {file_path}.")
io_option = {"symbolic_solver_labels": True}
energysystem_model.write(file_path, io_options=io_option)

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

logging.info("Store the energy system with the results.")

# The processing module of the outputlib can be used to extract the results
# from the model transfer them into a homogeneous structured dictionary.

# add results to the energy system to make it possible to store them.
energysystem.results["main"] = processing.results(model)
energysystem.results["meta"] = processing.meta_results(model)
energysystem.results["main"] = processing.results(energysystem_model)
energysystem.results["meta"] = processing.meta_results(energysystem_model)

# The default path is the '.oemof' folder in your $HOME directory.
# The default filename is 'es_dump.oemof'.
# You can omit the attributes (as None is the default value) for testing
# cases. You should use unique names/folders for valuable results to avoid
# overwriting.

# store energy system with results
energysystem.dump(dpath=None, filename=None)
if dump_results:
energysystem.dump(dpath=None, filename=None)

# *************************************************************************
# ********** PART 2 - Processing the results ******************************
# *************************************************************************

logging.info("**** The script can be divided into two parts here.")
logging.info("Restore the energy system and the results.")
energysystem = EnergySystem()
energysystem.restore(dpath=None, filename=None)
# Saved data can be restored in a second script. So you can work on the
# data analysis without re-running the optimisation every time. If you do
# so, make sure that you really load the results you want. For example,
# if dumping fails, you might exidentially load outdated results.
if restore_results:
logging.info("**** The script can be divided into two parts here.")
logging.info("Restore the energy system and the results.")

energysystem = EnergySystem()
energysystem.restore(dpath=None, filename=None)

# define an alias for shorter calls below (optional)
results = energysystem.results["main"]
storage = energysystem.groups["storage"]
storage = energysystem.groups[BATTERY_STORAGE]

# print a time slice of the state of charge
print("")
print("********* State of Charge (slice) *********")
print(
results[(storage, None)]["sequences"][
datetime(2012, 2, 25, 8, 0, 0) : datetime(2012, 2, 25, 17, 0, 0)
]
)
print("")
start_time = datetime(2012, 2, 25, 8, 0, 0)
end_time = datetime(2012, 2, 25, 17, 0, 0)

print("\n********* State of Charge (slice) *********")
print(f"{results[(storage, None)]['sequences'][start_time : end_time]}\n")

# get all variables of a specific component/bus
custom_storage = views.node(results, "storage")
custom_storage = views.node(results, BATTERY_STORAGE)
electricity_bus = views.node(results, "electricity")

# plot the time series (sequences) of a specific component/bus

fig, ax = plt.subplots(figsize=(10, 5))
custom_storage["sequences"].plot(
ax=ax, kind="line", drawstyle="steps-post"
)
plt.legend(
loc="upper center",
prop={"size": 8},
bbox_to_anchor=(0.5, 1.25),
ncol=2,
)
fig.subplots_adjust(top=0.8)
plt.show()

fig, ax = plt.subplots(figsize=(10, 5))
electricity_bus["sequences"].plot(
ax=ax, kind="line", drawstyle="steps-post"
)
plt.legend(
loc="upper center", prop={"size": 8}, bbox_to_anchor=(0.5, 1.3), ncol=2
)
fig.subplots_adjust(top=0.8)
plt.show()
plot_figures_for(custom_storage)
plot_figures_for(electricity_bus)

# print the solver results
print("********* Meta results *********")
pp.pprint(energysystem.results["meta"])
print("")
pp.pprint(f"{energysystem.results['meta']}\n")

# print the sums of the flows around the electricity bus
print("********* Main results *********")
Expand Down

0 comments on commit 0986292

Please sign in to comment.