import pyromat as pyro
import numpy as np
import matplotlib.pyplot as plt

# Rankine cycle analysis
#
#   A Rankine cycle is a closed loop usually using water as the working
# fluid.  A low-pressure reservoir contains liquid.  The liquid is 
# pumped into a high pressure boiler, where steam is generated.  
# Optionally, the steam is further super-heated beyond its saturation
# temperature.  The steam is then passed through a turbine (or piston)
# where useful work is extracted.  Finally, waste heat is rejected and
# the remaining steam is condensed into liquid before being returned
# to the reservoir.
#
#   (1 Reservoir) --> |Feed water pump| --> (2) --> |Boiler| --> (3)
#         ^                                                       |
#         |                                                       |
#    |Condenser| <-- (5) <-- |Turbine| <-- (4) <-- |Superheater| <-
#
#  Given the high and low pressures, this code determines the processes
# so that the turbine exit (5) is precisely saturated steam.

# Use different color codes to change the color of the plots
color = 'r'    # Red
#color = 'b'   # Blue
# This is a True/False flag to deactivate the plot text
show_text = True
# This is a True/False flag to allow over-plotting of previous results
clear_plots = False
# Liquid water reservoir is at ambient pressure
p1 = 1.013
# Operating pressure of the boiler
p2 = 18.3   # 18.3 bar is roughly 250 psig
#p2 = 11.4  # 11.4 bar is roughly 150 psig

# How much work do we need?
Wnet = 100. # Let's make a 100kW engine


# Get the steam data
steam = pyro.get('mp.H2O')

# Assume the reservoir is a saturated liquid, as it would likely
# be coming out of the condenser.  In reality some supercooling
# is likely, but we will neglect it here.
# States (5) and (1) will straddle the dome
T1 = steam.Ts(p1)
p5=p1   # Since it is spanning the done, (5) and (1) share T and p
T5=T1
h1,h5 = steam.hs(T=T1)
s1,s5 = steam.ss(T=T1)
d1,d5 = steam.ds(T=T1)

# Isentropic compression of liquid water
# It is common to assume that T1=T2 since liquid is very close 
# to incompressible.  To compare results, remove the comment 
# from the second line below.
T2 = steam.T_s(p=p2,s=s1)
#T2 = T1
h2,s2,d2 = steam.hsd(T=T2,p=p2)

# The boiler will span the dome
# s2s, h2s, and T2s are the points where the process enters the dome
T3 = steam.Ts(p=p2)
p3 = p2
p2s = p2
T2s = T3
s2s,s3 = steam.ss(T=T3) # these are faster with T and p
h2s,h3 = steam.hs(T=T3) # specified
d2s,d3 = steam.ds(T=T3)

# The turbine is an isentropic expansion to the low pressure
# The superheater will end with the same entropy as s5
p4 = p3
T4 = steam.T_s(s=s5,p=p4)
h4,s4,d4 = steam.hsd(T=T4,p=p4)
# State (5) is already determined


# All the states are known, now.
#
# How much work did the feed water pump do?
# This might also be approximated as volume flow times
# pressure change.
w12 = -(h2-h1)

# How much heat did the boiler add?
q23 = h3-h2

# How much heat did the superheater add?
q34 = h4-h3

# How much work did the turbine produce
w45 = -(h5-h4)

# How much heat is rejected by the condenser?
q51 = h1-h5

# calculate the net work per kg of water
wnet = w45 + w12
# calculate the total heat addition
qh = q23 + q34
# calculate the mass flow required to size the engine
mdot = Wnet/wnet
# calculate the engine's efficiency
n = wnet/qh
# heats
Qboil = q23 * mdot
Qsuper = q34 * mdot

# Generate some diagrams
# Let figure 1 be a T-s diagram
f1 = plt.figure(1)
if clear_plots:
    plt.clf()
ax1 = f1.add_subplot(111)
ax1.set_xlabel('Entropy, s (kJ/kg/K)')
ax1.set_ylabel('Temperature, T (K)')
ax1.set_title('Rankine Cycle T-s Diagram')

# Let figure 2 be a P-v diagram
f2 = plt.figure(2)
if clear_plots:
    plt.clf()
ax2 = f2.add_subplot(111)
ax2.set_ylabel('Pressure, p (bar)')
ax2.set_xlabel('Volume, v (m$^3$/kg)')
ax2.set_title('Rankine Cycle p-v Diagram')

# Generate the dome on both plots
Tt,pt = steam.triple()
Tc,pc = steam.critical()
T = np.arange(Tt,Tc,2.5)
p = steam.ps(T)
dL,dV = steam.ds(T=T)
sL,sV = steam.ss(T=T)
ax1.plot(sL,T,'k')
ax1.plot(sV,T,'k')
ax2.plot(1./dL,p,'k')
ax2.plot(1./dV,p,'k')

# Process 1-2 (isentropic compression of a liquid)
p = np.array([p1,p2])
T = np.array([T1,T2])
d = np.array([d1,d2])
s = np.array([s1,s2])
ax1.plot(s,T,color,linewidth=1.5)
ax2.plot(1./d,p,color,linewidth=1.5)

# Process 2-2s (constant-p heat until saturation)
T = np.linspace(T2,T2s,10)
p = p2 * np.ones(T.shape)
s = steam.s(T=T,p=p)
s[-1] = s2s # force the last points to be liquid - not vapor
d[-1] = d2s # force the last points to be liquid - not vapor
d = steam.d(T=T,p=p)
ax1.plot(s,T,color,linewidth=1.5)
ax2.plot(1./d,p,color,linewidth=1.5)

# Process 2s-3 (constant-p boiling)
ax1.plot([s2s,s3],[T2s,T3],color,linewidth=1.5)
ax2.plot([1./d2s, 1./d3],[p2s,p3],color,linewidth=1.5)

# Process 3-4 (constant-p superheating)
T = np.linspace(T3,T4,20)
p = p3*np.ones(T.shape)
s = steam.s(T=T,p=p)
d = steam.d(T=T,p=p)
s[0] = s3
d[0] = d3
ax1.plot(s,T,color,linewidth=1.5)
ax2.plot(1./d,p,color,linewidth=1.5)

# process 4-5 (isentropic expansion)
ax1.plot([s4,s5],[T4,T5],color,linewidth=1.5)
ax2.plot([1./d4,1./d5],[p4,p5],color,linewidth=1.5)
#p = np.linspace(p4,p5,20)
#T = np.zeros(p.shape)
#for index in range(p.size):
#    T[index],_ = steam.psolve(p=p[index],s=s4)
#d = steam.d(T=T,p=p)
#s = steam.s(T=T,p=p)
#ax1.plot(s,T,'r',linewidth=1.5)
#ax2.plot(1./d,p,'r',linewidth=1.5)

# process 5-1 (constant-p heat rejection)
# add the line across the dome
ax1.plot([s1,s5],[T1, T5],color,linewidth=1.5)
ax2.plot([1./d1,1./d5],[p1,p5],color,linewidth=1.5)


# Add some labels
if show_text:
    ax1.text(s1-2.5,T1,
    """(1) 
T={0:.1f}K
s={1:.3f}kJ/kg/K
(2)
T={2:.1f}K
s={3:.3f}kJ/kg/K""".format(float(T1),float(s1),float(T2),float(s2)))
    ax1.text(s3-3,T3+20,
    """(3) 
T={0:.1f}K
s={1:.3f}kJ/kg/K""".format(float(T3),float(s3)))
    ax1.text(s4+.2,T4-100,
    """(4) 
T={0:.1f}K
s={1:.3f}kJ/kg/K""".format(float(T4),float(s4)))
    ax1.text(s5+.2,T5,
    """(5) 
T={0:.1f}K
s={1:.3f}kJ/kg/K""".format(float(T5),float(s5)))
    
    v = 1./d1
    ax2.text(v,p1/5.,
    """(1) 
p={0:.2f}bar
v={1:f}m$^3$/kg""".format(float(p1),float(v)))
    v = 1./d2
    ax2.text(v*1.5,p2*1.1,
    """(2) 
p={0:.2f}bar
v={1:f}m$^3$/kg""".format(float(p2),float(v)))
    v = 1./d3
    ax2.text(v,p3,
    """(3) 
p={0:.2f}bar
v={1:f}m$^3$/kg
(4)
p={2:.2f}bar
v={3:f}m$^3$/kg""".format(float(p3),float(v),float(p4),float(1./d4)))
    v = 1./d5
    ax2.text(v/5,p5/5,
    """(5) 
p={0:.2f}bar
v={1:f}m$^3$/kg""".format(float(p5),float(v)))
    
    ax1.text(-.5,575,"""$\dot{{m}}$ = {0:.3f}kg/s
$\eta$ = {1:.3f}
$\dot{{W}}_{{NET}}$ = {2:.0f}kW
$\dot{{Q}}_{{BOIL}}$ = {3:.1f}kW
$\dot{{Q}}_{{SUPER}}$ = {4:.1f}kW""".format(float(mdot),float(n),float(Wnet),float(Qboil),float(Qsuper)))

ax1.grid('on')
ax2.grid('on')
ax2.set_xscale('log')
ax2.set_yscale('log')

ax1.set_xlim([-2,10])
ax1.set_ylim([300,800])
# adjust the volume scale
ax2.set_xlim([5e-4, 10])
ax2.set_ylim([.01,1000.])

plt.show()
#plt.show(block=False)
