# ==============================================================================
# LECTURE 19: Regenerative Rankine Cycle
# Script: rankine-regen.ipynb
# Author: Edward Maginn, CBE 20260
# Description: 
# This notebook performs a thermodynamic analysis of a regenerative Rankine 
# cycle with one open feedwater heater (OFWH). It calculates the states, work, 
# heat, and thermal efficiency of the cycle based on user inputs for boiler 
# pressure, boiler temperature, extraction pressure, condenser pressure, 
# and isentropic efficiency. The notebook uses CoolProp to obtain fluid 
# properties and ipywidgets to create an interactive interface for exploring 
# how different operating conditions affect cycle performance.
# ==============================================================================

# Install CoolProp if it is missing (e.g., on Google Colab)
try:
    import CoolProp
except ImportError:
    %pip install -q CoolProp

import CoolProp.CoolProp as CP
import ipywidgets as widgets
from IPython.display import display, clear_output

def solve_regenerative_rankine(P_boiler_bar, T_boiler_C, P_extract_bar, P_cond_bar, eta):
    """
    Calculates the states and efficiency of a Regenerative Rankine Cycle 
    with one Open Feedwater Heater (OFWH). Uses IUPAC sign convention.
    """
    fluid = 'Water'
    
    # Check for non-physical pressure selections from sliders
    if P_extract_bar >= P_boiler_bar or P_extract_bar <= P_cond_bar:
        print("Error: Extraction pressure must be between Boiler and Condenser pressures.")
        return

    # Convert pressures to Pa and Temp to K for CoolProp
    P1 = P_boiler_bar * 1e5
    T1 = T_boiler_C + 273.15
    P_ex = P_extract_bar * 1e5
    P_cond = P_cond_bar * 1e5

    try:
        # --- STEP 1: Boiler Out / HP Turbine In (State 5) ---
        # Properties come from CoolProp calls
        H1 = CP.PropsSI('H', 'P', P1, 'T', T1, fluid) / 1000  # kJ/kg
        S1 = CP.PropsSI('S', 'P', P1, 'T', T1, fluid) / 1000  # kJ/kg-K

        # --- STEP 2: HP Turbine Out / Extraction (State 2) ---
        # Ideal
        S2_ideal = S1
        H2_ideal = CP.PropsSI('H', 'P', P_ex, 'S', S2_ideal * 1000, fluid) / 1000
        W_HP_rev = H2_ideal - H1 # IUPAC: Negative
        # Actual
        W_HP = W_HP_rev * eta
        H2 = H1 + W_HP
        S2_actual = CP.PropsSI('S', 'P', P_ex, 'H', H2 * 1000, fluid) / 1000

        # --- STEP 3: LP Turbine Out / Condenser In (State 3) ---
        # Ideal
        S3_ideal = S2_actual
        H3_ideal = CP.PropsSI('H', 'P', P_cond, 'S', S3_ideal * 1000, fluid) / 1000
        W_LP_rev = H3_ideal - H2 # IUPAC: Negative
        # Actual
        W_LP = W_LP_rev * eta
        H3 = H2 + W_LP

        # --- STEP 4: Condenser Out / Pump 1 In (State 4) ---
        H4 = CP.PropsSI('H', 'P', P_cond, 'Q', 0, fluid) / 1000 # Sat Liquid
        D4 = CP.PropsSI('D', 'P', P_cond, 'Q', 0, fluid) # kg/m^3
        V4 = 1 / D4 # m^3/kg

        # --- STEP 5: Pump 1 Out / OFWH In (State 5) ---
        W_P1_rev = V4 * (P_ex - P_cond) / 1000 # kJ/kg. IUPAC: Positive
        W_P1 = W_P1_rev / eta
        H5 = H4 + W_P1

        # --- STEP 6: OFWH Out / Pump 2 In (State 6) ---
        H6 = CP.PropsSI('H', 'P', P_ex, 'Q', 0, fluid) / 1000 # Sat Liquid
        D6 = CP.PropsSI('D', 'P', P_ex, 'Q', 0, fluid)
        V6 = 1 / D6

        # --- MASS BALANCE: Open Feedwater Heater ---
        # y*H2 + (1-y)*H5 = H6
        y = (H6 - H5) / (H2 - H5)

        # --- STEP 7: Pump 2 Out / Boiler In (State 7) ---
        W_P2_rev = V6 * (P1 - P_ex) / 1000 # kJ/kg
        W_P2 = W_P2_rev / eta
        H7 = H6 + W_P2

        # --- CYCLE PERFORMANCE ---
        W_turb_total = W_HP + (1 - y) * W_LP
        W_pump_total = (1 - y) * W_P1 + W_P2
        W_net = W_turb_total + W_pump_total
        
        Q_H = H1 - H7
        
        thermal_efficiency = -W_net / Q_H

        # --- PRINT RESULTS ---
        print("-" * 50)
        print(f"REGENERATIVE RANKINE CYCLE ANALYSIS")
        print("-" * 50)
        print(f"Extraction Fraction (y):     {y:.4f}  ({y*100:.1f}%)")
        print("-" * 50)
        print(f"HP Turbine Work (per kg):    {W_HP:.2f} kJ/kg")
        print(f"LP Turbine Work (per kg):    {W_LP:.2f} kJ/kg")
        print(f"Total Turbine Work:          {W_turb_total:.2f} kJ/kg")
        print(f"Total Pump Work:             {W_pump_total:.2f} kJ/kg")
        print(f"Net Work (W_net):            {W_net:.2f} kJ/kg")
        print(f"Heat Added (Q_H):            {Q_H:.2f} kJ/kg")
        print("-" * 50)
        print(f"THERMAL EFFICIENCY:          {thermal_efficiency*100:.2f} %")
        print("-" * 50)
        
    except Exception as e:
        print(f"Fluid property error. Trying to calculate outside valid region. ({e})")

# --- CREATE INTERACTIVE WIDGETS TO INPUT OPERATING PARAMETERS ---
style = {'description_width': 'initial'}

slider_P_boiler = widgets.FloatSlider(value=60.0, min=20.0, max=150.0, step=1.0, description='Boiler Pressure (bar):', style=style)
slider_T_boiler = widgets.FloatSlider(value=500.0, min=300.0, max=600.0, step=5.0, description='Boiler Temp (°C):', style=style)
slider_P_extract = widgets.FloatSlider(value=10.0, min=2.0, max=30.0, step=1.0, description='Extraction Pressure (bar):', style=style)
slider_P_cond = widgets.FloatSlider(value=1.0, min=0.05, max=2.0, step=0.05, description='Condenser Pressure (bar):', style=style)
slider_eta = widgets.FloatSlider(value=0.75, min=0.5, max=1.0, step=0.01, description='Isentropic Efficiency (η):', style=style)

# Group them into a nice vertical layout
ui = widgets.VBox([slider_P_boiler, slider_T_boiler, slider_P_extract, slider_P_cond, slider_eta])

# Define the output display connection
out = widgets.interactive_output(solve_regenerative_rankine, {
    'P_boiler_bar': slider_P_boiler,
    'T_boiler_C': slider_T_boiler,
    'P_extract_bar': slider_P_extract,
    'P_cond_bar': slider_P_cond,
    'eta': slider_eta
})

# Display the interface
display(ui, out)

Figure 1. Modified Rankine Cycle with open feedwater heater.

# Static preview: results for the default inputs above.
# The output is saved so the course website can show it.
# To try other inputs, use the controls above, or open
# this notebook in Colab (badge on the course website).
solve_regenerative_rankine(P_boiler_bar=slider_P_boiler.value, T_boiler_C=slider_T_boiler.value,
                           P_extract_bar=slider_P_extract.value, P_cond_bar=slider_P_cond.value,
                           eta=slider_eta.value)
--------------------------------------------------
REGENERATIVE RANKINE CYCLE ANALYSIS
--------------------------------------------------
Extraction Fraction (y):     0.1308  (13.1%)
--------------------------------------------------
HP Turbine Work (per kg):    -377.03 kJ/kg
LP Turbine Work (per kg):    -346.55 kJ/kg
Total Turbine Work:          -678.24 kJ/kg
Total Pump Work:             8.60 kJ/kg
Net Work (W_net):            -669.63 kJ/kg
Heat Added (Q_H):            2653.08 kJ/kg
--------------------------------------------------
THERMAL EFFICIENCY:          25.24 %
--------------------------------------------------