Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Rankine Cycle Analysis Software Technical Documentation

1. Overview

This software is a C++-based thermodynamic analysis system for Rankine cycles. It adopts a modular design, uses JSON configuration files to describe the thermodynamic system structure, and implements automatic creation of equipment objects, port data sharing, and iterative cycle calculations.

1.1 Key Features

  • JSON-Driven Configuration: Describes system structure and parameters through JSON files
  • Equipment Factory Pattern: Automatically creates and manages equipment objects
  • Port Data Sharing: Achieves automatic data synchronization between equipment through pointer sharing
  • Polling Device-Readiness: Finds calculation order by repeatedly traversing and computing ready equipment
  • IAPWS-IF97 Standard: Uses seuif97 library for accurate thermodynamic property calculations
  • Modular Design: Easy to extend with new equipment types

1.2 Technology Stack

  • C++17
  • Custom JSON parser (no third-party dependencies)
  • IAPWS-IF97 water and steam thermodynamic properties library (seuif97)

2. System Architecture

2.1 Core Design Patterns

2.1.1 Factory Pattern

Dynamic creation of equipment objects through the ClassReg class, supporting automatic instantiation based on JSON configuration at runtime.

typedef std::map<std::string, std::function<CompBase *(umComponent)>> compfactory;

class ClassReg {
    compfactory compinstance;
    template <typename T>
    void register_type(const std::string &name);
    void register_type_all();
};

2.1.2 Composite Pattern

Equipment consists of ports, and systems consist of equipment and connectors, forming a hierarchical object structure.

2.1.3 Iterative Calculation Pattern

Iterates through equipment for state calculations and balance calculations, supporting iterative solving of complex systems.

2.2 System Hierarchy

RankineCycle (Cycle System)
 ├── Comps (Equipment Collection) 
 │   ├── Boiler
 │   ├── TurbineEx0/TurbineEx1 (Turbine)
 │   ├── Pump
 │   ├── Condenser
 │   └── OpenedHeaterDw0 (Open Heater)
 ├── curcon (Connector)
 │   └── Nodes (System Port Node List)
 └── Performance Metrics
     ├── netpoweroutput
     ├── efficiency
     ├── HeatRate
     └── SteamRate

2.3 Type Definitions

// JSON component data
typedef unordered_map<string, any> umComponent;

// Port data
typedef tuple<string, string> tupPort;
typedef tuple<tupPort, tupPort> tupConnector;

// Equipment collection
typedef map<string, Port *> mapPortObj;
typedef unordered_map<string, CompBase *> mComponentObj;

// Cycle system
typedef vector<umComponent> Components;
typedef vector<tupConnector> Connectors;
typedef tuple<Components, Connectors> rankinecycle;
typedef vector<rankinecycle> rankinecycles;

3. Core Components

3.1 Port Class

3.1.1 Description

Stores and calculates thermodynamic state parameters, serving as the basic unit for data exchange between equipment.

3.1.2 Property Definitions

Property Type Description
index int Index of the port in system nodes
p double Pressure (MPa)
t double Temperature (°C)
h double Enthalpy (kJ/kg)
s double Entropy (kJ/kg·K)
x double Quality
fdot double Mass flow fraction
mdot double Actual mass flow rate (kg/s)

3.1.3 Thermodynamic Calculation Methods

Method Known Parameters Calculated Parameters
pt_prop() p, t h, s, x
px_prop() p, x t, h, s
tx_prop() t, x p, h, s
ps_prop() p, s t, h, x
ph_prop() p, h t, s, x

3.1.4 Calculation Principle

Based on the IAPWS-IF97 industrial standard, providing high-precision water and steam thermodynamic property calculations through the seuif97 library. The constructor automatically calls the corresponding calculation method based on known parameters:

Port(mPort curmPort) {
    // Initialize parameters to NAN
    if (!isnan(p) && !isnan(t)) {
        pt_prop();
    } else if (!isnan(t) && !isnan(x)) {
        tx_prop();
    } else if (!isnan(p) && !isnan(x)) {
        px_prop();
    }
}

3.2 Connector Class

3.2.1 Description

Implements data sharing between equipment ports and maintains the system port node list.

3.2.2 Core Mechanism

void Connector::AddConnector(tupConnector tconn, mComponentObj &comps) {
    auto [comp0, port0] = get<0>(tconn);
    auto [comp1, port1] = get<1>(tconn);

    // 1. Get port index in Nodes
    index = Nodes.size();
    comps[comp0]->portdict[port0]->index = index;
    
    // 2. Initialize Node[index] with port0
    Nodes.push_back(comps[comp0]->portdict[port0]);
    
    // 3. Merge known parameters from port1 to Node[index]
    getnodevalue(comps[comp1]->portdict[port1]);
    
    // 4. Point port1 to Node[index] to achieve data sharing
    comps[comp1]->portdict[port1] = Nodes[index];
    comps[comp1]->setportaddress();
}

3.2.3 Data Sharing Principle

Achieves automatic port data synchronization through pointer sharing:

  1. The first port (port0) serves as the basis for the system node
  2. Known parameters from the second port (port1) are merged into the node
  3. port1's pointer is replaced to point to the system node
  4. When any equipment modifies port parameters, all connected equipment automatically receive updates

3.2.4 Parameter Merge Rules

The getnodevalue() method only copies values from the source port when the target node parameter is NAN, ensuring known parameters are not overwritten:

void Connector::getnodevalue(Port *port) {
    if (isnan(Nodes[index]->p) && !isnan(port->p))
        Nodes[index]->p = port->p;
    // ... Other parameters follow the same rule
}

3.3 Equipment Base Class (CompBase)

3.3.1 Description

Defines a unified interface for all equipment, implementing polymorphism.

3.3.2 Interface Definition

class CompBase {
public:
    string name;                    // Equipment name
    ENERGY energy;                  // Energy type
    Port *iPort;                    // Inlet port
    Port *oPort;                    // Outlet port
    mapPortObj portdict;            // Port dictionary
    
    // Energy statistics
    double workExtracted;           // Work output
    double heatAdded;               // Heat input
    double workRequired;            // Work input
    
    // Pure virtual methods
    virtual void set_port_address() = 0;
    virtual void process_state() = 0;
    virtual int balance_mass_energy() = 0;
    virtual string result_string() = 0;
};

3.3.3 Energy Type Enumeration

enum ENERGY {
    WORKEXTRACTED = 1,    // Work-producing equipment (turbine)
    HEATADDED,            // Heat-adding equipment (boiler)
    WORKREQUIRED,         // Work-consuming equipment (pump)
    INTERNAL              // Internal equipment (condenser, heater)
};

3.3.4 Pure Virtual Method Description

Method Description
setportaddress() Updates port pointers (called after connector modifies portdict)
process_state() Calculates outlet thermodynamic state based on known parameters
balance_mass_energy() Performs mass/energy balance, returns 1 for success, 0 for failure
resultstring() Formats and outputs equipment calculation results

4. Equipment Implementations

4.1 Boiler

4.1.1 Description

Heats feedwater to superheated steam, providing the heat required for the cycle.

4.1.2 Port Configuration

  • iPort: Feedwater inlet
  • oPort: Steam outlet

4.1.3 Calculation Model

int Boiler::balance_mass_energy() {
    // Mass balance
    if (!isnan(iPort->fdot)) {
        oPort->fdot = iPort->fdot;
    } else if (!isnan(oPort->fdot)) {
        iPort->fdot = oPort->fdot;
    }
    
    // Energy balance
    heatAdded = iPort->fdot * (oPort->h - iPort->h);
    
    return isnan(heatAdded) ? 0 : 1;
}

4.1.4 Key Parameters

  • Heat input: Q = fdot × (h_out - h_in)
  • Energy type: HEATADDED

4.2 Turbine (TurbineEx0)

4.2.1 Description

Converts steam thermal energy into mechanical work, single-stage expansion (no extraction).

4.2.2 Port Configuration

  • iPort: Steam inlet
  • oPort: Steam outlet

4.2.3 Calculation Model

void TurbineEx0::process_state() {
    if (ef == 1.0) {
        // Isentropic expansion (ideal cycle)
        oPort->s = iPort->s;
        oPort->ps_prop();
    } else {
        // Actual expansion process
        double isoh = ps(oPort->p, iPort->s, 4);
        oPort->h = iPort->h - ef * (iPort->h - isoh);
        oPort->ph_prop();
    }
}

int TurbineEx0::balance_mass_energy() {
    // Mass balance
    if (!isnan(iPort->fdot)) {
        oPort->fdot = iPort->fdot;
    } else if (!isnan(oPort->fdot)) {
        iPort->fdot = oPort->fdot;
    }
    
    // Energy balance
    workExtracted = iPort->fdot * (iPort->h - oPort->h);
    
    return isnan(workExtracted) ? 0 : 1;
}

4.2.4 Key Parameters

  • Work output: W = fdot × (h_in - h_out)
  • Efficiency: ef (isentropic efficiency, 1.0 for ideal)
  • Energy type: WORKEXTRACTED

4.3 Turbine (TurbineEx1)

4.3.1 Description

Converts steam thermal energy into mechanical work, supports single-stage extraction.

4.3.2 Port Configuration

  • iPort: Steam inlet
  • oPort: Steam outlet
  • ePort: Extraction outlet

4.3.3 Calculation Model

void TurbineEx1::process_state() {
    if (ef == 1.0) {
        // Isentropic expansion (ideal cycle): both stages use inlet entropy s_i
        ePort->s = iPort->s;
        ePort->ps_prop();
        oPort->s = iPort->s;
        oPort->ps_prop();
    } else {
        // Actual expansion (two stages)
        // Stage 1: iPort → ePort, uses inlet entropy s_i
        double isoh = ps(ePort->p, iPort->s, 4);
        ePort->h = iPort->h - ef * (iPort->h - isoh);
        ePort->ph_prop();
        // Stage 2: ePort → oPort, uses extraction port entropy s_e (note: not s_i)
        isoh = ps(oPort->p, ePort->s, 4);
        oPort->h = ePort->h - ef * (ePort->h - isoh);
        oPort->ph_prop();
    }
}

int TurbineEx1::balance_mass_energy() {
    // Mass balance
    oPort->fdot = iPort->fdot - ePort->fdot;
    
    // Energy balance
    double ienergy = iPort->fdot * iPort->h;
    double oenergy = ePort->fdot * ePort->h + oPort->fdot * oPort->h;
    workExtracted = ienergy - oenergy;
    
    return isnan(workExtracted) ? 0 : 1;
}

4.3.4 Key Parameters

  • Work output: W = fdot_in × h_in - fdot_e × h_e - fdot_out × h_out
  • Energy type: WORKEXTRACTED

4.4 Pump

4.4.1 Description

Pressurizes condensate to boiler pressure, consuming mechanical work.

4.4.2 Port Configuration

  • iPort: Inlet
  • oPort: Outlet

4.4.3 Calculation Model

void Pump::process_state() {
    // Isentropic compression calculation
    double sout_s = iPort->s;
    double hout_s = ps(oPort->p, sout_s, 4);
    oPort->h = iPort->h + (hout_s - iPort->h) / ef;
    oPort->ph_prop();
}

int Pump::balance_mass_energy() {
    // Mass balance
    if (!isnan(iPort->fdot)) {
        oPort->fdot = iPort->fdot;
    } else if (!isnan(oPort->fdot)) {
        iPort->fdot = oPort->fdot;
    }

    // Energy balance: Calculate work consumption
    workRequired = iPort->fdot * (oPort->h - iPort->h);
    
    return isnan(workRequired) ? 0 : 1;
}

4.4.4 Key Parameters

  • Work input: W = fdot × (h_out - h_in)
  • Efficiency: ef (pump efficiency)
  • Energy type: WORKREQUIRED

4.5 Condenser

4.5.1 Description

Condenses turbine exhaust to saturated water, releasing heat.

4.5.2 Port Configuration

  • iPort: Steam inlet
  • oPort: Condensate outlet

4.5.3 Calculation Model

int Condenser::balance_mass_energy() {
    // Mass balance
    if (!isnan(iPort->fdot)) {
        oPort->fdot = iPort->fdot;
    } else if (!isnan(oPort->fdot)) {
        iPort->fdot = oPort->fdot;
    }
    
    // Energy balance
    heatExtracted = iPort->fdot * (iPort->h - oPort->h);
    
    return isnan(heatExtracted) ? 0 : 1;
}

4.5.4 Key Parameters

  • Heat rejection: Q = fdot × (h_in - h_out)
  • Energy type: INTERNAL

4.6 Open Heater (OpenedHeaterDw0)

4.6.1 Description

Uses extraction steam to heat feedwater, improving cycle efficiency.

4.6.2 Port Configuration

  • iPort: Extraction steam inlet
  • iPort_fw: Feedwater inlet
  • oPort_fw: Feedwater outlet

4.6.3 Calculation Model

int OpenedHeaterDw0::balance_mass_energy() {
    // Energy balance equation
    double qes1 = iPort->h - oPort_fw->h;
    double qfw1 = oPort_fw->h - iPort_fw->h;
    
    // Solve for extraction flow fraction
    iPort->fdot = oPort_fw->fdot * qfw1 / (qes1 + qfw1);
    
    // Mass balance equation
    iPort_fw->fdot = oPort_fw->fdot - iPort->fdot;
    
    heatAdded = iPort_fw->fdot * qfw1;
    heatExtracted = iPort->fdot * qes1;
    
    return isnan(heatAdded) ? 0 : 1;
}

4.6.4 Key Parameters

  • Three-port configuration
  • Energy type: INTERNAL

5. JSON Configuration System

5.1 JSON Loader (JsonLoader)

5.1.1 Description

Custom JSON parser, no third-party library dependencies, directly parses system configuration from JSON files.

5.1.2 Core Methods

Method Description
load_from_file() Loads single cycle configuration from a JSON file
parseComponents() Parses equipment array
parseConnectors() Parses connector array
parsePort() Parses port parameters
extractJsonValue() Extracts JSON key-value
extractJsonArray() Extracts JSON array

5.1.3 initCyclesFromJson Function

inline rankinecycle initCyclesFromJson(const std::string& jsonFilePath) {
    JsonLoader loader;
    return loader.load_from_file(jsonFilePath);
}

This function simplifies loading configuration:

  • Accepts a JSON file path
  • Returns a single rankinecycle tuple (containing Components and Connectors)
  • No longer stores cycles in a global container

5.1.4 Parsing Flow

JSON File → readFile() → extractJsonArray("components") → parseComponent() → umComponent
                                       ↓
                          extractJsonArray("connectors") → parseConnector() → tupConnector
                                       ↓
                              rankinecycle {Components, Connectors}

5.2 Configuration File Structure

{
  "name": "Rankine Cycle 81",
  "description": "Basic Rankine cycle with ideal components",
  "components": [
    {
      "name": "Turbine1",
      "classstr": "TurbineEx0",
      "ef": 1.0,
      "iPort": { "p": null, "t": null, "x": null, "fdot": null },
      "oPort": { "p": 0.008, "t": null, "x": null, "fdot": null }
    }
  ],
  "connectors": [
    {
      "ports": [
        { "component": "Turbine1", "port": "oPort" },
        { "component": "Condenser", "port": "iPort" }
      ]
    }
  ]
}

5.3 Configuration Features

  • Declarative Description: Only need to describe system structure and known parameters
  • Flexible Configuration: Supports partially unknown parameters (null)
  • Easy Maintenance: Modifying configuration does not require recompilation

6. Algorithm Features

6.1 Equipment Factory Registration Mechanism

6.1.1 Factory Class Design

class ClassReg {
public:
    compfactory compinstance;
    
    template <typename T>
    void register_type(const std::string &name) {
        compinstance[name] = [](umComponent item) { 
            return new T(item); 
        };
    }
    
    void register_type_all() {
        register_type<Boiler>("Boiler");
        register_type<Condenser>("Condenser");
        register_type<Pump>("Pump");
        register_type<TurbineEx0>("TurbineEx0");
        register_type<TurbineEx1>("TurbineEx1");
        register_type<OpenedHeaterDw0>("OpenedHeaterDw0");
    }
};

6.1.2 Object Creation Flow

  1. Parse JSON to get equipment type and parameters
  2. Look up factory registration table
  3. Call the corresponding lambda creation function
  4. Return equipment object instance

6.2 Automatic Port Data Sharing

6.2.1 Sharing Principle

Achieves automatic port data synchronization through pointer sharing, avoiding data duplication and inconsistency.

6.2.2 Implementation Mechanism

  • Pointer Sharing: Multiple equipment ports point to the same system node
  • Data Merging: Automatically merges known parameters during connection (NAN values are overwritten)
  • Automatic Synchronization: When any equipment modifies parameters, other equipment automatically receive updates
  • Address Update: setportaddress() ensures equipment member pointers are consistent with portdict

6.2.3 Advantages

  • Reduces data copying
  • Ensures data consistency
  • Simplifies inter-equipment communication

6.3 Iterative Cycle Calculation Algorithm

6.3.1 Algorithm Flow

void RankineCycle::component_balance() {
    // 1. Initialize equipment list and status
    list<string> keys;
    vector<bool> balanceok;
    int devnum = Comps.size();
    
    // 2. Iterate until all equipment is balanced
    int numdeviceok = 0;
    int numiter = 0;
    
    while (numiter <= devnum && numdeviceok <= devnum) {
        for (int i = 0; i < keys.size(); i++) {
            if (!balanceok[i]) {
                try {
                    int rtn = Comps[keys[i]]->balance_mass_energy();
                    if (rtn == 1) {
                        balanceok[i] = true;
                        numdeviceok++;
                    }
                } catch (...) {}
            }
        }
        numiter++;
    }
}

6.3.2 Algorithm Features

  • Adaptive Calculation: Only calculates calculable equipment
  • Iterative Solving: Gradually resolves dependencies
  • Fault Tolerance: Abnormal equipment does not affect overall calculation
  • Convergence Guarantee: Maximum iterations limited to equipment count

6.3.3 Calculation Strategy

  1. Iterate through all equipment
  2. Check if equipment has been calculated
  3. Attempt to execute the equipment's balance_mass_energy() method
  4. If successful (returns 1), mark as complete
  5. Repeat until all equipment is calculated or maximum iterations reached

7. Calculation Flow

7.1 System Initialization

int main() {
    // 1. Register equipment types
    ClassReg curclassreg;
    curclassreg.register_type_all();
    RankineCycle::compinstance = curclassreg.compinstance;
    
    // 2. Load JSON configuration (returns single cycle)
    rankinecycle cycle = initCyclesFromJson("./model/rankine85.json");
    
    // 3. Create cycle object and calculate
    unique_ptr<RankineCycle> curcycle(new RankineCycle(cycle));
    curcycle->Simulator();
    curcycle->out_results();
}

7.2 RankineCycle Constructor

RankineCycle::RankineCycle(rankinecycle cycle) {
    // Extract components and connectors from cycle tuple
    Components dictComps = get<0>(cycle);
    Connectors vecConnectors = get<1>(cycle);
    
    // 1. Create equipment objects
    for (auto &item : dictComps) {
        string classstr = get_component_class(item);
        string name = get_component_name(item);
        Comps[name] = compinstance[classstr](item);
    }
    
    // 2. Establish connections
    curcon = new Connector();
    for (auto &tconn : vecConnectors) {
        curcon->add_connector(tconn, Comps);
    }
}

7.3 Simulator Main Flow

void RankineCycle::Simulator() {
    // 1. State calculation (calculate outlet states for each equipment)
    component_state();
    
    // 2. Balance calculation (solve mass/energy balance)
    component_balance();
    
    // 3. Sum total energy
    for (auto &comp : Comps) {
        switch (comp.second->energy) {
            case WORKEXTRACTED:
                totalworkExtracted += comp.second->workExtracted;
                break;
            case HEATADDED:
                totalheatAdded += comp.second->heatAdded;
                break;
            case WORKREQUIRED:
                totalworkRequired += comp.second->workRequired;
        }
    }
    
    // 4. Calculate performance metrics
    netpoweroutput = totalworkExtracted - totalworkRequired;
    efficiency = netpoweroutput / totalheatAdded;
    HeatRate = 3600.0 / efficiency;
    SteamRate = HeatRate / totalheatAdded;
}

7.4 Performance Metrics

Metric Formula Unit
Net Power Output W_net = W_extracted - W_required kJ/kg
Cycle Efficiency η = W_net / Q_added -
Heat Rate HR = 3600 / η kJ/kWh
Steam Rate SR = HR / Q_added kg/kWh

8. Thermodynamic Calculation Library (seuif97)

8.1 Overview

Water and steam thermodynamic property calculation library based on the IAPWS-IF97 industrial standard.

8.2 Interface Functions

Function Known Parameters Output ID Description
pt(p, t, o_id) p, t 4=h, 5=s, 15=x Pressure + Temperature
ph(p, h, o_id) p, h 1=t, 5=s, 15=x Pressure + Enthalpy
ps(p, s, o_id) p, s 1=t, 4=h, 15=x Pressure + Entropy
px(p, x, o_id) p, x 1=t, 4=h, 5=s Pressure + Quality
tx(t, x, o_id) t, x 0=p, 4=h, 5=s Temperature + Quality

8.3 Output ID Description

o_id Output Parameter
0 Pressure (MPa)
1 Temperature (°C)
4 Enthalpy (kJ/kg)
5 Entropy (kJ/kg·K)
15 Quality

9. Technical Features

9.1 Modular Design

9.1.1 Design Advantages

  • Independent Encapsulation: Each equipment class is independently implemented
  • Unified Interface: All equipment follows the same interface
  • Easy Extension: Adding new equipment types is simple

9.1.2 Extension Example

// 1. Create new equipment class
class Reheater : public CompBase {
public:
    Reheater(umComponent dictComp);
    ~Reheater();
    
    void setportaddress() override;
    void process_state() override;
    int balance_mass_energy() override;
    string resultstring() override;
};

// 2. Register to factory
void register_type_all() {
    register_type<Reheater>("Reheater");
}

9.2 Configuration-Driven

9.2.1 Configuration Advantages

  • Flexibility: Can change the system without modifying code
  • Maintainability: Configuration files are easy to understand and modify
  • Reusability: Same program supports multiple configurations

9.3 Automated Calculation

9.3.1 Automation Features

  • Automatic equipment creation and management
  • Automatic port data synchronization
  • Automatic iterative solving of system dependencies
  • Automatic performance metric calculation