From a6bbac9b55af2345322a40da3c87b3d3941e4193 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:23:32 -0600 Subject: [PATCH 01/27] Add physical heat sink model files to cmake. --- tcs/CMakeLists.txt | 8 +- tcs/csp_solver_pc_heat_sink_physical.cpp | 280 +++++++++++++++++++++++ tcs/csp_solver_pc_heat_sink_physical.h | 135 +++++++++++ 3 files changed, 420 insertions(+), 3 deletions(-) create mode 100644 tcs/csp_solver_pc_heat_sink_physical.cpp create mode 100644 tcs/csp_solver_pc_heat_sink_physical.h diff --git a/tcs/CMakeLists.txt b/tcs/CMakeLists.txt index a1f28c97d..3b05aa06d 100644 --- a/tcs/CMakeLists.txt +++ b/tcs/CMakeLists.txt @@ -17,7 +17,7 @@ set(TCS_SRC csp_solver_core.cpp csp_solver_cr_electric_resistance.cpp csp_solver_cr_heat_pump.cpp - csp_solver_fresnel_collector_receiver.cpp + csp_solver_fresnel_collector_receiver.cpp csp_solver_gen_collector_receiver.cpp csp_solver_lf_dsg_collector_receiver.cpp csp_solver_mono_eq_methods.cpp @@ -28,6 +28,7 @@ set(TCS_SRC csp_solver_packedbed_tes.cpp csp_solver_pc_gen.cpp csp_solver_pc_heat_sink.cpp + csp_solver_pc_heat_sink_physical.cpp csp_solver_pc_ptes.cpp csp_solver_pc_Rankine_indirect_224.cpp csp_solver_pc_steam_heat_sink.cpp @@ -52,7 +53,7 @@ set(TCS_SRC interconnect.cpp nlopt_callbacks.cpp numeric_solvers.cpp - ptes_solver_design_point.cpp + ptes_solver_design_point.cpp sam_type250_input_generator.cpp sco2_cycle_components.cpp sco2_partialcooling_cycle.cpp @@ -114,7 +115,7 @@ set(TCS_SRC csp_solver_core.h csp_solver_cr_electric_resistance.h csp_solver_cr_heat_pump.h - csp_solver_fresnel_collector_receiver.h + csp_solver_fresnel_collector_receiver.h csp_solver_gen_collector_receiver.h csp_solver_lf_dsg_collector_receiver.h csp_solver_mspt_collector_receiver.h @@ -124,6 +125,7 @@ set(TCS_SRC csp_solver_packedbed_tes.h csp_solver_pc_gen.h csp_solver_pc_heat_sink.h + csp_solver_pc_heat_sink_physical.h csp_solver_pc_ptes.h csp_solver_pc_Rankine_indirect_224.h csp_solver_pc_steam_heat_sink.h diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp new file mode 100644 index 000000000..3052349ba --- /dev/null +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -0,0 +1,280 @@ +/* +BSD 3-Clause License + +Copyright (c) Alliance for Sustainable Energy, LLC. See also https://github.com/NREL/ssc/blob/develop/LICENSE +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "csp_solver_pc_heat_sink_physical.h" +#include "csp_solver_core.h" + +#include "htf_props.h" + +static C_csp_reported_outputs::S_output_info S_output_info[]= +{ + {C_pc_heat_sink_physical::E_Q_DOT_HEAT_SINK, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_pc_heat_sink_physical::E_W_DOT_PUMPING, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {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}, + + csp_info_invalid +}; + +C_pc_heat_sink_physical::C_pc_heat_sink_physical() +{ + mc_reported_outputs.construct(S_output_info); + + m_max_frac = std::numeric_limits::quiet_NaN(); + + m_m_dot_htf_des = std::numeric_limits::quiet_NaN(); +} + +void C_pc_heat_sink_physical::check_double_params_are_set() +{ + if( !check_double(ms_params.m_T_htf_cold_des) ) + { + throw(C_csp_exception("The following parameter was not set prior to calling the C_pc_heat_sink init() method:", "m_W_dot_des")); + } + if( !check_double(ms_params.m_T_htf_hot_des) ) + { + throw(C_csp_exception("The following parameter was not set prior to calling the C_pc_heat_sink init() method:", "m_W_dot_des")); + } + if( !check_double(ms_params.m_q_dot_des) ) + { + throw(C_csp_exception("The following parameter was not set prior to calling the C_pc_heat_sink init() method:", "m_W_dot_des")); + } + if( !check_double(ms_params.m_htf_pump_coef) ) + { + throw(C_csp_exception("The following parameter was not set prior to calling the C_pc_heat_sink init() method:", "m_W_dot_des")); + } +} + +void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_params) +{ + check_double_params_are_set(); + + // Define initial HX parameters + //S_init_par + + // Declare instance of fluid class for FIELD fluid + if( ms_params.m_pc_fl != HTFProperties::User_defined && ms_params.m_pc_fl < HTFProperties::End_Library_Fluids ) + { + if( !mc_pc_htfProps.SetFluid(ms_params.m_pc_fl) ) + { + throw(C_csp_exception("Power cycle HTF code is not recognized", "Rankine Indirect Power Cycle Initialization")); + } + } + else if( ms_params.m_pc_fl == HTFProperties::User_defined ) + { + // Check that 'm_field_fl_props' is allocated and correct dimensions + int n_rows = (int)ms_params.m_pc_fl_props.nrows(); + int n_cols = (int)ms_params.m_pc_fl_props.ncols(); + if( n_rows > 2 && n_cols == 7 ) + { + if( !mc_pc_htfProps.SetUserDefinedFluid(ms_params.m_pc_fl_props) ) + { + std::string error_msg = util::format(mc_pc_htfProps.UserFluidErrMessage(), n_rows, n_cols); + throw(C_csp_exception(error_msg, "Heat Sink Initialization")); + } + } + else + { + std::string error_msg = util::format("The user defined field HTF table must contain at least 3 rows and exactly 7 columns. The current table contains %d row(s) and %d column(s)", n_rows, n_cols); + throw(C_csp_exception(error_msg, "Heat Sink Initialization")); + } + } + else + { + throw(C_csp_exception("Power cycle HTF code is not recognized", "Heat Sink Initialization")); + } + + // Calculate the design point HTF mass flow rate + double cp_htf_des = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des+273.15, ms_params.m_T_htf_hot_des+273.15); //[kJ/kg-K] + + m_m_dot_htf_des = ms_params.m_q_dot_des*1.E3 / (cp_htf_des*(ms_params.m_T_htf_hot_des - ms_params.m_T_htf_cold_des)); //[kg/s] + + // Set 'solved_params' structure + solved_params.m_W_dot_des = 0.0; //[MWe] Assuming heat sink is not generating electricity FOR THIS MODEL + solved_params.m_eta_des = 1.0; //[-] Same + solved_params.m_q_dot_des = ms_params.m_q_dot_des; //[MWt] + solved_params.m_q_startup = 0.0; //[MWt-hr] Assuming heat sink does not require any startup energy + + m_max_frac = ms_params.m_max_frac; //[-] + solved_params.m_max_frac = m_max_frac; //[-] For now (set in constructor), make this really large so heat sink can handle any collector-receiver output + solved_params.m_max_frac = 1.0; //[-] + + + solved_params.m_cutoff_frac = 0.0; //[-] Similarly, don't put a floor on the thermal power input + solved_params.m_sb_frac = 0.0; //[-] So, don't need standby + solved_params.m_T_htf_hot_ref = ms_params.m_T_htf_hot_des; //[C] + 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] +} + +C_csp_power_cycle::E_csp_power_cycle_modes C_pc_heat_sink_physical::get_operating_state() +{ + // Assume heat sink is always able to accept thermal power from solar field/TES + return C_csp_power_cycle::ON; +} + +double C_pc_heat_sink_physical::get_cold_startup_time() +{ + return 0.0; +} + +double C_pc_heat_sink_physical::get_warm_startup_time() +{ + return 0.0; +} + +double C_pc_heat_sink_physical::get_hot_startup_time() +{ + return 0.0; +} + +double C_pc_heat_sink_physical::get_standby_energy_requirement() +{ + return 0.0; //[MWt] +} + +double C_pc_heat_sink_physical::get_cold_startup_energy() +{ + return 0.0; //[MWh] +} + +double C_pc_heat_sink_physical::get_warm_startup_energy() +{ + return 0.0; //[MWh] +} + +double C_pc_heat_sink_physical::get_hot_startup_energy() +{ + return 0.0; //[MWh] +} + +double C_pc_heat_sink_physical::get_max_thermal_power() +{ + return m_max_frac * ms_params.m_q_dot_des; //[MWt] +} + +double C_pc_heat_sink_physical::get_min_thermal_power() +{ + return 0.0; //[MWt] +} + +void C_pc_heat_sink_physical::get_max_power_output_operation_constraints(double T_amb /*C*/, double & m_dot_HTF_ND_max, double & W_dot_ND_max) +{ + m_dot_HTF_ND_max = m_max_frac; //[-] + W_dot_ND_max = m_dot_HTF_ND_max; //[-] + + return ; //[-] +} + +double C_pc_heat_sink_physical::get_efficiency_at_TPH(double T_degC, double P_atm, double relhum_pct, double *w_dot_condenser) +{ + return 1.; +} + +double C_pc_heat_sink_physical::get_efficiency_at_load(double load_frac, double *w_dot_condenser) +{ + return 1.; +} + +double C_pc_heat_sink_physical::get_max_q_pc_startup() +{ + return 0.0; //[MWt] +} + +double C_pc_heat_sink_physical::get_htf_pumping_parasitic_coef() +{ + return ms_params.m_htf_pump_coef* (m_m_dot_htf_des) / (ms_params.m_q_dot_des*1000.0); // kWe/kWt +} + +void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather, + C_csp_solver_htf_1state &htf_state_in, + const C_csp_power_cycle::S_control_inputs &inputs, + C_csp_power_cycle::S_csp_pc_out_solver &out_solver, + const C_csp_solver_sim_info &sim_info) +{ + 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; +} + +void C_pc_heat_sink_physical::converged() +{ + // Nothing, so far, in model is time dependent + + + // But need to set final timestep outputs to reported outputs class + mc_reported_outputs.set_timestep_outputs(); + + return; +} + +void C_pc_heat_sink_physical::write_output_intervals(double report_time_start, + const std::vector & v_temp_ts_time_end, double report_time_end) +{ + mc_reported_outputs.send_to_reporting_ts_array(report_time_start, + v_temp_ts_time_end, report_time_end); +} + +void C_pc_heat_sink_physical::assign(int index, double *p_reporting_ts_array, size_t n_reporting_ts_array) +{ + mc_reported_outputs.assign(index, p_reporting_ts_array, n_reporting_ts_array); + + return; +} diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h new file mode 100644 index 000000000..33ee06964 --- /dev/null +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -0,0 +1,135 @@ +/* +BSD 3-Clause License + +Copyright (c) Alliance for Sustainable Energy, LLC. See also https://github.com/NREL/ssc/blob/develop/LICENSE +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __csp_solver_pc_heat_sink_phsyical_ +#define __csp_solver_pc_heat_sink_phsyical_ + +#include "csp_solver_core.h" +#include "csp_solver_util.h" +#include "heat_exchangers.h" + +#include "lib_util.h" +#include "htf_props.h" + +class C_pc_heat_sink_physical : public C_csp_power_cycle +{ + +public: + + enum + { + E_Q_DOT_HEAT_SINK, //[MWt] + E_W_DOT_PUMPING, //[MWe] + E_M_DOT_HTF, //[kg/s] + E_T_HTF_IN, //[C] + E_T_HTF_OUT //[C] + }; + + C_csp_reported_outputs mc_reported_outputs; + +private: + + C_HX_counterflow_CRM m_hx; + + double m_max_frac; //[-] + double m_m_dot_htf_des; //[kg/s] + + HTFProperties mc_pc_htfProps; + + void check_double_params_are_set(); + +public: + + struct S_params + { + double m_T_htf_cold_des; //[C] + double m_T_htf_hot_des; //[C] + double m_q_dot_des; //[MWt] + double m_htf_pump_coef; //[kW/kg/s] Pumping power to move 1 kg/s of HTF through power cycle + + double m_max_frac; //[-] max heat sink input (from TOD fracs) + + int m_pc_fl; //[-] integer flag identifying Heat Transfer Fluid (HTF) in power block {1-27} + util::matrix_t m_pc_fl_props; + + S_params() + { + m_T_htf_cold_des = m_T_htf_hot_des = + m_q_dot_des = m_htf_pump_coef = std::numeric_limits::quiet_NaN(); + } + }; + + S_params ms_params; + + C_pc_heat_sink_physical(); + + ~C_pc_heat_sink_physical(){}; + + virtual void init(C_csp_power_cycle::S_solved_params &solved_params); + + virtual C_csp_power_cycle::E_csp_power_cycle_modes get_operating_state(); + + virtual double get_cold_startup_time(); + virtual double get_warm_startup_time(); + virtual double get_hot_startup_time(); + virtual double get_standby_energy_requirement(); //[MW] + virtual double get_cold_startup_energy(); //[MWh] + virtual double get_warm_startup_energy(); //[MWh] + virtual double get_hot_startup_energy(); //[MWh] + virtual double get_max_thermal_power(); //MW + virtual double get_min_thermal_power(); //MW + virtual void get_max_power_output_operation_constraints(double T_amb /*C*/, double & m_dot_HTF_ND_max, double & W_dot_ND_max); //[-] Normalized over design power + virtual double get_efficiency_at_TPH(double T_degC, double P_atm, double relhum_pct, double *w_dot_condenser=0); + virtual double get_efficiency_at_load(double load_frac, double *w_dot_condenser=0); + virtual double get_htf_pumping_parasitic_coef(); //[kWe/kWt] + + // This can vary between timesteps for Type224, depending on remaining startup energy and time + virtual double get_max_q_pc_startup(); //[MWt] + + + virtual void call(const C_csp_weatherreader::S_outputs &weather, + C_csp_solver_htf_1state &htf_state_in, + const C_csp_power_cycle::S_control_inputs &inputs, + C_csp_power_cycle::S_csp_pc_out_solver &out_solver, + //C_csp_power_cycle::S_csp_pc_out_report &out_report, + const C_csp_solver_sim_info &sim_info); + + virtual void converged(); + + virtual void write_output_intervals(double report_time_start, + const std::vector & v_temp_ts_time_end, double report_time_end); + + virtual void assign(int index, double *p_reporting_ts_array, size_t n_reporting_ts_array); +}; + + +#endif // __csp_solver_pc_heat_sink_physical From ce989d8d1fcd6d7a60d73cd506a147e16dab3ed5 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:31:29 -0600 Subject: [PATCH 02/27] Implement initial heat sink physical model. --- ssc/cmod_trough_physical_iph.cpp | 70 ++++++++--- tcs/csp_solver_pc_heat_sink_physical.cpp | 143 +++++++++++++++++++---- tcs/csp_solver_pc_heat_sink_physical.h | 2 +- tcs/heat_exchangers.cpp | 134 +++++++++++++++++++++ tcs/heat_exchangers.h | 31 +++++ 5 files changed, 341 insertions(+), 39 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 0cdefa9c4..3b8f19bb7 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -42,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "csp_solver_core.h" #include "csp_solver_trough_collector_receiver.h" #include "csp_solver_pc_heat_sink.h" +#include "csp_solver_pc_heat_sink_physical.h" #include "csp_solver_two_tank_tes.h" #include "cst_iph_dispatch.h" #include "csp_solver_NTHeatTrap_tes.h" @@ -1091,7 +1092,12 @@ class cm_trough_physical_iph : public compute_module } // Heat Sink - C_pc_heat_sink c_heat_sink; + int heat_sink_type = 1; + C_csp_power_cycle* c_heat_sink_pointer; + C_pc_heat_sink c_heat_sink_simple; + C_pc_heat_sink_physical c_heat_sink_phys; + + if (heat_sink_type == 0) { size_t n_f_turbine1 = 0; ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine @@ -1100,24 +1106,60 @@ class cm_trough_physical_iph : public compute_module f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); } - c_heat_sink.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature - c_heat_sink.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature - c_heat_sink.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) + c_heat_sink_simple.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature + c_heat_sink_simple.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature + c_heat_sink_simple.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) // 9.18.2016 twn: assume for now there's no pressure drop though heat sink - c_heat_sink.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] - c_heat_sink.ms_params.m_max_frac = f_turbine_max1; + c_heat_sink_simple.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] + c_heat_sink_simple.ms_params.m_max_frac = f_turbine_max1; - c_heat_sink.ms_params.m_pc_fl = as_integer("Fluid"); - c_heat_sink.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + c_heat_sink_simple.ms_params.m_pc_fl = as_integer("Fluid"); + c_heat_sink_simple.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); // Allocate heat sink outputs - c_heat_sink.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.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.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.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.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_simple.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_simple.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_simple.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_simple.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_simple.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_pointer = &c_heat_sink_simple; } + else if (heat_sink_type == 1) + { + size_t n_f_turbine1 = 0; + ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine + double f_turbine_max1 = 1.0; + for (size_t i = 0; i < n_f_turbine1; i++) { + f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); + } + + c_heat_sink_phys.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature + c_heat_sink_phys.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature + c_heat_sink_phys.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) + // 9.18.2016 twn: assume for now there's no pressure drop though heat sink + c_heat_sink_phys.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] + c_heat_sink_phys.ms_params.m_max_frac = f_turbine_max1; + + c_heat_sink_phys.ms_params.m_pc_fl = as_integer("Fluid"); + c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + + + // 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_pointer = &c_heat_sink_phys; + } + else + { + throw exec_error("trough_physical", "heat_sink_type must be 0-1"); + } + // ******************************** // ******************************** @@ -1568,7 +1610,7 @@ class cm_trough_physical_iph : public compute_module // Instantiate Solver C_csp_solver csp_solver(weather_reader, c_trough, - c_heat_sink, + *c_heat_sink_pointer, *storage_pointer, tou, dispatch, diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 3052349ba..778fb5b0e 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -79,9 +79,6 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa { check_double_params_are_set(); - // Define initial HX parameters - //S_init_par - // Declare instance of fluid class for FIELD fluid if( ms_params.m_pc_fl != HTFProperties::User_defined && ms_params.m_pc_fl < HTFProperties::End_Library_Fluids ) { @@ -114,6 +111,38 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa throw(C_csp_exception("Power cycle HTF code is not recognized", "Heat Sink Initialization")); } + // USER INPUTS + int m_N_sub_hx = 2000; // This should be user input + double T_steam_cold = 60; //[C] User defined steam inlet temperature design + double T_steam_hot = 95; //[C] User defined steam outlet temperature design + double P_steam_cold = 100; //[kPa] User defined steam inlet pressure + double P_steam_hot = 100; //[kPa] User defined steam outlet pressure + double mdot_steam_min = 0.1; //[kg/s] + double mdot_steam_max = 10; //[kg/s] + + + // Define Heat Exchanger + NS_HX_counterflow_eqs::E_UA_target_type target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_calc_UA; + m_hx.initialize(ms_params.m_pc_fl, ms_params.m_pc_fl_props, m_N_sub_hx, target_type); + + // Define design point parameters + C_HX_counterflow_CRM::S_des_calc_UA_par des_par; + des_par.m_T_h_in = ms_params.m_T_htf_hot_des + 273.15; // [K] HTF Hot Inlet Design + des_par.m_P_h_in = 1.0; // Assuming HTF is incompressible... + des_par.m_P_h_out = 1.0; // Assuming HTF is incompressible... + des_par.m_T_c_in = T_steam_cold + 273.15; // [K] Cold Steam inlet temperature + des_par.m_P_c_in = P_steam_cold; // [kPa] Cold Steam inlet pressure + des_par.m_P_c_out = P_steam_hot; // [kPa] Cold Steam outlet pressure + des_par.m_m_dot_cold_des = std::numeric_limits::quiet_NaN(); // Steam mdot? + des_par.m_m_dot_hot_des = std::numeric_limits::quiet_NaN(); + des_par.m_eff_max = 1.0; + + // Design HX + C_HX_counterflow_CRM::S_des_solved des_solved; + m_hx.design_w_temps(des_par, ms_params.m_q_dot_des * 1e3, + ms_params.m_T_htf_cold_des + 273.15, T_steam_hot + 273.15, des_solved); + + // Calculate the design point HTF mass flow rate double cp_htf_des = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des+273.15, ms_params.m_T_htf_hot_des+273.15); //[kJ/kg-K] @@ -223,33 +252,99 @@ 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) { - 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 + // User Inputs + int m_N_sub_hx = 100; // This should be user input + double T_steam_cold = 60; //[C] User defined steam inlet temperature design + double T_steam_hot = 90; //[C] User defined steam outlet temperature design + double P_steam_cold = 100; //[kPa] User defined steam inlet pressure + double P_steam_hot = 100; //[kPa] User defined steam outlet pressure + double mdot_steam_min = 0.1; //[kg/s] + double mdot_steam_max = 10; //[kg/s] + double od_tol = 1e-3; + + // Process inputs + double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] + int standby_control = inputs.m_standby_control; //[-] 1: On, 2: Standby, 3: Off + + double q_dot, T_c_out, T_h_out, m_dot_c; + + if (inputs.m_m_dot < 1e-5 && standby_control == ON) + { + 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 = true; + return; + } + + switch (standby_control) + { + case STARTUP: + case ON: + case STANDBY: + { + try + { + m_hx.off_design_target_T_cold_out(T_steam_hot + 273.15, T_steam_cold + 273.15, P_steam_cold, P_steam_hot, + htf_state_in.m_temp + 273.15, 1.0, m_dot_htf, 1.0, od_tol, q_dot, T_c_out, T_h_out, m_dot_c); + + out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation + out_solver.m_T_htf_cold = T_h_out - 273.15; //[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; + } + catch (C_csp_exception exc) + { + double NaN = std::numeric_limits::quiet_NaN(); + 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] + + out_solver.m_was_method_successful = false; + return; + } + break; + } + case OFF: + { + q_dot = 0; + T_h_out = htf_state_in.m_temp + 273.15; + + 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 = true; + break; + } + + default: + int x = 0; + + } 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] + 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_Q_DOT_HEAT_SINK, q_dot*1e-3); //[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] + 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 - 273.15); //[C] return; } diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h index 33ee06964..9610efb41 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.h +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -58,7 +58,7 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle private: - C_HX_counterflow_CRM m_hx; + C_HX_water_to_htf m_hx; double m_max_frac; //[-] double m_m_dot_htf_des; //[kg/s] diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 0f24b8b19..1975ae358 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -2543,6 +2543,140 @@ double C_HX_counterflow_CRM::od_UA_frac(double m_dot_c /*kg/s*/, double m_dot_h return pow(m_dot_ratio, 0.8); } +void C_HX_water_to_htf::initialize(int hot_fl, util::matrix_t hot_fl_props, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) +{ + // Hard-code some of the design parameters + ms_init_par.m_N_sub_hx = N_sub_hx; //[-] + ms_init_par.m_cold_fl = NS_HX_counterflow_eqs::WATER; + + // Read-in hot side HTF props + ms_init_par.m_hot_fl = hot_fl; + ms_init_par.mc_hot_fl_props = hot_fl_props; + + m_od_UA_target_type = od_UA_target_type; + + // Set up HTFProperties for the hot fluid + if (ms_init_par.m_hot_fl != HTFProperties::User_defined && ms_init_par.m_hot_fl < HTFProperties::End_Library_Fluids) + { + if (!mc_hot_fl.SetFluid(ms_init_par.m_hot_fl, true)) + { + throw(C_csp_exception("Hot fluid code is not recognized", "C_HX_co2_to_htf::initialization")); + } + } + else if (ms_init_par.m_hot_fl == HTFProperties::User_defined) + { + int n_rows = (int)ms_init_par.mc_hot_fl_props.nrows(); + int n_cols = (int)ms_init_par.mc_hot_fl_props.ncols(); + if (n_rows > 2 && n_cols == 7) + { + if (!mc_hot_fl.SetUserDefinedFluid(ms_init_par.mc_hot_fl_props, true)) + { + std::string error_msg = util::format(mc_hot_fl.UserFluidErrMessage(), n_rows, n_cols); + throw(C_csp_exception(error_msg, "C_HX_co2_to_htf::initialization")); + } + } + else + { + std::string error_msg = util::format("The user defined hot fluid table must contain at least 3 rows and exactly 7 columns. The current table contains %d row(s) and %d column(s)", n_rows, n_cols); + throw(C_csp_exception(error_msg, "C_HX_co2_to_htf::initialization")); + } + } + else + { + throw(C_csp_exception("Hot fluid code is not recognized", "C_HX_co2_to_htf::initialization")); + } + + // Class is initialized + m_is_HX_initialized = true; + +} + +void C_HX_water_to_htf::initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) +{ + util::matrix_t null_fluid_props; + + initialize(hot_fl, null_fluid_props, N_sub_hx, od_UA_target_type); +} + +void C_HX_water_to_htf::design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& des_par, + double q_dot_design /*kWt*/, double T_h_out /*K*/, double T_c_out /*K*/, C_HX_counterflow_CRM::S_des_solved& des_solved) +{ + double T_htf_cold = T_h_out; //[C] + + double h_h_in = mc_hot_fl.enth_lookup(des_par.m_T_h_in); //[kJ/kg] + double h_h_out = mc_hot_fl.enth_lookup(T_htf_cold); //[kJ/kg] + des_par.m_m_dot_hot_des = q_dot_design / (h_h_in - h_h_out); + + water_state ms_water_props; + int prop_error_code = water_TP(des_par.m_T_c_in /*K*/, des_par.m_P_c_in /*kPa*/, &ms_water_props); + if (prop_error_code != 0) + { + throw(C_csp_exception("Hot side water/steam inlet enthalpy calculations failed", + "C_HX_counterflow::calc_max_q_dot_enth", 12)); + } + double h_steam_in = ms_water_props.enth; //[kJ/kg] + prop_error_code = water_TP(T_c_out /*K*/, des_par.m_P_c_out /*kPa*/, &ms_water_props); + if (prop_error_code != 0) + { + throw(C_csp_exception("Hot side water/steam inlet enthalpy calculations failed", + "C_HX_counterflow::calc_max_q_dot_enth", 12)); + } + double h_steam_out = ms_water_props.enth; //[kJ/kg] + des_par.m_m_dot_cold_des = q_dot_design / (h_steam_out - h_steam_in); //[kg/s] + + + + design_calc_UA(des_par, q_dot_design, des_solved); + + double T_htf_in_solve = des_par.m_T_h_in; + double T_htf_out_solve = des_solved.m_T_h_out; + double h_htf_in_solve = mc_hot_fl.enth_lookup(T_htf_in_solve); + double h_htf_out_solve = mc_hot_fl.enth_lookup(T_htf_out_solve); + double mdot_htf_solve = des_par.m_m_dot_hot_des; + double Q_htf_calc = mdot_htf_solve * (h_htf_in_solve - h_htf_out_solve); + + double T_steam_in_solve = des_par.m_T_c_in; + double T_steam_out_solve = des_solved.m_T_c_out; + prop_error_code = water_TP(T_steam_in_solve /*K*/, des_par.m_P_c_in /*kPa*/, &ms_water_props); + double h_steam_in_solve = ms_water_props.enth; + prop_error_code = water_TP(T_steam_out_solve /*K*/, des_par.m_P_c_out /*kPa*/, &ms_water_props); + double h_steam_out_solve = ms_water_props.enth; + double mdot_steam_solve = des_par.m_m_dot_cold_des; + double Q_steam_calc = mdot_steam_solve * (h_steam_out_solve - h_steam_in_solve); + + + int x = 0; + +} + +void C_HX_water_to_htf::off_design_target_T_cold_out(double T_c_out_target /*K*/, double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, + double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, + double od_tol, + double& q_dot, double& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/) +{ + double mdot_steam_max = 40; + double mdot_steam_min = 0.1; + double mdot_guess = 20; + + + double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; + double P_c_out_local, P_h_out_local; + + //off_design_solution_fixed_dP(T_c_in, P_c_in, mdot_guess, P_c_out, T_h_in, P_h_in, m_dot_h, P_h_out, od_tol, + // q_dot_local, T_c_out_local, T_h_out_local); + + off_design_solution_calc_dP(T_c_in, P_c_in, mdot_guess, T_h_in, P_h_in, m_dot_h, od_tol, + q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + + double T_c_out_calc = T_c_out_local; + + + q_dot = q_dot_local; + T_c_out = T_c_out_local; + T_h_out = T_h_out_local; + m_dot_c = mdot_guess; +} + double NS_HX_counterflow_eqs::UA_scale_vs_m_dot(double m_dot_cold_over_des /*-*/, double m_dot_hot_over_des /*-*/) { double m_dot_ratio = 0.5*(m_dot_cold_over_des + m_dot_hot_over_des); diff --git a/tcs/heat_exchangers.h b/tcs/heat_exchangers.h index 2ea3c606c..8cf5c1e76 100644 --- a/tcs/heat_exchangers.h +++ b/tcs/heat_exchangers.h @@ -724,8 +724,39 @@ class C_HX_counterflow_CRM }; +class C_HX_water_to_htf : public C_HX_counterflow_CRM +{ +private: + + + +public: + + C_HX_water_to_htf() + { + m_cost_model = C_HX_counterflow_CRM::E_CARLSON_17_PHX; + //m_od_solution_type = C_HX_counterflow_CRM::C_od_thermal_solution_type::E_CRM_UA_PER_NODE; + + m_od_solution_type = C_HX_counterflow_CRM::C_od_thermal_solution_type::E_DEFAULT; + } + + // This method calculates the flow rates and UA given all 4 temperatures and heat exchange + void design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& des_par, + double q_dot_design /*kWt*/, double T_h_out /*K*/, double T_c_out /*K*/, C_HX_counterflow_CRM::S_des_solved& des_solved); + + virtual void initialize(int hot_fl, util::matrix_t hot_fl_props, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type); + + virtual void initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type); + + void off_design_target_T_cold_out(double T_c_out_target /*K*/, double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, + double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, + double od_tol /*-*/, + double& q_dot /*kWt*/, double& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/); + +}; + class C_HX_co2_to_htf : public C_HX_counterflow_CRM { private: From bf35e9b9de3fc9c57f5de09bfe925808aa640b47 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:50:49 -0600 Subject: [PATCH 03/27] Move heat sink inputs into cmod. Begin implementing mdot solver. --- ssc/cmod_trough_physical_iph.cpp | 9 ++++ tcs/csp_solver_pc_heat_sink_physical.cpp | 64 +++++++++++++----------- tcs/csp_solver_pc_heat_sink_physical.h | 23 ++++++++- tcs/heat_exchangers.cpp | 39 ++++++++++++--- tcs/heat_exchangers.h | 38 +++++++++++++- 5 files changed, 135 insertions(+), 38 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 3b8f19bb7..2dac5b521 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -1145,6 +1145,15 @@ class cm_trough_physical_iph : public compute_module c_heat_sink_phys.ms_params.m_pc_fl = as_integer("Fluid"); c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + + c_heat_sink_phys.ms_params.m_T_ext_cold_des = 60; //[C] External fluid inlet temp + c_heat_sink_phys.ms_params.m_T_ext_hot_des = 95; //[C] External fluid hot temp target + c_heat_sink_phys.ms_params.m_P_ext_cold_des = 100; //[kPa] Ext fluid inlet pressure + c_heat_sink_phys.ms_params.m_P_ext_hot_des = 100; //[kPa] Ext fluid outlet pressure + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.2; //[] Min fraction ext fluid mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; //[] Max fraction ext fluid mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = 20000; //[] Number HX nodes + c_heat_sink_phys.ms_params.m_od_tol = 1e-3; //[] 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); diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 778fb5b0e..27e0cf87d 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -112,41 +112,42 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa } // USER INPUTS - int m_N_sub_hx = 2000; // This should be user input - double T_steam_cold = 60; //[C] User defined steam inlet temperature design - double T_steam_hot = 95; //[C] User defined steam outlet temperature design - double P_steam_cold = 100; //[kPa] User defined steam inlet pressure - double P_steam_hot = 100; //[kPa] User defined steam outlet pressure - double mdot_steam_min = 0.1; //[kg/s] - double mdot_steam_max = 10; //[kg/s] - + //int m_N_sub_hx = 2000; // This should be user input + //double T_steam_cold = 60; //[C] User defined steam inlet temperature design + //double T_steam_hot = 95; //[C] User defined steam outlet temperature design + //double P_steam_cold = 100; //[kPa] User defined steam inlet pressure + //double P_steam_hot = 100; //[kPa] User defined steam outlet pressure + //double mdot_steam_min = 0.1; //[kg/s] + //double mdot_steam_max = 10; //[kg/s] // Define Heat Exchanger NS_HX_counterflow_eqs::E_UA_target_type target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_calc_UA; - m_hx.initialize(ms_params.m_pc_fl, ms_params.m_pc_fl_props, m_N_sub_hx, target_type); + this->m_hx.initialize(ms_params.m_pc_fl, ms_params.m_pc_fl_props, ms_params.m_N_sub_hx, target_type); // Define design point parameters C_HX_counterflow_CRM::S_des_calc_UA_par des_par; des_par.m_T_h_in = ms_params.m_T_htf_hot_des + 273.15; // [K] HTF Hot Inlet Design des_par.m_P_h_in = 1.0; // Assuming HTF is incompressible... des_par.m_P_h_out = 1.0; // Assuming HTF is incompressible... - des_par.m_T_c_in = T_steam_cold + 273.15; // [K] Cold Steam inlet temperature - des_par.m_P_c_in = P_steam_cold; // [kPa] Cold Steam inlet pressure - des_par.m_P_c_out = P_steam_hot; // [kPa] Cold Steam outlet pressure - des_par.m_m_dot_cold_des = std::numeric_limits::quiet_NaN(); // Steam mdot? + des_par.m_T_c_in = ms_params.m_T_ext_cold_des + 273.15; // [K] Cold Steam inlet temperature + des_par.m_P_c_in = ms_params.m_P_ext_cold_des; // [kPa] Cold Steam inlet pressure + des_par.m_P_c_out = ms_params.m_P_ext_hot_des; // [kPa] Cold Steam outlet pressure + des_par.m_m_dot_cold_des = std::numeric_limits::quiet_NaN(); des_par.m_m_dot_hot_des = std::numeric_limits::quiet_NaN(); des_par.m_eff_max = 1.0; // Design HX C_HX_counterflow_CRM::S_des_solved des_solved; - m_hx.design_w_temps(des_par, ms_params.m_q_dot_des * 1e3, - ms_params.m_T_htf_cold_des + 273.15, T_steam_hot + 273.15, des_solved); - + this->m_hx.design_w_temps(des_par, ms_params.m_q_dot_des * 1e3, ms_params.m_T_htf_cold_des + 273.15, + ms_params.m_T_ext_hot_des + 273.15, des_solved); - // Calculate the design point HTF mass flow rate - double cp_htf_des = mc_pc_htfProps.Cp_ave(ms_params.m_T_htf_cold_des+273.15, ms_params.m_T_htf_hot_des+273.15); //[kJ/kg-K] + // Assign Design External mdot + this->m_m_dot_ext_des = des_par.m_m_dot_cold_des; //[kg/s] + this->m_m_dot_ext_min = this->m_m_dot_ext_des * ms_params.m_f_m_dot_ext_min; //[kg/s] + this->m_m_dot_ext_max = this->m_m_dot_ext_des * ms_params.m_f_m_dot_ext_max; //[kg/s] - m_m_dot_htf_des = ms_params.m_q_dot_des*1.E3 / (cp_htf_des*(ms_params.m_T_htf_hot_des - ms_params.m_T_htf_cold_des)); //[kg/s] + // Assign Design HTF mdot + this->m_m_dot_htf_des = des_par.m_m_dot_hot_des; //[kg/s] // Set 'solved_params' structure solved_params.m_W_dot_des = 0.0; //[MWe] Assuming heat sink is not generating electricity FOR THIS MODEL @@ -154,7 +155,7 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa solved_params.m_q_dot_des = ms_params.m_q_dot_des; //[MWt] solved_params.m_q_startup = 0.0; //[MWt-hr] Assuming heat sink does not require any startup energy - m_max_frac = ms_params.m_max_frac; //[-] + this->m_max_frac = ms_params.m_max_frac; //[-] solved_params.m_max_frac = m_max_frac; //[-] For now (set in constructor), make this really large so heat sink can handle any collector-receiver output solved_params.m_max_frac = 1.0; //[-] @@ -253,14 +254,14 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather const C_csp_solver_sim_info &sim_info) { // User Inputs - int m_N_sub_hx = 100; // This should be user input - double T_steam_cold = 60; //[C] User defined steam inlet temperature design - double T_steam_hot = 90; //[C] User defined steam outlet temperature design - double P_steam_cold = 100; //[kPa] User defined steam inlet pressure - double P_steam_hot = 100; //[kPa] User defined steam outlet pressure - double mdot_steam_min = 0.1; //[kg/s] - double mdot_steam_max = 10; //[kg/s] - double od_tol = 1e-3; + //int m_N_sub_hx = 100; // This should be user input + //double T_steam_cold = 60; //[C] User defined steam inlet temperature design + //double T_steam_hot = 90; //[C] User defined steam outlet temperature design + //double P_steam_cold = 100; //[kPa] User defined steam inlet pressure + //double P_steam_hot = 100; //[kPa] User defined steam outlet pressure + //double mdot_steam_min = 0.1; //[kg/s] + //double mdot_steam_max = 10; //[kg/s] + //double od_tol = 1e-3; // Process inputs double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] @@ -289,8 +290,11 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather { try { - m_hx.off_design_target_T_cold_out(T_steam_hot + 273.15, T_steam_cold + 273.15, P_steam_cold, P_steam_hot, - htf_state_in.m_temp + 273.15, 1.0, m_dot_htf, 1.0, od_tol, q_dot, T_c_out, T_h_out, m_dot_c); + m_hx.off_design_target_T_cold_out(ms_params.m_T_ext_hot_des + 273.15, + m_m_dot_ext_min, m_m_dot_ext_max, + ms_params.m_T_ext_cold_des + 273.15, + ms_params.m_P_ext_cold_des, ms_params.m_P_ext_hot_des, + htf_state_in.m_temp + 273.15, 1.0, m_dot_htf, 1.0, ms_params.m_od_tol, q_dot, T_c_out, T_h_out, m_dot_c); out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation out_solver.m_T_htf_cold = T_h_out - 273.15; //[C] diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h index 9610efb41..9bc874c44 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.h +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -62,6 +62,9 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle double m_max_frac; //[-] double m_m_dot_htf_des; //[kg/s] + double m_m_dot_ext_des; //[kg/s] External fluid design mdot + double m_m_dot_ext_min; //[kg/s] Min ext fluid mdot + double m_m_dot_ext_max; //[kg/s] Max ext fluid mdot HTFProperties mc_pc_htfProps; @@ -71,6 +74,7 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle struct S_params { + // Inputs double m_T_htf_cold_des; //[C] double m_T_htf_hot_des; //[C] double m_q_dot_des; //[MWt] @@ -81,10 +85,27 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle int m_pc_fl; //[-] integer flag identifying Heat Transfer Fluid (HTF) in power block {1-27} util::matrix_t m_pc_fl_props; + double m_T_ext_cold_des; //[C] External fluid inlet temperature (constant) + double m_T_ext_hot_des; //[C] External fluid outlet temperature (hot target) + double m_P_ext_cold_des; //[kPa] External fluid inlet pressure (constant) + double m_P_ext_hot_des; //[kPa] External fluid outlet pressure + double m_f_m_dot_ext_min; //[kg/s] Minimum fraction external fluid mass flow rate of design + double m_f_m_dot_ext_max; //[kg/s] Maximum fraction external fluid mass flow rate of design + + int m_N_sub_hx; //[] Number of HX nodes + double m_od_tol; //[] HX Off design tolerance + S_params() { m_T_htf_cold_des = m_T_htf_hot_des = - m_q_dot_des = m_htf_pump_coef = std::numeric_limits::quiet_NaN(); + m_q_dot_des = m_htf_pump_coef = + m_max_frac = m_T_ext_cold_des = m_T_ext_hot_des = + m_P_ext_cold_des = m_P_ext_hot_des = + m_f_m_dot_ext_min = m_f_m_dot_ext_max = m_od_tol = + std::numeric_limits::quiet_NaN(); + + m_pc_fl = -1; + m_N_sub_hx = -1; } }; diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 1975ae358..406f60222 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -2649,26 +2649,38 @@ void C_HX_water_to_htf::design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& } -void C_HX_water_to_htf::off_design_target_T_cold_out(double T_c_out_target /*K*/, double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, +void C_HX_water_to_htf::off_design_target_T_cold_out(double T_c_out_target /*K*/, + double m_dot_c_min /*kg/s*/, double m_dot_c_max /*kg/s*/, + double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, double od_tol, double& q_dot, double& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/) { - double mdot_steam_max = 40; - double mdot_steam_min = 0.1; double mdot_guess = 20; double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; double P_c_out_local, P_h_out_local; + + C_MEQ__target_T_c_out T_out_eq(this, T_c_out_target, T_c_in, P_c_in, T_h_in, P_h_in, m_dot_h, od_tol); + C_monotonic_eq_solver T_out_solver(T_out_eq); + + double solver_tol = 1e-3; + T_out_solver.settings(solver_tol, 1000, m_dot_c_min, m_dot_c_max, false); + + double m_dot_c_solved, tol_solved; + int iter_solved = -1; + + int m_dot_out_code = T_out_solver.solve(m_dot_c_min, m_dot_c_max, 0, m_dot_c_solved, tol_solved, iter_solved); + //off_design_solution_fixed_dP(T_c_in, P_c_in, mdot_guess, P_c_out, T_h_in, P_h_in, m_dot_h, P_h_out, od_tol, // q_dot_local, T_c_out_local, T_h_out_local); - off_design_solution_calc_dP(T_c_in, P_c_in, mdot_guess, T_h_in, P_h_in, m_dot_h, od_tol, - q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + //off_design_solution_calc_dP(T_c_in, P_c_in, mdot_guess, T_h_in, P_h_in, m_dot_h, od_tol, + // q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + - double T_c_out_calc = T_c_out_local; q_dot = q_dot_local; @@ -2677,6 +2689,20 @@ void C_HX_water_to_htf::off_design_target_T_cold_out(double T_c_out_target /*K*/ m_dot_c = mdot_guess; } +int C_HX_water_to_htf::C_MEQ__target_T_c_out::operator()(double m_dot_c /*kg/s*/, double* diff_T_c_out /*C/K*/) +{ + double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; + double P_c_out_local, P_h_out_local; + + mpc_hx->off_design_solution_calc_dP(m_T_c_in, m_P_c_in, m_dot_c, m_T_h_in, m_P_h_in, m_m_dot_h, m_tol, + q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + + *diff_T_c_out = T_c_out_local - m_T_c_out_target; //[C/K] + + return 0; +} + + double NS_HX_counterflow_eqs::UA_scale_vs_m_dot(double m_dot_cold_over_des /*-*/, double m_dot_hot_over_des /*-*/) { double m_dot_ratio = 0.5*(m_dot_cold_over_des + m_dot_hot_over_des); @@ -4213,3 +4239,4 @@ double C_CO2_to_air_cooler::air_pressure(double elevation /*m*/) // http://www.engineeringtoolbox.com/air-altitude-pressure-d_462.html return 101325.0*pow(1 - 2.25577E-5*elevation, 5.25588); //[Pa] } + diff --git a/tcs/heat_exchangers.h b/tcs/heat_exchangers.h index 8cf5c1e76..792beba61 100644 --- a/tcs/heat_exchangers.h +++ b/tcs/heat_exchangers.h @@ -750,11 +750,47 @@ class C_HX_water_to_htf : public C_HX_counterflow_CRM virtual void initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type); - void off_design_target_T_cold_out(double T_c_out_target /*K*/, double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, + void off_design_target_T_cold_out(double T_c_out_target /*K*/, + double m_dot_c_min /*kg/s*/, double m_dot_c_max /*kg/s*/, + double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, double od_tol /*-*/, double& q_dot /*kWt*/, double& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/); + class C_MEQ__target_T_c_out : public C_monotonic_equation + { + private: + C_HX_counterflow_CRM* mpc_hx; + + double m_T_c_out_target; //[K] + double m_T_c_in; //[K] + double m_P_c_in; //[kPa] + double m_T_h_in; //[K] + double m_P_h_in; //[kPa] + double m_m_dot_h; //[kg/s] + + double m_tol; //[-] + + public: + + C_MEQ__target_T_c_out(C_HX_counterflow_CRM* pc_hx, + double T_c_out_target, + double T_c_in, double P_c_in, + double T_h_in, double P_h_in, + double m_dot_h, + double tol) + : m_T_c_out_target(T_c_out_target), + m_T_c_in(T_c_in), m_P_c_in(P_c_in), + m_T_h_in(T_h_in), m_P_h_in(P_h_in), + m_m_dot_h(m_dot_h), m_tol(tol) + { + mpc_hx = pc_hx; + } + + virtual int operator()(double m_dot_c /*kg/s*/, double* diff_T_c_out /*C/K*/); + + }; + }; class C_HX_co2_to_htf : public C_HX_counterflow_CRM From 7d184f826b9001cb63363e121d9d3a0d6ceecaa3 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Thu, 21 Nov 2024 15:08:45 -0700 Subject: [PATCH 04/27] Add initial physical heat sink model draft. --- ssc/CMakeLists.txt | 1 + ssc/cmod_csp_heatsink.cpp | 121 +++++ ssc/cmod_trough_physical_iph.cpp | 18 +- ssc/sscapi.cpp | 4 +- tcs/csp_solver_pc_heat_sink_physical.cpp | 238 ++++++--- tcs/csp_solver_pc_heat_sink_physical.h | 9 +- tcs/heat_exchangers.cpp | 639 +++++++++++++++++++++-- tcs/heat_exchangers.h | 74 ++- 8 files changed, 963 insertions(+), 141 deletions(-) create mode 100644 ssc/cmod_csp_heatsink.cpp diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index f688508eb..9e1f9463e 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -30,6 +30,7 @@ set(SSC_SRC cmod_csp_common_eqns.cpp cmod_csp_common_eqns.h cmod_csp_dsg_lf_ui.cpp + cmod_csp_heatsink.cpp cmod_csp_subcomponent.cpp cmod_csp_trough_eqns.cpp cmod_csp_trough_eqns.h diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp new file mode 100644 index 000000000..bf1bd817b --- /dev/null +++ b/ssc/cmod_csp_heatsink.cpp @@ -0,0 +1,121 @@ +/* +BSD 3-Clause License + +Copyright (c) Alliance for Sustainable Energy, LLC. See also https://github.com/NREL/ssc/blob/develop/LICENSE +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "core.h" +#include "water_properties.h" +#include "heat_exchangers.h" + +static var_info _cm_vtab_csp_heatsink[] = { + /* VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS*/ + // Inputs + { SSC_INPUT, SSC_NUMBER, "t_step", "Timestep duration", "s", "", "system", "*", "", "" }, + + var_info_invalid }; + +class cm_csp_heatsink : public compute_module +{ +public: + cm_csp_heatsink() + { + add_var_info(_cm_vtab_csp_heatsink); + } + + void exec() + { + + double dT_subcool = 30; //[dC] Steam temp diff below outlet + + // Define Outlet Steam Conditions + water_state ms_water_props; + double Q_ext_hot = 0.75; // Outlet Steam Quality + double T_ext_hot = 150; // [C] Outlet Steam Temp + + int prop_error_code = water_TQ(T_ext_hot + 273.15, Q_ext_hot, &ms_water_props); + + double P_ext_hot = ms_water_props.pres; // [kPa] Outlet Steam Pressure + double h_ext_hot = ms_water_props.enth; // [kJ/kg] Outlet Steam Enthalpy + double dens_ext_hot = ms_water_props.dens; //[kg/m3] Outlet steam density + double k_ext_hot = water_cond(dens_ext_hot, T_ext_hot + 273.15); // [W/m-K] + double mu_ext_hot = water_visc(dens_ext_hot, T_ext_hot + 273.15); // [uPa-s] + + // Define Inlet Steam Conditions + double T_ext_cold = T_ext_hot - dT_subcool; // [C] Inlet water temp + double P_ext_cold = P_ext_hot; // [kPa] Inlet Steam Pressure + + prop_error_code = water_TP(T_ext_cold + 273.15, P_ext_cold, &ms_water_props); + + double h_ext_cold = ms_water_props.enth; // [kJ/kg] Inlet water enthalpy + double Q_ext_cold = ms_water_props.qual; // [] Inlet water quality (should be 0, n/a) + double dens_ext_cold = ms_water_props.dens; // [kg/m3] Inlet water density + double k_ext_cold = water_cond(dens_ext_cold, T_ext_cold + 273.15); // [W/m-K] + double mu_ext_cold = water_visc(dens_ext_cold, T_ext_cold + 273.15); // [uPa-s] + + // Initialize + C_HX_htf_to_steam m_hx; + int hot_fl = 21; // HTF fl id + int N_sub_hx = 5000; + NS_HX_counterflow_eqs::E_UA_target_type od_target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_constant_UA; + m_hx.initialize(hot_fl, N_sub_hx, od_target_type); + + // Design + double T_htf_hot = 250; //[C] + double T_htf_cold = 200; //[C] + double q_design = 5.19; //[MW] + C_HX_counterflow_CRM::S_des_solved des_solved; + m_hx.design_w_TP_PH(T_htf_hot + 273.15, 1.0, T_htf_cold + 273.15, 1.0, + P_ext_cold, h_ext_cold, P_ext_hot, h_ext_hot, q_design * 1e3, des_solved); + + // Off Design + double T_htf_hot_od = 250; + double T_htf_cold_od = 200; + double h_htf_hot_od = m_hx.mc_hot_fl.enth(T_htf_hot_od + 273.15) * 1e-3; //[kJ/kg] + double h_htf_cold_od = m_hx.mc_hot_fl.enth(T_htf_cold_od + 273.15) * 1e-3; //[kJ/kg] + + double q_dot_calc, h_ext_out_calc, h_htf_out_calc; + + m_hx.off_design_solution_fixed_dP_enth(h_ext_cold, P_ext_cold, m_hx.ms_des_calc_UA_par.m_m_dot_cold_des, P_ext_hot, + h_htf_hot_od, 1.0, m_hx.ms_des_calc_UA_par.m_m_dot_hot_des, 1.0, 1e-12, + q_dot_calc, h_ext_out_calc, h_htf_out_calc); + + double mdot_ext_calc; + + m_hx.off_design_target_cold_PH_out(h_ext_hot, 0.1, 2* m_hx.ms_des_calc_UA_par.m_m_dot_cold_des, P_ext_cold, h_ext_cold, + P_ext_hot, 1.0, h_htf_hot_od, 1.0, m_hx.ms_des_calc_UA_par.m_m_dot_hot_des, 1e-3, + q_dot_calc, h_ext_out_calc, h_htf_out_calc, mdot_ext_calc); + + + int x = 0; + } +}; + +DEFINE_MODULE_ENTRY(csp_heatsink, "CSP heat sink", 1) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index c8a390da2..cdafa6490 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -1147,7 +1147,7 @@ class cm_trough_physical_iph : public compute_module } // Heat Sink - int heat_sink_type = 1; + int heat_sink_type = 0; C_csp_power_cycle* c_heat_sink_pointer; C_pc_heat_sink c_heat_sink_simple; C_pc_heat_sink_physical c_heat_sink_phys; @@ -1200,14 +1200,14 @@ class cm_trough_physical_iph : public compute_module c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); - c_heat_sink_phys.ms_params.m_T_ext_cold_des = 60; //[C] External fluid inlet temp - c_heat_sink_phys.ms_params.m_T_ext_hot_des = 95; //[C] External fluid hot temp target - c_heat_sink_phys.ms_params.m_P_ext_cold_des = 100; //[kPa] Ext fluid inlet pressure - c_heat_sink_phys.ms_params.m_P_ext_hot_des = 100; //[kPa] Ext fluid outlet pressure - c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.2; //[] Min fraction ext fluid mass flow rate - c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; //[] Max fraction ext fluid mass flow rate - c_heat_sink_phys.ms_params.m_N_sub_hx = 20000; //[] Number HX nodes - c_heat_sink_phys.ms_params.m_od_tol = 1e-3; //[] HX off design tolerance + c_heat_sink_phys.ms_params.m_T_ext_cold_des = 120; //[C] Steam fluid inlet temp + c_heat_sink_phys.ms_params.m_Q_ext_hot_des = 0.75; //[C] Steam quality target + c_heat_sink_phys.ms_params.m_P_ext_cold_des = 476.1645; //[kPa] Steam inlet pressure + c_heat_sink_phys.ms_params.m_P_ext_hot_des = 476.1645; //[kPa] Steam outlet pressure + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.2; //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = 50000; //[] Number HX nodes + c_heat_sink_phys.ms_params.m_od_tol = 1e-1; //[] 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); diff --git a/ssc/sscapi.cpp b/ssc/sscapi.cpp index 12386e71d..03533a15e 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -165,7 +165,8 @@ extern module_entry_info cm_entry_battery_stateful, cm_entry_csp_subcomponent, cm_entry_hybrid_steps, - cm_entry_hybrid + cm_entry_hybrid, + cm_entry_csp_heatsink ; /* official module table */ @@ -269,6 +270,7 @@ static module_entry_info *module_table[] = { &cm_entry_csp_subcomponent, &cm_entry_hybrid_steps, &cm_entry_hybrid, + &cm_entry_csp_heatsink, 0 }; extern var_info vtab_oandm[]; diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 27e0cf87d..7a0a69ba4 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -111,43 +111,42 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa throw(C_csp_exception("Power cycle HTF code is not recognized", "Heat Sink Initialization")); } - // USER INPUTS - //int m_N_sub_hx = 2000; // This should be user input - //double T_steam_cold = 60; //[C] User defined steam inlet temperature design - //double T_steam_hot = 95; //[C] User defined steam outlet temperature design - //double P_steam_cold = 100; //[kPa] User defined steam inlet pressure - //double P_steam_hot = 100; //[kPa] User defined steam outlet pressure - //double mdot_steam_min = 0.1; //[kg/s] - //double mdot_steam_max = 10; //[kg/s] - // Define Heat Exchanger - NS_HX_counterflow_eqs::E_UA_target_type target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_calc_UA; + NS_HX_counterflow_eqs::E_UA_target_type target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_constant_UA; // Required for two phase steam outlet this->m_hx.initialize(ms_params.m_pc_fl, ms_params.m_pc_fl_props, ms_params.m_N_sub_hx, target_type); - // Define design point parameters - C_HX_counterflow_CRM::S_des_calc_UA_par des_par; - des_par.m_T_h_in = ms_params.m_T_htf_hot_des + 273.15; // [K] HTF Hot Inlet Design - des_par.m_P_h_in = 1.0; // Assuming HTF is incompressible... - des_par.m_P_h_out = 1.0; // Assuming HTF is incompressible... - des_par.m_T_c_in = ms_params.m_T_ext_cold_des + 273.15; // [K] Cold Steam inlet temperature - des_par.m_P_c_in = ms_params.m_P_ext_cold_des; // [kPa] Cold Steam inlet pressure - des_par.m_P_c_out = ms_params.m_P_ext_hot_des; // [kPa] Cold Steam outlet pressure - des_par.m_m_dot_cold_des = std::numeric_limits::quiet_NaN(); - des_par.m_m_dot_hot_des = std::numeric_limits::quiet_NaN(); - des_par.m_eff_max = 1.0; + // Get Steam Inlet Enthalpy (inputs are required to be subcooled) + water_state water_props; + int prop_error_code = water_TP(ms_params.m_T_ext_cold_des + 273.15, ms_params.m_P_ext_cold_des, &water_props); + if (prop_error_code != 0) + { + throw(C_csp_exception("Inlet water properties failed", + "Get Subcooled Enthalpy")); + } + m_h_ext_cold_des = water_props.enth; //[kJ/kg] Inlet steam enthalpy + + // Get Steam Target Enthalpy (inputs are required to be in two phase) + prop_error_code = water_PQ(ms_params.m_P_ext_cold_des, ms_params.m_Q_ext_hot_des, &water_props); + if (prop_error_code != 0) + { + throw(C_csp_exception("Water properties in two phase region failed", + "Get Target Enthalpy")); + } + m_h_ext_hot_des = water_props.enth; //[kJ/kg] Target enthalpy // Design HX C_HX_counterflow_CRM::S_des_solved des_solved; - this->m_hx.design_w_temps(des_par, ms_params.m_q_dot_des * 1e3, ms_params.m_T_htf_cold_des + 273.15, - ms_params.m_T_ext_hot_des + 273.15, 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); // Assign Design External mdot - this->m_m_dot_ext_des = des_par.m_m_dot_cold_des; //[kg/s] - this->m_m_dot_ext_min = this->m_m_dot_ext_des * ms_params.m_f_m_dot_ext_min; //[kg/s] - this->m_m_dot_ext_max = this->m_m_dot_ext_des * ms_params.m_f_m_dot_ext_max; //[kg/s] + 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] // Assign Design HTF mdot - this->m_m_dot_htf_des = des_par.m_m_dot_hot_des; //[kg/s] + m_m_dot_htf_des = m_hx.ms_des_calc_UA_par.m_m_dot_hot_des; //[kg/s] // Set 'solved_params' structure solved_params.m_W_dot_des = 0.0; //[MWe] Assuming heat sink is not generating electricity FOR THIS MODEL @@ -253,30 +252,91 @@ 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) { - // User Inputs - //int m_N_sub_hx = 100; // This should be user input - //double T_steam_cold = 60; //[C] User defined steam inlet temperature design - //double T_steam_hot = 90; //[C] User defined steam outlet temperature design - //double P_steam_cold = 100; //[kPa] User defined steam inlet pressure - //double P_steam_hot = 100; //[kPa] User defined steam outlet pressure - //double mdot_steam_min = 0.1; //[kg/s] - //double mdot_steam_max = 10; //[kg/s] - //double od_tol = 1e-3; + // 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; + } + + + + // Process inputs double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] int standby_control = inputs.m_standby_control; //[-] 1: On, 2: Standby, 3: Off - double q_dot, T_c_out, T_h_out, m_dot_c; + double q_dot, T_c_out, T_h_out_C, m_dot_c; + // Handle no mass flow coming in if (inputs.m_m_dot < 1e-5 && standby_control == ON) { - out_solver.m_P_cycle = 0; //[MWe] No electricity generation + // 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 form HTF - out_solver.m_W_dot_elec_parasitics_tot = 0; //[MWe] + 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; @@ -290,19 +350,48 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather { try { - m_hx.off_design_target_T_cold_out(ms_params.m_T_ext_hot_des + 273.15, - m_m_dot_ext_min, m_m_dot_ext_max, - ms_params.m_T_ext_cold_des + 273.15, - ms_params.m_P_ext_cold_des, ms_params.m_P_ext_hot_des, - htf_state_in.m_temp + 273.15, 1.0, m_dot_htf, 1.0, ms_params.m_od_tol, q_dot, T_c_out, T_h_out, m_dot_c); - - out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation - out_solver.m_T_htf_cold = T_h_out - 273.15; //[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; + // Get HTF inlet enthalpy + double h_htf_cold = 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 = 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_cold, 1.0, m_dot_htf, ms_params.m_od_tol, + q_dot, h_ext_out_calc, h_htf_out_calc, m_dot_c); + + if (solve_code == C_monotonic_eq_solver::CONVERGED) + { + // Solved succesfully + + // 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 + { + // 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 = true; + break; + } } catch (C_csp_exception exc) { @@ -321,8 +410,41 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather } 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 = htf_state_in.m_temp + 273.15; + 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] @@ -334,10 +456,8 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather out_solver.m_was_method_successful = true; break; } - default: int x = 0; - } double W_dot_cooling_parasitic = 0.0; //[MWe] No cooling load @@ -348,7 +468,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather 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, htf_state_in.m_temp); //[C] - mc_reported_outputs.value(E_T_HTF_OUT, T_h_out - 273.15); //[C] + mc_reported_outputs.value(E_T_HTF_OUT, T_h_out_C); //[C] return; } diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h index 9bc874c44..8ffd907a5 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.h +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -58,13 +58,15 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle private: - C_HX_water_to_htf m_hx; + C_HX_htf_to_steam m_hx; double m_max_frac; //[-] double m_m_dot_htf_des; //[kg/s] double m_m_dot_ext_des; //[kg/s] External fluid design mdot double m_m_dot_ext_min; //[kg/s] Min ext fluid mdot 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 HTFProperties mc_pc_htfProps; @@ -86,7 +88,8 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle util::matrix_t m_pc_fl_props; double m_T_ext_cold_des; //[C] External fluid inlet temperature (constant) - double m_T_ext_hot_des; //[C] External fluid outlet temperature (hot target) + double m_Q_ext_hot_des; //[] External fluid target outlet quality + //double m_T_ext_hot_des; //[C] External fluid outlet temperature (hot target) double m_P_ext_cold_des; //[kPa] External fluid inlet pressure (constant) double m_P_ext_hot_des; //[kPa] External fluid outlet pressure double m_f_m_dot_ext_min; //[kg/s] Minimum fraction external fluid mass flow rate of design @@ -99,7 +102,7 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle { m_T_htf_cold_des = m_T_htf_hot_des = m_q_dot_des = m_htf_pump_coef = - m_max_frac = m_T_ext_cold_des = m_T_ext_hot_des = + m_max_frac = m_T_ext_cold_des = m_Q_ext_hot_des = m_P_ext_cold_des = m_P_ext_hot_des = m_f_m_dot_ext_min = m_f_m_dot_ext_max = m_od_tol = std::numeric_limits::quiet_NaN(); diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 406f60222..8e0637263 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -1870,6 +1870,128 @@ void C_HX_counterflow_CRM::design_calc_UA(C_HX_counterflow_CRM::S_des_calc_UA_pa return; } +void C_HX_counterflow_CRM::design_calc_UA_TP_to_PH(C_HX_counterflow_CRM::S_des_calc_UA_par des_par, + double h_h_in /*kJ/kg*/, + double h_c_in /*kJ/kg*/, double h_c_out /*kJ/kg*/, + double q_dot_design /*kWt*/, + C_HX_counterflow_CRM::S_des_solved& des_solved) +{ + /*Designs heat exchanger given its mass flow rates, inlet temperatures, and a heat transfer rate. + Note: the heat transfer rate must be positive.*/ + ms_des_calc_UA_par = des_par; + ms_des_solved.m_DP_cold_des = des_par.m_P_c_in - des_par.m_P_c_out; //[kPa] + ms_des_solved.m_DP_hot_des = des_par.m_P_h_in - des_par.m_P_h_out; //[kPa] + + // Trying to solve design point, so set boolean to false until method solves successfully + m_is_HX_designed = false; + + // Check that design parameters are set + if (!m_is_HX_initialized) + { + throw(C_csp_exception("Design parameters are not initialized!", "C_HX_counterflow::design")); + } + + double UA_calc, min_DT_calc, eff_calc, NTU_calc, T_h_out_calc, T_c_out_calc, q_dot_calc; + UA_calc = min_DT_calc = eff_calc = NTU_calc = T_h_out_calc = T_c_out_calc = q_dot_calc = std::numeric_limits::quiet_NaN(); + + /*NS_HX_counterflow_eqs::calc_req_UA(ms_init_par.m_hot_fl, mc_hot_fl, + ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot_design, ms_des_calc_UA_par.m_m_dot_cold_des, ms_des_calc_UA_par.m_m_dot_hot_des, + ms_des_calc_UA_par.m_T_c_in, ms_des_calc_UA_par.m_T_h_in, ms_des_calc_UA_par.m_P_c_in, ms_des_calc_UA_par.m_P_c_out, ms_des_calc_UA_par.m_P_h_in, ms_des_calc_UA_par.m_P_h_out, + UA_calc, min_DT_calc, eff_calc, NTU_calc, T_h_out_calc, T_c_out_calc, q_dot_calc, + mv_s_node_info_des);*/ + + // Check inputs + { + if (q_dot_design < 0.0) + { + throw(C_csp_exception("Input heat transfer rate is less than 0.0. It must be >= 0.0", "C_HX_counterflow::design", 4)); + } + if (des_par.m_m_dot_cold_des < 1.E-14) + { + throw(C_csp_exception("The cold mass flow rate must be a positive value", "C_HX_counterflow::design")); + } + if (des_par.m_m_dot_hot_des < 1.E-14) + { + throw(C_csp_exception("The hot mass flow rate must be a positive value", "C_HX_counterflow::design")); + } + if (des_par.m_T_h_in < des_par.m_T_c_in) + { + throw(C_csp_exception("Inlet hot temperature is colder than the cold inlet temperature", "C_HX_counterflow::design", 5)); + } + if (des_par.m_P_h_in < des_par.m_P_h_out) + { + throw(C_csp_exception("Hot side outlet pressure is greater than hot side inlet pressure", "C_HX_counterflow::design", 6)); + } + if (des_par.m_P_c_in < des_par.m_P_c_out) + { + throw(C_csp_exception("Cold side outlet pressure is greater than cold side inlet pressure", "C_HX_counterflow::design", 7)); + } + if (q_dot_design <= 1.E-14) // very low Q_dot; assume it is zero + { + // Set outputs, and S_des_solved members + UA_calc = 0.0; //[kW/K] + NTU_calc = 0.0; //[-] + q_dot_calc = 0.0; //[kW] + min_DT_calc = des_par.m_T_h_in - des_par.m_T_c_in; //[K] + eff_calc = 0.0; //[-] + T_h_out_calc = des_par.m_T_h_in; //[K] + T_c_out_calc = des_par.m_T_c_in; //[K] + + return; + } + } + + // Know h_h_in and h_c_in, so can call 'calc_req_UA_enth' here + double h_c_out_calc = std::numeric_limits::quiet_NaN(); + double h_h_out_calc = std::numeric_limits::quiet_NaN(); + NS_HX_counterflow_eqs::calc_req_UA_enth(ms_init_par.m_hot_fl, mc_hot_fl, + ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot_design, ms_des_calc_UA_par.m_m_dot_cold_des, ms_des_calc_UA_par.m_m_dot_hot_des, + h_c_in, h_h_in, ms_des_calc_UA_par.m_P_c_in, ms_des_calc_UA_par.m_P_c_out, ms_des_calc_UA_par.m_P_h_in, ms_des_calc_UA_par.m_P_h_out, + h_h_out_calc, T_h_out_calc, h_c_out_calc, T_c_out_calc, + UA_calc, min_DT_calc, eff_calc, NTU_calc, q_dot_calc, + mv_s_node_info_des); + + // Check that calculated effectiveness is less than limit + if (eff_calc > ms_des_calc_UA_par.m_eff_max) + { + std::string msg = util::format("Calculated design effectiveness, %lg [-] is greater than the specified maximum effectiveness, %lg [-].", eff_calc, ms_des_calc_UA_par.m_eff_max); + + throw(C_csp_exception("Calculated design effectiveness, % lg[-] is greater than the specified maximum effectiveness, % lg[-].", + "C_HX_counterflow::design")); + } + + ms_des_solved.m_UA_allocated = 0.0; //[kW/K] + ms_des_solved.m_UA_calc_at_eff_max = UA_calc; //[kW/K] + + ms_des_solved.m_Q_dot_design = q_dot_design; //[kWt] + ms_des_solved.m_UA_design = UA_calc; //[kWt/K] + ms_des_solved.m_min_DT_design = min_DT_calc; + ms_des_solved.m_eff_design = eff_calc; + ms_des_solved.m_NTU_design = NTU_calc; + ms_des_solved.m_T_h_out = T_h_out_calc; + ms_des_solved.m_T_c_out = T_c_out_calc; + ms_des_solved.m_h_h_out = h_h_out_calc; + ms_des_solved.m_h_c_out = h_c_out_calc; + + ms_des_solved.m_cost_equipment = calculate_equipment_cost(ms_des_solved.m_UA_design, + ms_des_calc_UA_par.m_T_h_in, ms_des_calc_UA_par.m_P_h_in, ms_des_calc_UA_par.m_m_dot_hot_des, + ms_des_calc_UA_par.m_T_c_in, ms_des_calc_UA_par.m_P_c_in, ms_des_calc_UA_par.m_m_dot_cold_des); + + ms_des_solved.m_cost_bare_erected = calculate_bare_erected_cost(ms_des_solved.m_cost_equipment); + + // Specify that method solved successfully + m_is_HX_designed = true; + + des_solved = ms_des_solved; + + return; +} + + void C_HX_counterflow_CRM::design_for_target__calc_outlet(int hx_target_code /*-*/, double UA_target /*kW/K*/, double min_dT_target /*K*/, double eff_target /*-*/, double eff_max /*-*/, double T_c_in /*K*/, double P_c_in /*kPa*/, double m_dot_c /*kg/s*/, double P_c_out /*kPa*/, @@ -2512,6 +2634,398 @@ void C_HX_counterflow_CRM::off_design_solution_calc_dP(double T_c_in /*K*/, doub off_design_solution_fixed_dP(T_c_in, P_c_in, m_dot_c, P_c_out, T_h_in, P_h_in, m_dot_h, P_h_out, od_tol, q_dot, T_c_out, T_h_out); } +void C_HX_counterflow_CRM::off_design_solution_fixed_dP_enth(double h_c_in /*K*/, double P_c_in /*kPa*/, double m_dot_c /*kg/s*/, double P_c_out /*kPa*/, + double h_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, + double od_tol /*-*/, + double& q_dot /*kWt*/, double& h_c_out /*K*/, double& h_h_out /*K*/) +{ + if (m_od_solution_type == C_HX_counterflow_CRM::C_od_thermal_solution_type::E_CRM_UA_PER_NODE) + { + double h_cold_in = h_c_in; + double h_hot_in = h_h_in; + + double h_h_out_q_max, T_h_out_q_max, h_c_out_q_max, T_c_out_q_max, T_h_in_q_max, T_c_in_q_max; + h_h_out_q_max = T_h_out_q_max = h_c_out_q_max = T_c_out_q_max = T_h_in_q_max = T_c_in_q_max = std::numeric_limits::quiet_NaN(); + double q_dot_max = NS_HX_counterflow_eqs::calc_max_q_dot_enth(ms_init_par.m_hot_fl, mc_hot_fl, + ms_init_par.m_cold_fl, mc_cold_fl, + h_hot_in, P_h_in, P_h_out, m_dot_h, + h_cold_in, P_c_in, P_c_out, m_dot_c, + h_h_out_q_max, T_h_out_q_max, + h_c_out_q_max, T_c_out_q_max, + T_h_in_q_max, T_c_in_q_max); + + if (q_dot_max > 0.0) + { + double h_h_out_local, T_h_out_local, h_c_out_local, T_c_out_local, UA_local, min_DT_local, eff_local, NTU_local, q_dot_calc_local; + h_h_out_local = T_h_out_local = h_c_out_local = T_c_out_local = + UA_local = min_DT_local = eff_local = NTU_local = q_dot_calc_local = std::numeric_limits::quiet_NaN(); + std::vector v_s_node_info_local; + + double q_dot_max_local = 0.9999 * q_dot_max; + bool is_feasible_q_dot = false; + double q_dot_max_pinch = q_dot_max; + + while (!is_feasible_q_dot) + { + int err_code = 0; + try + { + /*NS_HX_counterflow_eqs::calc_req_UA(ms_init_par.m_hot_fl, mc_hot_fl, ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot_max_local, m_dot_c, m_dot_h, + T_c_in, T_h_in, P_c_in, P_c_out, P_h_in, P_h_out, + UA_local, min_DT_local, eff_local, NTU_local, + T_h_out_local, T_c_out_local, q_dot_calc_local, + v_s_node_info_local);*/ + + NS_HX_counterflow_eqs::calc_req_UA_enth(ms_init_par.m_hot_fl, mc_hot_fl, ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot_max_local, m_dot_c, m_dot_h, + h_c_in, h_h_in, P_c_in, P_c_out, P_h_in, P_h_out, + + h_h_out_local, T_h_out_local, h_c_out_local, T_c_out_local, + UA_local, min_DT_local, eff_local, NTU_local, q_dot_calc_local, + v_s_node_info_local); + } + catch (C_csp_exception& csp_except) + { + err_code = csp_except.m_error_code; + q_dot_max_pinch = q_dot_max_local; + q_dot_max_local *= 0.995; + } + + if (err_code == 0) + { + is_feasible_q_dot = true; + } + } + + C_MEQ__hx_total_q_dot c_q_dot_eq(this, m_dot_c, m_dot_h, + h_cold_in, h_hot_in, + P_c_in, P_c_out, + P_h_in, P_h_out, od_tol); + + C_monotonic_eq_solver c_q_dot_solver(c_q_dot_eq); + + double diff_q_dot_upper = std::numeric_limits::quiet_NaN(); + int q_dot_upper_test_code = c_q_dot_solver.test_member_function(q_dot_calc_local, &diff_q_dot_upper); + + if (q_dot_upper_test_code != 0 || diff_q_dot_upper < 0.0) + { + C_monotonic_eq_solver::S_xy_pair xy1; + C_monotonic_eq_solver::S_xy_pair xy2; + double q_dot_guess_upper = q_dot_calc_local; + + if (q_dot_upper_test_code == 0) + { + //if (q_dot_upper_test_code != 0) + //{ + // while (q_dot_upper_test_code != 0) + // { + // // Use design point effectiveness to generate 2 guess values + // //double q_dot_mult = max(0.99, min(0.95, eff_max_limit) / eff_max_limit); + // double q_dot_mult = 0.995; + // if (std::isfinite(ms_des_solved.m_eff_design)) + // { + // q_dot_mult = max(0.99, min(0.1, ms_des_solved.m_eff_design)); + // } + + // q_dot_guess_upper *= q_dot_mult; + + // q_dot_upper_test_code = c_q_dot_solver.test_member_function(q_dot_guess_upper, &diff_q_dot_upper); + // } + //} + + xy1.x = q_dot_guess_upper; + xy1.y = diff_q_dot_upper; + + xy2.x = 0.95 * q_dot_guess_upper; + + q_dot_upper_test_code = c_q_dot_solver.test_member_function(xy2.x, &xy2.y); + + while (q_dot_upper_test_code != 0) + { + xy2.x *= 1.005; + + if (xy2.x > xy1.x) + { + break; + } + + q_dot_upper_test_code = c_q_dot_solver.test_member_function(xy2.x, &xy2.y); + } + } + + if (q_dot_upper_test_code == 0) + { + c_q_dot_solver.settings(od_tol, 50, 0.0, q_dot_max_pinch, false); + + double tol_solved, q_dot_solved; + tol_solved = q_dot_solved = std::numeric_limits::quiet_NaN(); + int iter_solved = -1; + + int q_dot_hx_code = 0; + + try + { + q_dot_hx_code = c_q_dot_solver.solve(xy1, xy2, 0.0, + q_dot_solved, tol_solved, iter_solved); + } + catch (C_csp_exception) + { + throw(C_csp_exception("C_HX_counterflow_CRM::off_design_solution threw exception trying to solve for the off-design" + "heat transfer for off design hx conductance")); + } + + if (q_dot_hx_code != C_monotonic_eq_solver::CONVERGED) + { + if (!(q_dot_hx_code > C_monotonic_eq_solver::CONVERGED && std::abs(tol_solved) <= 0.1)) + { + throw(C_csp_exception("C_HX_counterflow_CRM::off_design_solution did not solve for the off-design" + "heat transfer for off design hx conductance within the specified tolerance")); + } + } + + q_dot = q_dot_solved; + } + else + { + q_dot_upper_test_code = c_q_dot_solver.test_member_function(q_dot_guess_upper, &diff_q_dot_upper); + q_dot = q_dot_guess_upper; + } + } + else + { + // Off-design HX wants a q_dot resulting in an effectiveness greater than limit + // What to do about this? + q_dot = q_dot_calc_local; + //T_h_out = T_h_out_q_max; + //T_c_out = T_c_out_q_max; + } + + double eff_max_limit = fmin(0.99999, ms_des_calc_UA_par.m_eff_max); //[-] + double q_dot_upper_eff = eff_max_limit * q_dot_max; //[kWt] + + if (q_dot_upper_eff < q_dot) + { + q_dot = q_dot_upper_eff; + + try + { + /*NS_HX_counterflow_eqs::calc_req_UA(ms_init_par.m_hot_fl, mc_hot_fl, ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot, m_dot_c, m_dot_h, + T_c_in, T_h_in, P_c_in, P_c_out, P_h_in, P_h_out, + UA_local, min_DT_local, eff_local, NTU_local, + T_h_out_local, T_c_out_local, q_dot_calc_local, + v_s_node_info_local);*/ + + NS_HX_counterflow_eqs::calc_req_UA_enth(ms_init_par.m_hot_fl, mc_hot_fl, ms_init_par.m_cold_fl, mc_cold_fl, + ms_init_par.m_N_sub_hx, + q_dot, m_dot_c, m_dot_h, + h_c_in, h_h_in, P_c_in, P_c_out, P_h_in, P_h_out, + h_h_out_local, T_h_out_local, h_c_out_local, T_c_out_local, + UA_local, min_DT_local, eff_local, NTU_local, q_dot_calc_local, + v_s_node_info_local); + } + catch (C_csp_exception& csp_except) + { + throw(C_csp_exception("C_HX_counterflow_CRM::off_design_solution failed to solve 'calc_req_UA'" + " at effectiveness-limited heat transfer.")); + } + + h_h_out = h_h_out_local; + h_c_out = h_c_out_local; + + ms_od_solved.m_min_DT = min_DT_local; + ms_od_solved.m_UA_total = UA_local; + } + else + { + double h_hot_out = c_q_dot_eq.m_h_hot_out; //[kJ/kg] + double h_cold_old = c_q_dot_eq.m_h_cold_out; //[kJ/kg] + + h_h_out = h_hot_out; + h_c_out = h_cold_old; + + ms_od_solved.m_min_DT = c_q_dot_eq.m_min_dT; //[K] + ms_od_solved.m_UA_total = c_q_dot_eq.m_UA_tot_calc; //[kW/K] + } + } + else + { + // HX q_dot = 0.0 + // What to do about this? + q_dot = q_dot_max; + h_h_out = h_h_out_q_max; + h_c_out = T_c_out_q_max; + ms_od_solved.m_min_DT = 0.0; + ms_od_solved.m_UA_total = 0.0; + } + + ms_od_solved.m_eff = q_dot / q_dot_max; //[-] + //ms_od_solved.m_NTU = NTU; //[-] + ms_od_solved.m_P_c_out = P_c_out; //[kPa] + ms_od_solved.m_P_h_out = P_h_out; //[kPa] + ms_od_solved.m_q_dot = q_dot; //[kWt] + //ms_od_solved.m_T_c_out = T_c_out; //[K] + //ms_od_solved.m_T_h_out = T_h_out; //[K] + + ms_od_solved.m_deltaP_cold = P_c_in - P_c_out; //[kPa] + ms_od_solved.m_deltaP_hot = P_h_in - P_h_out; //[kPa] + } + else + { + // Set o.d. UA as solver target + double UA_target = od_UA(m_dot_c, m_dot_h); //[kW/K] + double eff_target = ms_des_calc_UA_par.m_eff_max; + + + ms_od_solved.m_q_dot = ms_od_solved.m_T_c_out = ms_od_solved.m_P_c_out = + ms_od_solved.m_T_h_out = ms_od_solved.m_P_h_out = ms_od_solved.m_UA_total = + ms_od_solved.m_min_DT = ms_od_solved.m_eff = ms_od_solved.m_NTU = std::numeric_limits::quiet_NaN(); + + double eff_calc, min_DT, NTU, UA_calc; + eff_calc = min_DT = NTU = UA_calc = std::numeric_limits::quiet_NaN(); + + // Off-design should always use UA as the performance target + // So set min_dT_target and eff_target parameters = nan + int hx_target_code = NS_HX_counterflow_eqs::TARGET_UA; + + if (!m_is_single_node_des_set) + { + ms_node_info_des.UA = ms_des_solved.m_UA_design; + + // Hot fluid + double P_h_in_des = ms_des_calc_UA_par.m_P_h_in; //[kPa] + double T_h_in_des = ms_des_calc_UA_par.m_T_h_in; //[K] + double h_h_in_des = ms_des_calc_UA_par.m_h_h_in; //[kJ/kg] + + double P_h_out_des = ms_des_calc_UA_par.m_P_h_out; //[kPa] + double T_h_out_des = ms_des_solved.m_T_h_out; //[K] + double h_h_out_des = ms_des_solved.m_h_h_out; //[kJ/kg] + + double P_h_avg_des = 0.5 * (P_h_in_des + P_h_out_des); + double h_h_avg_des = 0.5 * (h_h_in_des + h_h_out_des); + + NS_HX_counterflow_eqs::C_hx_fl__Ph__core c_Ph_hot(ms_init_par.m_hot_fl, &mc_hot_fl, P_h_avg_des, h_h_avg_des, true); + ms_node_info_des.s_fl_hot.k = c_Ph_hot.m_k; + ms_node_info_des.s_fl_hot.rho = c_Ph_hot.m_rho; + ms_node_info_des.s_fl_hot.mu = c_Ph_hot.m_mu; + ms_node_info_des.s_fl_hot.cp = c_Ph_hot.m_cp; + ms_node_info_des.s_fl_hot.m_dot = ms_des_calc_UA_par.m_m_dot_hot_des; //[kg/s] + + // Cold fluid + double P_c_in_des = ms_des_calc_UA_par.m_P_c_in; //[kPa] + double T_c_in_des = ms_des_calc_UA_par.m_T_c_in; //[K] + double h_c_in_des = ms_des_calc_UA_par.m_h_c_in; + + double P_c_out_des = ms_des_calc_UA_par.m_P_c_out; //[kPa] + double T_c_out_des = ms_des_solved.m_T_c_out; //[K] + double h_c_out_des = ms_des_solved.m_h_c_out; //[kJ/kg] + + double P_c_avg_des = 0.5 * (P_c_in_des + P_c_out_des); + double h_c_avg_des = 0.5 * (h_c_in_des + h_c_out_des); + + NS_HX_counterflow_eqs::C_hx_fl__Ph__core c_Ph_cold(ms_init_par.m_cold_fl, &mc_cold_fl, P_c_avg_des, h_c_avg_des, true); + ms_node_info_des.s_fl_cold.k = c_Ph_cold.m_k; + ms_node_info_des.s_fl_cold.rho = c_Ph_cold.m_rho; + ms_node_info_des.s_fl_cold.mu = c_Ph_cold.m_mu; + ms_node_info_des.s_fl_cold.cp = c_Ph_cold.m_cp; + ms_node_info_des.s_fl_cold.m_dot = ms_des_calc_UA_par.m_m_dot_cold_des; + + m_is_single_node_des_set = true; + } + + double T_c_out_calc, T_h_out_calc; + + double P_c_out_guess = P_c_out; + double P_c_out_calc = P_c_out_guess; + + double P_h_out_guess = P_h_out; + double P_h_out_calc = P_h_out_guess; + + bool is_hx_deltaP_converge = true; + + double diff_P_c_out = std::numeric_limits::quiet_NaN(); + double diff_P_h_out = std::numeric_limits::quiet_NaN(); + + size_t iter_deltaP = 0; + + do + { + if (P_c_out_calc != P_c_out_guess) + { + P_c_out_guess = 0.9 * P_c_out_calc + 0.1 * P_c_out_guess; + } + + if (P_h_out_calc != P_h_out_guess) + { + P_h_out_guess = 0.9 * P_h_out_calc + 0.1 * P_h_out_guess; + } + + if (iter_deltaP > 10) + { + P_c_out_guess = P_c_in - (ms_des_calc_UA_par.m_P_c_in - ms_des_calc_UA_par.m_P_c_out); + P_h_out_guess = P_h_in - (ms_des_calc_UA_par.m_P_h_in - ms_des_calc_UA_par.m_P_h_out); + is_hx_deltaP_converge = false; + } + + NS_HX_counterflow_eqs::solve_q_dot_for_fixed_UA_enth( + ms_init_par.m_hot_fl, mc_hot_fl, + ms_init_par.m_cold_fl, mc_cold_fl, + ms_node_info_des, + ms_init_par.m_N_sub_hx, m_od_UA_target_type, + h_c_in, P_c_in, m_dot_c, P_c_out_guess, + h_h_in, P_h_in, m_dot_h, P_h_out_guess, + UA_target, + eff_target, ms_des_solved.m_eff_design, + od_tol, + T_c_out_calc, h_c_out, + T_h_out_calc, h_h_out, + q_dot, eff_calc, min_DT, NTU, UA_calc, + mv_s_node_info_des); + + double P_c_avg = 0.5 * (P_c_in + P_c_out_guess); + double h_c_avg = 0.5 * (h_c_in + h_c_out); + NS_HX_counterflow_eqs::C_hx_fl__Ph__core c_c_fl_props(ms_init_par.m_cold_fl, &mc_cold_fl, P_c_avg, h_c_avg, false); + double rho_c_avg = c_c_fl_props.m_rho; + + double P_h_avg = 0.5 * (P_h_in + P_h_out_guess); + double h_h_avg = 0.5 * (h_h_in + h_h_out); + NS_HX_counterflow_eqs::C_hx_fl__Ph__core c_h_fl_props(ms_init_par.m_hot_fl, &mc_hot_fl, P_h_avg, h_h_avg, false); + double rho_h_avg = c_h_fl_props.m_rho; + + P_c_out_calc = P_c_in - (ms_des_calc_UA_par.m_P_c_in - ms_des_calc_UA_par.m_P_c_out) * + (std::pow(m_dot_c, 2) / rho_c_avg) / + (std::pow(ms_node_info_des.s_fl_cold.m_dot, 2) / ms_node_info_des.s_fl_cold.rho); + + P_h_out_calc = P_h_in - (ms_des_calc_UA_par.m_P_h_in - ms_des_calc_UA_par.m_P_h_out) * + (std::pow(m_dot_h, 2) / rho_h_avg) / + (std::pow(ms_node_info_des.s_fl_hot.m_dot, 2) / ms_node_info_des.s_fl_hot.rho); + + diff_P_c_out = (P_c_out_calc - P_c_out_guess) / P_c_out_guess; //[-] + diff_P_h_out = (P_h_out_calc - P_h_out_guess) / P_h_out_guess; //[-] + + iter_deltaP++; + + } while (is_hx_deltaP_converge && (std::abs(diff_P_c_out) > od_tol || std::abs(diff_P_h_out) > od_tol)); + + ms_od_solved.m_eff = eff_calc; //[-] + ms_od_solved.m_min_DT = min_DT; //[K] + ms_od_solved.m_NTU = NTU; //[-] + ms_od_solved.m_P_c_out = P_c_out_guess; //[kPa] + ms_od_solved.m_P_h_out = P_h_out_guess; //[kPa] + ms_od_solved.m_q_dot = q_dot; //[kWt] + ms_od_solved.m_T_c_out = T_c_out_calc; //[K] + ms_od_solved.m_T_h_out = T_h_out_calc; //[K] + ms_od_solved.m_UA_total = UA_calc; //[kW/K] + + ms_od_solved.m_deltaP_cold = P_c_in - ms_od_solved.m_P_c_out; //[kPa] + ms_od_solved.m_deltaP_hot = P_h_in - ms_od_solved.m_P_h_out; //[kPa] + } +} + double C_HX_counterflow_CRM::od_delta_p_cold_frac(double m_dot_c /*kg/s*/) { return pow(m_dot_c / ms_des_calc_UA_par.m_m_dot_cold_des, 1.75); @@ -2543,7 +3057,7 @@ double C_HX_counterflow_CRM::od_UA_frac(double m_dot_c /*kg/s*/, double m_dot_h return pow(m_dot_ratio, 0.8); } -void C_HX_water_to_htf::initialize(int hot_fl, util::matrix_t hot_fl_props, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) +void C_HX_htf_to_steam::initialize(int hot_fl, util::matrix_t hot_fl_props, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) { // Hard-code some of the design parameters ms_init_par.m_N_sub_hx = N_sub_hx; //[-] @@ -2591,42 +3105,56 @@ void C_HX_water_to_htf::initialize(int hot_fl, util::matrix_t hot_fl_pro } -void C_HX_water_to_htf::initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) +void C_HX_htf_to_steam::initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type) { util::matrix_t null_fluid_props; initialize(hot_fl, null_fluid_props, N_sub_hx, od_UA_target_type); } -void C_HX_water_to_htf::design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& des_par, - double q_dot_design /*kWt*/, double T_h_out /*K*/, double T_c_out /*K*/, C_HX_counterflow_CRM::S_des_solved& des_solved) +void C_HX_htf_to_steam::design_w_TP_PH(double T_h_in /*K*/, double P_h_in /*kPa*/, double T_h_out /*K*/, double P_h_out /*kPa*/, + double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, double h_c_out /*kJ/kg*/, + double q_dot_design /*kWt*/, + C_HX_counterflow_CRM::S_des_solved& des_solved) { double T_htf_cold = T_h_out; //[C] - double h_h_in = mc_hot_fl.enth_lookup(des_par.m_T_h_in); //[kJ/kg] + double h_h_in = mc_hot_fl.enth_lookup(T_h_in); //[kJ/kg] double h_h_out = mc_hot_fl.enth_lookup(T_htf_cold); //[kJ/kg] - des_par.m_m_dot_hot_des = q_dot_design / (h_h_in - h_h_out); + double m_dot_hot_des = q_dot_design / (h_h_in - h_h_out); water_state ms_water_props; - int prop_error_code = water_TP(des_par.m_T_c_in /*K*/, des_par.m_P_c_in /*kPa*/, &ms_water_props); + int prop_error_code = water_PH(P_c_in, h_c_in, &ms_water_props); if (prop_error_code != 0) { throw(C_csp_exception("Hot side water/steam inlet enthalpy calculations failed", "C_HX_counterflow::calc_max_q_dot_enth", 12)); } double h_steam_in = ms_water_props.enth; //[kJ/kg] - prop_error_code = water_TP(T_c_out /*K*/, des_par.m_P_c_out /*kPa*/, &ms_water_props); + prop_error_code = water_PH(P_c_out, h_c_out, &ms_water_props); if (prop_error_code != 0) { throw(C_csp_exception("Hot side water/steam inlet enthalpy calculations failed", "C_HX_counterflow::calc_max_q_dot_enth", 12)); } double h_steam_out = ms_water_props.enth; //[kJ/kg] - des_par.m_m_dot_cold_des = q_dot_design / (h_steam_out - h_steam_in); //[kg/s] + double m_dot_cold_des = q_dot_design / (h_steam_out - h_steam_in); //[kg/s] - + S_des_calc_UA_par des_par; + des_par.m_T_h_in = T_h_in; + des_par.m_P_h_in = P_h_in; + des_par.m_P_h_out = P_h_out; + des_par.m_m_dot_hot_des = m_dot_hot_des; + des_par.m_h_h_in = h_h_in; + des_par.m_T_c_in = numeric_limits::quiet_NaN(); // Cold steam temp should never be used + des_par.m_P_c_in = P_c_in; + des_par.m_P_c_out = P_c_out; + des_par.m_m_dot_cold_des = m_dot_cold_des; + des_par.m_h_c_in = h_c_in; - design_calc_UA(des_par, q_dot_design, des_solved); + des_par.m_eff_max = 1.0; + + design_calc_UA_TP_to_PH(des_par, h_h_in, h_c_in, h_c_out, q_dot_design, des_solved); double T_htf_in_solve = des_par.m_T_h_in; double T_htf_out_solve = des_solved.m_T_h_out; @@ -2644,60 +3172,91 @@ void C_HX_water_to_htf::design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& double mdot_steam_solve = des_par.m_m_dot_cold_des; double Q_steam_calc = mdot_steam_solve * (h_steam_out_solve - h_steam_in_solve); - - int x = 0; + int x = 0; } -void C_HX_water_to_htf::off_design_target_T_cold_out(double T_c_out_target /*K*/, +int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/kg*/, double m_dot_c_min /*kg/s*/, double m_dot_c_max /*kg/s*/, - double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, - double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, - double od_tol, - double& q_dot, double& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/) + double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, + 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 mdot_guess = 20; + // ADD inputs + double max_iter = 1000; + double slvr_tol = od_tol; + double mdot_guess = this->ms_des_calc_UA_par.m_m_dot_cold_des; double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; double P_c_out_local, P_h_out_local; - - C_MEQ__target_T_c_out T_out_eq(this, T_c_out_target, T_c_in, P_c_in, T_h_in, P_h_in, m_dot_h, od_tol); - C_monotonic_eq_solver T_out_solver(T_out_eq); - - double solver_tol = 1e-3; - T_out_solver.settings(solver_tol, 1000, m_dot_c_min, m_dot_c_max, false); + C_MEQ__target_cold_PH_out h_out_eq(this, h_c_out_target, h_c_in, P_c_in, P_c_out, + h_h_in, P_h_in, P_h_out, m_dot_h, od_tol); + C_monotonic_eq_solver h_out_solver(h_out_eq); + h_out_solver.settings(1e-1, max_iter, m_dot_c_min, m_dot_c_max, false); double m_dot_c_solved, tol_solved; int iter_solved = -1; - int m_dot_out_code = T_out_solver.solve(m_dot_c_min, m_dot_c_max, 0, m_dot_c_solved, tol_solved, iter_solved); + if (false) + { + double frac = (m_dot_c_max - m_dot_c_min) / 1000; + std::vector mdot_vec; + std::vector h_c_out_vec; + std::vector h_c_out_diff_vec; + for (double mdot = m_dot_c_min; mdot < m_dot_c_max; mdot += frac) + { + double q_dot_temp, h_c_out_temp, h_h_out_temp; + off_design_solution_fixed_dP_enth(h_c_in, P_c_in, mdot, P_c_out, + h_h_in, P_h_in, m_dot_h, P_h_out, + od_tol, + q_dot_temp, h_c_out_temp, h_h_out_temp); - //off_design_solution_fixed_dP(T_c_in, P_c_in, mdot_guess, P_c_out, T_h_in, P_h_in, m_dot_h, P_h_out, od_tol, - // q_dot_local, T_c_out_local, T_h_out_local); + mdot_vec.push_back(mdot); + h_c_out_vec.push_back(h_c_out_temp); + h_c_out_diff_vec.push_back(h_c_out_temp - h_c_out_target); + } - //off_design_solution_calc_dP(T_c_in, P_c_in, mdot_guess, T_h_in, P_h_in, m_dot_h, od_tol, - // q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + int xasdf = 0; + } + + // Solve + int m_dot_code = h_out_solver.solve(m_dot_c_min, m_dot_c_max, 0.0, m_dot_c_solved, tol_solved, iter_solved); + if (m_dot_code == C_monotonic_eq_solver::CONVERGED) + { + // Converge Succesfully + q_dot = h_out_eq.m_q_dot; //[kJ] + h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] + h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] + m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] + } + else + { + // Could not converge + q_dot = 0.0; //[kJ] + h_c_out = h_c_in; //[kJ/kg] + h_h_out = h_h_in; //[kJ/kg] + m_dot_c = 0.0; //[kg/s] + } - q_dot = q_dot_local; - T_c_out = T_c_out_local; - T_h_out = T_h_out_local; - m_dot_c = mdot_guess; + return m_dot_code; } -int C_HX_water_to_htf::C_MEQ__target_T_c_out::operator()(double m_dot_c /*kg/s*/, double* diff_T_c_out /*C/K*/) +int C_HX_htf_to_steam::C_MEQ__target_cold_PH_out::operator()(double m_dot_c /*kg/s*/, double* diff_h_c_out /*kJ/kg*/) { - double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; - double P_c_out_local, P_h_out_local; + m_m_dot_c = m_dot_c; - mpc_hx->off_design_solution_calc_dP(m_T_c_in, m_P_c_in, m_dot_c, m_T_h_in, m_P_h_in, m_m_dot_h, m_tol, - q_dot_local, T_c_out_local, P_c_out_local, T_h_out_local, P_h_out_local); + mpc_hx->off_design_solution_fixed_dP_enth(m_h_c_in, m_P_c_in, m_dot_c, m_P_c_out, + m_h_h_in, m_P_h_in, m_m_dot_h, m_P_h_out, + m_tol, + m_q_dot, m_h_c_out, m_h_h_out); - *diff_T_c_out = T_c_out_local - m_T_c_out_target; //[C/K] + *diff_h_c_out = (m_h_c_out - m_h_c_out_target) / 200; //[kJ/kg] return 0; } diff --git a/tcs/heat_exchangers.h b/tcs/heat_exchangers.h index 792beba61..11bed2574 100644 --- a/tcs/heat_exchangers.h +++ b/tcs/heat_exchangers.h @@ -519,6 +519,9 @@ class C_HX_counterflow_CRM double m_P_c_out; //[kPa] Cold fluid outlet temperature double m_m_dot_cold_des; //[kg/s] cold fluid design mass flow rate + double m_h_h_in; //[kJ/kg] Design-point hot inlet enthalpy + double m_h_c_in; //[kJ/kg] Design-point cold inlet enthalpy + double m_eff_max; //[-] Max allowable effectiveness S_des_calc_UA_par() @@ -543,6 +546,8 @@ class C_HX_counterflow_CRM double m_NTU_design; //[-] NTU at design double m_T_h_out; //[K] Design-point hot outlet temperature double m_T_c_out; //[K] Design-point cold outlet temperature + double m_h_h_out; //[kJ/kg] Design-point hot outlet enthalpy + double m_h_c_out; //[kJ/kg] Design-point cold outlet enthalpy double m_DP_cold_des; //[kPa] cold fluid design pressure drop double m_DP_hot_des; //[kPa] hot fluid design pressure drop @@ -618,6 +623,11 @@ class C_HX_counterflow_CRM void design_calc_UA(C_HX_counterflow_CRM::S_des_calc_UA_par des_par, double q_dot_design /*kWt*/, C_HX_counterflow_CRM::S_des_solved &des_solved); + void design_calc_UA_TP_to_PH(C_HX_counterflow_CRM::S_des_calc_UA_par des_par, + double h_h_in /*kJ/kg*/, + double h_c_in /*kJ/kg*/, double h_c_out /*kJ/kg*/, + double q_dot_design /*kWt*/, C_HX_counterflow_CRM::S_des_solved& des_solved); + double calc_max_q_dot_enth(double h_h_in /*kJ/kg*/, double P_h_in /*kPa*/, double P_h_out /*kPa*/, double m_dot_h /*kg/s*/, double h_c_in /*kJ/kg*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, double m_dot_c /*kg/s*/); @@ -649,6 +659,11 @@ class C_HX_counterflow_CRM double od_tol /*-*/, double & q_dot /*kWt*/, double & T_c_out /*K*/, double & P_c_out /*kPa*/, double & T_h_out /*K*/, double & P_h_out /*kPa*/); + void off_design_solution_fixed_dP_enth(double h_c_in /*K*/, double P_c_in /*kPa*/, double m_dot_c /*kg/s*/, double P_c_out /*kPa*/, + double h_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, + double od_tol /*-*/, + double& q_dot /*kWt*/, double& h_c_out /*K*/, double& h_h_out /*K*/); + double od_delta_p_cold_frac(double m_dot_c /*kg/s*/); double od_delta_p_cold(double m_dot_c /*kg/s*/); @@ -724,70 +739,71 @@ class C_HX_counterflow_CRM }; -class C_HX_water_to_htf : public C_HX_counterflow_CRM +class C_HX_htf_to_steam : public C_HX_counterflow_CRM { -private: - - - public: - C_HX_water_to_htf() + C_HX_htf_to_steam() { m_cost_model = C_HX_counterflow_CRM::E_CARLSON_17_PHX; - //m_od_solution_type = C_HX_counterflow_CRM::C_od_thermal_solution_type::E_CRM_UA_PER_NODE; - m_od_solution_type = C_HX_counterflow_CRM::C_od_thermal_solution_type::E_DEFAULT; - - } - // This method calculates the flow rates and UA given all 4 temperatures and heat exchange - void design_w_temps(C_HX_counterflow_CRM::S_des_calc_UA_par& des_par, - double q_dot_design /*kWt*/, double T_h_out /*K*/, double T_c_out /*K*/, C_HX_counterflow_CRM::S_des_solved& des_solved); + // This method calculates the flow rates and UA given hot TP and cold PH at all points + void design_w_TP_PH(double T_h_in /*K*/, double P_h_in /*kPa*/, double T_h_out /*K*/, double P_h_out /*kPa*/, + double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, double h_c_out /*kJ/kg*/, double q_dot_design /*kWt*/, + C_HX_counterflow_CRM::S_des_solved& des_solved); + virtual void initialize(int hot_fl, util::matrix_t hot_fl_props, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type); virtual void initialize(int hot_fl, int N_sub_hx, NS_HX_counterflow_eqs::E_UA_target_type od_UA_target_type); - void off_design_target_T_cold_out(double T_c_out_target /*K*/, + int off_design_target_cold_PH_out(double h_c_out_target /*kJ/kg*/, double m_dot_c_min /*kg/s*/, double m_dot_c_max /*kg/s*/, - double T_c_in /*K*/, double P_c_in /*kPa*/, double P_c_out /*kPa*/, - double T_h_in /*K*/, double P_h_in /*kPa*/, double m_dot_h /*kg/s*/, double P_h_out /*kPa*/, + double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, + 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& T_c_out /*K*/, double& T_h_out /*K*/, double& m_dot_c /*kg/s*/); + double& q_dot /*kWt*/, double& h_c_out /*kJ/kg*/, double& h_h_out /*kJ/kg*/, double& m_dot_c /*kg/s*/); - class C_MEQ__target_T_c_out : public C_monotonic_equation + class C_MEQ__target_cold_PH_out : public C_monotonic_equation { private: C_HX_counterflow_CRM* mpc_hx; - double m_T_c_out_target; //[K] - double m_T_c_in; //[K] + double m_h_c_out_target; //[K] + double m_h_c_in; //[K] double m_P_c_in; //[kPa] - double m_T_h_in; //[K] + double m_P_c_out; //[kPa] + double m_h_h_in; //[K] double m_P_h_in; //[kPa] + double m_P_h_out; //[kPa] double m_m_dot_h; //[kg/s] double m_tol; //[-] public: - C_MEQ__target_T_c_out(C_HX_counterflow_CRM* pc_hx, - double T_c_out_target, - double T_c_in, double P_c_in, - double T_h_in, double P_h_in, + C_MEQ__target_cold_PH_out(C_HX_counterflow_CRM* pc_hx, + double h_c_out_target, + double h_c_in, double P_c_in, double P_c_out, + double h_h_in, double P_h_in, double P_h_out, double m_dot_h, double tol) - : m_T_c_out_target(T_c_out_target), - m_T_c_in(T_c_in), m_P_c_in(P_c_in), - m_T_h_in(T_h_in), m_P_h_in(P_h_in), + : m_h_c_out_target(h_c_out_target), + m_h_c_in(h_c_in), m_P_c_in(P_c_in), m_P_c_out(P_c_out), + m_h_h_in(h_h_in), m_P_h_in(P_h_in), m_P_h_out(P_h_out), m_m_dot_h(m_dot_h), m_tol(tol) { mpc_hx = pc_hx; } - virtual int operator()(double m_dot_c /*kg/s*/, double* diff_T_c_out /*C/K*/); + double m_h_h_out; + double m_h_c_out; + double m_m_dot_c; + double m_q_dot; + + virtual int operator()(double m_dot_c /*kg/s*/, double* diff_h_c_out /*C/K*/); }; From 3d101f8352779b39863c3a7ba72952f49e52f0fa Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Mon, 2 Dec 2024 13:54:39 -0700 Subject: [PATCH 05/27] Implement physical heat sink variables for tower, trough, and mslf --- ssc/cmod_fresnel_physical_iph.cpp | 40 ++++++++++ ssc/cmod_mspt_iph.cpp | 42 ++++++++++- ssc/cmod_trough_physical_iph.cpp | 120 ++++++++++-------------------- 3 files changed, 122 insertions(+), 80 deletions(-) diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp index 1e3364f12..95ef5d7ac 100644 --- a/ssc/cmod_fresnel_physical_iph.cpp +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -36,6 +36,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "csp_solver_fresnel_collector_receiver.h" #include "csp_solver_pc_heat_sink.h" +#include "csp_solver_pc_heat_sink_physical.h" #include "csp_dispatch.h" #include "csp_system_costs.h" #include "csp_solver_two_tank_tes.h" @@ -924,6 +925,7 @@ class cm_fresnel_physical_iph : public compute_module int hs_type = as_integer("hs_type"); C_csp_power_cycle* c_heat_sink_pointer; C_pc_heat_sink c_heat_sink; + C_pc_heat_sink_physical c_heat_sink_phys; // Ideal heat sink if (hs_type == 0) @@ -958,6 +960,44 @@ class cm_fresnel_physical_iph : public compute_module c_heat_sink_pointer = &c_heat_sink; } + else if (hs_type == 1) + { + size_t n_f_turbine1 = 0; + ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine + double f_turbine_max1 = 1.0; + for (size_t i = 0; i < n_f_turbine1; i++) { + f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); + } + + c_heat_sink_phys.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature + c_heat_sink_phys.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature + c_heat_sink_phys.ms_params.m_q_dot_des = q_dot_pc_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) + // 9.18.2016 twn: assume for now there's no pressure drop though heat sink + c_heat_sink_phys.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] + c_heat_sink_phys.ms_params.m_max_frac = f_turbine_max1; + + c_heat_sink_phys.ms_params.m_pc_fl = as_integer("Fluid"); + c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + + + c_heat_sink_phys.ms_params.m_T_ext_cold_des = as_double("hs_phys_T_steam_cold_des"); //[C] Steam fluid inlet temp + c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target + c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure + c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes + 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_pointer = &c_heat_sink_phys; + } else { throw exec_error("fresnel_physical_iph", "hs_type != 0; other heat sink models are not currently supported"); diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index 1bd84c8d3..a6e0a1107 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "csp_solver_mspt_receiver.h" #include "csp_solver_mspt_collector_receiver.h" #include "csp_solver_pc_heat_sink.h" +#include "csp_solver_pc_heat_sink_physical.h" #include "csp_solver_two_tank_tes.h" #include "csp_dispatch.h" @@ -1221,6 +1222,7 @@ class cm_mspt_iph : public compute_module int hs_type = as_integer("hs_type"); C_csp_power_cycle* c_heat_sink_pointer; C_pc_heat_sink c_heat_sink; + C_pc_heat_sink_physical c_heat_sink_phys; size_t n_f_turbine1 = 0; ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine @@ -1252,9 +1254,47 @@ class cm_mspt_iph : public compute_module c_heat_sink_pointer = &c_heat_sink; } + else if (hs_type == 1) + { + size_t n_f_turbine1 = 0; + ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine + double f_turbine_max1 = 1.0; + for (size_t i = 0; i < n_f_turbine1; i++) { + f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); + } + + c_heat_sink_phys.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature + c_heat_sink_phys.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature + c_heat_sink_phys.ms_params.m_q_dot_des = q_dot_pc_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) + // 9.18.2016 twn: assume for now there's no pressure drop though heat sink + c_heat_sink_phys.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] + c_heat_sink_phys.ms_params.m_max_frac = f_turbine_max1; + + c_heat_sink_phys.ms_params.m_pc_fl = as_integer("rec_htf"); + c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + + + c_heat_sink_phys.ms_params.m_T_ext_cold_des = as_double("hs_phys_T_steam_cold_des"); //[C] Steam fluid inlet temp + c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target + c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure + c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes + 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_pointer = &c_heat_sink_phys; + } else { - throw exec_error("mspt_iph", "hs_type != 0; other heat sink models are not currently supported"); + throw exec_error("mspt_iph", "hs_type must be 0-1"); } diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index d98529f43..9ed134349 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -1155,83 +1155,6 @@ class cm_trough_physical_iph : public compute_module } - // Heat Sink - int heat_sink_type = 0; - C_csp_power_cycle* c_heat_sink_pointer; - C_pc_heat_sink c_heat_sink_simple; - C_pc_heat_sink_physical c_heat_sink_phys; - - if (heat_sink_type == 0) - { - size_t n_f_turbine1 = 0; - ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine - double f_turbine_max1 = 1.0; - for (size_t i = 0; i < n_f_turbine1; i++) { - f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); - } - - c_heat_sink_simple.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature - c_heat_sink_simple.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature - c_heat_sink_simple.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) - // 9.18.2016 twn: assume for now there's no pressure drop though heat sink - c_heat_sink_simple.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] - c_heat_sink_simple.ms_params.m_max_frac = f_turbine_max1; - - c_heat_sink_simple.ms_params.m_pc_fl = as_integer("Fluid"); - c_heat_sink_simple.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); - - // Allocate heat sink outputs - c_heat_sink_simple.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_simple.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_simple.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_simple.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_simple.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_pointer = &c_heat_sink_simple; - } - else if (heat_sink_type == 1) - { - size_t n_f_turbine1 = 0; - ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine - double f_turbine_max1 = 1.0; - for (size_t i = 0; i < n_f_turbine1; i++) { - f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); - } - - c_heat_sink_phys.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature - c_heat_sink_phys.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature - c_heat_sink_phys.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) - // 9.18.2016 twn: assume for now there's no pressure drop though heat sink - c_heat_sink_phys.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] - c_heat_sink_phys.ms_params.m_max_frac = f_turbine_max1; - - c_heat_sink_phys.ms_params.m_pc_fl = as_integer("Fluid"); - c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); - - - c_heat_sink_phys.ms_params.m_T_ext_cold_des = 120; //[C] Steam fluid inlet temp - c_heat_sink_phys.ms_params.m_Q_ext_hot_des = 0.75; //[C] Steam quality target - c_heat_sink_phys.ms_params.m_P_ext_cold_des = 476.1645; //[kPa] Steam inlet pressure - c_heat_sink_phys.ms_params.m_P_ext_hot_des = 476.1645; //[kPa] Steam outlet pressure - c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.2; //[] Min fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; //[] Max fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_N_sub_hx = 50000; //[] Number HX nodes - c_heat_sink_phys.ms_params.m_od_tol = 1e-1; //[] 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_pointer = &c_heat_sink_phys; - } - else - { - throw exec_error("trough_physical", "heat_sink_type must be 0-1"); - } - // Check if system configuration includes a heater parallel to primary collector receiver C_csp_collector_receiver* p_heater; C_csp_cr_electric_resistance* p_electric_resistance = NULL; @@ -1585,9 +1508,10 @@ class cm_trough_physical_iph : public compute_module int hs_type = as_integer("hs_type"); C_csp_power_cycle* c_heat_sink_pointer; C_pc_heat_sink c_heat_sink; + C_pc_heat_sink_physical c_heat_sink_phys; // Ideal heat sink - if(hs_type == 0) + if (hs_type == 0) { //size_t n_f_turbine1 = 0; //ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine @@ -1620,9 +1544,47 @@ class cm_trough_physical_iph : public compute_module c_heat_sink_pointer = &c_heat_sink; } + else if (hs_type == 1) + { + size_t n_f_turbine1 = 0; + ssc_number_t* p_f_turbine1 = as_array("f_turb_tou_periods", &n_f_turbine1); // heat sink, not turbine + double f_turbine_max1 = 1.0; + for (size_t i = 0; i < n_f_turbine1; i++) { + f_turbine_max1 = max(f_turbine_max1, p_f_turbine1[i]); + } + + c_heat_sink_phys.ms_params.m_T_htf_hot_des = T_htf_hot_des; //[C] FIELD design outlet temperature + c_heat_sink_phys.ms_params.m_T_htf_cold_des = T_htf_cold_des; //[C] FIELD design inlet temperature + c_heat_sink_phys.ms_params.m_q_dot_des = q_dot_hs_des; //[MWt] HEAT SINK design thermal power (could have field solar multiple...) + // 9.18.2016 twn: assume for now there's no pressure drop though heat sink + c_heat_sink_phys.ms_params.m_htf_pump_coef = as_double("pb_pump_coef"); //[kWe/kg/s] + c_heat_sink_phys.ms_params.m_max_frac = f_turbine_max1; + + c_heat_sink_phys.ms_params.m_pc_fl = as_integer("Fluid"); + c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); + + + c_heat_sink_phys.ms_params.m_T_ext_cold_des = as_double("hs_phys_T_steam_cold_des"); //[C] Steam fluid inlet temp + c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target + c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure + c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes + 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_pointer = &c_heat_sink_phys; + } else { - throw exec_error("trough_physical_iph", "hs_type != 0; other heat sink models are not currently supported"); + throw exec_error("trough_physical_iph", "hs_type must be 0-1"); } // Electricity pricing schedule From eda47e0d9d0e412c43dcf987d8728a908314b288 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:53:55 -0700 Subject: [PATCH 06/27] Use od_tol for targeting enthalpy --- tcs/heat_exchangers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 8e0637263..50be36d58 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -3195,7 +3195,7 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ C_MEQ__target_cold_PH_out h_out_eq(this, h_c_out_target, h_c_in, P_c_in, P_c_out, h_h_in, P_h_in, P_h_out, m_dot_h, od_tol); C_monotonic_eq_solver h_out_solver(h_out_eq); - h_out_solver.settings(1e-1, max_iter, m_dot_c_min, m_dot_c_max, false); + h_out_solver.settings(od_tol, max_iter, m_dot_c_min, m_dot_c_max, false); double m_dot_c_solved, tol_solved; int iter_solved = -1; From 83b795f84d9964c36f11d807b40af81288414216 Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:07:18 -0700 Subject: [PATCH 07/27] Test varying steam mdot. --- ssc/cmod_csp_heatsink.cpp | 41 +++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp index bf1bd817b..7f84a48e1 100644 --- a/ssc/cmod_csp_heatsink.cpp +++ b/ssc/cmod_csp_heatsink.cpp @@ -96,24 +96,53 @@ class cm_csp_heatsink : public compute_module P_ext_cold, h_ext_cold, P_ext_hot, h_ext_hot, q_design * 1e3, des_solved); // Off Design - double T_htf_hot_od = 250; - double T_htf_cold_od = 200; + double T_htf_hot_od = T_htf_hot; + double T_htf_cold_od = T_htf_cold; + double od_tol = 1e-3; + double mdot_htf_od = 0.5 * m_hx.ms_des_calc_UA_par.m_m_dot_hot_des; double h_htf_hot_od = m_hx.mc_hot_fl.enth(T_htf_hot_od + 273.15) * 1e-3; //[kJ/kg] double h_htf_cold_od = m_hx.mc_hot_fl.enth(T_htf_cold_od + 273.15) * 1e-3; //[kJ/kg] double q_dot_calc, h_ext_out_calc, h_htf_out_calc; - m_hx.off_design_solution_fixed_dP_enth(h_ext_cold, P_ext_cold, m_hx.ms_des_calc_UA_par.m_m_dot_cold_des, P_ext_hot, - h_htf_hot_od, 1.0, m_hx.ms_des_calc_UA_par.m_m_dot_hot_des, 1.0, 1e-12, - q_dot_calc, h_ext_out_calc, h_htf_out_calc); + std::vector mdot_vec; + std::vector h_vec; + //double mdot_min = 0.1; + //double mdot_max = 2 * m_hx.ms_des_calc_UA_par.m_m_dot_cold_des; + double mdot_min = 2.605; + double mdot_max = 2.8; + int total_runs = 20; + for (int i = 0; i < total_runs; i++) + { + double frac = (double)i / (double)total_runs; + double mdot = mdot_min + (frac * (mdot_max - mdot_min)); + try + { + m_hx.off_design_solution_fixed_dP_enth(h_ext_cold, P_ext_cold, mdot, P_ext_hot, + h_htf_hot_od, 1.0, mdot_htf_od, 1.0, od_tol, + q_dot_calc, 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); + } + + + double mdot_ext_calc; m_hx.off_design_target_cold_PH_out(h_ext_hot, 0.1, 2* m_hx.ms_des_calc_UA_par.m_m_dot_cold_des, P_ext_cold, h_ext_cold, - P_ext_hot, 1.0, h_htf_hot_od, 1.0, m_hx.ms_des_calc_UA_par.m_m_dot_hot_des, 1e-3, + 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); + + int x = 0; } }; From 8206cebbdc47e935a106e444b816d980ddb5417c Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:24:17 -0700 Subject: [PATCH 08/27] Fix HX q_dot guess bug. --- tcs/heat_exchangers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 50be36d58..c76112906 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -1475,7 +1475,7 @@ void NS_HX_counterflow_eqs::solve_q_dot_for_fixed_UA_enth(int hot_fl_code /*-*/, double q_dot_mult = max(0.99, min(0.95, eff_limit) / eff_limit); if ( std::isfinite(eff_guess) ) { - q_dot_mult = max(0.99, min(0.1, eff_guess)); + q_dot_mult = min(0.99, max(0.1, eff_guess)); } double q_dot_guess_upper = q_dot_mult*q_dot_upper; From 9db25bdb1ac6727455d730d7ec8cdb9a76f2e4ac Mon Sep 17 00:00:00 2001 From: Taylor Brown <60201147+taylorbrown75@users.noreply.github.com> Date: Fri, 6 Dec 2024 12:03:01 -0700 Subject: [PATCH 09/27] Pass optimizer results even if it fails. Update heat_sink test problem. --- ssc/cmod_csp_heatsink.cpp | 107 +++++++++++------------ tcs/csp_solver_pc_heat_sink_physical.cpp | 39 ++++++++- tcs/heat_exchangers.cpp | 14 +-- tcs/heat_exchangers.h | 3 +- 4 files changed, 96 insertions(+), 67 deletions(-) diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp index 7f84a48e1..a9bbe3f09 100644 --- a/ssc/cmod_csp_heatsink.cpp +++ b/ssc/cmod_csp_heatsink.cpp @@ -53,95 +53,90 @@ class cm_csp_heatsink : public compute_module void exec() { - double dT_subcool = 30; //[dC] Steam temp diff below outlet + // Define Steam Inlet Conditions + double T_ext_cold = 120; //[C] Steam inlet temp + double P_ext_cold = 4.762 * 100.0; //[kPa] Inlet steam pressure - // Define Outlet Steam Conditions + // Get inlet steam properties water_state ms_water_props; - double Q_ext_hot = 0.75; // Outlet Steam Quality - double T_ext_hot = 150; // [C] Outlet Steam Temp - - int prop_error_code = water_TQ(T_ext_hot + 273.15, Q_ext_hot, &ms_water_props); - - double P_ext_hot = ms_water_props.pres; // [kPa] Outlet Steam Pressure - double h_ext_hot = ms_water_props.enth; // [kJ/kg] Outlet Steam Enthalpy - double dens_ext_hot = ms_water_props.dens; //[kg/m3] Outlet steam density - double k_ext_hot = water_cond(dens_ext_hot, T_ext_hot + 273.15); // [W/m-K] - double mu_ext_hot = water_visc(dens_ext_hot, T_ext_hot + 273.15); // [uPa-s] - - // Define Inlet Steam Conditions - double T_ext_cold = T_ext_hot - dT_subcool; // [C] Inlet water temp - double P_ext_cold = P_ext_hot; // [kPa] Inlet Steam Pressure - - prop_error_code = water_TP(T_ext_cold + 273.15, P_ext_cold, &ms_water_props); - + int prop_error_code = water_TP(T_ext_cold + 273.15, P_ext_cold, &ms_water_props); double h_ext_cold = ms_water_props.enth; // [kJ/kg] Inlet water enthalpy double Q_ext_cold = ms_water_props.qual; // [] Inlet water quality (should be 0, n/a) double dens_ext_cold = ms_water_props.dens; // [kg/m3] Inlet water density - double k_ext_cold = water_cond(dens_ext_cold, T_ext_cold + 273.15); // [W/m-K] - double mu_ext_cold = water_visc(dens_ext_cold, T_ext_cold + 273.15); // [uPa-s] + + // Define Outlet Steam Conditions + double Q_ext_hot = 0.75; // Outlet Steam Quality + double P_ext_hot = P_ext_cold; //[kPa] Outlet Steam Pressure + + // Outlet steam properties + prop_error_code = water_PQ(P_ext_hot, Q_ext_hot, &ms_water_props); + double h_ext_hot = ms_water_props.enth; // [kJ/kg] Outlet Steam Enthalpy + double dens_ext_hot = ms_water_props.dens; //[kg/m3] Outlet steam density + double T_ext_hot = ms_water_props.temp - 273.15; // [C] Outlet Steam Temp // Initialize C_HX_htf_to_steam m_hx; int hot_fl = 21; // HTF fl id - int N_sub_hx = 5000; + int N_sub_hx = 500; NS_HX_counterflow_eqs::E_UA_target_type od_target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_constant_UA; m_hx.initialize(hot_fl, N_sub_hx, od_target_type); // Design - double T_htf_hot = 250; //[C] + double T_htf_hot = 300; //[C] double T_htf_cold = 200; //[C] - double q_design = 5.19; //[MW] + double q_design = 5; //[MW] C_HX_counterflow_CRM::S_des_solved des_solved; m_hx.design_w_TP_PH(T_htf_hot + 273.15, 1.0, T_htf_cold + 273.15, 1.0, P_ext_cold, h_ext_cold, P_ext_hot, h_ext_hot, q_design * 1e3, des_solved); // Off Design - double T_htf_hot_od = T_htf_hot; - double T_htf_cold_od = T_htf_cold; - double od_tol = 1e-3; - double mdot_htf_od = 0.5 * m_hx.ms_des_calc_UA_par.m_m_dot_hot_des; + double T_htf_hot_od = 295.9725; //[C] + double od_tol = 1e-5; + double mdot_htf_od = 22.90448; //[kg/s] double h_htf_hot_od = m_hx.mc_hot_fl.enth(T_htf_hot_od + 273.15) * 1e-3; //[kJ/kg] - double h_htf_cold_od = m_hx.mc_hot_fl.enth(T_htf_cold_od + 273.15) * 1e-3; //[kJ/kg] double q_dot_calc, h_ext_out_calc, h_htf_out_calc; std::vector mdot_vec; std::vector h_vec; - //double mdot_min = 0.1; - //double mdot_max = 2 * m_hx.ms_des_calc_UA_par.m_m_dot_cold_des; - double mdot_min = 2.605; - double mdot_max = 2.8; - int total_runs = 20; - for (int i = 0; i < total_runs; i++) + double mdot_min = 0.75 * m_hx.ms_des_calc_UA_par.m_m_dot_cold_des; + double mdot_max = 1.5 * m_hx.ms_des_calc_UA_par.m_m_dot_cold_des; + + // Manually run range of steam mass flow rates + if (true) { - double frac = (double)i / (double)total_runs; - double mdot = mdot_min + (frac * (mdot_max - mdot_min)); - try - { - m_hx.off_design_solution_fixed_dP_enth(h_ext_cold, P_ext_cold, mdot, P_ext_hot, - h_htf_hot_od, 1.0, mdot_htf_od, 1.0, od_tol, - q_dot_calc, h_ext_out_calc, h_htf_out_calc); - } - catch (C_csp_exception exc) + int total_runs = 200; + for (int i = 0; i < total_runs; i++) { - h_ext_out_calc = 0; + double frac = (double)i / (double)total_runs; + double mdot = mdot_min + (frac * (mdot_max - mdot_min)); + try + { + m_hx.off_design_solution_fixed_dP_enth(h_ext_cold, P_ext_cold, mdot, P_ext_hot, + h_htf_hot_od, 1.0, mdot_htf_od, 1.0, od_tol, + q_dot_calc, 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); } - - h_vec.push_back(h_ext_out_calc); - mdot_vec.push_back(mdot); } - - - double mdot_ext_calc; - - m_hx.off_design_target_cold_PH_out(h_ext_hot, 0.1, 2* m_hx.ms_des_calc_UA_par.m_m_dot_cold_des, P_ext_cold, h_ext_cold, + // Optimize to find steam mdot + double mdot_ext_calc, tol_solved; + 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); - - + q_dot_calc, h_ext_out_calc, h_htf_out_calc, mdot_ext_calc, tol_solved); + // Off design Outlet steam properties + prop_error_code = water_PH(P_ext_hot, h_ext_out_calc, &ms_water_props); + double Q_ext_hot_od = ms_water_props.qual; // [] Outlet Steam Quality + double T_ext_hot_od = ms_water_props.temp - 273.15; // [C] Outlet Steam Temp int x = 0; } diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 7a0a69ba4..3631fd3db 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -293,7 +293,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] int standby_control = inputs.m_standby_control; //[-] 1: On, 2: Standby, 3: Off - double q_dot, T_c_out, T_h_out_C, m_dot_c; + double q_dot, T_c_out, T_h_out_C, m_dot_c, tol_solved; // Handle no mass flow coming in if (inputs.m_m_dot < 1e-5 && standby_control == ON) @@ -351,14 +351,14 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather try { // Get HTF inlet enthalpy - double h_htf_cold = mc_pc_htfProps.enth(htf_state_in.m_temp + 273.15) * 1e-3; //[kJ/kg] + 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 = 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_cold, 1.0, m_dot_htf, ms_params.m_od_tol, - q_dot, h_ext_out_calc, h_htf_out_calc, m_dot_c); + 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); if (solve_code == C_monotonic_eq_solver::CONVERGED) { @@ -378,6 +378,37 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather } else { + // 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++) + { + 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); + } + + + int x = 0; + } + + + // Could not solve q_dot = 0; T_h_out_C = htf_state_in.m_temp; diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index c76112906..694ce6499 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -3181,7 +3181,8 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, 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& q_dot /*kWt*/, double& h_c_out /*kJ/kg*/, double& h_h_out /*kJ/kg*/, double& m_dot_c /*kg/s*/, + double& tol_solved) { // ADD inputs double max_iter = 1000; @@ -3197,7 +3198,7 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ C_monotonic_eq_solver h_out_solver(h_out_eq); h_out_solver.settings(od_tol, max_iter, m_dot_c_min, m_dot_c_max, false); - double m_dot_c_solved, tol_solved; + double m_dot_c_solved; int iter_solved = -1; if (false) @@ -3234,14 +3235,15 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] + } else { // Could not converge - q_dot = 0.0; //[kJ] - h_c_out = h_c_in; //[kJ/kg] - h_h_out = h_h_in; //[kJ/kg] - m_dot_c = 0.0; //[kg/s] + q_dot = h_out_eq.m_q_dot; //[kJ] + h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] + h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] + m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] } return m_dot_code; diff --git a/tcs/heat_exchangers.h b/tcs/heat_exchangers.h index 11bed2574..957b501d1 100644 --- a/tcs/heat_exchangers.h +++ b/tcs/heat_exchangers.h @@ -764,7 +764,8 @@ class C_HX_htf_to_steam : public C_HX_counterflow_CRM double P_c_in /*kPa*/, double h_c_in /*kJ/kg*/, double P_c_out /*kPa*/, 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& q_dot /*kWt*/, double& h_c_out /*kJ/kg*/, double& h_h_out /*kJ/kg*/, double& m_dot_c /*kg/s*/, + double& tol_solved); class C_MEQ__target_cold_PH_out : public C_monotonic_equation { From 6ebdb5de3c21aa06244bc5e8721690a77d6a842a Mon Sep 17 00:00:00 2001 From: tyneises Date: Thu, 2 Jan 2025 14:00:02 -0600 Subject: [PATCH 10/27] normalize hx output difference decrease nodes in example cmod --- ssc/cmod_csp_heatsink.cpp | 4 ++-- tcs/heat_exchangers.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp index a9bbe3f09..b9e187253 100644 --- a/ssc/cmod_csp_heatsink.cpp +++ b/ssc/cmod_csp_heatsink.cpp @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static var_info _cm_vtab_csp_heatsink[] = { /* VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS*/ // Inputs - { SSC_INPUT, SSC_NUMBER, "t_step", "Timestep duration", "s", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "t_step", "Timestep duration", "s", "", "system", "", "", "" }, var_info_invalid }; @@ -77,7 +77,7 @@ class cm_csp_heatsink : public compute_module // Initialize C_HX_htf_to_steam m_hx; int hot_fl = 21; // HTF fl id - int N_sub_hx = 500; + int N_sub_hx = 50; NS_HX_counterflow_eqs::E_UA_target_type od_target_type = NS_HX_counterflow_eqs::E_UA_target_type::E_constant_UA; m_hx.initialize(hot_fl, N_sub_hx, od_target_type); diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 694ce6499..00ba14403 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -3258,7 +3258,7 @@ 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); - *diff_h_c_out = (m_h_c_out - m_h_c_out_target) / 200; //[kJ/kg] + *diff_h_c_out = (m_h_c_out - m_h_c_out_target) / m_h_c_out_target; //[kJ/kg] return 0; } From aab8bf8ef934b2ecbd32dd3430295f578427bb98 Mon Sep 17 00:00:00 2001 From: tyneises Date: Thu, 2 Jan 2025 15:53:19 -0600 Subject: [PATCH 11/27] change hx mass flow iteration method to return negative integer on fail --- tcs/csp_solver_pc_heat_sink_physical.cpp | 13 +++----- tcs/heat_exchangers.cpp | 41 +++++++++++++----------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index 3631fd3db..2065cde88 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -285,15 +285,13 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather return; } - - - - // Process inputs double m_dot_htf = inputs.m_m_dot / 3600.0; //[kg/s] 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; // Handle no mass flow coming in if (inputs.m_m_dot < 1e-5 && standby_control == ON) @@ -360,7 +358,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather 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); - if (solve_code == C_monotonic_eq_solver::CONVERGED) + if (solve_code == 0) { // Solved succesfully @@ -407,8 +405,6 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather int x = 0; } - - // Could not solve q_dot = 0; T_h_out_C = htf_state_in.m_temp; @@ -420,13 +416,12 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather 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 = true; + out_solver.m_was_method_successful = false; break; } } catch (C_csp_exception exc) { - double NaN = std::numeric_limits::quiet_NaN(); 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 diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 00ba14403..fc4feef2e 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -3222,31 +3222,34 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ int xasdf = 0; } - - - + // Solve - int m_dot_code = h_out_solver.solve(m_dot_c_min, m_dot_c_max, 0.0, m_dot_c_solved, tol_solved, iter_solved); - - if (m_dot_code == C_monotonic_eq_solver::CONVERGED) + int m_dot_code = -1; + try { - // Converge Succesfully - q_dot = h_out_eq.m_q_dot; //[kJ] - h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] - h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] - m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] - + m_dot_code = h_out_solver.solve(m_dot_c_min, m_dot_c_max, 0.0, m_dot_c_solved, tol_solved, iter_solved); } - else + catch (C_csp_exception) + { + return -1; + } + + if (m_dot_code != C_monotonic_eq_solver::CONVERGED) { - // Could not converge - q_dot = h_out_eq.m_q_dot; //[kJ] - h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] - h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] - m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] + if (!(m_dot_code > C_monotonic_eq_solver::CONVERGED && std::abs(tol_solved) <= 0.01)) + { + return -2; + } } - return m_dot_code; + // Converge Succesfully + q_dot = h_out_eq.m_q_dot; //[kJ] + h_c_out = h_out_eq.m_h_c_out; //[kJ/kg] + h_h_out = h_out_eq.m_h_h_out; //[kJ/kg] + m_dot_c = h_out_eq.m_m_dot_c; //[kg/s] + + + return 0; } int C_HX_htf_to_steam::C_MEQ__target_cold_PH_out::operator()(double m_dot_c /*kg/s*/, double* diff_h_c_out /*kJ/kg*/) From a7f6897e2a55257db503187fe139d598f161bb1e Mon Sep 17 00:00:00 2001 From: tyneises Date: Fri, 3 Jan 2025 09:25:20 -0600 Subject: [PATCH 12/27] add htf-to-sco2 hx test to heat sink cmod --- ssc/cmod_csp_heatsink.cpp | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ssc/cmod_csp_heatsink.cpp b/ssc/cmod_csp_heatsink.cpp index b9e187253..6d6d047bd 100644 --- a/ssc/cmod_csp_heatsink.cpp +++ b/ssc/cmod_csp_heatsink.cpp @@ -52,6 +52,56 @@ class cm_csp_heatsink : public compute_module void exec() { + // Test HTF-CO2 HX design + if (false) { + C_HX_co2_to_htf mc_phx; + + int hot_fl = 17; + + NS_HX_counterflow_eqs::E_UA_target_type ua_type = NS_HX_counterflow_eqs::E_UA_target_type::E_constant_UA; + + mc_phx.initialize(hot_fl, 10, ua_type); + + double q_dot = 1000.0; //[kWt] + double T_co2_hot = 700.0; //[C] + double P_co2 = 25000.0; //[kPa] + double T_co2_cold = 500.0; //[C] + + CO2_state co2_props; + CO2_TP(T_co2_hot + 273.17, P_co2, &co2_props); + + double h_co2_hot = co2_props.enth; + + CO2_TP(T_co2_cold + 273.15, P_co2, &co2_props); + + double h_co2_cold = co2_props.enth; + + double m_dot_co2 = q_dot / (h_co2_hot - h_co2_cold); + + C_HX_counterflow_CRM::S_des_calc_UA_par des_par; + des_par.m_T_h_in = 720.0; + des_par.m_P_h_in = 100.0; //[kPa] + des_par.m_P_h_out = 100.0; //[kPa] + des_par.m_m_dot_cold_des = m_dot_co2; //[kg/s] cold fluid design mass flow rate + + des_par.m_T_c_in = T_co2_cold; //[K] Design-point cold inlet temperature + des_par.m_P_c_in = P_co2; //[kPa] Cold fluid inlet temperature + des_par.m_P_c_out = P_co2; //[kPa] Cold fluid outlet temperature + + /* + double m_m_dot_hot_des; //[kg/s] hot fluid design mass flow rate + double m_eff_max; //[-] Max allowable effectiveness + */ + C_HX_counterflow_CRM::S_des_solved ms_phx_des_solved; + mc_phx.design_and_calc_m_dot_htf(des_par, q_dot, 20, ms_phx_des_solved); + + double UA_calc = ms_phx_des_solved.m_UA_design; + double T_htf_out = ms_phx_des_solved.m_T_h_out; + double m_dot_htf = des_par.m_m_dot_hot_des; + + double blahhh = 1.23; + + } // Define Steam Inlet Conditions double T_ext_cold = 120; //[C] Steam inlet temp From 57df6999f170d61ff60bfdc89872a4987aa7c413 Mon Sep 17 00:00:00 2001 From: tyneises Date: Wed, 8 Jan 2025 14:02:11 -0600 Subject: [PATCH 13/27] set up mass flow rate guesses for htf-steam hx iteration --- tcs/heat_exchangers.cpp | 94 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 8 deletions(-) diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index fc4feef2e..1253f1df3 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -3188,19 +3188,94 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ double max_iter = 1000; double slvr_tol = od_tol; - double mdot_guess = this->ms_des_calc_UA_par.m_m_dot_cold_des; - - double q_dot_local, T_c_out_local, T_h_out_local, m_dot_c_local; - double P_c_out_local, P_h_out_local; C_MEQ__target_cold_PH_out h_out_eq(this, h_c_out_target, h_c_in, P_c_in, P_c_out, h_h_in, P_h_in, P_h_out, m_dot_h, od_tol); C_monotonic_eq_solver h_out_solver(h_out_eq); h_out_solver.settings(od_tol, max_iter, m_dot_c_min, m_dot_c_max, false); - double m_dot_c_solved; - int iter_solved = -1; + double mdot_guess = ms_des_calc_UA_par.m_m_dot_cold_des * (m_dot_h / ms_des_calc_UA_par.m_m_dot_hot_des); + + double delta_h_target_rel = std::numeric_limits::quiet_NaN(); + + double m_dot_guess_i = std::min(mdot_guess, m_dot_c_max); + + C_monotonic_eq_solver::S_xy_pair xy_1; + xy_1.x = m_dot_guess_i; //[kg/s] + + C_monotonic_eq_solver::S_xy_pair xy_2; + + int solver_code = h_out_solver.test_member_function(xy_1.x, &delta_h_target_rel); + + double adj = 0.1; + + if (solver_code != 0) { + while (solver_code != 0) + { + if (m_dot_guess_i > m_dot_c_max) { + return -3; + } + + m_dot_guess_i *= 1.0 + adj; // increase guess if solver code != 0 + + xy_1.x = std::min(m_dot_guess_i, m_dot_c_max); + solver_code = h_out_solver.test_member_function(xy_1.x, &delta_h_target_rel); + } + xy_1.y = delta_h_target_rel; + + // if first successful x is the max, then we're going to have trouble finding a second value + if (xy_1.x == m_dot_c_max) { + xy_2.x = xy_1.x * 0.995; + + solver_code = h_out_solver.test_member_function(xy_2.x, &delta_h_target_rel); + + if (solver_code != 0) { + return -4; + } + + xy_2.y = delta_h_target_rel; + } + } + else { + xy_1.y = delta_h_target_rel; + + // if error is negative, then outlet enthalpy is too low and mass flow rate is too high + if (delta_h_target_rel < 0.0) { + + // So decrease mass flow rate to get new guess + // Decreasing mass flow rate can lead to pinch fails, so cut down 'adj' a bit + xy_2.x = xy_1.x * (1.0 - 0.5 * adj); + + solver_code = h_out_solver.test_member_function(xy_2.x, &delta_h_target_rel); + + // 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); + + } + } + if (delta_h_target_rel > 0.0 || solver_code != 0) { + + // Increase mass flow rate to get new guess + xy_2.x = std::min(xy_1.x * (1.0 + adj), m_dot_c_max); + + solver_code = h_out_solver.test_member_function(xy_2.x, &delta_h_target_rel); + + // If higher mass flow rate fails, try a guess close to xy1 guess + // If that fails, give up + if (solver_code != 0) { + xy_2.x = std::min(xy_1.x * 1.01, m_dot_c_max); + solver_code = h_out_solver.test_member_function(xy_2.x, &delta_h_target_rel); + + if (solver_code != 0) { + return -5; + } + } + } + } + + /* if (false) { double frac = (m_dot_c_max - m_dot_c_min) / 1000; @@ -3221,13 +3296,16 @@ int C_HX_htf_to_steam::off_design_target_cold_PH_out(double h_c_out_target /*kJ/ } int xasdf = 0; - } + } */ // Solve + double m_dot_c_solved; + int iter_solved = -1; int m_dot_code = -1; try { - m_dot_code = h_out_solver.solve(m_dot_c_min, m_dot_c_max, 0.0, m_dot_c_solved, tol_solved, iter_solved); + //m_dot_code = h_out_solver.solve(m_dot_c_min, m_dot_c_max, 0.0, m_dot_c_solved, tol_solved, iter_solved); + m_dot_code = h_out_solver.solve(xy_1, xy_2, 0.0, m_dot_c_solved, tol_solved, iter_solved); } catch (C_csp_exception) { From ffc7c9ecb8de32711c41ef6149d6af6373b0c35d Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 10:35:22 -0600 Subject: [PATCH 14/27] calc and report timestep solution duration --- ssc/cmod_trough_physical_iph.cpp | 4 +++- tcs/csp_solver_core.cpp | 8 ++++++++ tcs/csp_solver_core.h | 1 + tcs/csp_solver_util.cpp | 16 ++++++++++++++-- tcs/csp_solver_util.h | 3 ++- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 6db7026c8..499bdec50 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -515,7 +515,8 @@ static var_info _cm_vtab_trough_physical_iph[] = { // Simulation Kernel { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "solver", "sim_type=1", "", "" }, - + { SSC_OUTPUT, SSC_ARRAY, "timestep_sim_duration", "Simulation duration of timestep", "s", "", "solver", "sim_type=1", "", "" }, + // Weather Reader { SSC_OUTPUT, SSC_ARRAY, "month", "Resource Month", "", "", "weather", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "hour_day", "Resource Hour of Day", "", "", "weather", "sim_type=1", "", "" }, @@ -1747,6 +1748,7 @@ class cm_trough_physical_iph : public compute_module { // Simulation Kernel csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TIME_FINAL, allocate("time_hr", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SIM_DURATION, allocate("timestep_sim_duration", n_steps_fixed), n_steps_fixed); // Weather reader csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::MONTH, allocate("month", n_steps_fixed), n_steps_fixed); csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::HOUR_DAY, allocate("hour_day", n_steps_fixed), n_steps_fixed); diff --git a/tcs/csp_solver_core.cpp b/tcs/csp_solver_core.cpp index ce321c32f..e21c65e96 100644 --- a/tcs/csp_solver_core.cpp +++ b/tcs/csp_solver_core.cpp @@ -174,6 +174,7 @@ static C_csp_reported_outputs::S_output_info S_solver_output_info[] = // Ouputs that are NOT reported as weighted averages // Simulation {C_csp_solver::C_solver_outputs::TIME_FINAL, C_csp_reported_outputs::TS_LAST}, //[hr] + {C_csp_solver::C_solver_outputs::SIM_DURATION, C_csp_reported_outputs::SUMMED}, //[s] // Weather Reader { C_csp_solver::C_solver_outputs::MONTH, C_csp_reported_outputs::TS_1ST}, //[-] Month of year { C_csp_solver::C_solver_outputs::HOUR_DAY, C_csp_reported_outputs::TS_1ST}, //[hr] hour of day @@ -597,6 +598,8 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) while( mc_kernel.mc_sim_info.ms_ts.m_time <= mc_kernel.get_sim_setup()->m_sim_time_end ) { + std::clock_t clock_start = std::clock(); + // Report simulation progress double calc_frac_current = (mc_kernel.mc_sim_info.ms_ts.m_time - mc_kernel.get_sim_setup()->m_sim_time_start) / (mc_kernel.get_sim_setup()->m_sim_time_end - mc_kernel.get_sim_setup()->m_sim_time_start); if( calc_frac_current > progress_msg_frac_current ) @@ -1126,6 +1129,11 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) mc_pc_out_solver.m_q_dot_htf - mc_tes_outputs.m_q_dot_ch_from_htf) / m_cycle_q_dot_des; //[-] + std::clock_t clock_end = std::clock(); + double timestep_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + + mc_reported_outputs.value(C_solver_outputs::SIM_DURATION, timestep_cpu_run_time); + mc_reported_outputs.value(C_solver_outputs::ERR_M_DOT, m_dot_bal_max); mc_reported_outputs.value(C_solver_outputs::ERR_Q_DOT, q_dot_bal); diff --git a/tcs/csp_solver_core.h b/tcs/csp_solver_core.h index 0469d5cf9..30a915581 100644 --- a/tcs/csp_solver_core.h +++ b/tcs/csp_solver_core.h @@ -844,6 +844,7 @@ class C_csp_solver // Ouputs that are NOT reported as weighted averages // Simulation TIME_FINAL, //[hr] Simulation timestep + SIM_DURATION, //[s] Timestep simulation duration // Weather Reader MONTH, //[-] Month of year HOUR_DAY, //[hr] hour of day diff --git a/tcs/csp_solver_util.cpp b/tcs/csp_solver_util.cpp index 25e36b818..4dd839e25 100644 --- a/tcs/csp_solver_util.cpp +++ b/tcs/csp_solver_util.cpp @@ -70,7 +70,8 @@ void C_csp_reported_outputs::C_output::set_m_is_ts_weighted(int subts_weight_typ m_subts_weight_type == TS_1ST || m_subts_weight_type == TS_LAST || m_subts_weight_type == TS_MAX || - m_subts_weight_type == DEPENDENT )) + m_subts_weight_type == DEPENDENT || + m_subts_weight_type == SUMMED)) { throw(C_csp_exception("C_csp_reported_outputs::C_output::send_to_reporting_ts_array did not recognize subtimestep weighting type")); } @@ -166,7 +167,18 @@ void C_csp_reported_outputs::C_output::send_to_reporting_ts_array(double report_ // if multiple csp-timesteps for one reporting timestep // ************************************************************ mp_reporting_ts_array[m_counter_reporting_ts_array] =(float)(*std::max_element(mv_temp_outputs.begin(), mv_temp_outputs.end())); - } + } + else if (m_subts_weight_type == SUMMED){ + // ******************************************************* + // If multiple csp-timesteps for one reporting timestep, sum them + // --- original use case is timestep simulation duration + // ************************************************************* + double sum_val = 0; + for (size_t i = 0; i < n_report; i++) { + sum_val += (float)mv_temp_outputs[i]; + } + mp_reporting_ts_array[m_counter_reporting_ts_array] = sum_val; + } else { throw(C_csp_exception("C_csp_reported_outputs::C_output::send_to_reporting_ts_array did not recognize subtimestep weighting type")); diff --git a/tcs/csp_solver_util.h b/tcs/csp_solver_util.h index 09a7d60a9..0efe5f985 100644 --- a/tcs/csp_solver_util.h +++ b/tcs/csp_solver_util.h @@ -50,7 +50,8 @@ class C_csp_reported_outputs TS_1ST, TS_LAST, TS_MAX, - DEPENDENT + DEPENDENT, + SUMMED }; enum E_AB_relationship From 4d45a29afdff33893405f7e5848b67f499fa1ffa Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 14:19:58 -0600 Subject: [PATCH 15/27] try fixing new subtimestep aggregator for linux --- tcs/csp_solver_util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcs/csp_solver_util.cpp b/tcs/csp_solver_util.cpp index 4dd839e25..38d2d650e 100644 --- a/tcs/csp_solver_util.cpp +++ b/tcs/csp_solver_util.cpp @@ -175,9 +175,9 @@ void C_csp_reported_outputs::C_output::send_to_reporting_ts_array(double report_ // ************************************************************* double sum_val = 0; for (size_t i = 0; i < n_report; i++) { - sum_val += (float)mv_temp_outputs[i]; + sum_val += mv_temp_outputs[i]; } - mp_reporting_ts_array[m_counter_reporting_ts_array] = sum_val; + mp_reporting_ts_array[m_counter_reporting_ts_array] = (float)sum_val; } else { From 1b3f59649944736e8daf8ff1d4a037123d741827 Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 14:47:22 -0600 Subject: [PATCH 16/27] try fixing new subtimestep aggregator for linux v2 --- ssc/cmod_trough_physical_iph.cpp | 4 ++-- tcs/csp_solver_util.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 499bdec50..7b88413dd 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -515,7 +515,7 @@ static var_info _cm_vtab_trough_physical_iph[] = { // Simulation Kernel { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "solver", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "timestep_sim_duration", "Simulation duration of timestep", "s", "", "solver", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "timestep_sim_duration", "Simulation duration of timestep", "s", "", "solver", "sim_type=1", "", "" }, // Weather Reader { SSC_OUTPUT, SSC_ARRAY, "month", "Resource Month", "", "", "weather", "sim_type=1", "", "" }, @@ -1748,7 +1748,7 @@ class cm_trough_physical_iph : public compute_module { // Simulation Kernel csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TIME_FINAL, allocate("time_hr", n_steps_fixed), n_steps_fixed); - csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SIM_DURATION, allocate("timestep_sim_duration", n_steps_fixed), n_steps_fixed); + //csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SIM_DURATION, allocate("timestep_sim_duration", n_steps_fixed), n_steps_fixed); // Weather reader csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::MONTH, allocate("month", n_steps_fixed), n_steps_fixed); csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::HOUR_DAY, allocate("hour_day", n_steps_fixed), n_steps_fixed); diff --git a/tcs/csp_solver_util.h b/tcs/csp_solver_util.h index 0efe5f985..820e84ef2 100644 --- a/tcs/csp_solver_util.h +++ b/tcs/csp_solver_util.h @@ -50,8 +50,8 @@ class C_csp_reported_outputs TS_1ST, TS_LAST, TS_MAX, - DEPENDENT, - SUMMED + SUMMED, + DEPENDENT }; enum E_AB_relationship From a54ece610d5ac6f94795ba56424d9d68945c39ff Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 14:52:44 -0600 Subject: [PATCH 17/27] try fixing new subtimestep aggregator for linux v3 --- tcs/csp_solver_core.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tcs/csp_solver_core.cpp b/tcs/csp_solver_core.cpp index e21c65e96..41fb891f9 100644 --- a/tcs/csp_solver_core.cpp +++ b/tcs/csp_solver_core.cpp @@ -598,7 +598,7 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) while( mc_kernel.mc_sim_info.ms_ts.m_time <= mc_kernel.get_sim_setup()->m_sim_time_end ) { - std::clock_t clock_start = std::clock(); + //std::clock_t clock_start = std::clock(); // Report simulation progress double calc_frac_current = (mc_kernel.mc_sim_info.ms_ts.m_time - mc_kernel.get_sim_setup()->m_sim_time_start) / (mc_kernel.get_sim_setup()->m_sim_time_end - mc_kernel.get_sim_setup()->m_sim_time_start); @@ -1129,10 +1129,10 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) mc_pc_out_solver.m_q_dot_htf - mc_tes_outputs.m_q_dot_ch_from_htf) / m_cycle_q_dot_des; //[-] - std::clock_t clock_end = std::clock(); - double timestep_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + //std::clock_t clock_end = std::clock(); + //double timestep_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] - mc_reported_outputs.value(C_solver_outputs::SIM_DURATION, timestep_cpu_run_time); + //mc_reported_outputs.value(C_solver_outputs::SIM_DURATION, timestep_cpu_run_time); mc_reported_outputs.value(C_solver_outputs::ERR_M_DOT, m_dot_bal_max); mc_reported_outputs.value(C_solver_outputs::ERR_Q_DOT, q_dot_bal); From 8ab3e82884dcf6630b0ce9aff895f7f7ad3644c2 Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 15:05:00 -0600 Subject: [PATCH 18/27] try fixing new subtimestep aggregator for linux v4 --- tcs/csp_solver_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcs/csp_solver_core.cpp b/tcs/csp_solver_core.cpp index 41fb891f9..3f6fd9093 100644 --- a/tcs/csp_solver_core.cpp +++ b/tcs/csp_solver_core.cpp @@ -179,7 +179,7 @@ static C_csp_reported_outputs::S_output_info S_solver_output_info[] = { C_csp_solver::C_solver_outputs::MONTH, C_csp_reported_outputs::TS_1ST}, //[-] Month of year { C_csp_solver::C_solver_outputs::HOUR_DAY, C_csp_reported_outputs::TS_1ST}, //[hr] hour of day // Controller, TES, & Dispatch - {C_csp_solver::C_solver_outputs::ERR_M_DOT, C_csp_reported_outputs::TS_1ST}, //[-] Relative mass conservation error + {C_csp_solver::C_solver_outputs::ERR_M_DOT, C_csp_reported_outputs::SUMMED}, //[-] Relative mass conservation error {C_csp_solver::C_solver_outputs::ERR_Q_DOT, C_csp_reported_outputs::TS_1ST}, //[-] Relative energy conservation error {C_csp_solver::C_solver_outputs::N_OP_MODES, C_csp_reported_outputs::TS_LAST}, //[-] Number of subtimesteps in reporting timestep {C_csp_solver::C_solver_outputs::OP_MODE_1, C_csp_reported_outputs::TS_1ST}, //[-] Operating mode in first subtimestep From 28520c9d0e31d150c0ef56a98e82ffda1b6d36c8 Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 15:13:47 -0600 Subject: [PATCH 19/27] try fixing new subtimestep aggregator for linux v5 --- tcs/csp_solver_core.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tcs/csp_solver_core.cpp b/tcs/csp_solver_core.cpp index 3f6fd9093..e0a5a0383 100644 --- a/tcs/csp_solver_core.cpp +++ b/tcs/csp_solver_core.cpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include + #undef min #undef max @@ -179,7 +181,7 @@ static C_csp_reported_outputs::S_output_info S_solver_output_info[] = { C_csp_solver::C_solver_outputs::MONTH, C_csp_reported_outputs::TS_1ST}, //[-] Month of year { C_csp_solver::C_solver_outputs::HOUR_DAY, C_csp_reported_outputs::TS_1ST}, //[hr] hour of day // Controller, TES, & Dispatch - {C_csp_solver::C_solver_outputs::ERR_M_DOT, C_csp_reported_outputs::SUMMED}, //[-] Relative mass conservation error + {C_csp_solver::C_solver_outputs::ERR_M_DOT, C_csp_reported_outputs::TS_1ST}, //[-] Relative mass conservation error {C_csp_solver::C_solver_outputs::ERR_Q_DOT, C_csp_reported_outputs::TS_1ST}, //[-] Relative energy conservation error {C_csp_solver::C_solver_outputs::N_OP_MODES, C_csp_reported_outputs::TS_LAST}, //[-] Number of subtimesteps in reporting timestep {C_csp_solver::C_solver_outputs::OP_MODE_1, C_csp_reported_outputs::TS_1ST}, //[-] Operating mode in first subtimestep @@ -598,7 +600,7 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) while( mc_kernel.mc_sim_info.ms_ts.m_time <= mc_kernel.get_sim_setup()->m_sim_time_end ) { - //std::clock_t clock_start = std::clock(); + std::clock_t clock_start = std::clock(); // Report simulation progress double calc_frac_current = (mc_kernel.mc_sim_info.ms_ts.m_time - mc_kernel.get_sim_setup()->m_sim_time_start) / (mc_kernel.get_sim_setup()->m_sim_time_end - mc_kernel.get_sim_setup()->m_sim_time_start); @@ -1129,10 +1131,10 @@ void C_csp_solver::Ssimulate(C_csp_solver::S_sim_setup & sim_setup) mc_pc_out_solver.m_q_dot_htf - mc_tes_outputs.m_q_dot_ch_from_htf) / m_cycle_q_dot_des; //[-] - //std::clock_t clock_end = std::clock(); - //double timestep_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + std::clock_t clock_end = std::clock(); + double timestep_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] - //mc_reported_outputs.value(C_solver_outputs::SIM_DURATION, timestep_cpu_run_time); + mc_reported_outputs.value(C_solver_outputs::SIM_DURATION, timestep_cpu_run_time); mc_reported_outputs.value(C_solver_outputs::ERR_M_DOT, m_dot_bal_max); mc_reported_outputs.value(C_solver_outputs::ERR_Q_DOT, q_dot_bal); From b543909d6a3e834c7022413aef30537fd74542ec Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 13 Jan 2025 15:23:19 -0600 Subject: [PATCH 20/27] fixed new subtimestep aggregator linux bug --- ssc/cmod_trough_physical_iph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 7b88413dd..499bdec50 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -515,7 +515,7 @@ static var_info _cm_vtab_trough_physical_iph[] = { // Simulation Kernel { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "solver", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "timestep_sim_duration", "Simulation duration of timestep", "s", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "timestep_sim_duration", "Simulation duration of timestep", "s", "", "solver", "sim_type=1", "", "" }, // Weather Reader { SSC_OUTPUT, SSC_ARRAY, "month", "Resource Month", "", "", "weather", "sim_type=1", "", "" }, @@ -1748,7 +1748,7 @@ class cm_trough_physical_iph : public compute_module { // Simulation Kernel csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TIME_FINAL, allocate("time_hr", n_steps_fixed), n_steps_fixed); - //csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SIM_DURATION, allocate("timestep_sim_duration", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SIM_DURATION, allocate("timestep_sim_duration", n_steps_fixed), n_steps_fixed); // Weather reader csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::MONTH, allocate("month", n_steps_fixed), n_steps_fixed); csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::HOUR_DAY, allocate("hour_day", n_steps_fixed), n_steps_fixed); From 50491f25d8819f866e92c32a6e983845bb1d6f5c Mon Sep 17 00:00:00 2001 From: tyneises Date: Fri, 17 Jan 2025 14:26:45 -0600 Subject: [PATCH 21/27] add to trough iph htf to steam hx code --- ssc/cmod_csp_heatsink.cpp | 4 +- ssc/cmod_trough_physical_iph.cpp | 49 +++- tcs/csp_solver_core.cpp | 2 +- tcs/csp_solver_pc_heat_sink_physical.cpp | 298 +++++++++-------------- tcs/csp_solver_pc_heat_sink_physical.h | 18 +- tcs/heat_exchangers.cpp | 25 +- tcs/heat_exchangers.h | 5 +- 7 files changed, 199 insertions(+), 202 deletions(-) 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*/); }; From 15a30233bb06368eb784029a172a2e75ba7df897 Mon Sep 17 00:00:00 2001 From: tyneises Date: Fri, 17 Jan 2025 14:55:27 -0600 Subject: [PATCH 22/27] fix bug in hx UA calc when cold stream is 2 phase --- tcs/heat_exchangers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcs/heat_exchangers.cpp b/tcs/heat_exchangers.cpp index 85995ed62..8328a7b68 100644 --- a/tcs/heat_exchangers.cpp +++ b/tcs/heat_exchangers.cpp @@ -792,7 +792,7 @@ void NS_HX_counterflow_eqs::calc_req_UA_enth(int hot_fl_code /*-*/, HTFPropertie C_dot_min = m_dot_c * cp_c_avg; // [kW/K] cold stream capacitance rate C_R = 0.0; } - else if (!is_c_2phase && is_h_2phase) + else if (!is_h_2phase && is_c_2phase) { C_dot_min = m_dot_h * cp_h_avg; // [kW/K] hot stream capacitance rate C_R = 0.0; From 345c91a578e83dc01d3c6d3a0db2ec18242fc740 Mon Sep 17 00:00:00 2001 From: tyneises Date: Fri, 24 Jan 2025 15:59:27 -0600 Subject: [PATCH 23/27] update trough op mode test for recent code improvements --- test/ssc_test/cmod_trough_physical_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ssc_test/cmod_trough_physical_test.cpp b/test/ssc_test/cmod_trough_physical_test.cpp index 64c9af1c2..1b7c303d0 100644 --- a/test/ssc_test/cmod_trough_physical_test.cpp +++ b/test/ssc_test/cmod_trough_physical_test.cpp @@ -84,7 +84,7 @@ NAMESPACE_TEST(csp_trough, PowerTroughCmod, Default_NoFinancial) EXPECT_NEAR_FRAC(power_trough.GetOutputSum("defocus"), 8747, kErrorToleranceHi); EXPECT_NEAR_FRAC(power_trough.GetOutputSum("q_dc_tes"), 343833, kErrorToleranceHi); EXPECT_NEAR_FRAC(power_trough.GetOutputSum("P_fixed"), 5348, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutputSum("op_mode_1"), 53565, kErrorToleranceHi); + EXPECT_NEAR_FRAC(power_trough.GetOutputSum("op_mode_1"), 54965, kErrorToleranceHi); EXPECT_NEAR_FRAC(power_trough.GetOutputSum("n_op_modes"), 9800, kErrorToleranceHi); EXPECT_NEAR_FRAC(power_trough.GetOutputSum("is_rec_su_allowed"), 8759, kErrorToleranceHi); //EXPECT_NEAR_FRAC(power_trough.GetOutputSum("operating_modes_a"), 35458021, kErrorToleranceHi); From 6ab6bcdeef1393e65c2929dcc5808d00312b1a0b Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 3 Feb 2025 11:09:12 -0600 Subject: [PATCH 24/27] remove and add some inputs to trough iph cmod --- ssc/cmod_trough_physical_iph.cpp | 25 ++++++++++++++---------- tcs/csp_solver_pc_heat_sink_physical.cpp | 3 ++- tcs/csp_solver_pc_heat_sink_physical.h | 3 +++ 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index bcc388867..771e67dc9 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -173,10 +173,10 @@ static var_info _cm_vtab_trough_physical_iph[] = { { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Heat Sink", "*", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_type", "0: ideal model, 1: physical steam model", "", "", "Heat Sink", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_T_steam_cold_des", "Steam inlet temperature for physical heat sink", "C", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_P_steam_hot_des", "Steam outlet (and inlet) pressure for physical heat sink", "bar", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_Q_steam_hot_des", "Steam outlet quality for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, @@ -457,6 +457,7 @@ static var_info _cm_vtab_trough_physical_iph[] = { { 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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "hx_UA_des", "Heat sink hx conductance", "MW/K", "", "System Control", "*", "", "" }, // System Control @@ -1579,10 +1580,12 @@ class cm_trough_physical_iph : public compute_module c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure - c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes - c_heat_sink_phys.ms_params.m_od_tol = as_double("hs_phys_tol"); //[] HX off design tolerance + + // 25.01.30 twn: hardcode these for now so users don't have to set them + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.01; // as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; // as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = 15; // as_double("hs_phys_N_sub"); //[] Number HX nodes + c_heat_sink_phys.ms_params.m_od_tol = 0.001; // 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_physical::E_Q_DOT_HEAT_SINK, allocate("q_dot_to_heat_sink", n_steps_fixed), n_steps_fixed); @@ -2156,17 +2159,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(); + double m_dot_hs_ext_des, T_hs_ext_out_des, hx_min_dT_des, hx_UA_des; + m_dot_hs_ext_des = T_hs_ext_out_des = hx_min_dT_des = hx_UA_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] + hx_UA_des = c_heat_sink_phys.get_hx_UA_des(); //[kW/K] } 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); + assign("hx_UA_des", hx_UA_des); } // Calculate Costs and assign outputs diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index f5e3f78c8..e541bf036 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -59,7 +59,7 @@ C_pc_heat_sink_physical::C_pc_heat_sink_physical() 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_T_ext_hot_des = m_hx_UA_des = std::numeric_limits::quiet_NaN(); m_did_init_pass = false; } @@ -161,6 +161,7 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa 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_hx_UA_des = mc_hx_des_solved.m_UA_design; //[kW/K] // Assign Design HTF mdot m_m_dot_htf_des = m_hx.ms_des_calc_UA_par.m_m_dot_hot_des; //[kg/s] diff --git a/tcs/csp_solver_pc_heat_sink_physical.h b/tcs/csp_solver_pc_heat_sink_physical.h index f081ded31..9c9c25a58 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.h +++ b/tcs/csp_solver_pc_heat_sink_physical.h @@ -74,6 +74,8 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle double m_h_ext_hot_des; // [kJ/kg] Steam target outlet enthalpy double m_T_ext_hot_des; //[C] + double m_hx_UA_des; //[kW/K] + bool m_did_init_pass; //[-] HTFProperties mc_pc_htfProps; @@ -87,6 +89,7 @@ class C_pc_heat_sink_physical : public C_csp_power_cycle 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; } + double get_hx_UA_des() { return m_hx_UA_des; } struct S_params { From 497041caa0d62f3f393f4341c761c3a93605e12f Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 3 Feb 2025 13:36:52 -0600 Subject: [PATCH 25/27] switch to enth and temp lookup methods --- tcs/csp_solver_pc_heat_sink_physical.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcs/csp_solver_pc_heat_sink_physical.cpp b/tcs/csp_solver_pc_heat_sink_physical.cpp index e541bf036..21563fde8 100644 --- a/tcs/csp_solver_pc_heat_sink_physical.cpp +++ b/tcs/csp_solver_pc_heat_sink_physical.cpp @@ -91,7 +91,7 @@ void C_pc_heat_sink_physical::init(C_csp_power_cycle::S_solved_params &solved_pa // Declare instance of fluid class for FIELD fluid if( ms_params.m_pc_fl != HTFProperties::User_defined && ms_params.m_pc_fl < HTFProperties::End_Library_Fluids ) { - if( !mc_pc_htfProps.SetFluid(ms_params.m_pc_fl) ) + if( !mc_pc_htfProps.SetFluid(ms_params.m_pc_fl, true) ) { throw(C_csp_exception("Power cycle HTF code is not recognized", "Rankine Indirect Power Cycle Initialization")); } @@ -297,7 +297,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather case STANDBY: { // Get HTF inlet enthalpy - double h_htf_hot = mc_pc_htfProps.enth(htf_state_in.m_temp + 273.15) * 1e-3; //[kJ/kg] + double h_htf_hot = mc_pc_htfProps.enth_lookup(htf_state_in.m_temp + 273.15); //[kJ/kg] // Run Off design to find steam mdot to hit enthalpy target double h_ext_out_calc, h_htf_out_calc; @@ -322,7 +322,7 @@ void C_pc_heat_sink_physical::call(const C_csp_weatherreader::S_outputs &weather // Solved succesfully // Get HTF temperature from enthalpy - T_h_out_C = mc_pc_htfProps.temp(h_htf_out_calc*1e3); //[C] + T_h_out_C = mc_pc_htfProps.temp_lookup(h_htf_out_calc) - 273.15; //[C] out_solver.m_P_cycle = 0.0; //[MWe] No electricity generation out_solver.m_T_htf_cold = T_h_out_C; //[C] From 1f1376094e4fa47871ee7a3e94989540991dfefa Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 3 Feb 2025 15:02:03 -0600 Subject: [PATCH 26/27] add mspt iph outputs --- ssc/cmod_mspt_iph.cpp | 106 ++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 62 deletions(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index 358d8c698..c4e898205 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -226,10 +226,10 @@ static var_info _cm_vtab_mspt_iph[] = { { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Heat Sink", "*", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_type", "0: ideal model, 1: physical steam model", "", "", "Heat Sink", "?=0", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_T_steam_cold_des", "Steam inlet temperature for physical heat sink", "C", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_P_steam_hot_des", "Steam outlet (and inlet) pressure for physical heat sink", "bar", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_Q_steam_hot_des", "Steam outlet quality for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, @@ -402,11 +402,11 @@ static var_info _cm_vtab_mspt_iph[] = { { SSC_OUTPUT, SSC_NUMBER, "W_dot_heater_des", "Heater electricity consumption at design", "MWe", "", "Heater", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "E_heater_su_des", "Heater startup energy", "MWht", "", "Heater", "*", "", "" }, -// Power Cycle -//{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_cycle_des", "PC HTF mass flow rate at design", "kg/s", "", "Power Cycle", "*", "", "" }, -//{ SSC_OUTPUT, SSC_NUMBER, "q_dot_cycle_des", "PC thermal input at design", "MWt", "", "Power Cycle", "*", "", "" }, -//{ SSC_OUTPUT, SSC_NUMBER, "W_dot_cycle_pump_des", "PC HTF pump power at design", "MWe", "", "Power Cycle", "*", "", "" }, -//{ SSC_OUTPUT, SSC_NUMBER, "W_dot_cycle_cooling_des", "PC cooling power at design", "MWe", "", "Power Cycle", "*", "", "" }, +// Heat sink +{ 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", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "hx_UA_des", "Heat sink hx conductance", "MW/K", "", "System Control", "*", "", "" }, // TES { SSC_OUTPUT, SSC_NUMBER, "Q_tes_des", "TES design capacity", "MWht", "", "TES Design Calc", "*", "", "" }, @@ -526,11 +526,16 @@ static var_info _cm_vtab_mspt_iph[] = { { SSC_OUTPUT, SSC_ARRAY, "T_htf_heater_out", "Parallel heater HTF outlet temperature", "C", "", "Parallel Heater", "sim_type=1&is_parallel_htr=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", "", "" }, // Thermal energy storage outputs { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "", "sim_type=1", "", "" }, @@ -1273,15 +1278,16 @@ class cm_mspt_iph : public compute_module c_heat_sink_phys.ms_params.m_pc_fl = as_integer("rec_htf"); c_heat_sink_phys.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); - c_heat_sink_phys.ms_params.m_T_ext_cold_des = as_double("hs_phys_T_steam_cold_des"); //[C] Steam fluid inlet temp c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure - c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes - c_heat_sink_phys.ms_params.m_od_tol = as_double("hs_phys_tol"); //[] HX off design tolerance + + // 25.02.03 twn: hardcode these for now so users don't have to set them + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.01; // as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; // as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = 15; // as_double("hs_phys_N_sub"); //[] Number HX nodes + c_heat_sink_phys.ms_params.m_od_tol = 0.001; // 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); @@ -1290,6 +1296,11 @@ class cm_mspt_iph : public compute_module 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_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; } else @@ -2027,49 +2038,20 @@ class cm_mspt_iph : public compute_module assign("tshours_heater", tshours_heater); - // ************************* - // Power Cycle - //double m_dot_htf_pc_des; //[kg/s] - //double cp_htf_pc_des; //[kJ/kg-K] - //double W_dot_pc_pump_des; //[MWe] - //double W_dot_pc_cooling_des; //[MWe] - //int n_T_htf_pars, n_T_amb_pars, n_m_dot_pars; - //n_T_htf_pars = n_T_amb_pars = n_m_dot_pars = -1; - //double T_htf_ref_calc, T_htf_low_calc, T_htf_high_calc, T_amb_ref_calc, T_amb_low_calc, T_amb_high_calc, - // m_dot_htf_ND_ref_calc, m_dot_htf_ND_low_calc, m_dot_htf_ND_high_calc, W_dot_gross_ND_des, Q_dot_HTF_ND_des, - // W_dot_cooling_ND_des, m_dot_water_ND_des; - //T_htf_ref_calc = T_htf_low_calc = T_htf_high_calc = - // T_amb_ref_calc = T_amb_low_calc = T_amb_high_calc = - // m_dot_htf_ND_ref_calc = m_dot_htf_ND_low_calc = m_dot_htf_ND_high_calc = - // W_dot_gross_ND_des = Q_dot_HTF_ND_des = W_dot_cooling_ND_des = m_dot_water_ND_des = std::numeric_limits::quiet_NaN(); - // - //rankine_pc.get_design_parameters(m_dot_htf_pc_des, cp_htf_pc_des, W_dot_pc_pump_des, W_dot_pc_cooling_des, - // n_T_htf_pars, n_T_amb_pars, n_m_dot_pars, - // T_htf_ref_calc /*C*/, T_htf_low_calc /*C*/, T_htf_high_calc /*C*/, - // T_amb_ref_calc /*C*/, T_amb_low_calc /*C*/, T_amb_high_calc /*C*/, - // m_dot_htf_ND_ref_calc, m_dot_htf_ND_low_calc /*-*/, m_dot_htf_ND_high_calc /*-*/, - // W_dot_gross_ND_des, Q_dot_HTF_ND_des, W_dot_cooling_ND_des, m_dot_water_ND_des); - //m_dot_htf_pc_des /= 3600.0; // convert from kg/hr to kg/s - //assign("m_dot_htf_cycle_des", m_dot_htf_pc_des); - //assign("q_dot_cycle_des", q_dot_pc_des); - //assign("W_dot_cycle_pump_des", W_dot_pc_pump_des); - //assign("W_dot_cycle_cooling_des", W_dot_pc_cooling_des); - //assign("n_T_htf_pars_calc", n_T_htf_pars); - //assign("n_T_amb_pars_calc", n_T_amb_pars); - //assign("n_m_dot_pars_calc", n_m_dot_pars); - //assign("T_htf_ref_calc", T_htf_ref_calc); - //assign("T_htf_low_calc", T_htf_low_calc); - //assign("T_htf_high_calc", T_htf_high_calc); - //assign("T_amb_ref_calc", T_amb_ref_calc); - //assign("T_amb_low_calc", T_amb_low_calc); - //assign("T_amb_high_calc", T_amb_high_calc); - //assign("m_dot_htf_ND_ref_calc", m_dot_htf_ND_ref_calc); - //assign("m_dot_htf_ND_low_calc", m_dot_htf_ND_low_calc); - //assign("m_dot_htf_ND_high_calc", m_dot_htf_ND_high_calc); - //assign("W_dot_gross_ND_des_calc", W_dot_gross_ND_des); - //assign("Q_dot_HTF_ND_des_calc", Q_dot_HTF_ND_des); - //assign("W_dot_cooling_ND_des_calc", W_dot_cooling_ND_des); - //assign("m_dot_water_ND_des_calc", m_dot_water_ND_des); + // Heat sink + double m_dot_hs_ext_des, T_hs_ext_out_des, hx_min_dT_des, hx_UA_des; + m_dot_hs_ext_des = T_hs_ext_out_des = hx_min_dT_des = hx_UA_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] + hx_UA_des = c_heat_sink_phys.get_hx_UA_des(); //[kW/K] + } + + 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); + assign("hx_UA_des", hx_UA_des); // ************************* // System From 993fa2a78939dcdce08db5547f99f19986913f4d Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 3 Feb 2025 15:59:58 -0600 Subject: [PATCH 27/27] add heat sink outputs to fresnel iph --- ssc/cmod_fresnel_physical_iph.cpp | 96 +++++++++++++------------------ 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp index b44ca1888..501c55bbd 100644 --- a/ssc/cmod_fresnel_physical_iph.cpp +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -157,10 +157,10 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Heat Sink", "*", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_type", "0: ideal model, 1: physical steam model", "", "", "Heat Sink", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_N_sub", "Number physical heat sink HX nodes", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_tol", "Physical heat sink solve tolerance", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_min", "Min steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hs_phys_f_mdot_steam_max", "Max steam mdot fraction for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_T_steam_cold_des", "Steam inlet temperature for physical heat sink", "C", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_P_steam_hot_des", "Steam outlet (and inlet) pressure for physical heat sink", "bar", "", "Heat Sink", "hs_type=1", "", "" }, { SSC_INPUT, SSC_NUMBER, "hs_phys_Q_steam_hot_des", "Steam outlet quality for physical heat sink", "", "", "Heat Sink", "hs_type=1", "", "" }, @@ -353,10 +353,12 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { SSC_OUTPUT, SSC_NUMBER, "opt_derate", "Receiver optical derate", "", "", "Receiver", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "opt_normal", "Collector optical loss at normal incidence", "", "", "Receiver", "*", "", "" }, - // Power Cycle - //{ SSC_OUTPUT, SSC_NUMBER, "q_dot_cycle_des", "PC thermal input at design", "MWt", "", "Power Cycle", "*", "", "" }, - //{ SSC_OUTPUT, SSC_NUMBER, "mdot_cycle_des", "PC thermal input at design", "MWt", "", "Power Cycle", "*", "", "" }, - + // Heat sink + { 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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "hx_UA_des", "Heat sink hx conductance", "MW/K", "", "System Control", "*", "", "" }, + // Thermal Storage { SSC_OUTPUT, SSC_NUMBER, "vol_tank", "Total tank volume", "m3", "", "Power Cycle", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "Q_tes_des", "TES design capacity", "MWht", "", "Power Cycle", "*", "", "" }, @@ -475,19 +477,6 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { SSC_OUTPUT, SSC_ARRAY, "W_dot_sca_track", "Field collector tracking power", "MWe", "", "Solar_Field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "W_dot_field_pump", "Field htf pumping power", "MWe", "", "Solar_Field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "recirculating", "Field recirculating (bypass valve open)", "-", "", "Solar_Field", "sim_type=1", "", "" }, - - // power block - //{ SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency: gross", "", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output: gross", "MWe", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption: makeup + cooling", "kg/s", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "PC HTF pump power", "MWe", "", "powerblock", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "powerblock", "sim_type=1", "", "" }, // Heat Sink { SSC_OUTPUT, SSC_ARRAY, "q_dot_to_heat_sink", "Heat sink thermal power", "MWt", "", "Heat_Sink", "sim_type=1", "", "" }, @@ -495,8 +484,12 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { 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", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "q_tes_heater", "TES freeze protection power", "MWt", "", "TES", "sim_type=1", "", "" }, @@ -984,10 +977,12 @@ class cm_fresnel_physical_iph : public compute_module c_heat_sink_phys.ms_params.m_Q_ext_hot_des = as_double("hs_phys_Q_steam_hot_des"); //[C] Steam quality target c_heat_sink_phys.ms_params.m_P_ext_cold_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam inlet pressure c_heat_sink_phys.ms_params.m_P_ext_hot_des = as_double("hs_phys_P_steam_hot_des") * 100.0; //[kPa] Steam outlet pressure - c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate - c_heat_sink_phys.ms_params.m_N_sub_hx = as_double("hs_phys_N_sub"); //[] Number HX nodes - c_heat_sink_phys.ms_params.m_od_tol = as_double("hs_phys_tol"); //[] HX off design tolerance + + // 25.02.02 twn: hardcode these for now so users don't have to set them + c_heat_sink_phys.ms_params.m_f_m_dot_ext_min = 0.01; // as_double("hs_phys_f_mdot_steam_min"); //[] Min fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_f_m_dot_ext_max = 1.5; // as_double("hs_phys_f_mdot_steam_max"); //[] Max fraction Steam mass flow rate + c_heat_sink_phys.ms_params.m_N_sub_hx = 15; // as_double("hs_phys_N_sub"); //[] Number HX nodes + c_heat_sink_phys.ms_params.m_od_tol = 0.001; // 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); @@ -996,6 +991,11 @@ class cm_fresnel_physical_iph : public compute_module 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_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; } else @@ -1417,34 +1417,20 @@ class cm_fresnel_physical_iph : public compute_module assign("tes_htf_cp", tes_htf_cp); } - // Power Cycle - - //double m_dot_htf_pc_des_perhr; //[kg/hr] - //double cp_htf_pc_des; //[kJ/kg-K] - //double W_dot_pc_pump_des; //[MWe] - //double W_dot_pc_cooling_des; //[MWe] - //int n_T_htf_pars, n_T_amb_pars, n_m_dot_pars; - //n_T_htf_pars = n_T_amb_pars = n_m_dot_pars = -1; - //double T_htf_ref_calc, T_htf_low_calc, T_htf_high_calc, T_amb_ref_calc, T_amb_low_calc, T_amb_high_calc, - // m_dot_htf_ND_ref_calc, m_dot_htf_ND_low_calc, m_dot_htf_ND_high_calc, W_dot_gross_ND_des, Q_dot_HTF_ND_des, - // W_dot_cooling_ND_des, m_dot_water_ND_des; - //T_htf_ref_calc = T_htf_low_calc = T_htf_high_calc = - // T_amb_ref_calc = T_amb_low_calc = T_amb_high_calc = - // m_dot_htf_ND_ref_calc = m_dot_htf_ND_low_calc = m_dot_htf_ND_high_calc = - // W_dot_gross_ND_des = Q_dot_HTF_ND_des = W_dot_cooling_ND_des = m_dot_water_ND_des = std::numeric_limits::quiet_NaN(); - - //rankine_pc.get_design_parameters(m_dot_htf_pc_des_perhr, cp_htf_pc_des, W_dot_pc_pump_des, W_dot_pc_cooling_des, - // n_T_htf_pars, n_T_amb_pars, n_m_dot_pars, - // T_htf_ref_calc /*C*/, T_htf_low_calc /*C*/, T_htf_high_calc /*C*/, - // T_amb_ref_calc /*C*/, T_amb_low_calc /*C*/, T_amb_high_calc /*C*/, - // m_dot_htf_ND_ref_calc, m_dot_htf_ND_low_calc /*-*/, m_dot_htf_ND_high_calc /*-*/, - // W_dot_gross_ND_des, Q_dot_HTF_ND_des, W_dot_cooling_ND_des, m_dot_water_ND_des); - - //// Assign - //{ - // assign("q_dot_cycle_des", q_dot_pc_des); - // assign("mdot_cycle_des", m_dot_htf_pc_des_perhr / 3600.0); // [kg/s] - //} + // Heat sink + double m_dot_hs_ext_des, T_hs_ext_out_des, hx_min_dT_des, hx_UA_des; + m_dot_hs_ext_des = T_hs_ext_out_des = hx_min_dT_des = hx_UA_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] + hx_UA_des = c_heat_sink_phys.get_hx_UA_des(); //[kW/K] + } + + 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); + assign("hx_UA_des", hx_UA_des); // System Design double W_dot_bop_design, W_dot_fixed_parasitic_design; //[MWe]