Water gas shift equilibria via the NIST Webbook

| categories: miscellaneous, nonlinear algebra | View Comments

Water gas shift equilibria via the NIST Webbook

Water gas shift equilibria via the NIST Webbook

John Kitchin

The NIST webbook provides parameterized models of the enthalpy, entropy and heat capacity of many molecules. In this example, we will examine how to use these to compute the equilibrium constant for the water gas shift reaction $CO + H_2O \rightleftharpoons CO_2 + H_2$ in the temperature range of 500K to 1000K.

Parameters are provided for:

Contents

   Cp = heat capacity (J/mol*K)
   H° = standard enthalpy (kJ/mol)
   S° = standard entropy (J/mol*K)

with models in the form: $Cp^\circ = A + B*t + C*t^2 + D*t^3 +  E/t^2$

$H^\circ - H^\circ_{298.15}= A*t + B*t^2/2 +  C*t^3/3 + D*t^4/4 - E/t + F - H$

$S^\circ = A*ln(t) + B*t + C*t^2/2 + D*t^3/3 -  E/(2*t^2) + G$

where $t=T/1000$, and $T$ is the temperature in Kelvin. We can use this data to calculate equilibrium constants in the following manner. First, we have heats of formation at standard state for each compound; for elements, these are zero by definition, and for non-elements, they have values available from the NIST webbook. There are also values for the absolute entropy at standard state. Then, we have an expression for the change in enthalpy from standard state as defined above, as well as the absolute entropy. From these we can derive the reaction enthalpy, free energy and entropy at standard state, as well as at other temperatures.

We will examine the water gas shift enthalpy, free energy and equilibrium constant from 500K to 1000K, and finally compute the equilibrium composition of a gas feed containing 5 atm of CO and H2 at 1000K.

close all; clear all; clc;

T = linspace(500,1000); % degrees K
t = T/1000;

Setup equations for each species

First we enter in the parameters and compute the enthalpy and entropy for each species.

Hydrogen

link

% T = 298-1000K valid temperature range
A =  33.066178;
B = -11.363417;
C =  11.432816;
D = -2.772874;
E = -0.158558;
F = -9.980797;
G =  172.707974;
H =  0.0;

Hf_29815_H2 = 0.0; % kJ/mol
S_29815_H2 = 130.68; % J/mol/K

dH_H2 = A*t + B*t.^2/2 + C*t.^3/3 + D*t.^4/4 - E./t + F - H;
S_H2 = (A*log(t) + B*t + C*t.^2/2 + D*t.^3/3 - E./(2*t.^2) + G);

H2O

link Note these parameters limit the temperature range we can examine, as these parameters are not valid below 500K. There is another set of parameters for lower temperatures, but we do not consider them here.

% 500-1700 K valid temperature range
A =   30.09200;
B =   6.832514;
C =   6.793435;
D =  -2.534480;
E =   0.082139;
F =  -250.8810;
G =   223.3967;
H =  -241.8264;

Hf_29815_H2O = -241.83; %this is Hf.
S_29815_H2O = 188.84;

dH_H2O = A*t + B*t.^2/2 + C*t.^3/3 + D*t.^4/4 - E./t + F - H;
S_H2O = (A*log(t) + B*t + C*t.^2/2 + D*t.^3/3 - E./(2*t.^2) + G);

CO

link

% 298. - 1300K valid temperature range
A =   25.56759;
B =   6.096130;
C =   4.054656;
D =  -2.671301;
E =   0.131021;
F =  -118.0089;
G =   227.3665;
H = -110.5271;

Hf_29815_CO = -110.53; %this is Hf kJ/mol.
S_29815_CO = 197.66;

dH_CO = A*t + B*t.^2/2 + C*t.^3/3 + D*t.^4/4 - E./t + F - H;
S_CO = (A*log(t) + B*t + C*t.^2/2 + D*t.^3/3 - E./(2*t.^2) + G);

CO2

link

% 298. - 1200.K valid temperature range
A =   24.99735;
B =   55.18696;
C =  -33.69137;
D =   7.948387;
E =  -0.136638;
F =  -403.6075;
G =   228.2431;
H =  -393.5224;

Hf_29815_CO2 = -393.51; %this is Hf.
S_29815_CO2 = 213.79;

dH_CO2 = A*t + B*t.^2/2 + C*t.^3/3 + D*t.^4/4 - E./t + F - H;
S_CO2 = (A*log(t) + B*t + C*t.^2/2 + D*t.^3/3 - E./(2*t.^2) + G);

Standard state heat of reaction

We compute the enthalpy and free energy of reaction at 298.15 K for the following reaction $CO + H2O \rightleftharpoons H2 + CO2$.

Hrxn_29815 = Hf_29815_CO2 + Hf_29815_H2 - Hf_29815_CO - Hf_29815_H2O;
Srxn_29815 = S_29815_CO2 + S_29815_H2 - S_29815_CO - S_29815_H2O;
Grxn_29815 = Hrxn_29815 - 298.15*(Srxn_29815)/1000;

sprintf('deltaH = %1.2f',Hrxn_29815)
sprintf('deltaG = %1.2f',Grxn_29815)
ans =

deltaH = -41.15


ans =

deltaG = -28.62

Correcting $\Delta H$ and $\Delta G$

we have to correct for temperature change away from standard state. We only correct the enthalpy for this temperature change. The correction looks like this:

$$ \Delta H_{rxn}(T) = \Delta H_{rxn}(T_{ref}) + \sum_i \nu_i
(H_i(T)-H_i(T_{ref}))$$

Where $\nu_i$ are the stoichiometric coefficients of each species, with appropriate sign for reactants and products, and $(H_i(T)-H_i(T_{ref})$ is precisely what is calculated for each species with the equations

Hrxn = Hrxn_29815 + dH_CO2 + dH_H2 - dH_CO - dH_H2O;

The entropy is on an absolute scale, so we directly calculate entropy at each temperature. Recall that H is in kJ/mol and S is in J/mol/K, so we divide S by 1000 to make the units match.

Grxn = Hrxn - T.*(S_CO2 + S_H2 - S_CO - S_H2O)/1000;

Plot how the $\Delta G$ varies with temperature

over this temperature range the reaction is exothermic, although near 1000K it is just barely exothermic. At higher temperatures we expect the reaction to become endothermic.

figure; hold all
plot(T,Grxn)
plot(T,Hrxn)
xlabel('Temperature (K)')
ylabel('(kJ/mol)')
legend('\Delta G_{rxn}', '\Delta H_{rxn}', 'location','best')

Equilibrium constant calculation

Note the equilibrium constant starts out high, i.e. strongly favoring the formation of products, but drops very quicky with increasing temperature.

R = 8.314e-3; %kJ/mol/K
K = exp(-Grxn/R./T);

figure
plot(T,K)
xlim([500 1000])
xlabel('Temperature (K)')
ylabel('Equilibrium constant')

Equilibrium yield of WGS

Now let's suppose we have a reactor with a feed of H2O and CO at 10atm at 1000K. What is the equilibrium yield of H2? Let $\epsilon$ be the extent of reaction, so that $F_i = F_{i,0} + \nu_i \epsilon$. For reactants, $\nu_i$ is negative, and for products, $\nu_i$ is positive. We have to solve for the extent of reaction that satisfies the equilibrium condition.

A = CO
B = H2O
C = H2
D = CO2
Pa0 = 5; Pb0 = 5; Pc0 = 0; Pd0 = 0;  % pressure in atm
R = 0.082;
Temperature = 1000;

% we can estimate the equilibrium like this. We could also calculate it
% using the equations above, but we would have to evaluate each term. Above
% we simple computed a vector of enthalpies, entropies, etc...
K_Temperature = interp1(T,K,Temperature);

% If we let X be fractional conversion then we have $C_A = C_{A0}(1-X)$,
% $C_B = C_{B0}-C_{A0}X$, $C_C = C_{C0}+C_{A0}X$, and $C_D =
% C_{D0}+C_{A0}X$. We also have $K(T) = (C_C C_D)/(C_A C_B)$, which finally
% reduces to $0 = K(T) - Xeq^2/(1-Xeq)^2$ under these conditions.

f = @(X) K_Temperature - X^2/(1-X)^2;

Xeq = fzero(f,[1e-3 0.999]);
sprintf('The equilibrium conversion for these feed conditions is: %1.2f',Xeq)
ans =

The equilibrium conversion for these feed conditions is: 0.55

Compute gas phase pressures of each species

Since there is no change in moles for this reaction, we can directly calculation the pressures from the equilibrium conversion and the initial pressure of gases. you can see there is a slightly higher pressure of H2 and CO2 than the reactants, consistent with the equilibrium constant of about 1.44 at 1000K. At a lower temperature there would be a much higher yield of the products. For example, at 550K the equilibrium constant is about 58, and the pressure of H2 is 4.4 atm due to a much higher equilibrium conversion of 0.88.

P_CO = Pa0*(1-Xeq)
P_H2O = Pa0*(1-Xeq)
P_H2 = Pa0*Xeq
P_CO2 = Pa0*Xeq
P_CO =

    2.2748


P_H2O =

    2.2748


P_H2 =

    2.7252


P_CO2 =

    2.7252

Compare the equilibrium constants

We can compare the equilibrium constant from the Gibbs free energy and the one from the ratio of pressures. They should be the same!

K_Temperature
(P_CO2*P_H2)/(P_CO*P_H2O)
K_Temperature =

    1.4352


ans =

    1.4352

Summary

The NIST Webbook provides a plethora of data for computing thermodynamic properties. It is a little tedious to enter it all into Matlab, and a little tricky to use the data to estimate temperature dependent reaction energies. A limitation of the Webbook is that it does not tell you have the thermodynamic properties change with pressure. Luckily, those changes tend to be small.

% categories: Miscellaneous, nonlinear algebra
% tags: thermodynamics, reaction engineering
Read and Post Comments

Interacting with graphs with keypresses

| categories: plotting | View Comments

interactive_graph_keypress

Contents

Interacting with graphs with keypresses

John Kitchin

In Post 1489 we saw how to interact with a contour plot using mouse clicks. That is helpful, but limited to a small number of click types, e.g. left or right clicks, double clicks, etc... And it is not entirely intuitive which one to use if you don't know in advance. Today we examine how to make key presses while the mouse is over the graph do something useful. We will examine an enthalpy-pressure diagram for steam, and setup functions so you can press u, s, p, h, or t to display the relevant thermodynamic property under the mouse cursor.

Watch the video here: http://screencast.com/t/nxAiiBXr

function main

The XSteam module makes it pretty easy to generate figures of the steam tables. Here we make the enthalpy-pressure graph.

clear all; close all
P = logspace(-2,3,500); %bar

temps = [1 50 100 150 200 300 400 500 600 700 800]; % degC
figure; hold all
for Ti=temps
    Hh = @(p) XSteam('h_PT',p,Ti);
    H = arrayfun(Hh,P);
    plot(H,P,'k-')
    % add a text label to each isotherm of the temperature
    text(H(end),P(end),sprintf(' %d ^{\\circ}C',Ti),'rotation',90);
end

% add the saturated liquid line
hsat_l = arrayfun(@(p) XSteam('hL_p',p),P);
plot(hsat_l,P)

% add the saturated vapor line
hsat_v = arrayfun(@(p) XSteam('hV_p',p),P);
plot(hsat_v,P)

xlabel('Enthalpy (kJ/kg)')
ylabel('Pressure (bar)')
set(gca,'YScale','log') % this is the traditional view
% adjust axes position so the temperature labels aren't cutoff.
set(gca,'Position',[0.13 0.11 0.775 0.7])

Now we add a textbox to the graph where we will eventually print the thermodynamic properties. We also put a tooltip on the box, which will provide some help if you hold your mouse over it long enough.

textbox_handle = uicontrol('Style','text',...
    'String','',... % start out empty
     'Position', [0 0 200 25],...
     'TooltipString','Press u,s,p,h or t to get the thermodynamic property under the mouse cursor. Ctrl-q to close the figure.');

% we will create a simple cursor to show us what point the printed property
% corresponds too
cursor_handle = plot(0,0,'r+');

Call back functions

we need to define two callback functions that will get called when the mouse moves over the figure, and when a key is pressed.

    function mouse_motion(~,~)
        % since we don't use (gcbo,event) the input arguments, we
        % replace them by ~
        current_point = get(gca,'Currentpoint');
        H = current_point(1,1);
        P = current_point(1,2);
        if (P > 0.01 && P < 1000) && (H > 0.01 && H < 4500)
            set(cursor_handle,'XData',H,'YData',P)
        end
    end

    function keypress_callback(gcbo,event)
        % We get the point under the cursor.
        H = get(cursor_handle,'XData');
        P = get(cursor_handle,'YData');

        % solve  for T
        T = fzero(@(T) H - XSteam('h_PT',P,T),[1 800]);

        % now define a string for each type of key press.
        str = ''; % default string to display
        switch event.Key
            case 'u'
                % compute internal energy for the P,T under cursor
                str = sprintf('U   = %1.4g kJ/kg', XSteam('u_PT',P,T));
            case 's'
                % compute entropy for the P,T under cursor
                str = sprintf('S   = %1.4g kJ/kg/K',XSteam('s_PT',P,T));
            case 'p'
                str = sprintf('P   = %1.4g bar',P);
            case 'h'
                str = sprintf('H   = %1.4g kJ/kg', H);
            case 't'
                str = sprintf('T   = %1.4g C',  T);
            case 'uparrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'YData', P + 10)
                else
                    set(cursor_handle,'YData', P + 100)
                end
            case 'downarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'YData', P - 10)
                else
                    set(cursor_handle,'YData', P - 100)
                end
            case 'leftarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'XData', H - 10)
                else
                    set(cursor_handle,'XData', H - 100)
                end
            case 'rightarrow'
                if strcmp(event.Modifier,'control')
                    set(cursor_handle,'XData', H + 10)
                else
                    set(cursor_handle,'XData', H + 100)
                end
            case 'q'
                % quit. We use a dialog box to make sure we really
                % want to quit, in case q was accidentally pressed.
                choice = questdlg('Are you sure you want to quit?','','yes','no','no');
                switch choice
                    case 'yes'
                        close
                        return
                    case 'no'
                        str = '';
                end
            otherwise
                str = '';
        end

        % and put the string in our text box
        set(textbox_handle, 'String',str,...
            'FontWeight','bold','FontSize',12)
    end

finally, attach the callback functions to the current figure.

set(gcf,'KeyPressFcn',@keypress_callback)
set(gcf,'WindowButtonMotionFcn',@mouse_motion)
end

% categories: plotting
% tags: interactive, thermodynamics
% post_id = 1513; %delete this line to force new post;
% permaLink = http://matlab.cheme.cmu.edu/2011/12/07/interacting-with-graphs-with-keypresses/;
Read and Post Comments

3D plots of the steam tables

| categories: plotting | View Comments

steam_3d_pst

Contents

3D plots of the steam tables

John Kitchin

For fun let's make some 3D plots of the steam tables. We will use the XSteam module to map out the pressure, entropy and temperature of steam, and plot the 3D mapping.

clear all; close all; clc

create vectors that we will calculate T for

P = logspace(-2,3); %bar
S = linspace(0,10);

create meshgrid

these are matrices that map out a plane of P,S space

[xdata,ydata] = meshgrid(P,S);
zdata = zeros(size(xdata));

calculate T for each (p,S) pair in the meshgrids

zdata = arrayfun(@(p,s) XSteam('T_ps',p,s),xdata,ydata);

create the 3D plot

we use a lighted surface plot here

surfl(xdata,ydata,zdata)
xlabel('P (bar)')
ylabel('S (kJ/kg/K)')
zlabel('T (K)')
zlim([0 400])

customize the look of the surface plot

this smooths the faces out

shading interp
colormap pink

Add the saturated vapor and liquid lines, and the critical point

hold all

Tsat = arrayfun(@(p) XSteam('Tsat_p',p),P);
sV = arrayfun(@(p) XSteam('sV_p',p),P);
sL = arrayfun(@(p) XSteam('sL_p',p),P);
plot3(P,sV,Tsat,'r-','linewidth',5)
plot3(P,sL,Tsat,'g-','linewidth',5)

tc = 373.946; % C
pc = 220.584525; % bar
sc =XSteam('sV_p',pc);
plot3(pc,sc,tc,'ro','markerfacecolor','b') % the critical point
legend '' 'sat. vapor' 'sat. liquid' 'critical point'
Warning: Legend not supported for patches with FaceColor 'interp' 

Summary

There are lots of options in making 3D plots. They look nice, and from the right perspective can help see how different properties are related. Compare this graph to the one in Post 1484 , where isobars had to be plotted in the 2d graph.

% categories: plotting
% tags: thermodynamics
Read and Post Comments

Interacting with the steam Entropy-temperature chart

| categories: plotting | View Comments

Interacting with the steam Entropy-temperature chart

Interacting with the steam Entropy-temperature chart

John Kitchin

Contents

The XSteam module makes it pretty easy to generate figures of the steam tables. Here we generate an entropy-Temperature graph. In :postid:1296` we saw how to generate one of these graphs. In this post, we examine a way to interact with the graph to get more information about each point. In Post 1431 we saw how to use the gname command to add data to datapoints. You can also use the datatip cursor from the toolbar. You could use the standard datatip, but it basically only tells you what you can already see: the entropy and temperature at each point. There was a recent post on one of the Mathwork's blogs on creating a custom datatip, which we will adapt in this post to get all the thermodynamic properties of each data point.

%
function main
close all

Setup the S-T chart

T = linspace(0,800,200); % range of temperatures

fh = figure; % store file handle for later
hold on
% we need to compute S-T for a range of pressures.
pressures = [0.01 0.1 1 5 30 100 250 500 1000]; % bar
for P = pressures
    % XSteam is not vectorized, so here is an easy way to compute a
    % vector of entropies
    S = arrayfun(@(t) XSteam('s_PT',P,t),T);
    plot(S,T,'k-')
    text(S(end),T(end),sprintf('%1.1f bar',P),'rotation',90)
end
% adjust axes positions so the pressure labels don't get cutoff
set(gca,'Position',[0.13 0.11 0.775 0.7])

% plot saturated vapor and liquid entropy lines
svap = arrayfun(@(t) XSteam('sV_T',t),T);
sliq = arrayfun(@(t) XSteam('sL_T',t),T);

plot(svap,T,'r-')
plot(sliq,T,'b-')
xlabel('Entropy (kJ/(kg K)')
ylabel('Temperature (^\circC)')

The regular data tip

Here is what the standard data tip looks like. Not very great! There is so much more information to know about steam. Let's customize the datatip to show all the thermodynamic properties at each point.

Setup the custom output text

we want to display all the thermodynamic properties at each point

    function output_txt = myfunction(obj,event_obj)

        pos = get(event_obj,'Position');
        S = pos(1);
        T = pos(2);
        P = fzero(@(p) XSteam('s_PT',p,T) - S,[0.01 1000]);

        s1 = sprintf('P   = %1.4g bar',P);
        s2 = sprintf('T   = %1.4g K',  T);
        s3 = sprintf('U   = %1.4g kJ/kg', XSteam('u_ps',P,S));
        s4 = sprintf('H   = %1.4g kJ/kg', XSteam('h_ps',P,S));
        s5 = sprintf('S   = %1.4g kJ/kg/K',S);
        s6 = sprintf('rho = %1.4g kg/m^3', XSteam('rho_pT',P,T));
        s7 = sprintf('V   = %1.4g m^3/kg', XSteam('V_pT',P,T));
        s8 = sprintf('Cp  = %1.4g J/kg/K', XSteam('Cp_ps',P,S));
        s9 = sprintf('Cv  = %1.4g J/kg/K', XSteam('Cv_ps',P,S));

        % a cell array of strings makes the output to display in the
        % datatip
        output_txt = {s1; s2; s3; s4; s5; s6; s7; s8; s9};
    end

attach the custom function to the figure

dcm = datacursormode(fh);
set(dcm,'Enable','on');
set(dcm,'updatefcn',@myfunction);
% When published, an extra figure shows up here.

Here is our new datatip.

It looks pretty good! If you move the datatip cursor around, it automatically updates itself.

Summary

Customizing the datatip is a nice way to get additional information about your data. It has some limitations, notably it only works on actual data points! we can't click between the curves to get information about those regions.

end

% categories: plotting
% tags: thermodynamics
Read and Post Comments

Matlab meets the steam tables

| categories: miscellaneous | View Comments

Matlab meets the steam tables

Matlab meets the steam tables

John Kitchin

A Matlab module for the steam tables is available for download at http://www.x-eng.com/XSteam_Matlab.htm. To do this example yourself, you need to download this zipfile and unzip it into your Matlab path. Today we will examine using this module for examining the Rankine power generation cycle, following example3.7-1 in Sandler's Thermodynamics book.

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.

We will use the XSteam module to compute the required properties. There is only one function in XSteam, which takes a string and one or two arguments. The string specifies which subfunction to use.

We will use these functions from the module.

Contents

psat_T Saturation pressure (bar) at a specific T in degC
sL_T   Entropy (kJ/kg/K) of saturated liquid at T(degC)
hL_T   Enthalpy (kJ/kg)of saturated liquid at T(degC)
vL_T   Saturated liquid volume (m^3/kg)
T_ps   steam Temperature (degC) for a given pressure (bar) and entropy (kJ/kg/K)
h_pt   steam enthalpy (kJ/kg) at a given pressure (bar) and temperature (degC)
s_pt   steam entropy (kJ/kg/K) at a given pressure (bar) and temperature (degC)

We will use the cmu.units package to keep track of the units. Remember that the units package internally stores units in terms of base units, so to get a pressure in bar, we use the syntax P/u.bar, which is a dimensionless number with a magnitude equal to the number of bar.

clear all; clc; close all
u = cmu.units;

Starting point in the Rankine cycle in condenser.

we have saturated liquid here, and we get the thermodynamic properties for the given temperature.

T1 = 100; % degC
P1 = XSteam('psat_T',T1)*u.bar;
s1 = XSteam('sL_T', T1)*u.kJ/u.kg/u.K;
h1 = XSteam('hL_t', T1)*u.kJ/u.kg;
v1 = XSteam('vL_T', T1)*u.m^3/u.kg; % we need this to compute compressor work

isentropic compression of liquid to point 2

The final pressure is given, and we need to compute the new temperatures, and enthalpy.

P2 = 3*u.MPa;
s2 = s1;  % this is what isentropic means
T2 = XSteam('T_ps',P2/u.bar, s1/(u.kJ/u.kg/u.K));
h2 = XSteam('h_pt', P2/u.bar,T2)*u.kJ/u.kg;

% work done to compress liquid. This is an approximation, since the
% volume does change a little with pressure, but the overall work here
% is pretty small so we neglect the volume change.
WdotP = v1*(P2-P1);
fprintf('the compressor work is: %s\n', WdotP.as(u.kJ/u.kg,'%1.2f'))
the compressor work is: 3.02*kJ/kg

isobaric heating to T3 in Boiler where we make steam

T3 = 600; % degC
P3 = P2; % this is what isobaric means
h3 = XSteam('h_pt', P3/u.bar, T3)*u.kJ/u.kg;
s3 = XSteam('s_pt', P3/u.bar, T3)*u.kJ/u.kg/u.K;

Qb = h3 - h2; % heat required to make the steam
fprintf('the boiler heat duty is: %s\n', Qb.as(u.kJ/u.kg,'%1.2f'))
the boiler heat duty is: 3260.68*kJ/kg

isentropic expansion through turbine to point 4

Still steam

T4 = XSteam('T_ps',P1/u.bar, s3/(u.kJ/u.kg/u.K));
h4 = XSteam('h_pt', P1/u.bar, T4)*u.kJ/u.kg;
s4 = s3; % isentropic
Qc = h4 - h1; % work required to cool from T4 to T1 in the condenser
fprintf('the condenser heat duty is: %s\n', Qc.as(u.kJ/u.kg,'%1.2f'))
the condenser heat duty is: 2316.99*kJ/kg

This heat flow is not counted against the overall efficiency. Presumably a heat exchanger can do this cooling against the cooler environment, so only heat exchange fluid pumping costs would be incurred.

to get from point 4 to point 1

WdotTurbine = h4 - h3; % work extracted from the expansion
fprintf('the turbine work is: %s\n', WdotTurbine.as(u.kJ/u.kg,'%1.2f'))
the turbine work is: -946.72*kJ/kg

efficiency

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.

eta = -(WdotTurbine - WdotP)/Qb

fprintf('the overall efficiency is %1.0f%%\n', (eta*100))
eta =

   0.291271696419079

the overall efficiency is 29%

Entropy-temperature chart

The XSteam module makes it pretty easy to generate figures of the steam tables. Here we generate an entropy-Temperature graph.

T = linspace(0,800,200); % range of temperatures

figure; hold on
% we need to compute S-T for a range of pressures. Here
for P = [0.01 0.1 1 5 30 100 250 500 1000] % bar
    % XSteam is not vectorized, so here is an easy way to compute a
    % vector of entropies
    S = arrayfun(@(t) XSteam('s_PT',P,t),T);
    plot(S,T,'k-')
    text(S(end),T(end),sprintf('%1.1f bar',P),'rotation',90)
end
set(gca,'Position',[0.13 0.11 0.775 0.7])

% saturated vapor and liquid entropy lines
svap = arrayfun(@(t) XSteam('sV_T',t),T);
sliq = arrayfun(@(t) XSteam('sL_T',t),T);

plot(svap,T,'r-')
plot(sliq,T,'b-')
xlabel('Entropy (kJ/(kg K)')
ylabel('Temperature (^\circC)')

Plot Path

The path from 1 to 2 is isentropic, and has a small T change.

plot([s1 s2]/(u.kJ/u.kg/u.K),...
    [T1 T2],'ro ','markersize',8,'markerfacecolor','r')

% the path from 2 to 3 is isobaric
T23 = linspace(T2, T3);
S23 = arrayfun(@(t) XSteam('s_PT',P2/u.bar,t),T23);
plot(S23,T23,'b-','linewidth',4)

% the path from 3 to 4 is isentropic
plot([s3 s4]/(u.kJ/u.kg/u.K),...
    [T3 T4],'g-','linewidth',4)

% and from 4 to 1 is isobaric
T41 = linspace(T4,T1-0.01); % subtract 0.01 to get the liquid entropy
S41 = arrayfun(@(t) XSteam('s_PT',P1/u.bar,t),T41);
plot(S41,T41,'r-','linewidth',4)

Note steps 1 and 2 are very close together on this graph and hard to see.

Summary

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 XSteam interface is a little clunky, and takes some getting used to. It would be nice if it was vectorized to avoid the arrayfun calls.

% categories: Miscellaneous
% tags: Thermodynamics
Read and Post Comments

« Previous Page -- Next Page »