-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
.. _validation_stratified_thermal_storage_label: | ||
|
||
|
||
Stratified thermal storage | ||
=================================== | ||
|
||
Scope | ||
_____ | ||
|
||
The validation of the stratified thermal storage has been conducted within the oemof_heat project. | ||
Measurement data of a reference storage has been provided by the energy supplier Naturstrom AG. | ||
The set of data contains the storage geometry (height, diameter, insulation thickness), | ||
temperatures at top and bottom of the storage and a time series of the storage level. | ||
|
||
Method | ||
_______ | ||
|
||
In order to calculate the storage level using the StratifiedThermalStorage | ||
component from oemof-thermal | ||
the storage geometry, | ||
the temperatures of the hot and cold layers (top and bottom), | ||
the temperature of the environment, | ||
the heat conductivity of the insulation and | ||
the heat transfer coefficients inside and outside of the storage surface | ||
are required. | ||
Tab.1 shows the required input parameter and the respective values of the reference storage. | ||
For some parameters assumptions had to be made. | ||
|
||
================================ ============================= | ||
Name Value | ||
================================ ============================= | ||
height 2.96 m | ||
diameter 1.15 m | ||
insulation thickness 100 mm | ||
temperature of hot layer 82°C | ||
temperature of cold layer 55°C | ||
**Own Assumptions** | ||
-------------------------------------------------------------- | ||
temperature of environment 25°C | ||
conductivity of insulation 0.039 W/(m*K) | ||
heat transfer coef. inside 7 W/(m2*K) | ||
heat transfer coef. outside 4 W/(m2*K) | ||
================================ ============================= | ||
Tab.1: Input parameters used for the model validation | ||
|
||
Please see the | ||
`USER'S GUIDE <https://oemof-thermal.readthedocs.io/en/latest/stratified_thermal_storage.html>`_ on the stratified thermal storage for further information. | ||
|
||
The level of the reference storage is not measured directly but is | ||
determined from the temperatures at different heights :math:`T_{i}` in the storage. | ||
|
||
.. math:: | ||
level = \frac{T_\mathrm{mean} - T_\mathrm{cold}}{T_\mathrm{hot} - T_\mathrm{cold}} | ||
where :math:`T_\mathrm{mean}` is the arithmetic mean temperature of the storage. | ||
|
||
.. math:: | ||
T_\mathrm{mean} = \frac{\sum_{i=1}^{n} T_{i}}{n} | ||
where :math:`n` is the amount of temperature sensors. | ||
|
||
Measurement data | ||
_______ | ||
|
||
The measurement data come from an energy system that contains several identical storages. | ||
Here, only a single storage is calculated to keep the model simple. | ||
|
||
The validation aims on checking how accurately the losses of the storage are predicted. | ||
This does not include losses during the charging and discharging | ||
(inflow_conversion_factor and outflow_conversion_factor). | ||
Therefore a short time series of measurement data (see Tab.2) is used for the | ||
validation where no charging or discharging occurs. | ||
|
||
======= ============== | ||
time level in % | ||
======= ============== | ||
0.0 78.50 | ||
0.25 78.21 | ||
0.5 78.38 | ||
0.75 78.00 | ||
1.0 78.25 | ||
1.25 77.79 | ||
1.5 77.75 | ||
1.75 77.04 | ||
2.0 77.17 | ||
2.25 77.63 | ||
2.5 78.00 | ||
2.75 77.71 | ||
3.0 77.79 | ||
3.15 77.29 | ||
3.5 77.00 | ||
3.75 76.38 | ||
4.0 77.33 | ||
4.25 77.21 | ||
4.5 77.00 | ||
4.75 77.29 | ||
5.0 77.08 | ||
5.25 76.54 | ||
5.5 76.33 | ||
======= ============== | ||
Tab.2: Measured storage level. | ||
|
||
Results | ||
________ | ||
|
||
|
||
|
||
.. figure:: _pics/storage_level.png | ||
:width: 80 % | ||
:alt: storage_level.png | ||
:align: center | ||
:figclass: align-center | ||
|
||
Fig.1: Measured storage level (red) and calculated storage level (blue). | ||
|
||
|
||
|
||
References | ||
__________ |
24 changes: 24 additions & 0 deletions
24
examples/stratified_thermal_storage/data/storage_soc_measured.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
,level | ||
0,78.5 | ||
1,78.21 | ||
2,78.38 | ||
3,78.0 | ||
4,78.25 | ||
5,77.79 | ||
6,77.75 | ||
7,77.04 | ||
8,77.17 | ||
9,77.63 | ||
10,78.0 | ||
11,77.71 | ||
12,77.79 | ||
13,77.29 | ||
14,77.0 | ||
15,76.38 | ||
16,77.33 | ||
17,77.21 | ||
18,77.0 | ||
19,77.29 | ||
20,77.08 | ||
21,76.54 | ||
22,76.33 |
17 changes: 17 additions & 0 deletions
17
examples/stratified_thermal_storage/data/validation_data.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
stratified_thermal_storage.csvvar_name,var_value,var_unit | ||
height,2.96,m | ||
diameter,1.15,m | ||
temp_h,82,degC | ||
temp_c,55,degC | ||
temp_env,25,degC | ||
inflow_conversion_factor,1, | ||
outflow_conversion_factor,1, | ||
min_storage_level,0.25, | ||
max_storage_level,0.95, | ||
initial_storage_level,0.785, | ||
maximum_heat_flow_charging,2,MW | ||
maximum_heat_flow_discharging,2,MW | ||
s_iso,100,mm | ||
lamb_iso,0.039,W/(m*K) | ||
alpha_inside,7,W/(m2*K) | ||
alpha_outside,4,W/(m2*K) |
172 changes: 172 additions & 0 deletions
172
examples/stratified_thermal_storage/model_validation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import os | ||
import matplotlib.pyplot as plt | ||
import pandas as pd | ||
import numpy as np | ||
import oemof.solph as solph | ||
from oemof.thermal.stratified_thermal_storage import ( # noqa | ||
calculate_storage_u_value, | ||
calculate_storage_dimensions, | ||
calculate_capacities | ||
) | ||
from oemof.thermal import facades | ||
|
||
Source = solph.Source | ||
Sink = solph.Sink | ||
Bus = solph.Bus | ||
Flow = solph.Flow | ||
Model = solph.Model | ||
EnergySystem = solph.EnergySystem | ||
|
||
data_path = os.path.join( | ||
os.path.dirname(os.path.abspath(__file__)), | ||
'./data/validation_data.csv') | ||
|
||
input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] | ||
|
||
|
||
def run_storage_model(initial_storage_level, temp_h, temp_c): | ||
data_path = os.path.join( | ||
os.path.dirname(os.path.abspath(__file__)), | ||
'./data/validation_data.csv') | ||
|
||
input_data = pd.read_csv(data_path, index_col=0, header=0)['var_value'] | ||
|
||
u_value = calculate_storage_u_value( | ||
input_data['s_iso'], | ||
input_data['lamb_iso'], | ||
input_data['alpha_inside'], | ||
input_data['alpha_outside']) | ||
|
||
# Set up an energy system model | ||
periods = 10 | ||
datetimeindex = pd.date_range('1/1/2019', periods=periods, freq='H') | ||
demand_timeseries = np.zeros(periods) | ||
heat_feedin_timeseries = np.zeros(periods) | ||
|
||
energysystem = EnergySystem(timeindex=datetimeindex) | ||
|
||
bus_heat = Bus(label='bus_heat') | ||
|
||
heat_source = Source( | ||
label='heat_source', | ||
outputs={bus_heat: Flow( | ||
nominal_value=1, | ||
fix=heat_feedin_timeseries)}) | ||
|
||
shortage = Source( | ||
label='shortage', | ||
outputs={bus_heat: Flow(variable_costs=1e6)}) | ||
|
||
excess = Sink( | ||
label='excess', | ||
inputs={bus_heat: Flow()}) | ||
|
||
heat_demand = Sink( | ||
label='heat_demand', | ||
inputs={bus_heat: Flow( | ||
nominal_value=1, | ||
fix=demand_timeseries)}) | ||
|
||
thermal_storage = facades.StratifiedThermalStorage( | ||
label='thermal_storage', | ||
bus=bus_heat, | ||
diameter=input_data['diameter'], | ||
height=input_data['height'], | ||
temp_h=temp_h, | ||
temp_c=temp_c, | ||
temp_env=input_data['temp_env'], | ||
u_value=u_value, # W/(m2*K) | ||
min_storage_level=input_data['min_storage_level'], | ||
max_storage_level=input_data['max_storage_level'], | ||
initial_storage_level=initial_storage_level, | ||
capacity=input_data['maximum_heat_flow_charging'], | ||
efficiency=1, | ||
marginal_cost=0.0001 | ||
) | ||
|
||
energysystem.add( | ||
bus_heat, | ||
heat_source, | ||
shortage, | ||
excess, | ||
heat_demand, | ||
thermal_storage) | ||
|
||
# create and solve the optimization model | ||
optimization_model = Model(energysystem) | ||
optimization_model.write('storage_model_facades.lp', | ||
io_options={'symbolic_solver_labels': True}) | ||
optimization_model.solve(solver='cbc', solve_kwargs={'tee': False}) | ||
|
||
energysystem.results['main'] = solph.processing.results(optimization_model) | ||
string_results = solph.views.convert_keys_to_strings(energysystem.results['main']) | ||
|
||
# Get time series of level (state of charge) of the thermal energy storage | ||
TES_soc = (string_results['thermal_storage', 'None']['sequences']) | ||
|
||
# Save results to csv file | ||
TES_soc.to_csv("./data/storage_soc_calculated.csv") | ||
|
||
return | ||
|
||
|
||
initial_storage_level = input_data['initial_storage_level'] | ||
|
||
run_storage_model(initial_storage_level=input_data['initial_storage_level'], | ||
temp_h=input_data['temp_h'], | ||
temp_c=input_data['temp_c']) | ||
|
||
volume, surface = calculate_storage_dimensions( | ||
input_data['height'], | ||
input_data['diameter']) | ||
|
||
# Max capacity in MWh | ||
nominal_storage_capacity = calculate_capacities( | ||
volume, | ||
input_data['temp_h'], | ||
input_data['temp_c']) | ||
|
||
# Get measurement data | ||
filename = './data/storage_soc_measured.csv' | ||
level_meas = pd.read_csv(filename, header=0) | ||
|
||
# Get simulation results (hourly values) | ||
filename = './data/storage_soc_calculated.csv' | ||
TES_soc_df = pd.read_csv(filename, header=0) | ||
|
||
# Convert to list | ||
TES_soc_hourly = TES_soc_df['storage_content'].values | ||
|
||
# Convert simulation data to relative values in % | ||
TES_soc_relative = [soc / nominal_storage_capacity * 100 for soc in | ||
TES_soc_hourly] | ||
|
||
end_step = 7 | ||
|
||
# Make list with time steps for x-axes in plot | ||
t_meas = np.arange(0, (len(level_meas) / 4), 0.25) | ||
|
||
plt.style.use('ggplot') | ||
fig, ax = plt.subplots() | ||
|
||
# Plot horizontal line (initial level) | ||
init_level = level_meas['level'][0] | ||
plt.plot([init_level] * end_step, '--', color='gray') | ||
|
||
# Plot simulation data | ||
TES_soc_relative_list = [initial_storage_level * 100] | ||
[TES_soc_relative_list.append(TES_soc_relative[i]) for i in range(10)] | ||
plt.plot(TES_soc_relative_list[:end_step], | ||
label="storage level (simulation)") | ||
|
||
# Plot measurement data | ||
plt.plot(t_meas, level_meas['level'], label="storage level (measurement)") | ||
|
||
plt.legend() | ||
ax.set_xlabel("Time in h") | ||
ax.set_ylabel("Storage level in %") | ||
ax.set_xlim([0, 5.5]) | ||
|
||
ax.set_ylim([75, 80]) | ||
|
||
plt.savefig("validation.png") |