-
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
12 changed files
with
898 additions
and
1 deletion.
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.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
.. _absorption_chillers_label: | ||
|
||
|
||
~~~~~~~~~~~~~~~~~~~~~~~ | ||
Absorption chillers | ||
~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Calculations for absorption chillers based on the characteristic equation method. | ||
|
||
|
||
Scope | ||
_____ | ||
|
||
This module was developed to provide cooling capacity and COP calculations | ||
based on temperatures for energy system optimizations with oemof.solph. | ||
|
||
|
||
Concept | ||
_______ | ||
|
||
A characteristic equation model to describe the performance of | ||
absorption chillers. | ||
|
||
|
||
.. figure:: _pics/abs_chiller_col.png | ||
:width: 40 % | ||
:alt: abs_chiller_col.png | ||
:align: center | ||
:figclass: align-center | ||
|
||
Fig.1: Absorption cooling (or heating) process. | ||
|
||
The cooling capacity (:math:`\dot{Q}_{E}`) is determined by a function of | ||
the characteristic | ||
temperature difference (:math:`\Delta\Delta t'`) that combines the external | ||
mean temperatures of the heat exchangers. | ||
|
||
Various approaches of the characteristic equation method exists. | ||
Here we use the approach described by Kühn and Ziegler [1]: | ||
|
||
.. math:: | ||
\Delta\Delta t = t_{G} - a \cdot t_{AC} + e \cdot t_{E} | ||
with the assumption | ||
|
||
.. math:: | ||
t_{A} = t_{C} = t_{AC} | ||
where :math:`t` is the external mean fluid temperature of the heat exchangers | ||
(G: Generator, AC: Absorber and Condenser, E: Evaporator) | ||
and :math:`a` and :math:`e` are characteristic parameters. | ||
|
||
The cooling capacity (:math:`\dot{Q}_{E}`) and the driving | ||
heat (:math:`\dot{Q}_{G}`) can be expressed as linear functions of :math:`\Delta\Delta t'`: | ||
|
||
.. math:: | ||
\dot{Q}_{E} = s_{E} \cdot \Delta\Delta t' + r_{E} | ||
.. math:: | ||
\dot{Q}_{G} = s_{G} \cdot \Delta\Delta t' + r_{G} | ||
with the characteristic parameters :math:`s_{E}`, :math:`r_{E}`, | ||
:math:`s_{G}`, and :math:`r_{G}`. | ||
|
||
The COP can then be calculated from :math:`\dot{Q}_{E}` and :math:`\dot{Q}_{G}`: | ||
|
||
.. math:: | ||
COP = \frac{\dot{Q}_{E}}{\dot{Q}_{G}} | ||
These arguments are used in the formulas of the function: | ||
|
||
========================= =================================================== =========== | ||
symbol argument explanation | ||
========================= =================================================== =========== | ||
:math:`\Delta\Delta t'` :py:obj:`ddt` Characteristic temperature difference | ||
|
||
:math:`t_G` :py:obj:`t_hot` External mean fluid temperature of generator | ||
|
||
:math:`t_{AC}` :py:obj:`t_cool` External mean fluid temperature of absorber and condenser | ||
|
||
:math:`t_E` :py:obj:`t_chill` External mean fluid temperature of evaporator | ||
|
||
:math:`a` :py:obj:`coef_a` Characteristic parameter | ||
|
||
:math:`e` :py:obj:`coef_e` Characteristic parameter | ||
|
||
:math:`s` :py:obj:`coef_s` Characteristic parameter | ||
|
||
:math:`r` :py:obj:`coef_r` Characteristic parameter | ||
|
||
:math:`\dot{Q}` :py:obj:`Q_dots` Heat flux | ||
|
||
:math:`\dot{Q}_{E}` :py:obj:`Q_dots_evap` Cooling capacity (heat flux at evaporator) | ||
|
||
:math:`\dot{Q}_{G}` :py:obj:`Q_dots_gen` Driving heat (heat flux at generator) | ||
|
||
:math:`COP` :py:obj:`COP` Coefficient of performance | ||
========================= =================================================== =========== | ||
|
||
Usage | ||
_____ | ||
|
||
The following example shows how to calculate the COP of a small absorption chiller. | ||
The characteristic coefficients used in this examples belong to a 10 kW absorption chiller developed and | ||
tested at the Technische Universität Berlin [1]. | ||
|
||
.. code-block:: python | ||
|
||
import oemof.thermal.absorption_heatpumps_and_chillers as abs_chiller | ||
|
||
# Characteristic temperature difference | ||
ddt = abs_chiller.calc_characteristic_temp( | ||
t_hot=[85], # in °C | ||
t_cool=[26], # in °C | ||
t_chill=[15], # in °C | ||
coef_a=10, | ||
coef_e=2.5, | ||
method='kuehn_and_ziegler') | ||
|
||
# Cooling capacity | ||
Q_dots_evap = abs_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=0.42, | ||
coef_r=0.9, | ||
method='kuehn_and_ziegler') | ||
|
||
# Driving heat | ||
Q_dots_gen = abs_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=0.51, | ||
coef_r=2, | ||
method='kuehn_and_ziegler') | ||
|
||
COPs = Q_dots_evap / Q_dots_gen | ||
|
||
Fig.2 illustrates how the cooling capacity and the COP of an absorption | ||
chiller (here the 10 kW absorption chiller mentioned above) depend on the | ||
cooling water temperature, i.e. the mean external fluid temperature at | ||
absorber and condenser. | ||
|
||
.. figure:: _pics/cooling_capacity_over_cooling_water_temperature_Kuehn.png | ||
:width: 80 % | ||
:alt: cooling_capacity_over_cooling_water_temperature_Kuehn.png | ||
:align: center | ||
:figclass: align-center | ||
|
||
Fig.2: Dependency of the cooling capacity and the COP of a 10 kW absorption | ||
chiller on the cooling water temperature. | ||
|
||
You find the code that is behind Fig.2 in our examples: | ||
https://github.com/oemof/oemof-thermal/tree/master/examples | ||
|
||
You can run the calculations for any other absorption heat pump or chiller by | ||
entering the specific parameters (a, e, s, r) belonging to that specific machine. | ||
The specific parameters are determined by a numerical fit of the | ||
four parameters with testing data or data from the fact sheet (technical | ||
specifications from the manufacturer) if temperatures for at least two | ||
points of operation are given. | ||
You find detailed information in the referenced papers. | ||
|
||
This package comes with characteristic parameters for five absorption chillers. | ||
Four published by Puig-Arnavat et al. [3]: 'Rotartica', 'Safarik', 'Broad_01' and 'Broad_02' | ||
and one published by Kühn and Ziegler [1]: 'Kuehn'. | ||
If you like to contribute parameters for other machines, | ||
please feel free to contact us or to contribute directly via github. | ||
|
||
To model one of the machines provided by this package you can adapt the code | ||
above in the following way. | ||
|
||
.. code-block:: python | ||
|
||
import oemof.thermal.absorption_heatpumps_and_chillers as abs_chiller | ||
import pandas as pd | ||
import os | ||
|
||
filename_charpara = os.path.join(os.path.dirname(__file__), 'data/characteristic_parameters.csv') | ||
charpara = pd.read_csv(filename_charpara) | ||
|
||
chiller_name = 'Kuehn' # 'Rotartica', 'Safarik', 'Broad_01', 'Broad_02' | ||
|
||
# Characteristic temperature difference | ||
ddt = abs_chiller.calc_characteristic_temp( | ||
t_hot=[85], # in °C | ||
t_cool=[26], # in °C | ||
t_chill=[15], # in °C | ||
coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], | ||
coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], | ||
method='kuehn_and_ziegler') | ||
|
||
# Cooling capacity | ||
Q_dots_evap = abs_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=charpara[(charpara['name'] == chiller_name)]['s_E'].values[0], | ||
coef_r=charpara[(charpara['name'] == chiller_name)]['r_E'].values[0], | ||
method='kuehn_and_ziegler') | ||
|
||
# Driving heat | ||
Q_dots_gen = abs_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=charpara[(charpara['name'] == chiller_name)]['s_G'].values[0], | ||
coef_r=charpara[(charpara['name'] == chiller_name)]['r_G'].values[0], | ||
method='kuehn_and_ziegler') | ||
|
||
COPs = [Qevap / Qgen for Qgen, Qevap in zip(Q_dots_gen, Q_dots_evap)] | ||
|
||
You find information on the machines in [1], [2] and [3]. | ||
Please be aware that [2] introduces a slightly different approach | ||
(using an improved characteristic equation with :math:`\Delta\Delta t''` | ||
instead of :math:`\Delta\Delta t'`). | ||
The characteristic parameters that we use are derived from [2] and therefore differ from those in [1]. | ||
|
||
References | ||
__________ | ||
|
||
|
||
.. include:: ../src/oemof/thermal/absorption_heatpumps_and_chillers.py | ||
:start-after: Reference** | ||
:end-before: """ |
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
140 changes: 140 additions & 0 deletions
140
examples/absorption_heatpumps_and_chiller/absorption_chiller.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,140 @@ | ||
|
||
# import absorption_heatpumps_and_chillers as abs_hp_chiller | ||
import oemof.thermal.absorption_heatpumps_and_chillers as abs_hp_chiller | ||
import oemof.solph as solph | ||
import oemof.outputlib as outputlib | ||
import pandas as pd | ||
import os | ||
import matplotlib.pyplot as plt | ||
|
||
solver = 'cbc' | ||
debug = False | ||
number_of_time_steps = 48 | ||
solver_verbose = False | ||
|
||
date_time_index = pd.date_range('1/1/2012', periods=number_of_time_steps, | ||
freq='H') | ||
energysystem = solph.EnergySystem(timeindex=date_time_index) | ||
|
||
# Read data file | ||
filename_data = os.path.join(os.path.dirname(__file__), 'data/AC_example.csv') | ||
data = pd.read_csv(filename_data) | ||
|
||
filename_charpara = os.path.join(os.path.dirname(__file__), | ||
'data/characteristic_parameters.csv') | ||
charpara = pd.read_csv(filename_charpara) | ||
chiller_name = 'Kuehn' | ||
|
||
# Buses with three different temperature levels | ||
b_th_high = solph.Bus(label="hot") | ||
# b_th_medium = solph.Bus(label="cool") | ||
b_th_low = solph.Bus(label="chilled") | ||
energysystem.add(b_th_high, b_th_low) | ||
|
||
energysystem.add(solph.Source( | ||
label='driving_heat', | ||
outputs={b_th_high: solph.Flow(variable_costs=0)})) | ||
energysystem.add(solph.Source( | ||
label='cooling_shortage', | ||
outputs={b_th_low: solph.Flow(variable_costs=20)})) | ||
# energysystem.add(solph.Sink( | ||
# label='dry_cooling_tower', | ||
# inputs={b_th_medium: solph.Flow(variable_costs=0)})) | ||
energysystem.add(solph.Sink( | ||
label='cooling_demand', | ||
inputs={b_th_low: solph.Flow(actual_value=1, | ||
fixed=True, | ||
nominal_value=35)})) | ||
|
||
# Mean cooling water temperature in degC (dry cooling tower) | ||
temp_difference = 4 | ||
t_cooling = [t + temp_difference for t in data['air_temperature']] | ||
n = len(t_cooling) | ||
|
||
# Pre-Calculations | ||
ddt = abs_hp_chiller.calc_characteristic_temp( | ||
t_hot=[85], | ||
t_cool=t_cooling, | ||
t_chill=[15] * n, | ||
coef_a=charpara[(charpara['name'] == chiller_name)]['a'].values[0], | ||
coef_e=charpara[(charpara['name'] == chiller_name)]['e'].values[0], | ||
method='kuehn_and_ziegler') | ||
Q_dots_evap = abs_hp_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=charpara[(charpara['name'] == chiller_name)]['s_E'].values[0], | ||
coef_r=charpara[(charpara['name'] == chiller_name)]['r_E'].values[0], | ||
method='kuehn_and_ziegler') | ||
Q_dots_gen = abs_hp_chiller.calc_heat_flux( | ||
ddts=ddt, | ||
coef_s=charpara[(charpara['name'] == chiller_name)]['s_G'].values[0], | ||
coef_r=charpara[(charpara['name'] == chiller_name)]['r_G'].values[0], | ||
method='kuehn_and_ziegler') | ||
COPs = [Qevap / Qgen for Qgen, Qevap in zip(Q_dots_gen, Q_dots_evap)] | ||
nominal_Q_dots_evap = 10 | ||
actual_value = [Q_e / nominal_Q_dots_evap for Q_e in Q_dots_evap] | ||
actual_value_df = pd.DataFrame(actual_value).clip(0) | ||
|
||
# Absorption Chiller | ||
energysystem.add(solph.Transformer( | ||
label="AC", | ||
inputs={b_th_high: solph.Flow()}, | ||
outputs={b_th_low: solph.Flow(nominal_value=nominal_Q_dots_evap, | ||
max=actual_value_df.values, | ||
variable_costs=5)}, | ||
conversion_factors={b_th_low: COPs})) | ||
|
||
model = solph.Model(energysystem) | ||
|
||
model.solve(solver=solver, solve_kwargs={'tee': solver_verbose}) | ||
|
||
energysystem.results['main'] = outputlib.processing.results(model) | ||
energysystem.results['meta'] = outputlib.processing.meta_results(model) | ||
|
||
energysystem.dump(dpath=None, filename=None) | ||
|
||
|
||
# **************************************************************************** | ||
# ********** PART 2 - Processing the results ********************************* | ||
# **************************************************************************** | ||
|
||
energysystem = solph.EnergySystem() | ||
energysystem.restore(dpath=None, filename=None) | ||
|
||
results = energysystem.results['main'] | ||
|
||
high_temp_bus = outputlib.views.node(results, 'hot') | ||
low_temp_bus = outputlib.views.node(results, 'chilled') | ||
|
||
string_results = outputlib.views.convert_keys_to_strings( | ||
energysystem.results['main']) | ||
AC_output = string_results[ | ||
'AC', 'chilled']['sequences'].values | ||
demand_cooling = string_results[ | ||
'chilled', 'cooling_demand']['sequences'].values | ||
ASHP_input = string_results[ | ||
'hot', 'AC']['sequences'].values | ||
|
||
|
||
fig2, axs = plt.subplots(3, 1, figsize=(8, 5), sharex=True) | ||
axs[0].plot(AC_output, label='cooling output') | ||
axs[0].plot(demand_cooling, linestyle='--', label='cooling demand') | ||
axs[1].plot(COPs, linestyle='-.') | ||
axs[2].plot(data['air_temperature']) | ||
axs[0].set_title('Cooling capacity and demand') | ||
axs[1].set_title('Coefficient of Performance') | ||
axs[2].set_title('Air Temperature') | ||
axs[0].legend() | ||
|
||
axs[0].grid() | ||
axs[1].grid() | ||
axs[2].grid() | ||
axs[0].set_ylabel('Cooling capacity in kW') | ||
axs[1].set_ylabel('COP') | ||
axs[2].set_ylabel('Temperature in $°$C') | ||
axs[2].set_xlabel('Time in h') | ||
plt.tight_layout() | ||
plt.show() | ||
|
||
print('********* Main results *********') | ||
print(high_temp_bus['sequences'].sum(axis=0)) | ||
print(low_temp_bus['sequences'].sum(axis=0)) |
Oops, something went wrong.