<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <atom:link href="http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml" rel="self" type="application/rss+xml" />
    <title>The Kitchin Research Group</title>
    <link>https://kitchingroup.cheme.cmu.edu/blog</link>
    <description>Chemical Engineering at Carnegie Mellon University</description>
    <pubDate>Sat, 01 Nov 2025 13:47:46 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    
    <item>
      <title>Meet the steam tables</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2013/02/28/Meet-the-steam-tables</link>
      <pubDate>Thu, 28 Feb 2013 22:09:29 EST</pubDate>
      <category><![CDATA[uncategorized]]></category>
      <guid isPermaLink="false">a9q2uEoIwVNXU6iFwbuJlBvHks4=</guid>
      <description>Meet the steam tables</description>
      <content:encoded><![CDATA[


&lt;div id="table-of-contents"&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;div id="text-table-of-contents"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#sec-1"&gt;1. Starting point in the Rankine cycle in condenser.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-2"&gt;2. Isentropic compression of liquid to point 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-3"&gt;3. Isobaric heating to T3 in boiler where we make steam&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-4"&gt;4. Isentropic expansion through turbine to point 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-5"&gt;5. To get from point 4 to point 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-6"&gt;6. Efficiency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-7"&gt;7. Entropy-temperature chart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#sec-8"&gt;8. Summary&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;a href="http://matlab.cheme.cmu.edu/2011/10/31/matlab-meets-the-steam-tables/"&gt;Matlab post&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
We will use the &lt;a href="https://pypi.python.org/pypi/iapws"&gt;iapws&lt;/a&gt; module. Install it like this:
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-sh"&gt;pip install iapws
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
Problem statement: A Rankine cycle operates using steam with the condenser at 100 degC, a pressure of 3.0 MPa and temperature of 600 degC in the boiler. Assuming the compressor and turbine operate reversibly, estimate the efficiency of the cycle.
&lt;/p&gt;

&lt;p&gt;
Starting point in the Rankine cycle in condenser.
&lt;/p&gt;

&lt;p&gt;
we have saturated liquid here, and we get the thermodynamic properties for the given temperature. In this python module, these properties are all in attributes of an IAPWS object created at a set of conditions.
&lt;/p&gt;

&lt;div id="outline-container-sec-1" class="outline-2"&gt;
&lt;h2 id="sec-1"&gt;&lt;span class="section-number-2"&gt;1&lt;/span&gt; Starting point in the Rankine cycle in condenser.&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-1"&gt;
&lt;p&gt;
We have saturated liquid here, and we get the thermodynamic properties for the given temperature.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;&lt;span style="color: #8b0000;"&gt;from&lt;/span&gt; iapws &lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; IAPWS97

T1 = 100 + 273.15 &lt;span style="color: #ff0000; font-weight: bold;"&gt;#in K&lt;/span&gt;

sat_liquid1  = IAPWS97(T=T1, x=0) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# x is the steam quality. 0 = liquid&lt;/span&gt;

P1 = sat_liquid1.P
s1 = sat_liquid1.s
h1 = sat_liquid1.h
v1 = sat_liquid1.v
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-2" class="outline-2"&gt;
&lt;h2 id="sec-2"&gt;&lt;span class="section-number-2"&gt;2&lt;/span&gt; Isentropic compression of liquid to point 2&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-2"&gt;
&lt;p&gt;
The final pressure is given, and we need to compute the new temperatures, and enthalpy.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;P2 = 3.0 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# MPa&lt;/span&gt;
s2 = s1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# this is what isentropic means&lt;/span&gt;

sat_liquid2 = IAPWS97(P=P2, s=s1)
T2, = sat_liquid2.T
h2 = sat_liquid2.h

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# work done to compress liquid. This is an approximation, since the&lt;/span&gt;
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# volume does change a little with pressure, but the overall work here&lt;/span&gt;
&lt;span style="color: #ff0000; font-weight: bold;"&gt;# is pretty small so we neglect the volume change.&lt;/span&gt;
WdotP = v1*(P2 - P1);
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'The compressor work is: {0:1.4f} kJ/kg'&lt;/span&gt;.format(WdotP))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; ... ... ... &amp;gt;&amp;gt;&amp;gt;
The compressor work is: 0.0030 kJ/kg
&lt;/pre&gt;

&lt;p&gt;
The compression work is almost negligible. This number is 1000 times smaller than we computed with Xsteam. I wonder what the units of v1 actually are.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-3" class="outline-2"&gt;
&lt;h2 id="sec-3"&gt;&lt;span class="section-number-2"&gt;3&lt;/span&gt; Isobaric heating to T3 in boiler where we make steam&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-3"&gt;
&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;T3 = 600 + 273.15 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# K&lt;/span&gt;
P3 = P2 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# definition of isobaric&lt;/span&gt;
steam = IAPWS97(P=P3, T=T3)

h3 = steam.h
s3 = steam.s

Qb, = h3 - h2 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# heat required to make the steam&lt;/span&gt;

&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'The boiler heat duty is: {0:1.2f} kJ/kg'&lt;/span&gt;.format(Qb)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt;
The boiler heat duty is: 3260.69 kJ/kg
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-4" class="outline-2"&gt;
&lt;h2 id="sec-4"&gt;&lt;span class="section-number-2"&gt;4&lt;/span&gt; Isentropic expansion through turbine to point 4&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-4"&gt;
&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;steam =  IAPWS97(P=P1, s=s3)
T4, = steam.T
h4 = steam.h
s4 = s3 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# isentropic&lt;/span&gt;
Qc, = h4 - h1 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# work required to cool from T4 to T1&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; 
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt; &lt;span style="color: #228b22;"&gt;'The condenser heat duty is {0:1.2f} kJ/kg'&lt;/span&gt;.format(Qc)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt;
The condenser heat duty is 2317.00 kJ/kg
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-5" class="outline-2"&gt;
&lt;h2 id="sec-5"&gt;&lt;span class="section-number-2"&gt;5&lt;/span&gt; To get from point 4 to point 1&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-5"&gt;
&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;WdotTurbine, = h4 - h3 &lt;span style="color: #ff0000; font-weight: bold;"&gt;# work extracted from the expansion&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'The turbine work is: {0:1.2f} kJ/kg'&lt;/span&gt;.format(WdotTurbine))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
The turbine work is: -946.71 kJ/kg
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-6" class="outline-2"&gt;
&lt;h2 id="sec-6"&gt;&lt;span class="section-number-2"&gt;6&lt;/span&gt; Efficiency&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-6"&gt;
&lt;p&gt;
This is a ratio of the work put in to make the steam, and the net work obtained from the turbine. The answer here agrees with the efficiency calculated in Sandler on page 135.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;eta = -(WdotTurbine - WdotP) / Qb
&lt;span style="color: #8b0000;"&gt;print&lt;/span&gt;(&lt;span style="color: #228b22;"&gt;'The overall efficiency is {0:1.2%}.'&lt;/span&gt;.format(eta))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
The overall efficiency is 29.03%.
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-7" class="outline-2"&gt;
&lt;h2 id="sec-7"&gt;&lt;span class="section-number-2"&gt;7&lt;/span&gt; Entropy-temperature chart&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-7"&gt;
&lt;p&gt;
The IAPWS module makes it pretty easy to generate figures of the steam tables. Here we generate an entropy-Temperature graph. We do this to illustrate the path of the Rankine cycle. We need to compute the values of steam entropy for a range of pressures and temperatures.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; numpy &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; np
&lt;span style="color: #8b0000;"&gt;import&lt;/span&gt; matplotlib.pyplot &lt;span style="color: #8b0000;"&gt;as&lt;/span&gt; plt

plt.figure()
plt.clf()
T = np.linspace(300, 372+273, 200) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# range of temperatures&lt;/span&gt;
&lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; P &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [0.1, 1, 2, 5, 10, 20]: &lt;span style="color: #ff0000; font-weight: bold;"&gt;#MPa&lt;/span&gt;
    steam = [IAPWS97(T=t, P=P) &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; t &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; T]
    S = [s.s &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; s &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; steam]
    plt.plot(S, T, &lt;span style="color: #228b22;"&gt;'k-'&lt;/span&gt;)

&lt;span style="color: #ff0000; font-weight: bold;"&gt;# saturated vapor and liquid entropy lines&lt;/span&gt;
svap = [s.s &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; s &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [IAPWS97(T=t, x=1) &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; t &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; T]]
sliq = [s.s &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; s &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [IAPWS97(T=t, x=0) &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; t &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; T]]

plt.plot(svap, T, &lt;span style="color: #228b22;"&gt;'r-'&lt;/span&gt;)
plt.plot(sliq, T, &lt;span style="color: #228b22;"&gt;'b-'&lt;/span&gt;)

plt.xlabel(&lt;span style="color: #228b22;"&gt;'Entropy (kJ/(kg K)'&lt;/span&gt;)
plt.ylabel(&lt;span style="color: #228b22;"&gt;'Temperature (K)'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/iawps-steam.png'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;lt;matplotlib.figure.Figure object at 0x000000000638BC18&amp;gt;
&amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; ... ... ... ... [&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9C208&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9C400&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9C8D0&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9CD30&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9E1D0&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9E630&amp;gt;]
... &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; &amp;gt;&amp;gt;&amp;gt; [&amp;lt;matplotlib.lines.Line2D object at 0x0000000001FDCEB8&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000007F9EA90&amp;gt;]
&amp;gt;&amp;gt;&amp;gt; &amp;lt;matplotlib.text.Text object at 0x0000000007F7BE48&amp;gt;
&amp;lt;matplotlib.text.Text object at 0x0000000007F855F8&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;img src="/img/./images/iawps-steam.png"&gt;&lt;p&gt;


&lt;p&gt;
We can plot our Rankine cycle path like this. We compute the entropies along the non-isentropic paths.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;T23 = np.linspace(T2, T3)
S23 = [s.s &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; s &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [IAPWS97(P=P2, T=t) &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; t &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; T23]]

T41 = np.linspace(T4, T1 - 0.01) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# subtract a tiny bit to make sure we get a liquid&lt;/span&gt;
S41 = [s.s &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; s &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; [IAPWS97(P=P1, T=t) &lt;span style="color: #8b0000;"&gt;for&lt;/span&gt; t &lt;span style="color: #8b0000;"&gt;in&lt;/span&gt; T41]]
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
And then we plot the paths.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-python"&gt;plt.plot([s1, s2], [T1, T2], &lt;span style="color: #228b22;"&gt;'r-'&lt;/span&gt;, lw=4) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# Path 1 to 2&lt;/span&gt;
plt.plot(S23, T23, &lt;span style="color: #228b22;"&gt;'b-'&lt;/span&gt;, lw=4) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# path from 2 to 3 is isobaric&lt;/span&gt;
plt.plot([s3, s4], [T3, T4], &lt;span style="color: #228b22;"&gt;'g-'&lt;/span&gt;, lw=4) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# path from 3 to 4 is isentropic&lt;/span&gt;
plt.plot(S41, T41, &lt;span style="color: #228b22;"&gt;'k-'&lt;/span&gt;, lw=4) &lt;span style="color: #ff0000; font-weight: bold;"&gt;# and from 4 to 1 is isobaric&lt;/span&gt;
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/iawps-steam-2.png'&lt;/span&gt;)
plt.savefig(&lt;span style="color: #228b22;"&gt;'images/iawps-steam-2.svg'&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000008350908&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x00000000083358D0&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x000000000835BEB8&amp;gt;]
[&amp;lt;matplotlib.lines.Line2D object at 0x0000000008357160&amp;gt;]
&lt;/pre&gt;

&lt;p&gt;&lt;img src="/img/./images/iawps-steam-2.png"&gt;&lt;p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-sec-8" class="outline-2"&gt;
&lt;h2 id="sec-8"&gt;&lt;span class="section-number-2"&gt;8&lt;/span&gt; Summary&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-8"&gt;
&lt;p&gt;
This was an interesting exercise. On one hand, the tedium of interpolating the steam tables is gone. On the other hand, you still have to know exactly what to ask for to get an answer that is correct. The iapws interface is a little clunky, and takes some getting used to. It does not seem as robust as the Xsteam module I used in Matlab.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Copyright (C) 2013 by John Kitchin. See the &lt;a href="/copying.html"&gt;License&lt;/a&gt; for information about copying.&lt;p&gt;&lt;p&gt;&lt;a href="/org/2013/02/28/Meet-the-steam-tables.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
