///|
pub struct RankineCycleResult {
boiler_duty_w : Double
turbine_work_w : Double
pump_work_w : Double
condenser_duty_w : Double
net_work_w : Double
thermal_efficiency : Double
steam_quality_at_turbine_exit : Double
} derive(Debug)
///|
pub struct TurbineExpansionResult {
inlet_enthalpy_kj_per_kg : Double
outlet_enthalpy_kj_per_kg : Double
specific_work_kj_per_kg : Double
outlet_quality : Double
outlet_temperature_c : Double
} derive(Debug)
///|
pub fn turbine_expand(
inlet : SteamState,
outlet_pressure_kpa : Double,
isentropic_efficiency : Double,
) -> TurbineExpansionResult {
let outlet_saturated = steam_state_from_quality(outlet_pressure_kpa, 1.0)
let ideal_drop = (inlet.enthalpy_kj_per_kg -
outlet_saturated.enthalpy_kj_per_kg).max(0.0)
let work = ideal_drop * isentropic_efficiency.max(0.0).min(1.0)
let outlet_h = inlet.enthalpy_kj_per_kg - work
let quality = table_quality_from_enthalpy(outlet_pressure_kpa, outlet_h).value
let outlet = steam_state_from_quality(outlet_pressure_kpa, quality)
{
inlet_enthalpy_kj_per_kg: inlet.enthalpy_kj_per_kg,
outlet_enthalpy_kj_per_kg: outlet_h,
specific_work_kj_per_kg: work,
outlet_quality: quality,
outlet_temperature_c: outlet.temperature_c,
}
}
///|
pub fn boiler_duty_w(
mass_flow_kg_per_s : Double,
feedwater : SteamState,
steam : SteamState,
) -> Double {
mass_flow_kg_per_s.max(0.0) *
(steam.enthalpy_kj_per_kg - feedwater.enthalpy_kj_per_kg) *
1000.0
}
///|
pub fn condenser_duty_w(
mass_flow_kg_per_s : Double,
turbine_exit : SteamState,
saturated_liquid : SteamState,
) -> Double {
mass_flow_kg_per_s.max(0.0) *
(saturated_liquid.enthalpy_kj_per_kg - turbine_exit.enthalpy_kj_per_kg) *
1000.0
}
///|
pub fn feedwater_pump_work_w(
mass_flow_kg_per_s : Double,
pressure_rise_kpa : Double,
density_kg_per_m3 : Double,
efficiency : Double,
) -> Double {
if density_kg_per_m3 <= 0.0 || efficiency <= 0.0 {
0.0
} else {
mass_flow_kg_per_s.max(0.0) *
kpa_to_pa(pressure_rise_kpa) /
density_kg_per_m3 /
efficiency.min(1.0)
}
}
///|
pub fn rankine_cycle(
boiler_temperature_c : Double,
condenser_pressure_kpa : Double,
mass_flow_kg_per_s : Double,
turbine_efficiency : Double,
pump_efficiency : Double,
) -> RankineCycleResult {
let boiler_pressure = condenser_pressure_kpa.max(1.0) * 100.0
let steam = steam_state(boiler_temperature_c, boiler_pressure)
let condensate = steam_state_from_quality(condenser_pressure_kpa, 0.0)
let expansion = turbine_expand(
steam, condenser_pressure_kpa, turbine_efficiency,
)
let feedwater = steam_state_from_quality(boiler_pressure, 0.0)
let boiler_duty = boiler_duty_w(mass_flow_kg_per_s, feedwater, steam)
let turbine_work = mass_flow_kg_per_s.max(0.0) *
expansion.specific_work_kj_per_kg *
1000.0
let pump_work = feedwater_pump_work_w(
mass_flow_kg_per_s,
boiler_pressure - condenser_pressure_kpa,
saturated_water_density_kg_per_m3(condensate.temperature_c),
pump_efficiency,
)
let condenser_duty = condenser_duty_w(
mass_flow_kg_per_s,
steam_state_from_quality(condenser_pressure_kpa, expansion.outlet_quality),
condensate,
)
let net = turbine_work - pump_work
{
boiler_duty_w: boiler_duty,
turbine_work_w: turbine_work,
pump_work_w: pump_work,
condenser_duty_w: condenser_duty.abs(),
net_work_w: net,
thermal_efficiency: if boiler_duty <= 0.0 {
0.0
} else {
net / boiler_duty
},
steam_quality_at_turbine_exit: expansion.outlet_quality,
}
}
///|
pub fn cycle_heat_rate_j_per_j(cycle : RankineCycleResult) -> Double {
if cycle.net_work_w <= 0.0 {
0.0
} else {
cycle.boiler_duty_w / cycle.net_work_w
}
}
///|
pub fn cycle_capacity_factor(
cycle : RankineCycleResult,
operating_hours : Double,
period_hours : Double,
) -> Double {
let availability = if cycle.net_work_w > 0.0 { 1.0 } else { 0.0 }
if period_hours <= 0.0 {
0.0
} else {
availability * (operating_hours / period_hours).max(0.0).min(1.0)
}
}