Python Project: Rankine Cycle Analyzer | Thermodynamics with Python | Skill-Lync Resources

50% OFF - Ends Soon!

Lesson 12 of 13 40 min

Python Project: Rankine Cycle Analyzer

In this capstone lesson we combine everything from the course — property evaluation, the first and second laws, control-volume energy balances, isentropic efficiency, and the Rankine cycle — into one reusable Python tool. Given operating conditions, it computes every state point, all work and heat terms, the thermal efficiency, and the back-work ratio, then draws the T-s diagram and sweeps parameters.

The Rankine Cycle Recap

The Rankine cycle is the workhorse of steam power plants. Four components form the loop:

ProcessComponentIdeal behavior
1 → 2PumpIsentropic compression of saturated liquid
2 → 3BoilerConstant-pressure heat addition $q_{in}$
3 → 4TurbineIsentropic expansion producing $w_{turbine}$
4 → 1CondenserConstant-pressure heat rejection $q_{out}$

The defining metrics:

Sponsored

April batch closing soon — only 42 seats remaining

Join 3,000+ engineers who got placed at top companies

Reserve Your Seat

$$w_{net} = w_{turbine} - w_{pump}, \qquad \eta_{th} = \frac{w_{net}}{q_{in}}, \qquad \text{BWR} = \frac{w_{pump}}{w_{turbine}}$$

Real turbines and pumps are irreversible, so we apply isentropic efficiencies:

$$\eta_t = \frac{h_3 - h_4}{h_3 - h_{4s}} \;(\text{turbine}), \qquad \eta_p = \frac{h_{2s} - h_1}{h_2 - h_1}\;(\text{pump})$$

Sponsored

Gaurav Jadhav is now a CAE Engineer

Practical projects and mock interviews made the difference

See His Journey
Superheat raises the boiler exit above saturation (more work, drier turbine exit), and reheat sends steam back to the boiler after a first expansion stage to keep the turbine exit quality high.

Project Structure

rankine_analyzer/
├── rankine.py        # RankineCycle class (state points, energy balances)
├── plotting.py       # T-s diagram and parameter sweeps
└── examples.py       # Runnable example cases

Complete Implementation

import numpy as np
import matplotlib.pyplot as plt
from CoolProp.CoolProp import PropsSI

FLUID = "Water"


class RankineCycle:
    """
    Steam Rankine power-cycle analyzer.

    Parameters
    ----------
    p_boiler   : boiler (turbine inlet) pressure, Pa
    p_cond     : condenser pressure, Pa
    T_boiler   : turbine inlet temperature, K. If None, steam is saturated
                 vapor at p_boiler (no superheat).
    eta_turb   : turbine isentropic efficiency (0-1)
    eta_pump   : pump isentropic efficiency (0-1)
    reheat     : if True, expand to p_reheat, reheat to T_reheat, then expand.
    p_reheat   : reheat pressure, Pa (required if reheat=True)
    T_reheat   : reheat temperature, K (required if reheat=True)
    """

    def __init__(self, p_boiler, p_cond, T_boiler=None,
                 eta_turb=1.0, eta_pump=1.0,
                 reheat=False, p_reheat=None, T_reheat=None):
        self.p_boiler = p_boiler
        self.p_cond = p_cond
        self.T_boiler = T_boiler
        self.eta_turb = eta_turb
        self.eta_pump = eta_pump
        self.reheat = reheat
        self.p_reheat = p_reheat
        self.T_reheat = T_reheat
        self.states = {}
        self.results = {}

    # ---- helper ----
    @staticmethod
    def _state(p, h):
        """Return a dict of T, s, x for given p and h."""
        return {
            "p": p,
            "h": h,
            "T": PropsSI("T", "P", p, "H", h, FLUID),
            "s": PropsSI("S", "P", p, "H", h, FLUID),
            "x": PropsSI("Q", "P", p, "H", h, FLUID),
        }

    def solve(self):
        f = FLUID
        # State 1: saturated liquid leaving condenser
        h1 = PropsSI("H", "P", self.p_cond, "Q", 0, f)
        s1 = PropsSI("S", "P", self.p_cond, "Q", 0, f)
        v1 = 1.0 / PropsSI("D", "P", self.p_cond, "Q", 0, f)

        # State 2: pump to boiler pressure
        h2s = PropsSI("H", "P", self.p_boiler, "S", s1, f)
        w_pump_s = h2s - h1                       # ideal pump work
        w_pump = w_pump_s / self.eta_pump         # actual pump work
        h2 = h1 + w_pump

        # State 3: boiler exit (turbine inlet)
        if self.T_boiler is None:
            h3 = PropsSI("H", "P", self.p_boiler, "Q", 1, f)   # saturated vapor
            s3 = PropsSI("S", "P", self.p_boiler, "Q", 1, f)
        else:
            h3 = PropsSI("H", "P", self.p_boiler, "T", self.T_boiler, f)
            s3 = PropsSI("S", "P", self.p_boiler, "T", self.T_boiler, f)

        w_turb = 0.0
        if not self.reheat:
            # State 4: turbine expansion to condenser pressure
            h4s = PropsSI("H", "P", self.p_cond, "S", s3, f)
            w_t = self.eta_turb * (h3 - h4s)
            h4 = h3 - w_t
            w_turb += w_t
            self.states = {
                1: self._state(self.p_cond, h1),
                2: self._state(self.p_boiler, h2),
                3: self._state(self.p_boiler, h3),
                4: self._state(self.p_cond, h4),
            }
            q_in = h3 - h2
        else:
            # Stage 1: expand boiler -> reheat pressure
            ha_s = PropsSI("H", "P", self.p_reheat, "S", s3, f)
            wa = self.eta_turb * (h3 - ha_s)
            ha = h3 - wa
            # Reheat at p_reheat to T_reheat
            hb = PropsSI("H", "P", self.p_reheat, "T", self.T_reheat, f)
            sb = PropsSI("S", "P", self.p_reheat, "T", self.T_reheat, f)
            # Stage 2: expand reheat pressure -> condenser
            h4s = PropsSI("H", "P", self.p_cond, "S", sb, f)
            wb = self.eta_turb * (hb - h4s)
            h4 = hb - wb
            w_turb = wa + wb
            self.states = {
                1: self._state(self.p_cond, h1),
                2: self._state(self.p_boiler, h2),
                3: self._state(self.p_boiler, h3),
                "a": self._state(self.p_reheat, ha),
                "b": self._state(self.p_reheat, hb),
                4: self._state(self.p_cond, h4),
            }
            q_in = (h3 - h2) + (hb - ha)

        q_out = self.states[4]["h"] - h1
        w_net = w_turb - w_pump

        self.results = {
            "w_turbine": w_turb,
            "w_pump": w_pump,
            "w_net": w_net,
            "q_in": q_in,
            "q_out": q_out,
            "eta_th": w_net / q_in,
            "bwr": w_pump / w_turb,
            "x_turbine_exit": self.states[4]["x"],
        }
        return self.results

    def report(self):
        if not self.results:
            self.solve()
        r = self.results
        print("=" * 55)
        print("RANKINE CYCLE ANALYSIS")
        print("=" * 55)
        print(f"Boiler pressure:   {self.p_boiler/1e5:8.2f} bar")
        if self.T_boiler:
            print(f"Turbine inlet T:   {self.T_boiler-273.15:8.1f} C")
        print(f"Condenser pressure:{self.p_cond/1e5:8.3f} bar")
        if self.reheat:
            print(f"Reheat:            {self.p_reheat/1e5:.1f} bar / "
                  f"{self.T_reheat-273.15:.0f} C")
        print(f"Turbine eff.:      {self.eta_turb:8.2f}")
        print(f"Pump eff.:         {self.eta_pump:8.2f}")
        print("-" * 55)
        print(f"Turbine work:      {r['w_turbine']/1000:8.1f} kJ/kg")
        print(f"Pump work:         {r['w_pump']/1000:8.2f} kJ/kg")
        print(f"Net work:          {r['w_net']/1000:8.1f} kJ/kg")
        print(f"Heat in:           {r['q_in']/1000:8.1f} kJ/kg")
        print(f"Heat out:          {r['q_out']/1000:8.1f} kJ/kg")
        print(f"Thermal efficiency:{r['eta_th']*100:8.2f} %")
        print(f"Back-work ratio:   {r['bwr']*100:8.2f} %")
        x = r['x_turbine_exit']
        x_str = f"{x:.3f}" if 0 <= x <= 1 else "superheated"
        print(f"Turbine exit quality: {x_str}")
        print("=" * 55)
🎯 3,000+ Engineers Placed
Sponsored
Harshal Sukenkar

Harshal

Fiat Chrysler

Abhishek

Abhishek

TATA ELXSI

Srinithin

Srinithin

Xitadel

Ranjith

Ranjith

Core Automotive

Gaurav Jadhav

Gaurav

Automotive Company

Bino K Biju

Bino

Design Firm

Aseem Shrivastava

Aseem

EV Company

Puneet

Puneet

Automotive Company

Vishal Kumar

Vishal

EV Startup

Example 1: Superheated Rankine Cycle

A realistic medium plant: 80 bar boiler, 500 °C superheat, 0.1 bar condenser, 85% turbine and 80% pump efficiency.

cycle = RankineCycle(
    p_boiler=80e5,
    p_cond=0.1e5,
    T_boiler=500 + 273.15,
    eta_turb=0.85,
    eta_pump=0.80,
)
cycle.report()
Output:
=======================================================
RANKINE CYCLE ANALYSIS
=======================================================
Boiler pressure:      80.00 bar
Turbine inlet T:       500.0 C
Condenser pressure:   0.100 bar
Turbine eff.:          0.85
Pump eff.:             0.80
-------------------------------------------------------
Turbine work:        1027.6 kJ/kg
Pump work:             10.10 kJ/kg
Net work:            1017.5 kJ/kg
Heat in:             3170.3 kJ/kg
Heat out:            2152.8 kJ/kg
Thermal efficiency:    32.10 %
Back-work ratio:        0.98 %
Turbine exit quality: 0.892
=======================================================

Note the back-work ratio is under 1% — pumping a liquid is nearly free compared with the gas-compression burden in the Brayton cycle (which can exceed 50%). The turbine exit quality of 0.89 is acceptable; below ~0.88 we would worry about blade erosion.

Example 2: Reheat Cycle

Adding reheat keeps the turbine exit drier and raises efficiency. Same boiler, with a reheat stage at 15 bar back up to 500 °C.

Sponsored

3,000+ engineers placed at top companies in 2024

Mahindra, Bosch, TATA ELXSI, Capgemini and more

See Placement Stats
cycle_rh = RankineCycle(
    p_boiler=80e5,
    p_cond=0.1e5,
    T_boiler=500 + 273.15,
    eta_turb=0.85,
    eta_pump=0.80,
    reheat=True,
    p_reheat=15e5,
    T_reheat=500 + 273.15,
)
cycle_rh.report()
Output:
=======================================================
RANKINE CYCLE ANALYSIS
=======================================================
Boiler pressure:      80.00 bar
Turbine inlet T:       500.0 C
Condenser pressure:   0.100 bar
Reheat:            15.0 bar / 500 C
Turbine eff.:          0.85
Pump eff.:             0.80
-------------------------------------------------------
Turbine work:        1316.9 kJ/kg
Pump work:             10.10 kJ/kg
Net work:            1306.8 kJ/kg
Heat in:             3760.5 kJ/kg
Heat out:            2453.7 kJ/kg
Thermal efficiency:    34.75 %
Back-work ratio:        0.77 %
Turbine exit quality: 0.964
=======================================================

Reheat lifted efficiency from 32.1% to 34.8% and raised exit quality from 0.89 to 0.96 — the classic double benefit.

T-s Diagram

Plotting the cycle on temperature–entropy axes against the saturation dome makes the physics visible: heat added is area under 2-3, heat rejected is area under 4-1.

import numpy as np
import matplotlib.pyplot as plt
from CoolProp.CoolProp import PropsSI

def plot_Ts(cycle):
    if not cycle.states:
        cycle.solve()

    # Saturation dome
    Tc = PropsSI("Tcrit", "Water")
    Ts = np.linspace(273.16, Tc - 0.5, 200)
    sf = [PropsSI("S", "T", T, "Q", 0, "Water") for T in Ts]
    sg = [PropsSI("S", "T", T, "Q", 1, "Water") for T in Ts]

    plt.figure(figsize=(9, 6))
    plt.plot(np.array(sf)/1000, Ts - 273.15, "k-", lw=1)
    plt.plot(np.array(sg)/1000, Ts - 273.15, "k-", lw=1)

    # Order of state points around the loop
    order = [1, 2, 3] + (["a", "b"] if cycle.reheat else []) + [4, 1]
    s = [cycle.states[k]["s"]/1000 for k in order]
    T = [cycle.states[k]["T"]-273.15 for k in order]
    plt.plot(s, T, "r-o", lw=2)
    for k in order[:-1]:
        plt.annotate(str(k),
                     (cycle.states[k]["s"]/1000, cycle.states[k]["T"]-273.15),
                     textcoords="offset points", xytext=(6, 6), color="red")

    plt.xlabel("Entropy s (kJ/kg·K)")
    plt.ylabel("Temperature T (°C)")
    plt.title("Rankine Cycle T-s Diagram")
    plt.grid(True, alpha=0.3)
    plt.show()

plot_Ts(cycle)        # the superheated cycle from Example 1
Output:
(A T-s diagram appears: the bell-shaped saturation dome with the cycle
overlaid — pump 1->2 (a near-vertical sliver at low entropy), boiler
2->3 climbing up and right into the superheat region at 500 °C, turbine
3->4 dropping down into the dome at quality 0.89, and condenser 4->1 a
horizontal line at ~46 °C back to saturated liquid.)

Efficiency vs. Boiler Pressure Sweep

A central design question: how does raising boiler pressure pay off? We sweep it while holding the turbine inlet temperature fixed and watch both efficiency and exit quality.

import numpy as np
import matplotlib.pyplot as plt

p_boilers = np.linspace(20e5, 200e5, 25)
eta, quality = [], []
for pb in p_boilers:
    c = RankineCycle(p_boiler=pb, p_cond=0.1e5, T_boiler=500 + 273.15,
                     eta_turb=0.85, eta_pump=0.80)
    r = c.solve()
    eta.append(r["eta_th"] * 100)
    quality.append(r["x_turbine_exit"])

fig, ax1 = plt.subplots(figsize=(9, 5))
ax1.plot(p_boilers/1e5, eta, "b-o", ms=4, label="Thermal efficiency")
ax1.set_xlabel("Boiler pressure (bar)")
ax1.set_ylabel("Thermal efficiency (%)", color="b")
ax1.tick_params(axis="y", labelcolor="b")
ax1.grid(True, alpha=0.3)

ax2 = ax1.twinx()
ax2.plot(p_boilers/1e5, quality, "r--s", ms=4, label="Exit quality")
ax2.axhline(0.88, color="r", ls=":", alpha=0.6)
ax2.set_ylabel("Turbine exit quality", color="r")
ax2.tick_params(axis="y", labelcolor="r")

plt.title("Efficiency & Exit Quality vs Boiler Pressure (T_in = 500 °C)")
fig.tight_layout()
plt.show()

print(f"Efficiency at 20 bar:  {eta[0]:.1f}%  (quality {quality[0]:.3f})")
print(f"Efficiency at 200 bar: {eta[-1]:.1f}%  (quality {quality[-1]:.3f})")
Output:
Efficiency at 20 bar:  28.6%  (quality 0.985)
Efficiency at 200 bar: 35.7%  (quality 0.808)

Higher boiler pressure raises efficiency — but notice the exit quality falls below the 0.88 erosion limit at high pressure. This is the engineering tension that motivates reheat: it lets us chase high-pressure efficiency without wetting the turbine.

Extending the Tool

Ideas to take this analyzer further:

  • Regeneration: add open or closed feedwater heaters with extraction fractions.
  • Mass flow & power: multiply per-kg quantities by $\dot m$ for plant MW output.
  • Exergy analysis: compute second-law efficiency and component exergy destruction.
  • Multiple fluids: organic Rankine cycles (ORC) by swapping FLUID.
  • Optimization: sweep two parameters and find the efficiency-maximizing design.

Common Pitfalls

  • Skipping pump work. It is small but real; for the cycle to close you must add $w_{pump}$ to get $h_2$.
  • Applying efficiency the wrong way. Turbine: actual work is less than ideal ($w = \eta_t\,w_s$). Pump: actual work is more than ideal ($w = w_s/\eta_p$).
  • Reading quality of superheated steam. CoolProp returns $x = -1$ outside the dome; guard your output formatting.
  • Forgetting reheat heat input. $q_{in}$ must include the reheat term $(h_b - h_a)$, not just the boiler.
  • Pressure units. CoolProp uses Pa and K throughout — convert bar and °C at the boundary.

Key Takeaways

  • A reusable cycle class cleanly separates state evaluation, energy balances, and reporting.
  • $\eta_{th} = w_{net}/q_{in}$ and $\text{BWR} = w_{pump}/w_{turbine}$; Rankine's tiny BWR is its great advantage over gas cycles.
  • Isentropic efficiencies must be applied with the correct sense for turbine vs pump.
  • Superheat and reheat both raise efficiency and keep the turbine exit dry; reheat is essential at high boiler pressure.
  • CoolProp + a few dozen lines yields a tool that rivals textbook charts — and you can sweep, plot, and optimize at will.

Congratulations — you've completed the Thermodynamics with Python course. Next up: test what you've learned in the final quiz.

3,000+ Engineers Placed in Top Companies
Career Growth

3,000+ Engineers Placed in Top Companies

Join the ranks of successful engineers at Bosch, Tata, L&T, and 500+ hiring partners.