%load_ext autoreload
%autoreload 2
This Jupyter Notebook is part of the documentation for openACHP. It has been exported to HTML and reveal.js slides.
If you are viewing the slides, just type space
to advance each slide. Type ?
to view keyboard shortcuts.
To start, I'll import the modules I need ...
import ammonia1
import system_aqua1
import numpy as np
import matplotlib.pyplot as plt
Now I can create what I call a boundary for the chiller, in other words the specification of available resources, such as flow streams for bringing in or rejecting heat. I'll need that to evaluate heat exchangers later. In the output, we can see each of the flow streams defined where it goes into the chiller.
T_heat_reject = 305.
U = 100
xB = [400, 1, T_heat_reject, 3, T_heat_reject, 5, 285, 4, T_heat_reject, 0.15]
bdry = system_aqua1.makeBoundary(xB)
bdry
We select a chiller model. I'm showing ammonia-aqua, single-effect system. Just for reference, here is a drawing explaining the chiller model...
secret_sauce="""
<script class="comment">
/*
These are all the state points:
rich_abs_outlet
rich_pump_outlet
rich_shx_outlet
rich_gen_sat_liquid
weak_gen_outlet
weak_shx_outlet
weak_exp_outlet
gen_vapor_outlet
gen_reflux_inlet
refrig_rect_outlet
refrig_cond_outlet
refrig_cehx_liquid_outlet
refrig_exp_outlet
refrig_evap_outlet
refrig_cehx_sat_vapor
refrig_cehx_vapor_outlet
rectifier_liquid
gen_vapor_formation
abs_vapor_final
*/
;
</script>
<script type="application/json" id="stuff">
{
"1": "rich_abs_outlet",
"2": "rich_pump_outlet",
"3": "rich_shx_outlet",
"3'": "rich_gen_sat_liquid",
"4": "weak_gen_outlet",
"5": "weak_shx_outlet",
"6": "weak_exp_outlet",
"7(f)": "gen_reflux_inlet",
"8(g)": "gen_vapor_outlet",
"9": "refrig_rect_outlet",
"10": "refrig_cond_outlet",
"11": "refrig_cehx_liquid_outlet",
"12": "refrig_exp_outlet",
"13": "refrig_evap_outlet",
"14": "refrig_cehx_vapor_outlet"
}
</script>
<script type="application/json" id="mydiagramdata">
{}
</script>
<script>
// TODO: security problem
map = JSON.parse($("#stuff").text());
$("svg:first").children("text").children().each(function(){
key = $(this).text();
if (key in map) {
// If we got here, then the element is a point label
// Note that this line won't work due to a bug with jQuery and svg
//$(this).addClass(map[key]);
this.classList.add('point_label');
$(this).data('point_label',map[key]);
$(this).hover(tooltipHoverIn, tooltipHoverOut);
}
});
function tooltipHoverIn(event){
var key = $(this).data('point_label');
console.log('Creating a tooltip at ' + event.pageX + ',' + event.pageY + ' for "' + key +'"');
// $('<div class="tooltip">test</div>').appendTo('body');
var diagram_data = JSON.parse($("#mydiagramdata").text());
var point = diagram_data[key];
var divtext = '<b>' +key + '</b><br/>';
for (point_key in point) {
divtext += point_key + ': ' + point[point_key].toPrecision(6) + '<br/>';
}
$('<div class="tooltip">' + divtext + '</div>').appendTo('body');
var tPosX = event.pageX + 10;
var tPosY = event.pageY - 50;
$('div.tooltip').css({'position': 'absolute', 'top': tPosY+'px', 'left': tPosX+'px',
'opacity':1, 'background-color':'white', 'border':'1px solid black'});
}
function tooltipHoverOut(event){
$('div.tooltip').remove();
console.log("and you're out");
}
$("svg:first").click(function(){
for (var key in map) {
cls = "." + map[key]
$(cls).html(map[key]);
}
});
</script>
<p>Single-effect ammonia-water absorption cycle model. Mouse over state points to view fluid state data.</p>
"""
from IPython.display import HTML, SVG
filename='../img/Diagram_for_ammonia.svg'
img = SVG(filename=filename)
display(img)
display(HTML(secret_sauce))
If you are viewing this as slides you may want to see that as a video...
Now I'll specify and create a chiller. This specification is an output from one of my optimization runs, so it is supposed to be the "best" chiller with a total heat exchange inventory of 100 kW/K. The specification I've chosen is based on temperatures at a few key points, which allows me to solve the model procedurally, without an iterative solver.
xC = np.array([0.51284472, 277.97717012, 312.16427764, 313.6952877,
310.24856734, 374.14020482])
ch = system_aqua1.makeChiller(xC)
We can display a table of all the state points.
table = ch.getStateTable()
display(table)
# This stores the table into the webpage ... now go mouse over the diagram.
display(HTML("<script>$('#mydiagramdata').text('{}');</script>".format(table.toJSON())))
We also get a table of the performance variables, such as heat flows (with a convention that positive indicates heat flowing into the chiller).
ch.getVariablesTable()
I'll also ask for some plots ...
ch.display()
plt.show()
The first one shows heat vs temperature curves for each of the heat exchangers facing the external streams. Temperature is measured on the internal side of the heat exchanger. These curves are used to calculate heat exchanger size vs. total heat rate.
The other two show some heat exchangers that are entirely internal, ie not facing the external streams. These were specified with effectiveness, so I don't need to calculate anything. But we can see that the curves are not linear because specific heats are changing or phase change is occuring through the process.
Next I'll couple the boundary and chiller into a system object. I'll use it to evaluate the heat exchange parameters:
sys = system_aqua1.System(boundary=bdry, chiller=ch)
display(sys)
Okay, the next thing I want to do is show how we can interact with the model and manually adjust the specification. Jupyter has some useful widgets, but I had to get the ipywidgets package from different conda channel (conda-forge) to get a version compatible with the notebook extensions module (then enable it on command line).
import ipywidgets as widgets
print("widgets.__version__ = ",widgets.__version__)
import widgetsnbextension
print("widgetsnbextension.__version__ = ",widgetsnbextension.__version__)
print("Note: this won't work if the versions are incompatible.")
This cell sets up the widgets ...
%matplotlib inline
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, interact_manual
common_opts = dict(
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True
)
w_m_rich = widgets.FloatSlider(
value=0.5,
min=0.1,
max=2.0,
step=0.05,
description='m_rich',
readout_format='.2f',
**common_opts
)
w_T_evap = widgets.FloatSlider(
value=278.0,
min=275.0,
max=285.0,
step=0.1,
description='T_evap',
readout_format='.1f',
**common_opts
)
w_T_cond = widgets.FloatSlider(
value= 312.2,
min=290,
max=330,
step=0.1,
description='T_cond',
readout_format='.1f',
**common_opts
)
w_T_rect = widgets.FloatSlider(
value=313.7,
min=290.0,
max=330.0,
step=0.1,
description='T_rect',
readout_format='.1f',
**common_opts
)
w_T_abs_outlet = widgets.FloatSlider(
value=310.2,
min=290.0,
max=330.0,
step=0.1,
description='T_abs_outlet',
readout_format='.1f',
**common_opts
)
w_T_gen_outlet = widgets.FloatSlider(
value=374.1,
min=360.0,
max=400.0,
step=0.1,
description='T_gen_outlet',
readout_format='.1f',
**common_opts
)
And this cell launches the interaction handler...
modes = ['chiller glob','chiller state points','chiller performance','chiller plots','system text']
def f(m_rich, T_evap, T_cond, T_rect, T_abs_outlet, T_gen_outlet, mode='system text'):
xC = (m_rich, T_evap, T_cond, T_rect, T_abs_outlet, T_gen_outlet)
ch = system_aqua1.makeChiller(xC)
if mode == modes[0]:
display(ch)
elif mode == modes[1]:
display(ch.getStateTable())
elif mode == modes[2]:
display(ch.getVariablesTable())
elif mode == modes[3]:
ch.display()
plt.plot()
elif mode == modes[4]:
sys = system_aqua1.System(boundary=bdry, chiller=ch)
display(sys)
#widgets.VBox(w_m_rich,w_T_evap,w_T_cond,w_T_rect,w_T_abs_outlet,w_T_gen_outlet)
interactive(f,
m_rich=w_m_rich,
T_evap=w_T_evap,
T_cond=w_T_cond,
T_rect=w_T_rect,
T_abs_outlet=w_T_abs_outlet,
T_gen_outlet=w_T_gen_outlet,
mode=modes)
Now I can play with the inputs and see how the system responds ...
Where do we go with this? For the purpose of fiddling with the inputs, we could implement a web app not requiring the Jupyter Notebook. However, at the moment the code is designed to run on server-side Python, so there would be some hosting cost for a reasonable amount of CPU time, or we would need our own server. Implementing a client-side model in javascript would be subject to the considerable challenge of getting fluid properties and convenience libraries. So, most likely I will publish it as a Python package.
Please contact me for questions.