Plotting - Nouman090/ThermoSim GitHub Wiki

All visualisation is handled by the CyclePlotter class, completely separated from computation.

Setup

from ThermoSim.plotting import CyclePlotter

# Create plotter from a solved model
plotter = CyclePlotter(Model)

1. T-s Diagram

plotter.plot_Ts_diagram(
    loop_points=['1', '2', '3', '4', '1'],   # cycle path
    fluid='water',           # for saturation dome (auto-detected)
    show_dome=True,          # show saturation dome
    show_labels=True,        # label state points
    title='My Rankine Cycle',
    figsize=(10, 7),
    save_csv=False,          # NEW: export data to CSV
    csv_path=None,           # NEW: custom CSV filename (optional)
)

Scatter Mode (No Loop)

# Plot all points without connecting lines
plotter.plot_Ts_diagram()

Export Data to CSV

# Auto-generated filename with timestamp
plotter.plot_Ts_diagram(
    loop_points=['1', '2', '3', '4', '1'],
    save_csv=True
)
# Saves to: Ts_diagram_20240115_143022.csv

# Custom filename
plotter.plot_Ts_diagram(
    loop_points=['1', '2', '3', '4', '1'],
    save_csv=True,
    csv_path='results/rankine_Ts.csv'
)

CSV Format:

Point,Entropy_kJ_per_kg_K,Temperature_C,Entropy_J_per_kg_K,Temperature_K
1,6.234,450.5,6234.0,723.65
2,6.234,120.3,6234.0,393.45
3,0.592,29.0,592.0,302.15
4,0.592,30.5,592.0,303.65

2. P-h Diagram

plotter.plot_Ph_diagram(
    loop_points=['1', '2', '3', '4', '1'],
    show_dome=True,
    title='P-h Diagram',
    save_csv=False,          # NEW: export data to CSV
    csv_path=None,           # NEW: custom CSV filename (optional)
)

CSV Format:

Point,Enthalpy_kJ_per_kg,Pressure_bar,Enthalpy_J_per_kg,Pressure_Pa
1,3398.3,80.0,3398300,8000000
2,2346.2,0.08,2346200,8000

3. h-s Diagram (Mollier)

plotter.plot_hs_diagram(
    loop_points=['1', '2', '3', '4', '1'],
    show_dome=True,
    title='Mollier Diagram',
    save_csv=False,          # NEW: export data to CSV
    csv_path=None,           # NEW: custom CSV filename (optional)
)

CSV Format:

Point,Entropy_kJ_per_kg_K,Enthalpy_kJ_per_kg,Entropy_J_per_kg_K,Enthalpy_J_per_kg
1,6.234,3398.3,6234.0,3398300
2,6.234,2346.2,6234.0,2346200

4. Heat Exchanger Temperature Profile

Shows how temperature varies along a heat exchanger and identifies the pinch point.

min_dT = plotter.plot_hex_profile(
    hex_id='Recuperator',     # component ID
    div_N=200,                # number of segments
    save_csv=False,           # NEW: export profile data to CSV
    csv_path=None,            # NEW: custom CSV filename (optional)
)
print(f"Minimum ΔT (pinch): {min_dT:.2f} K")

⚠️ Only works for double_pipe, Condenser, Evaporator types (not SimpleHEX).

Export Profile Data

min_dT = plotter.plot_hex_profile(
    hex_id='Recuperator',
    div_N=200,
    save_csv=True,
    csv_path='recuperator_profile.csv'
)

CSV Format:

Heat_Transferred_kW,Hot_Temperature_C,Cold_Temperature_C,Delta_T_K,Hot_Enthalpy_J_per_kg,Cold_Enthalpy_J_per_kg,Hot_Pressure_Pa,Cold_Pressure_Pa
0.0,450.5,25.0,425.5,2800000,105000,8000000,100000
5.0,445.2,30.1,415.1,2750000,126000,7998000,99800
10.0,440.1,35.3,404.8,2700000,148000,7996000,99600
...

5. Exergy Destruction Bar Chart

# Absolute values (kW)
plotter.plot_exergy_bar()

# As percentage of total
plotter.plot_exergy_bar(as_percentage=True)

# NEW: Export to CSV
plotter.plot_exergy_bar(
    as_percentage=True,
    save_csv=True,
    csv_path='exergy_analysis.csv'
)

CSV Format:

Component,Exergy_Destruction_W,Exergy_Destruction_kW,Percentage
Turbine,45000,45.0,35.5
Boiler,30000,30.0,23.7
Condenser,28000,28.0,22.1
Pump,24000,24.0,18.7

6. Exergy Destruction Pie Chart

plotter.plot_exergy_pie(
    save_csv=False,           # NEW: export data to CSV
    csv_path=None,            # NEW: custom CSV filename (optional)
)

CSV Format: Same as exergy bar chart.


7. Energy Flow Summary

plotter.plot_energy_summary(
    save_csv=False,           # NEW: export summary to CSV
    csv_path=None,            # NEW: custom CSV filename (optional)
)

Shows bar chart of: Turbine Work, Pump Work, Heat Added, Heat Rejected.

CSV Format (Summary):

Category,Value_W,Value_kW
Turbine Work,500000,500.0
Pump Work,50000,50.0
Heat Added,800000,800.0
Heat Rejected,250000,250.0

*CSV Format (Detailed - auto-saved as _detailed.csv):

Component,Type,Value_W,Value_kW
Turbine1,Turbine,500000,500.0
Pump1,Pump,50000,50.0
Boiler,Heat_Added,800000,800.0
Condenser,Heat_Rejected,250000,250.0

8. Export All State Points (NEW)

Export all thermodynamic properties of every state point to CSV.

csv_file = plotter.export_all_points(
    csv_path='all_state_points.csv'  # optional
)
print(f"Saved to: {csv_file}")

CSV Format:

Point,T,P,H,S,D,Q,fluid,T_C,P_bar,H_kJ_per_kg,S_kJ_per_kg_K
1,723.65,8000000,3398300,6234.0,38.45,None,water,450.5,80.0,3398.3,6.234
2,393.45,8000,2346200,6234.0,0.0039,0.95,water,120.3,0.08,2346.2,6.234
3,302.15,8000,105000,592.0,997.04,0.0,water,29.0,0.08,105.0,0.592
4,303.65,8000000,113000,592.0,999.8,0.0,water,30.5,80.0,113.0,0.592

9. Export All Components (NEW)

Export performance data for all components.

csv_file = plotter.export_all_components(
    csv_path='all_components.csv'  # optional
)
print(f"Saved to: {csv_file}")

CSV Format:

Component,Type,work,Q,Ex_D,eta,Solution_Status,work_kW,Q_kW,Ex_D_kW
Turbine,Turbine,500000,None,45000,0.85,True,500.0,None,45.0
Pump,Pump,50000,None,24000,0.85,True,50.0,None,24.0
Boiler,HeatExchanger,None,800000,30000,None,True,None,800.0,30.0
Condenser,HeatExchanger,None,250000,28000,None,True,None,250.0,28.0

Complete Plotting Example

from ThermoSim import (
    ThermodynamicModel, Turbine, Pump, HeatExchanger,
)
from ThermoSim.plotting import CyclePlotter

# Build cycle
Model = ThermodynamicModel()
Model.set_dead_state()

Model.add_point('water', '1', P=8e6, T=753.15, Mass_flowrate=1)
Model.add_point('water', '2', P=0.008e6)
Model.add_point('water', '3', P=0.008e6, Q=0)
Model.add_point('water', '4', P=8e6)

Turbine(Model, 'Turbine', '1', '2', n_isen=0.85, Calculate=True)
HeatExchanger(Model, 'Condenser', PPT=5, HEX_type='SimpleHEX',
              HeatAdded=False, Hot_In_state='2', Hot_Out_state='3',
              Cold_In_state=None, Cold_Out_state=None, Calculate=True)
Pump(Model, 'Pump', '3', '4', n_isen=0.85, Calculate=True)
HeatExchanger(Model, 'Boiler', PPT=5, HEX_type='SimpleHEX',
              HeatAdded=True, Hot_In_state=None, Hot_Out_state=None,
              Cold_In_state='4', Cold_Out_state='1', Calculate=True)

Model.ModelSummary()

# Create plotter
plotter = CyclePlotter(Model)

# Generate all plots
plotter.plot_Ts_diagram(['1', '2', '3', '4', '1'])
plotter.plot_Ph_diagram(['1', '2', '3', '4', '1'])
plotter.plot_hs_diagram(['1', '2', '3', '4', '1'])
plotter.plot_exergy_bar(as_percentage=True)
plotter.plot_exergy_pie()
plotter.plot_energy_summary()

Complete Example with CSV Export

from ThermoSim import (
    ThermodynamicModel, Turbine, Pump, HeatExchanger,
)
from ThermoSim.plotting import CyclePlotter
import os

# Build and solve cycle (same as above)
Model = ThermodynamicModel()
Model.set_dead_state()

Model.add_point('water', '1', P=8e6, T=753.15, Mass_flowrate=1)
Model.add_point('water', '2', P=0.008e6)
Model.add_point('water', '3', P=0.008e6, Q=0)
Model.add_point('water', '4', P=8e6)

Turbine(Model, 'Turbine', '1', '2', n_isen=0.85, Calculate=True)
HeatExchanger(Model, 'Condenser', PPT=5, HEX_type='SimpleHEX',
              HeatAdded=False, Hot_In_state='2', Hot_Out_state='3',
              Cold_In_state=None, Cold_Out_state=None, Calculate=True)
Pump(Model, 'Pump', '3', '4', n_isen=0.85, Calculate=True)
HeatExchanger(Model, 'Boiler', PPT=5, HEX_type='SimpleHEX',
              HeatAdded=True, Hot_In_state=None, Hot_Out_state=None,
              Cold_In_state='4', Cold_Out_state='1', Calculate=True)

Model.ModelSummary()

# Create output directory
os.makedirs('output/plots', exist_ok=True)

# Create plotter
plotter = CyclePlotter(Model)

# Generate plots with CSV export
plotter.plot_Ts_diagram(
    ['1', '2', '3', '4', '1'],
    save_csv=True,
    csv_path='output/plots/Ts_diagram.csv'
)

plotter.plot_Ph_diagram(
    ['1', '2', '3', '4', '1'],
    save_csv=True,
    csv_path='output/plots/Ph_diagram.csv'
)

plotter.plot_hs_diagram(
    ['1', '2', '3', '4', '1'],
    save_csv=True,
    csv_path='output/plots/hs_diagram.csv'
)

plotter.plot_exergy_bar(
    as_percentage=True,
    save_csv=True,
    csv_path='output/plots/exergy_bar.csv'
)

plotter.plot_exergy_pie(
    save_csv=True,
    csv_path='output/plots/exergy_pie.csv'
)

plotter.plot_energy_summary(
    save_csv=True,
    csv_path='output/plots/energy_summary.csv'
)

# Export complete data
plotter.export_all_points('output/plots/all_state_points.csv')
plotter.export_all_components('output/plots/all_components.csv')

print("✓ All plots and data exported to output/plots/")

Directory Structure After Export:

output/plots/
├── Ts_diagram.csv
├── Ph_diagram.csv
├── hs_diagram.csv
├── exergy_bar.csv
├── exergy_pie.csv
├── energy_summary.csv
├── energy_summary_detailed.csv
├── all_state_points.csv
└── all_components.csv

Batch Processing Example

Process multiple cycles and export data:

from ThermoSim.plotting import CyclePlotter
import pandas as pd

# Assume you have multiple models
models = {
    'Case_1_Low_Pressure': Model_1,
    'Case_2_Med_Pressure': Model_2,
    'Case_3_High_Pressure': Model_3,
}

# Process each case
all_results = []

for case_name, model in models.items():
    plotter = CyclePlotter(model)
    
    # Export data
    plotter.export_all_points(f'results/{case_name}_points.csv')
    plotter.export_all_components(f'results/{case_name}_components.csv')
    
    # Plot with auto-generated filenames
    plotter.plot_Ts_diagram(
        ['1', '2', '3', '4', '1'],
        save_csv=True,
        title=f'{case_name} - T-s Diagram'
    )
    
    plotter.plot_exergy_bar(
        as_percentage=True,
        save_csv=True
    )
    
    print(f"✓ Processed {case_name}")

print("✓ All cases processed")

CSV Data Analysis Example

Use exported CSV data for further analysis:

import pandas as pd
import matplotlib.pyplot as plt

# Load exported data
df_points = pd.read_csv('all_state_points.csv')
df_components = pd.read_csv('all_components.csv')
df_exergy = pd.read_csv('exergy_bar.csv')

# Analyze state points
print("State Point Summary:")
print(df_points['Point', 'T_C', 'P_bar', 'H_kJ_per_kg'](/Nouman090/ThermoSim/wiki/'Point',-'T_C',-'P_bar',-'H_kJ_per_kg').to_string())

# Calculate cycle efficiency
turbine_work = df_components[df_components['Type'] == 'Turbine']['work_kW'].sum()
pump_work = df_components[df_components['Type'] == 'Pump']['work_kW'].sum()
heat_added = df_components[df_components['Type'] == 'HeatExchanger']['Q_kW'].max()

net_work = turbine_work - pump_work
thermal_efficiency = net_work / heat_added * 100

print(f"\nCycle Performance:")
print(f"Net Work Output: {net_work:.2f} kW")
print(f"Thermal Efficiency: {thermal_efficiency:.2f}%")

# Exergy analysis
total_exergy = df_exergy['Exergy_Destruction_kW'].sum()
print(f"Total Exergy Destruction: {total_exergy:.2f} kW")

# Find worst component
worst = df_exergy.loc[df_exergy['Exergy_Destruction_kW'].idxmax()]
print(f"Largest Exergy Loss: {worst['Component']} ({worst['Percentage']:.1f}%)")

Tips and Best Practices

1. Organize Output Files

from datetime import datetime
import os

# Create timestamped directory
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_dir = f'results/run_{timestamp}'
os.makedirs(output_dir, exist_ok=True)

# Save all plots there
plotter.plot_Ts_diagram(
    ['1', '2', '3', '4', '1'],
    save_csv=True,
    csv_path=f'{output_dir}/Ts_diagram.csv'
)

2. Validate Before Plotting

# Check if model is solved
if not all(comp.Solution_Status for comp in Model.Component.values()):
    print("⚠️ Warning: Some components not solved")

# Check for missing data
for name, point in Model.Point.items():
    if point.T is None or point.P is None:
        print(f"⚠️ Warning: Point {name} has missing properties")

3. Handle Errors Gracefully

try:
    plotter.plot_hex_profile('Recuperator', save_csv=True)
except ValueError as e:
    print(f"Could not plot heat exchanger: {e}")

4. Combine CSV Files for Comparison

import pandas as pd
import glob

# Load all Ts diagram CSVs
csv_files = glob.glob('results/*/Ts_diagram.csv')
combined = pd.concat([pd.read_csv(f) for f in csv_files], 
                     keys=csv_files, names=['File', 'Row'])
combined.to_csv('combined_Ts_data.csv')

Troubleshooting

Issue: "No valid points to plot"

Cause: State points have None values for required properties.
Solution: Ensure all components are solved before plotting.

Model.ModelSummary()  # Check solution status

Issue: CSV file not created

Cause: Permission denied or invalid path.
Solution: Check directory exists and permissions.

import os
os.makedirs('output', exist_ok=True)
plotter.plot_Ts_diagram(['1','2','3','4','1'], 
                        save_csv=True, 
                        csv_path='output/Ts.csv')

Issue: "Could not draw saturation dome"

Cause: Fluid not supported by CoolProp or is incompressible.
Solution: Check fluid name or disable dome.

plotter.plot_Ts_diagram(['1','2','3','4','1'], show_dome=False)

Issue: Heat exchanger profile fails

Cause: HEX type is SimpleHEX or not solved.
Solution: Use detailed HEX models (double_pipe, Condenser, Evaporator).


API Reference Summary

Method Purpose CSV Export Returns
plot_Ts_diagram() Temperature-Entropy plot fig, ax
plot_Ph_diagram() Pressure-Enthalpy plot fig, ax
plot_hs_diagram() Enthalpy-Entropy plot fig, ax
plot_hex_profile() Heat exchanger profile min_dT
plot_exergy_bar() Exergy destruction bars fig, ax
plot_exergy_pie() Exergy destruction pie fig, ax
plot_energy_summary() Energy flow summary fig, ax
export_all_points() Export all state points ✅ (only) csv_path
export_all_components() Export all components ✅ (only) csv_path

Version Information

  • Version: 2.0.0
  • New Features: CSV export for all plots, data export methods, improved error handling
  • Breaking Changes: None (backward compatible)
  • Dependencies: pandas, matplotlib, numpy, CoolProp

Additional Resources