diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp index 6d6d047bd..7df3705bf 100644 --- a/ssc/cmod_csp_heatsink.cpp +++ b/ssc/cmod_csp_heatsink.cpp @@ -178,10 +178,10 @@ class cm_csp_heatsink : public compute_module // Optimize to find steam mdot - double mdot_ext_calc, tol_solved; + double mdot_ext_calc, tol_solved, T_c_out, x_c_out, hx_min_dT; int solve_code = m_hx.off_design_target_cold_PH_out(h_ext_hot, mdot_min, mdot_max, P_ext_cold, h_ext_cold, P_ext_hot, 1.0, h_htf_hot_od, 1.0, mdot_htf_od, od_tol, - q_dot_calc, h_ext_out_calc, h_htf_out_calc, mdot_ext_calc, tol_solved); + q_dot_calc, h_ext_out_calc, h_htf_out_calc, mdot_ext_calc, tol_solved, T_c_out, x_c_out, hx_min_dT); // Off design Outlet steam properties prop_error_code = water_PH(P_ext_hot, h_ext_out_calc, &ms_water_props); diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 499bdec50..bcc388867 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -454,6 +454,11 @@ static var_info _cm_vtab_trough_physical_iph[] = { { SSC_OUTPUT, SSC_NUMBER, "csp_dtr_sca_calc_latitude", "Latitude", "degree", "", "Collector", "?=0", "", "" }, { SSC_OUTPUT, SSC_MATRIX, "csp_dtr_sca_calc_iams", "IAM at summer solstice", "", "", "Collector", "?=0", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_hs_ext_des", "Heat sink fluid mass flow rate", "kg/s", "", "System Control", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "T_hs_ext_out_des", "Heat sink fluid outlet temperature", "C", "", "System Control", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "hx_min_dT_des", "Heat sink hx min temp difference", "C", "", "System Control", "*", "", "" }, + + // System Control { SSC_OUTPUT, SSC_NUMBER, "bop_design", "BOP parasitics at design", "MWe", "", "System Control", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "aux_design", "Aux parasitics at design", "MWe", "", "System Control", "*", "", "" }, @@ -586,11 +591,16 @@ static var_info _cm_vtab_trough_physical_iph[] = { { SSC_OUTPUT, SSC_ARRAY, "pipe_loop_P_dsn", "Field piping loop pressure at design", "bar", "", "solar_field", "sim_type=1", "", "" }, // Heat Sink - { SSC_OUTPUT, SSC_ARRAY, "q_dot_to_heat_sink", "Heat sink thermal power", "MWt", "", "Heat_Sink", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "W_dot_pc_pump", "Heat sink pumping power", "MWe", "", "Heat_Sink", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_htf_heat_sink", "Heat sink HTF mass flow", "kg/s", "", "Heat_Sink", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_heat_sink_in", "Heat sink HTF inlet temp", "C", "", "Heat_Sink", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_heat_sink_out", "Heat sink HTF outlet temp", "C", "", "Heat_Sink", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_to_heat_sink", "Heat sink thermal power", "MWt", "", "Heat_Sink", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "W_dot_pc_pump", "Heat sink pumping power", "MWe", "", "Heat_Sink", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_htf_heat_sink", "Heat sink HTF mass flow", "kg/s", "", "Heat_Sink", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_heat_sink_in", "Heat sink HTF inlet temp", "C", "", "Heat_Sink", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_heat_sink_out", "Heat sink HTF outlet temp", "C", "", "Heat_Sink", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "m_dot_wf_heat_sink", "Heat sink steam mass flow rate", "kg/s", "", "Heat_Sink", "sim_type=1&hs_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "x_out_wf_heat_sink", "Heat sink steam outlet quality", "-", "", "Heat_Sink", "sim_type=1&hs_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_out_wf_heat_sink", "Heat sink steam outlet temp", "C", "", "Heat_Sink", "sim_type=1&hs_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "hx_min_dT_heat_sink", "Heat sink HX min temp difference", "C", "", "Heat_Sink", "sim_type=1&hs_type=1", "", "" }, // TES { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "TES", "sim_type=1", "", "" }, @@ -1575,11 +1585,17 @@ class cm_trough_physical_iph : public compute_module c_heat_sink_phys.ms_params.m_od_tol = as_double("hs_phys_tol"); //[] HX off design tolerance // Allocate heat sink outputs - c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink::E_Q_DOT_HEAT_SINK, allocate("q_dot_to_heat_sink", n_steps_fixed), n_steps_fixed); - c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink::E_W_DOT_PUMPING, allocate("W_dot_pc_pump", n_steps_fixed), n_steps_fixed); - c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink::E_M_DOT_HTF, allocate("m_dot_htf_heat_sink", n_steps_fixed), n_steps_fixed); - c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink::E_T_HTF_IN, allocate("T_heat_sink_in", n_steps_fixed), n_steps_fixed); - c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink::E_T_HTF_OUT, allocate("T_heat_sink_out", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_Q_DOT_HEAT_SINK, allocate("q_dot_to_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_W_DOT_PUMPING, allocate("W_dot_pc_pump", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_M_DOT_HTF, allocate("m_dot_htf_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_T_HTF_IN, allocate("T_heat_sink_in", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_T_HTF_OUT, allocate("T_heat_sink_out", n_steps_fixed), n_steps_fixed); + + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_M_DOT_EXT, allocate("m_dot_wf_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_X_OUT_EXT, allocate("x_out_wf_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_T_OUT_EXT, allocate("T_out_wf_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_phys.mc_reported_outputs.assign(C_pc_heat_sink_physical::E_HX_MIN_DT, allocate("hx_min_dT_heat_sink", n_steps_fixed), n_steps_fixed); + c_heat_sink_pointer = &c_heat_sink_phys; } @@ -2138,6 +2154,19 @@ class cm_trough_physical_iph : public compute_module } } + + // Heat sink + double m_dot_hs_ext_des, T_hs_ext_out_des, hx_min_dT_des; + m_dot_hs_ext_des = T_hs_ext_out_des = hx_min_dT_des = std::numeric_limits::quiet_NaN(); + if (hs_type == 1) { + m_dot_hs_ext_des = c_heat_sink_phys.get_m_dot_ext_des(); //[kg/s] + T_hs_ext_out_des = c_heat_sink_phys.get_T_ext_out_des(); //[C] + hx_min_dT_des = c_heat_sink_phys.get_hx_min_dT_des(); //[C] + } + + assign("m_dot_hs_ext_des", m_dot_hs_ext_des); + assign("T_hs_ext_out_des", T_hs_ext_out_des); + assign("hx_min_dT_des", hx_min_dT_des); } // Calculate Costs and assign outputs diff --git a/tcs/csp_solver_core.cpp b/tcs/csp_solver_core.cpp index e0a5a0383..e7e42fcbc 100644 --- a/tcs/csp_solver_core.cpp +++ b/tcs/csp_solver_core.cpp @@ -1787,7 +1787,7 @@ void C_csp_solver::C_CR_OFF__PC_TARGET__TES_DC__AUX_OFF::check_system_limits(C_c } else if ((q_dot_pc_solved - q_dot_pc_on_dispatch_target) / q_dot_pc_on_dispatch_target < -limit_comp_tol) { - if (m_dot_pc_solved < m_dot_pc_max) + if (m_dot_pc_solved / m_dot_pc_max < 1.0 - limit_comp_tol) { // TES cannot provide enough thermal power - step down to next operating mode m_is_mode_available = false; diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 2065cde88..f5e3f78c8 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -42,6 +42,11 @@ static C_csp_reported_outputs::S_output_info S_output_info[]= {C_pc_heat_sink_physical::E_M_DOT_HTF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_pc_heat_sink_physical::E_T_HTF_IN, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_pc_heat_sink_physical::E_T_HTF_OUT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + {C_pc_heat_sink_physical::E_M_DOT_EXT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_pc_heat_sink_physical::E_X_OUT_EXT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_pc_heat_sink_physical::E_T_OUT_EXT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_pc_heat_sink_physical::E_HX_MIN_DT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, csp_info_invalid }; @@ -52,7 +57,11 @@ C_pc_heat_sink_physical::C_pc_heat_sink_physical() m_max_frac = std::numeric_limits::quiet_NaN(); - m_m_dot_htf_des = std::numeric_limits::quiet_NaN(); + m_m_dot_htf_des = m_m_dot_ext_des = m_m_dot_ext_min = + m_m_dot_ext_max = m_h_ext_cold_des = m_h_ext_hot_des = + m_T_ext_hot_des = std::numeric_limits::quiet_NaN(); + + m_did_init_pass = false; } void C_pc_heat_sink_physical::check_double_params_are_set() @@ -134,16 +143,24 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa } m_h_ext_hot_des = water_props.enth; //[kJ/kg] Target enthalpy + m_T_ext_hot_des = water_props.temp - 273.15; //[C] + // Design HX - C_HX_counterflow_CRM::S_des_solved des_solved; - this->m_hx.design_w_TP_PH(ms_params.m_T_htf_hot_des + 273.15, 1.0, ms_params.m_T_htf_cold_des + 273.15, 1.0, - ms_params.m_P_ext_cold_des, m_h_ext_cold_des, ms_params.m_P_ext_hot_des, m_h_ext_hot_des, - ms_params.m_q_dot_des * 1e3, des_solved); + + try { + this->m_hx.design_w_TP_PH(ms_params.m_T_htf_hot_des + 273.15, 1.0, ms_params.m_T_htf_cold_des + 273.15, 1.0, + ms_params.m_P_ext_cold_des, m_h_ext_cold_des, ms_params.m_P_ext_hot_des, m_h_ext_hot_des, + ms_params.m_q_dot_des * 1e3, mc_hx_des_solved); + } + catch (C_csp_exception) { + m_did_init_pass = false; + return; + } // Assign Design External mdot m_m_dot_ext_des = this->m_hx.ms_des_calc_UA_par.m_m_dot_cold_des; //[kg/s] m_m_dot_ext_min = m_m_dot_ext_des * ms_params.m_f_m_dot_ext_min; //[kg/s] - m_m_dot_ext_max = m_m_dot_ext_des * ms_params.m_f_m_dot_ext_max; //[kg/s] + m_m_dot_ext_max = m_m_dot_ext_des * ms_params.m_f_m_dot_ext_max; //[kg/s] // Assign Design HTF mdot m_m_dot_htf_des = m_hx.ms_des_calc_UA_par.m_m_dot_hot_des; //[kg/s] @@ -165,6 +182,8 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa solved_params.m_m_dot_design = m_m_dot_htf_des*3600.0; //[kg/hr] solved_params.m_m_dot_min = solved_params.m_m_dot_design*solved_params.m_cutoff_frac; //[kg/hr] solved_params.m_m_dot_max = solved_params.m_m_dot_design*solved_params.m_max_frac; //[kg/hr] + + m_did_init_pass = true; } C_csp_power_cycle::E_csp_power_cycle_modes C_pc_heat_sink_physical::get_operating_state() @@ -252,37 +271,8 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather C_csp_power_cycle::S_csp_pc_out_solver &out_solver, const C_csp_solver_sim_info &sim_info) { - // Test SIMPLE heat sink - if (false) - { - double T_htf_hot = htf_state_in.m_temp; //[C] - double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] - - double cp_htf = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des + 273.15, T_htf_hot + 273.15); //[kJ/kg-K] - - // For now, let's assume the Heat Sink can always return the HTF at the design cold temperature - double q_dot_htf = m_dot_htf * cp_htf * (T_htf_hot - ms_params.m_T_htf_cold_des) / 1.E3; //[MWt] - - out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = ms_params.m_T_htf_cold_des; //[C] - out_solver.m_m_dot_htf = m_dot_htf * 3600.0; //[kg/hr] Return inlet mass flow rate - - double W_dot_cooling_parasitic = 0.0; //[MWe] No cooling load - - out_solver.m_time_required_su = 0.0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = q_dot_htf; //[MWt] Thermal power form HTF - - double W_dot_htf_pump = ms_params.m_htf_pump_coef * m_dot_htf / 1.E3; //[MWe] - out_solver.m_W_dot_elec_parasitics_tot = W_dot_cooling_parasitic + W_dot_htf_pump; //[MWe] - - out_solver.m_was_method_successful = true; - - mc_reported_outputs.value(E_Q_DOT_HEAT_SINK, q_dot_htf); //[MWt] - mc_reported_outputs.value(E_W_DOT_PUMPING, W_dot_htf_pump); //[MWe] - mc_reported_outputs.value(E_M_DOT_HTF, m_dot_htf); //[kg/s] - mc_reported_outputs.value(E_T_HTF_IN, T_htf_hot); //[C] - mc_reported_outputs.value(E_T_HTF_OUT, out_solver.m_T_htf_cold); //[C] - return; + if (!m_did_init_pass) { + throw(C_csp_exception("C_pc_heat_sink_physical did not pass initialization. Cannot run Call method")); } // Process inputs @@ -290,54 +280,13 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather int standby_control = inputs.m_standby_control; //[-] 1: On, 2: Standby, 3: Off double NaN = std::numeric_limits::quiet_NaN(); - double q_dot, T_c_out, T_h_out_C, m_dot_c, tol_solved; - q_dot = T_c_out = T_h_out_C = m_dot_c = tol_solved = NaN; + double q_dot, T_c_out, T_h_out_C, m_dot_c, tol_solved, x_c_out, hx_min_dT; + q_dot = T_c_out = T_h_out_C = m_dot_c = tol_solved = x_c_out = hx_min_dT = NaN; // Handle no mass flow coming in if (inputs.m_m_dot < 1e-5 && standby_control == ON) { - // Test SIMPLE heat sink - if (true) - { - double T_htf_hot = htf_state_in.m_temp; //[C] - double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] - - double cp_htf = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des + 273.15, T_htf_hot + 273.15); //[kJ/kg-K] - - // For now, let's assume the Heat Sink can always return the HTF at the design cold temperature - double q_dot_htf = m_dot_htf * cp_htf * (T_htf_hot - ms_params.m_T_htf_cold_des) / 1.E3; //[MWt] - - out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = ms_params.m_T_htf_cold_des; //[C] - out_solver.m_m_dot_htf = m_dot_htf * 3600.0; //[kg/hr] Return inlet mass flow rate - - double W_dot_cooling_parasitic = 0.0; //[MWe] No cooling load - - out_solver.m_time_required_su = 0.0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = q_dot_htf; //[MWt] Thermal power form HTF - - double W_dot_htf_pump = ms_params.m_htf_pump_coef * m_dot_htf / 1.E3; //[MWe] - out_solver.m_W_dot_elec_parasitics_tot = W_dot_cooling_parasitic + W_dot_htf_pump; //[MWe] - - out_solver.m_was_method_successful = true; - - mc_reported_outputs.value(E_Q_DOT_HEAT_SINK, q_dot_htf); //[MWt] - mc_reported_outputs.value(E_W_DOT_PUMPING, W_dot_htf_pump); //[MWe] - mc_reported_outputs.value(E_M_DOT_HTF, m_dot_htf); //[kg/s] - mc_reported_outputs.value(E_T_HTF_IN, T_htf_hot); //[C] - mc_reported_outputs.value(E_T_HTF_OUT, out_solver.m_T_htf_cold); //[C] - return; - } - - out_solver.m_P_cycle = 0; //[MWt] No steam generation - out_solver.m_T_htf_cold = htf_state_in.m_temp; //[C] - out_solver.m_m_dot_htf = inputs.m_m_dot; //[kg/hr] Return inlet mass flow rate - out_solver.m_time_required_su = 0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = 0; //[MWt] Thermal power from HTF - out_solver.m_W_dot_elec_parasitics_tot = 0; //[MWe] - - out_solver.m_was_method_successful = true; - return; + standby_control = E_csp_power_cycle_modes::OFF; } switch (standby_control) @@ -346,129 +295,98 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather case ON: case STANDBY: { - try - { - // Get HTF inlet enthalpy - double h_htf_hot = mc_pc_htfProps.enth(htf_state_in.m_temp + 273.15) * 1e-3; //[kJ/kg] + // Get HTF inlet enthalpy + double h_htf_hot = mc_pc_htfProps.enth(htf_state_in.m_temp + 273.15) * 1e-3; //[kJ/kg] + + // Run Off design to find steam mdot to hit enthalpy target + double h_ext_out_calc, h_htf_out_calc; + + int solve_code = 0; - // Run Off design to find steam mdot to hit enthalpy target - double h_ext_out_calc, h_htf_out_calc; - int solve_code = m_hx.off_design_target_cold_PH_out(m_h_ext_hot_des, m_m_dot_ext_min, m_m_dot_ext_max, + try + { + solve_code = m_hx.off_design_target_cold_PH_out(m_h_ext_hot_des, m_m_dot_ext_min, m_m_dot_ext_max, ms_params.m_P_ext_cold_des, m_h_ext_cold_des, ms_params.m_P_ext_hot_des, 1.0, h_htf_hot, 1.0, m_dot_htf, ms_params.m_od_tol, - q_dot, h_ext_out_calc, h_htf_out_calc, m_dot_c, tol_solved); + q_dot, h_ext_out_calc, h_htf_out_calc, m_dot_c, + tol_solved, T_c_out, x_c_out, hx_min_dT); + } + catch (C_csp_exception exc) + { + solve_code = -89; + } - if (solve_code == 0) - { - // Solved succesfully + if (solve_code == 0) + { + // Solved succesfully - // Get HTF temperature from enthalpy - T_h_out_C = mc_pc_htfProps.temp(h_htf_out_calc*1e3); //[C] + // Get HTF temperature from enthalpy + T_h_out_C = mc_pc_htfProps.temp(h_htf_out_calc*1e3); //[C] - out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = T_h_out_C; //[C] - out_solver.m_m_dot_htf = m_dot_htf * 3600.0;//[kg/hr] Return inlet mass flow rate - out_solver.m_time_required_su = 0.0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = q_dot * 1e-3; //[MWt] Thermal power form HTF - - out_solver.m_was_method_successful = true; - break; - } - else + out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation + out_solver.m_T_htf_cold = T_h_out_C; //[C] + out_solver.m_m_dot_htf = m_dot_htf * 3600.0;//[kg/hr] Return inlet mass flow rate + out_solver.m_time_required_su = 0.0; //[s] No startup requirements, for now + out_solver.m_q_dot_htf = q_dot * 1e-3; //[MWt] Thermal power form HTF + + out_solver.m_was_method_successful = true; + } + else + { + /* + // test why it failed + if (true) { - // test why it failed - if (true) + std::vector mdot_vec; + std::vector h_vec; + int total_runs = 200; + for (int i = 0; i < total_runs; i++) { - std::vector mdot_vec; - std::vector h_vec; - int total_runs = 200; - for (int i = 0; i < total_runs; i++) + double frac = (double)i / (double)total_runs; + double mdot = m_m_dot_ext_min + (frac * (m_m_dot_ext_max - m_m_dot_ext_min)); + try { - double frac = (double)i / (double)total_runs; - double mdot = m_m_dot_ext_min + (frac * (m_m_dot_ext_max - m_m_dot_ext_min)); - try - { - m_hx.off_design_solution_fixed_dP_enth(m_h_ext_cold_des, ms_params.m_P_ext_cold_des, mdot, ms_params.m_P_ext_hot_des, - h_htf_hot, 1.0, m_dot_htf, 1.0, ms_params.m_od_tol, - q_dot, h_ext_out_calc, h_htf_out_calc); - } - catch (C_csp_exception exc) - { - h_ext_out_calc = 0; - } - - h_vec.push_back(h_ext_out_calc); - mdot_vec.push_back(mdot); + m_hx.off_design_solution_fixed_dP_enth(m_h_ext_cold_des, ms_params.m_P_ext_cold_des, mdot, ms_params.m_P_ext_hot_des, + h_htf_hot, 1.0, m_dot_htf, 1.0, ms_params.m_od_tol, + q_dot, h_ext_out_calc, h_htf_out_calc); + } + catch (C_csp_exception exc) + { + h_ext_out_calc = 0; } - - int x = 0; + h_vec.push_back(h_ext_out_calc); + mdot_vec.push_back(mdot); } - // Could not solve - q_dot = 0; - T_h_out_C = htf_state_in.m_temp; - out_solver.m_P_cycle = 0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = htf_state_in.m_temp; //[C] - out_solver.m_m_dot_htf = inputs.m_m_dot; //[kg/hr] Return inlet mass flow rate - out_solver.m_time_required_su = 0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = 0; //[MWt] Thermal power form HTF - out_solver.m_W_dot_elec_parasitics_tot = 0; //[MWe] + int x = 0; + } */ - out_solver.m_was_method_successful = false; - break; - } - } - catch (C_csp_exception exc) - { - out_solver.m_P_cycle = NaN; //[MWe] No electricity generation - out_solver.m_T_htf_cold = NaN; //[C] - out_solver.m_m_dot_htf = NaN; //[kg/hr] Return inlet mass flow rate - out_solver.m_time_required_su = NaN; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = NaN; //[MWt] Thermal power form HTF - out_solver.m_W_dot_elec_parasitics_tot = NaN; //[MWe] + // Could not solve + q_dot = 0; + T_h_out_C = htf_state_in.m_temp; + + out_solver.m_P_cycle = 0; //[MWe] No electricity generation + out_solver.m_T_htf_cold = htf_state_in.m_temp; //[C] + out_solver.m_m_dot_htf = inputs.m_m_dot; //[kg/hr] Return inlet mass flow rate + out_solver.m_time_required_su = 0; //[s] No startup requirements, for now + out_solver.m_q_dot_htf = 0; //[MWt] Thermal power form HTF + out_solver.m_W_dot_elec_parasitics_tot = 0; //[MWe] out_solver.m_was_method_successful = false; - return; - } + + m_dot_c = 0.0; //[kg/s] + // Use design-point values if off or failed + // Will help not mess up plotting scales with 0s + T_c_out = m_T_ext_hot_des; + x_c_out = ms_params.m_Q_ext_hot_des; + hx_min_dT = mc_hx_des_solved.m_min_DT_design; + } break; } case OFF: { - // Test SIMPLE heat sink - if (true) - { - double T_htf_hot = htf_state_in.m_temp; //[C] - double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] - - double cp_htf = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des + 273.15, T_htf_hot + 273.15); //[kJ/kg-K] - - // For now, let's assume the Heat Sink can always return the HTF at the design cold temperature - double q_dot_htf = m_dot_htf * cp_htf * (T_htf_hot - ms_params.m_T_htf_cold_des) / 1.E3; //[MWt] - - out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = ms_params.m_T_htf_cold_des; //[C] - out_solver.m_m_dot_htf = m_dot_htf * 3600.0; //[kg/hr] Return inlet mass flow rate - - double W_dot_cooling_parasitic = 0.0; //[MWe] No cooling load - - out_solver.m_time_required_su = 0.0; //[s] No startup requirements, for now - out_solver.m_q_dot_htf = q_dot_htf; //[MWt] Thermal power form HTF - - double W_dot_htf_pump = ms_params.m_htf_pump_coef * m_dot_htf / 1.E3; //[MWe] - out_solver.m_W_dot_elec_parasitics_tot = W_dot_cooling_parasitic + W_dot_htf_pump; //[MWe] - - out_solver.m_was_method_successful = true; - - mc_reported_outputs.value(E_Q_DOT_HEAT_SINK, q_dot_htf); //[MWt] - mc_reported_outputs.value(E_W_DOT_PUMPING, W_dot_htf_pump); //[MWe] - mc_reported_outputs.value(E_M_DOT_HTF, m_dot_htf); //[kg/s] - mc_reported_outputs.value(E_T_HTF_IN, T_htf_hot); //[C] - mc_reported_outputs.value(E_T_HTF_OUT, out_solver.m_T_htf_cold); //[C] - return; - } - q_dot = 0; T_h_out_C = htf_state_in.m_temp; @@ -480,6 +398,14 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather out_solver.m_W_dot_elec_parasitics_tot = 0; //[MWe] out_solver.m_was_method_successful = true; + + m_dot_c = 0.0; //[kg/s] + // Use design-point values if off or failed + // Will help not mess up plotting scales with 0s + T_c_out = m_T_ext_hot_des; + x_c_out = ms_params.m_Q_ext_hot_des; + hx_min_dT = mc_hx_des_solved.m_min_DT_design; + break; } default: @@ -488,6 +414,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather double W_dot_cooling_parasitic = 0.0; //[MWe] No cooling load double W_dot_htf_pump = ms_params.m_htf_pump_coef * m_dot_htf / 1.E3; //[MWe] + out_solver.m_W_dot_elec_parasitics_tot = W_dot_cooling_parasitic + W_dot_htf_pump; //[MWe] mc_reported_outputs.value(E_Q_DOT_HEAT_SINK, q_dot*1e-3); //[MWt] @@ -496,6 +423,11 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather mc_reported_outputs.value(E_T_HTF_IN, htf_state_in.m_temp); //[C] mc_reported_outputs.value(E_T_HTF_OUT, T_h_out_C); //[C] + mc_reported_outputs.value(E_M_DOT_EXT, m_dot_c); //[kg/s] + mc_reported_outputs.value(E_X_OUT_EXT, x_c_out); //[-] + mc_reported_outputs.value(E_T_OUT_EXT, T_c_out); //[C] + mc_reported_outputs.value(E_HX_MIN_DT, hx_min_dT); //[C] + return; } diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h index 8ffd907a5..f081ded31 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.h +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -51,10 +51,15 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle E_W_DOT_PUMPING, //[MWe] E_M_DOT_HTF, //[kg/s] E_T_HTF_IN, //[C] - E_T_HTF_OUT //[C] + E_T_HTF_OUT, //[C] + + E_M_DOT_EXT, //[kg/s] + E_X_OUT_EXT, //[-] + E_T_OUT_EXT, //[C] + E_HX_MIN_DT //[C] }; - C_csp_reported_outputs mc_reported_outputs; + C_csp_reported_outputs mc_reported_outputs; private: @@ -67,13 +72,22 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle double m_m_dot_ext_max; //[kg/s] Max ext fluid mdot double m_h_ext_cold_des; // [kJ/kg] Steam inlet enthalpy double m_h_ext_hot_des; // [kJ/kg] Steam target outlet enthalpy + double m_T_ext_hot_des; //[C] + + bool m_did_init_pass; //[-] HTFProperties mc_pc_htfProps; + C_HX_counterflow_CRM::S_des_solved mc_hx_des_solved; + void check_double_params_are_set(); public: + double get_m_dot_ext_des() { return m_m_dot_ext_des; } + double get_T_ext_out_des() { return m_T_ext_hot_des; } + double get_hx_min_dT_des() { return mc_hx_des_solved.m_min_DT_design; } + struct S_params { // Inputs diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 1253f1df3..85995ed62 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -600,6 +600,8 @@ void NS_HX_counterflow_eqs::calc_req_UA_enth(int hot_fl_code /*-*/, HTFPropertie double P_h = P_h_in - i * (P_h_in - P_h_out) / (double)(N_nodes - 1); // Calculate the entahlpy at the node + // Starting from HX hot side + // i = 0 is global cold stream outlet and hot stream inlet double h_c = h_c_out + i * (h_c_in - h_c_out) / (double)(N_nodes - 1); double h_h = h_h_in - i * (h_h_in - h_h_out) / (double)(N_nodes - 1); @@ -768,7 +770,7 @@ void NS_HX_counterflow_eqs::calc_req_UA_enth(int hot_fl_code /*-*/, HTFPropertie else { double T_c_avg = cold_htf_class.temp_lookup(h_c_avg); //[K] - cp_c_avg = cold_htf_class.Cp(T_c_avg); //[K] + cp_c_avg = cold_htf_class.Cp(T_c_avg); //[kJ/kg-K] v_s_node_info[i - 1].s_fl_cold.cp = cp_c_avg; //[kJ/kg-K] v_s_node_info[i - 1].s_fl_cold.rho = hot_htf_class.dens(T_c_avg, P_c_avg); //[kg/m3] @@ -3182,7 +3184,7 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ double P_h_in /*kPa*/, double h_h_in /*kJ/kg*/, double P_h_out /*kPa*/, double m_dot_h /*kg/s*/, double od_tol /*-*/, double& q_dot /*kWt*/, double& h_c_out /*kJ/kg*/, double& h_h_out /*kJ/kg*/, double& m_dot_c /*kg/s*/, - double& tol_solved) + double& tol_solved, double& T_c_out /*C*/, double& x_c_out /**/, double& hx_min_dT /*C*/) { // ADD inputs double max_iter = 1000; @@ -3252,7 +3254,11 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ // If new guess failed, just try larger than xy1 guess. This approach isn't optimal but should work... if (solver_code != 0) { xy_2.x = std::min(xy_1.x * 1.1, m_dot_c_max); - + + solver_code = h_out_solver.test_member_function(xy_2.x, &delta_h_target_rel); + if (solver_code != 0) { + return -6; + } } } if (delta_h_target_rel > 0.0 || solver_code != 0) { @@ -3273,6 +3279,8 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ } } } + + xy_2.y = delta_h_target_rel; } /* @@ -3326,6 +3334,13 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] + T_c_out = h_out_eq.m_T_c_out; //[C] + hx_min_dT = h_out_eq.m_hx_min_dT; //[C] + + water_state ms_water_props; + int prop_error_code = water_PH(P_c_out, h_c_out, &ms_water_props); + + x_c_out = ms_water_props.qual; return 0; } @@ -3339,6 +3354,10 @@ int C_HX_htf_to_steam::C_MEQ__target_cold_PH_out::operator()(double m_dot_c /*kg m_tol, m_q_dot, m_h_c_out, m_h_h_out); + m_T_c_out = mpc_hx->ms_od_solved.m_T_c_out - 273.15;//[C] + m_P_c_out = mpc_hx->ms_od_solved.m_P_c_out; //[kPa] + m_hx_min_dT = mpc_hx->ms_od_solved.m_min_DT; //[C] + *diff_h_c_out = (m_h_c_out - m_h_c_out_target) / m_h_c_out_target; //[kJ/kg] return 0; diff --git a/tcs/heat_exchangers.h b/tcs/heat_exchangers.h index 957b501d1..51beab3d6 100644 --- a/tcs/heat_exchangers.h +++ b/tcs/heat_exchangers.h @@ -765,7 +765,7 @@ class C_HX_htf_to_steam : public C_HX_counterflow_CRM double P_h_in /*kPa*/, double h_h_in /*kJ/kg*/, double P_h_out /*kPa*/, double m_dot_h /*kg/s*/, double od_tol /*-*/, double& q_dot /*kWt*/, double& h_c_out /*kJ/kg*/, double& h_h_out /*kJ/kg*/, double& m_dot_c /*kg/s*/, - double& tol_solved); + double& tol_solved, double& T_c_out /*C*/, double& x_c_out /**/, double& hx_min_dT /*C*/); class C_MEQ__target_cold_PH_out : public C_monotonic_equation { @@ -804,6 +804,9 @@ class C_HX_htf_to_steam : public C_HX_counterflow_CRM double m_m_dot_c; double m_q_dot; + double m_T_c_out; //[C] + double m_hx_min_dT; //[C] + virtual int operator()(double m_dot_c /*kg/s*/, double* diff_h_c_out /*C/K*/); };