diff --git a/ssc/cmod_trough_physical.cpp b/ssc/cmod_trough_physical.cpp index 1783d227e..a3bafce77 100644 --- a/ssc/cmod_trough_physical.cpp +++ b/ssc/cmod_trough_physical.cpp @@ -91,8 +91,6 @@ static var_info _cm_vtab_trough_physical[] = { { SSC_INPUT, SSC_NUMBER, "Row_Distance", "Spacing between rows (centerline to centerline)", "m", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_startup", "Required temperature of the system before the power block can be switched on", "C", "", "solar_field", "?", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_shutdown", "Temperature when solar field begins recirculating", "C", "", "solar_field", "?", "", "" }, { SSC_INPUT, SSC_NUMBER, "use_abs_or_rel_mdot_limit", "Use mass flow abs (0) or relative (1) limits", "", "", "solar_field", "?=0", "", "" }, { SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "solar_field", "use_abs_or_rel_mdot_limit=0", "", "" }, @@ -130,6 +128,11 @@ static var_info _cm_vtab_trough_physical[] = { { SSC_INPUT, SSC_ARRAY, "L_aperture", "Length of a single mirror/HCE unit", "m", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_ARRAY, "ColperSCA", "Number of individual collector sections in an SCA ", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_ARRAY, "Distance_SCA", "Piping distance between SCA's in the field", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "opt_model", "Optical model (1=Solar position ; 2=Collector incidence table ; 3=IAM matrix)", "", "", "solar_field", "?=[3,3,3,3]", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_1", "Values of the optical efficiency table for collector type 1", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_2", "Values of the optical efficiency table for collector type 2", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_3", "Values of the optical efficiency table for collector type 3", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_4", "Values of the optical efficiency table for collector type 4", "", "", "solar_field", "", "", "" }, { SSC_INPUT, SSC_MATRIX, "IAM_matrix", "IAM coefficients, matrix for 4 collectors", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_MATRIX, "HCE_FieldFrac", "Fraction of the field occupied by this HCE type ", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_MATRIX, "D_2", "Inner absorber tube diameter", "m", "", "solar_field", "*", "", "" }, @@ -593,7 +596,7 @@ static var_info _cm_vtab_trough_physical[] = { // Solar Field { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "opt_derate_ave", "Field collector optical derate modifier", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "solar_field", "sim_type=1", "", "" }, @@ -1149,8 +1152,23 @@ class cm_trough_physical : public compute_module for (size_t i = 0; i < nval_Distance_SCA; i++) c_trough.m_Distance_SCA[i] = (double)Distance_SCA[i]; + c_trough.m_opt_model = as_vector_integer("opt_model"); // Optical model 1=Solar position ; 2=Collector incidence table ; 3 = IAM matrix) c_trough.m_IAM_matrix = as_matrix("IAM_matrix"); //[-] IAM coefficients, matrix for 4 collectors + for (int i = 0; i < c_trough.m_opt_model.size(); i++) + { + int opt_model = c_trough.m_opt_model[i]; + c_trough.m_OpticalTables_in.push_back(util::matrix_t()); + if (opt_model != 3) + { + std::string OpticalTable_varname = "OpticalTable_" + std::to_string(i + 1); + if (!is_assigned(OpticalTable_varname)) + throw exec_error("trough_physical", "opt_model 1 and 2 must have OpticalTable assigned."); + else + c_trough.m_OpticalTables_in[i] = as_matrix(OpticalTable_varname); + } + } + // Why are these matrices - can't they be arrays? c_trough.m_HCE_FieldFrac = as_matrix("HCE_FieldFrac"); //[-] Fraction of the field occupied by this HCE type c_trough.m_D_2 = as_matrix("D_2"); //[m] Inner absorber tube diameter @@ -1238,7 +1256,7 @@ class cm_trough_physical : public compute_module { c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_THETA_AVE, allocate("Theta_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_COSTH_AVE, allocate("CosTh_ave", n_steps_fixed), n_steps_fixed); - c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_IAM_AVE, allocate("IAM_ave", n_steps_fixed), n_steps_fixed); + c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_OPT_DERATE_AVE, allocate("opt_derate_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_ROWSHADOW_AVE, allocate("RowShadow_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_ENDLOSS_AVE, allocate("EndLoss_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_DNI_COSTH, allocate("dni_costh", n_steps_fixed), n_steps_fixed); @@ -1785,7 +1803,7 @@ class cm_trough_physical : public compute_module else if (csp_financial_model == 7 || csp_financial_model == 8) { // LCOE (7) and None (8) if (is_dispatch) { - throw exec_error("fresnel_physical", "Can't select dispatch optimization if No Financial model"); + throw exec_error("trough_physical", "Can't select dispatch optimization if No Financial model"); } else { // if no dispatch optimization, don't need an input pricing schedule // If electricity pricing data is not available, then dispatch to a uniform schedule diff --git a/ssc/cmod_trough_physical_iph.cpp b/ssc/cmod_trough_physical_iph.cpp index 03dbd4295..3504b2e70 100644 --- a/ssc/cmod_trough_physical_iph.cpp +++ b/ssc/cmod_trough_physical_iph.cpp @@ -132,6 +132,11 @@ static var_info _cm_vtab_trough_physical_iph[] = { { SSC_INPUT, SSC_ARRAY, "L_aperture", "Length of a single mirror/HCE unit", "m", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_ARRAY, "ColperSCA", "Number of individual collector sections in an SCA ", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_ARRAY, "Distance_SCA", "Piping distance between SCA's in the field", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "opt_model", "Optical model (1=Solar position ; 2=Collector incidence table ; 3=IAM matrix)", "", "", "solar_field", "?=[3,3,3,3]", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_1", "Values of the optical efficiency table for collector type 1", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_2", "Values of the optical efficiency table for collector type 2", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_3", "Values of the optical efficiency table for collector type 3", "", "", "solar_field", "", "", "" }, + { SSC_INPUT, SSC_MATRIX, "OpticalTable_4", "Values of the optical efficiency table for collector type 4", "", "", "solar_field", "", "", "" }, { SSC_INPUT, SSC_MATRIX, "IAM_matrix", "IAM coefficients, matrix for 4 collectors", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_MATRIX, "HCE_FieldFrac", "Fraction of the field occupied by this HCE type ", "none", "", "solar_field", "*", "", "" }, { SSC_INPUT, SSC_MATRIX, "D_2", "Inner absorber tube diameter", "m", "", "solar_field", "*", "", "" }, @@ -552,7 +557,7 @@ static var_info _cm_vtab_trough_physical_iph[] = { // Solar Field { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "opt_derate_ave", "Field collector optical derate modifier", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "solar_field", "sim_type=1", "", "" }, @@ -1051,8 +1056,23 @@ class cm_trough_physical_iph : public compute_module for (size_t i = 0; i < nval_Distance_SCA; i++) c_trough.m_Distance_SCA[i] = (double)Distance_SCA[i]; + c_trough.m_opt_model = as_vector_integer("opt_model"); // Optical model 1=Solar position ; 2=Collector incidence table ; 3 = IAM matrix) c_trough.m_IAM_matrix = as_matrix("IAM_matrix"); //[-] IAM coefficients, matrix for 4 collectors + for (int i = 0; i < c_trough.m_opt_model.size(); i++) + { + int opt_model = c_trough.m_opt_model[i]; + c_trough.m_OpticalTables_in.push_back(util::matrix_t()); + if (opt_model != 3) + { + std::string OpticalTable_varname = "OpticalTable_" + std::to_string(i + 1); + if (!is_assigned(OpticalTable_varname)) + throw exec_error("trough_physical_iph", "opt_model 1 and 2 must have OpticalTable assigned."); + else + c_trough.m_OpticalTables_in[i] = as_matrix(OpticalTable_varname); + } + } + // Why are these matrices - can't they be arrays? c_trough.m_HCE_FieldFrac = as_matrix("HCE_FieldFrac"); //[-] Fraction of the field occupied by this HCE type c_trough.m_D_2 = as_matrix("D_2"); //[m] Inner absorber tube diameter @@ -1146,7 +1166,7 @@ class cm_trough_physical_iph : public compute_module { c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_THETA_AVE, allocate("Theta_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_COSTH_AVE, allocate("CosTh_ave", n_steps_fixed), n_steps_fixed); - c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_IAM_AVE, allocate("IAM_ave", n_steps_fixed), n_steps_fixed); + c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_OPT_DERATE_AVE, allocate("opt_derate_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_ROWSHADOW_AVE, allocate("RowShadow_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_ENDLOSS_AVE, allocate("EndLoss_ave", n_steps_fixed), n_steps_fixed); c_trough.mc_reported_outputs.assign(C_csp_trough_collector_receiver::E_DNI_COSTH, allocate("dni_costh", n_steps_fixed), n_steps_fixed); diff --git a/tcs/csp_solver_trough_collector_receiver.cpp b/tcs/csp_solver_trough_collector_receiver.cpp index 25593323a..9762339f5 100644 --- a/tcs/csp_solver_trough_collector_receiver.cpp +++ b/tcs/csp_solver_trough_collector_receiver.cpp @@ -43,7 +43,7 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = { {C_csp_trough_collector_receiver::E_THETA_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_trough_collector_receiver::E_COSTH_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_trough_collector_receiver::E_IAM_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_trough_collector_receiver::E_OPT_DERATE_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_trough_collector_receiver::E_ROWSHADOW_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_trough_collector_receiver::E_ENDLOSS_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_trough_collector_receiver::E_DNI_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE}, @@ -236,7 +236,7 @@ C_csp_trough_collector_receiver::C_csp_trough_collector_receiver() m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); m_Theta_ave = std::numeric_limits::quiet_NaN(); m_CosTh_ave = std::numeric_limits::quiet_NaN(); - m_IAM_ave = std::numeric_limits::quiet_NaN(); + m_opt_derate_ave = std::numeric_limits::quiet_NaN(); m_RowShadow_ave = std::numeric_limits::quiet_NaN(); m_EndLoss_ave = std::numeric_limits::quiet_NaN(); m_dni_costh = std::numeric_limits::quiet_NaN(); @@ -273,69 +273,144 @@ C_csp_trough_collector_receiver::~C_csp_trough_collector_receiver() } } -void C_csp_trough_collector_receiver::init(const C_csp_collector_receiver::S_csp_cr_init_inputs init_inputs, - C_csp_collector_receiver::S_csp_cr_solved_params & solved_params) +void C_csp_trough_collector_receiver::init(const C_csp_collector_receiver::S_csp_cr_init_inputs init_inputs, + C_csp_collector_receiver::S_csp_cr_solved_params& solved_params) { // If solar multiple is not yet calculated if (m_is_solar_mult_designed == false) throw(C_csp_exception("design_solar_mult() must be called before init()", "Trough collector solver")); - - // double some_calc = m_nSCA + m_nHCEt; - /* - --Initialization call-- - Do any setup required here. - Get the values of the inputs and parameters - */ + // double some_calc = m_nSCA + m_nHCEt; + /* + --Initialization call-- - //Initialize air properties -- used in reeiver calcs - m_airProps.SetFluid(HTFProperties::Air); + Do any setup required here. + Get the values of the inputs and parameters + */ - // Save init_inputs to member data - m_latitude = init_inputs.m_latitude; //[deg] - m_longitude = init_inputs.m_longitude; //[deg] - m_shift = init_inputs.m_shift; //[deg] - m_latitude *= m_d2r; //[rad] convert from [deg] - m_longitude *= m_d2r; //[rad] convert from [deg] - m_shift *= m_d2r; //[rad] convert from [deg] + //Initialize air properties -- used in reeiver calcs + m_airProps.SetFluid(HTFProperties::Air); + + // Save init_inputs to member data + m_latitude = init_inputs.m_latitude; //[deg] + m_longitude = init_inputs.m_longitude; //[deg] + m_shift = init_inputs.m_shift; //[deg] + m_latitude *= m_d2r; //[rad] convert from [deg] + m_longitude *= m_d2r; //[rad] convert from [deg] + m_shift *= m_d2r; //[rad] convert from [deg] m_P_field_in = 17 / 1.e-5; //Assumed inlet htf pressure for property lookups (DP_tot_max = 16 bar + 1 atm) [Pa] - // Adjust parameters - m_ColTilt = m_ColTilt*m_d2r; //[rad] Collector tilt angle (0 is horizontal, 90deg is vertical), convert from [deg] - m_ColAz = m_ColAz*m_d2r; //[rad] Collector azimuth angle, convert from [deg] + // Adjust parameters + m_ColTilt = m_ColTilt * m_d2r; //[rad] Collector tilt angle (0 is horizontal, 90deg is vertical), convert from [deg] + m_ColAz = m_ColAz * m_d2r; //[rad] Collector azimuth angle, convert from [deg] - // Check m_IAM matrix against number of collectors: m_nColt - m_n_r_iam_matrix = (int)m_IAM_matrix.nrows(); - m_n_c_iam_matrix = (int)m_IAM_matrix.ncols(); + if (m_nColt != m_opt_model.size()) + { + throw(C_csp_exception("m_opt_model length must equal m_nColt", "Trough collector solver")); + } - if (m_n_c_iam_matrix < 3) - { - throw(C_csp_exception("There must be at least 3 incident angle modifier coefficients", "Trough collector solver")); - } + // Check IAM matrix specific values + bool is_IAM = false; + for (int opt_model : m_opt_model) + { + if (opt_model == 3) + { + is_IAM = true; + break; + } + } + if(is_IAM) + { + // Check m_IAM matrix against number of collectors: m_nColt + m_n_r_iam_matrix = (int)m_IAM_matrix.nrows(); + m_n_c_iam_matrix = (int)m_IAM_matrix.ncols(); - if (m_n_r_iam_matrix < m_nColt) - { - m_error_msg = util::format("The number of groups of m_IAM coefficients (%d) is less than the number of collector types in this simulation (%d)", m_n_r_iam_matrix, m_nColt); - throw(C_csp_exception(m_error_msg, "Trough collector solver")); - } + if (m_n_c_iam_matrix < 3) + { + throw(C_csp_exception("There must be at least 3 incident angle modifier coefficients", "Trough collector solver")); + } + + if (m_n_r_iam_matrix < m_nColt) + { + m_error_msg = util::format("The number of groups of m_IAM coefficients (%d) is less than the number of collector types in this simulation (%d)", m_n_r_iam_matrix, m_nColt); + throw(C_csp_exception(m_error_msg, "Trough collector solver")); + } + + // Check that for each collector, at least 3 coefficients are != 0.0 + for (int i = 0; i < m_nColt; i++) + { + bool is_IAM = m_opt_model[i] == 3; + if (!is_IAM) + continue; + for (int j = 0; j < 3; j++) + { + if (m_IAM_matrix(i, j) == 0.0) + { + m_error_msg = util::format("For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); + throw(C_csp_exception(m_error_msg, "Trough collector solver")); + //message(TCS_ERROR, "For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); + //return -1; + } + } + } + } + + // Initialize optical tables (if necessary) + m_optical_tables.resize(m_nColt); + for (int i_col = 0; i_col < m_opt_model.size(); i_col++) + { + int opt_model_local = m_opt_model[i_col]; + + // Optical model + if (opt_model_local < 1 || opt_model_local > 3) + { + throw(C_csp_exception("m_opt_model must be between 1-3", "Trough collector solver")); + } + + if(opt_model_local != 3) + { + /* + The input should be defined as follows: + - Data of size nx, ny + - OpticalTable of size (nx+1)*(ny+1) + - First nx+1 values (row 1) are x-axis values, not data, starting at index 1 + - First value of remaining ny rows are y-axis values, not data + - Data is contained in cells i,j : where i>1, j>1 + */ + int ncol_OpticalTable = m_OpticalTables_in[i_col].ncols(); + int nrow_OpticalTable = m_OpticalTables_in[i_col].nrows(); + + std::vector xax(ncol_OpticalTable - 1); + std::vector yax(nrow_OpticalTable - 1); + std::vector data((ncol_OpticalTable - 1) * (nrow_OpticalTable - 1)); + + //get the xaxis data values + for (int i = 1; i < ncol_OpticalTable; i++) { + xax[i - 1] = m_OpticalTables_in[i_col].at(0, i) * m_d2r; + } + //get the yaxis data values + for (int j = 1; j < nrow_OpticalTable; j++) { + yax[j - 1] = m_OpticalTables_in[i_col].at(j, 0) * m_d2r; + } + //Get the data values + for (int j = 1; j < nrow_OpticalTable; j++) { + for (int i = 1; i < ncol_OpticalTable; i++) { + data[i - 1 + (ncol_OpticalTable - 1) * (j - 1)] = m_OpticalTables_in[i_col].at(j, i); + } + } + + auto& opt_table = m_optical_tables[i_col].emplace(); + opt_table.AddXAxis(xax.data(), static_cast(xax.size())); + opt_table.AddYAxis(yax.data(), static_cast(yax.size())); + opt_table.AddData(data.data()); + } + } + + - // Check that for each collector, at least 3 coefficients are != 0.0 - for (int i = 0; i < m_nColt; i++) - { - for (int j = 0; j < 3; j++) - { - if (m_IAM_matrix(i, j) == 0.0) - { - m_error_msg = util::format("For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); - throw(C_csp_exception(m_error_msg, "Trough collector solver")); - //message(TCS_ERROR, "For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); - //return -1; - } - } - } // ****************************************************************** //Organize the emittance tables here @@ -394,7 +469,7 @@ void C_csp_trough_collector_receiver::init(const C_csp_collector_receiver::S_csp m_q_reflect_tot.resize(m_nSCA); m_q_reflect.resize(m_nHCEVar); m_q_i.resize(m_nColt); - m_IAM.resize(m_nColt); + m_opt_derate.resize(m_nColt); m_ColOptEff.resize(m_nColt, m_nSCA); m_EndGain.resize(m_nColt, m_nSCA); m_EndLoss.resize(m_nColt, m_nSCA); @@ -661,7 +736,7 @@ bool C_csp_trough_collector_receiver::init_fieldgeom() // Max Field Flow Velocity m_max_field_flow_velocity = 0; { - double density = m_htfProps.dens(m_T_loop_out_des + 273.15, std::numeric_limits::quiet_NaN()); + double density = m_htfProps.dens(m_T_loop_out_des, std::numeric_limits::quiet_NaN()); m_max_field_flow_velocity = m_m_dot_htfmax * 4 / (density * M_PI * m_min_inner_diameter * m_min_inner_diameter); } @@ -669,9 +744,9 @@ bool C_csp_trough_collector_receiver::init_fieldgeom() // Min Field Flow Velocity m_min_field_flow_velocity = 0; { - double density = m_htfProps.dens(m_T_loop_in_des + 273.15, std::numeric_limits::quiet_NaN()); + double density = m_htfProps.dens(m_T_loop_in_des, std::numeric_limits::quiet_NaN()); - m_min_field_flow_velocity = m_m_dot_htfmin * 4 / (density * M_PI * m_min_inner_diameter * m_min_inner_diameter); + m_min_field_flow_velocity = m_m_dot_htfmin * 4 / (density * M_PI * m_max_inner_diameter * m_max_inner_diameter); } //need to provide fluid density @@ -866,7 +941,7 @@ bool C_csp_trough_collector_receiver::init_fieldgeom() // Calculate Design Point Outputs double T_avg = 0.5 * (m_T_loop_in_des + m_T_loop_out_des); - m_field_htf_cp_avg_des = m_htfProps.Cp(T_avg + 273.15); //[kJ/kg-K] + m_field_htf_cp_avg_des = m_htfProps.Cp(T_avg); //[kJ/kg-K] // ********************************************* @@ -1672,7 +1747,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta_off() m_costh = 0.0; //[-] Cosine of the incident angle between the sun and trough aperture m_q_i.assign(m_q_i.size(),0.0); //[W/m] DNI * A_aper / L_sca - m_IAM.assign(m_IAM.size(),0.0); //[-] Incidence angle modifiers + m_opt_derate.assign(m_opt_derate.size(),0.0); //[-] Incidence angle modifiers m_ColOptEff.fill(0.0); //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack m_EqOpteff = 0.; m_EndGain.fill(0.0); //[-] Light from different collector hitting receiver @@ -1682,7 +1757,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta_off() m_Theta_ave = 0.0; m_CosTh_ave = 0.0; - m_IAM_ave = 0.0; + m_opt_derate_ave = 0.0; m_RowShadow_ave = 0.0; m_EndLoss_ave = 0.0; m_dni_costh = 0.0; @@ -1696,6 +1771,43 @@ void C_csp_trough_collector_receiver::loop_optical_eta_off() return; } +double C_csp_trough_collector_receiver::calculate_opt_derate(const int i, const double SolarAz, + const double SolarZenRad, const double theta) +{ + double opt_derate = 0; + + switch (m_opt_model[i]) + { + // Sun position + case(1): + { + opt_derate = std::max(m_optical_tables[i]->interpolate(SolarAz, std::min(SolarZenRad, CSP::pi / 2.)), 0.0); + return opt_derate; + } + // Incidence angle table + case(2): + { + double phi_t, theta_L; + CSP::theta_trans(SolarAz, SolarZenRad, m_ColAz, phi_t, theta_L); + opt_derate = std::max(m_optical_tables[i]->interpolate(phi_t, std::max(theta_L, 0.0)), 0.0); + return opt_derate; + } + // IAM poly (original) + case(3): + { + double IAM = m_IAM_matrix(i, 0); + for (int j = 1; j < m_n_c_iam_matrix; j++) + IAM += m_IAM_matrix(i, j) * std::pow(theta, j) / m_costh; + opt_derate = fmax(0.0, fmin(IAM, 1.0)); + return opt_derate; + } + default: + { + throw(C_csp_exception("Unsupported opt_model value.")); + } + } +} + void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim_info) { @@ -1705,8 +1817,6 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader } else { - - // First, clear all the values calculated below loop_optical_eta_off(); @@ -1783,6 +1893,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader double SolarAz = weather.m_solazi; //[deg] Solar azimuth angle SolarAz = (SolarAz - 180.0) * m_d2r; //[rad] convert from [deg] double SolarAlt; + double SolarZenRad = weather.m_solzen * m_d2r; // [rad] if (m_accept_mode == 1) { SolarAlt = CSP::pi/2 - weather.m_solzen; //[deg] Solar altitude angle @@ -1806,22 +1917,18 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader } // m_theta in radians - double theta = acos(m_costh); //[rad] Incidene angle + double theta = acos(m_costh); //[rad] Incidence angle for (int i = 0; i < m_nColt; i++) { m_q_i[i] = weather.m_beam*m_A_aperture[i] / m_L_actSCA[i]; //[W/m] The incoming solar irradiation per aperture length - m_IAM[i] = m_IAM_matrix(i, 0); - for (int j = 1; j < m_n_c_iam_matrix; j++) - m_IAM[i] += m_IAM_matrix(i, j)*pow(theta, j) / m_costh; - - m_IAM[i] = fmax(0.0, fmin(m_IAM[i], 1.0)); + m_opt_derate[i] = calculate_opt_derate(i, SolarAz, SolarZenRad, theta); //Calculate the Optical efficiency of the collector for (int j = 0; j < m_nSCA; j++) { - m_ColOptEff(i, j) = m_TrackingError[i] * m_GeomEffects[i] * m_Rho_mirror_clean[i] * m_Dirt_mirror[i] * m_Error[i] * m_IAM[i]; + m_ColOptEff(i, j) = m_TrackingError[i] * m_GeomEffects[i] * m_Rho_mirror_clean[i] * m_Dirt_mirror[i] * m_Error[i] * m_opt_derate[i]; } //Account for light reflecting off the collector and missing the receiver, also light from other @@ -1829,7 +1936,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader //mjw 4.21.11 - rescope this to be for each specific collector j=1,m_nSCA for (int j = 0; j < m_nSCA; j++) { - if (std::abs(SolarAz) <= 90.0) + if (std::abs(SolarAz) <= (CSP::pi / 2.0)) { //mjw 5.1.11 The sun is in the southern sky (towards equator) if (j == 0 || j == m_nSCA - 1) { @@ -1875,7 +1982,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader //Calculate the flux level associated with each SCA //but only calculate for the first call of the timestep<----[NO// messes with defocusing control: mjw 11.4.2010] - m_Theta_ave = 0.0; m_CosTh_ave = 0.0; m_IAM_ave = 0.0; m_RowShadow_ave = 0.0; m_EndLoss_ave = 0.0; + m_Theta_ave = 0.0; m_CosTh_ave = 0.0; m_opt_derate_ave = 0.0; m_RowShadow_ave = 0.0; m_EndLoss_ave = 0.0; for (int i = 0; i < m_nSCA; i++) { int CT = (int)m_SCAInfoArray(i, 1) - 1; //Collector type @@ -1883,7 +1990,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader //Also use this chance to calculate average optical values m_Theta_ave = m_Theta_ave + theta*m_L_actSCA[CT] / m_L_tot; //[rad] m_CosTh_ave = m_CosTh_ave + m_costh*m_L_actSCA[CT] / m_L_tot; //[-] - m_IAM_ave = m_IAM_ave + m_IAM[CT] * m_L_actSCA[CT] / m_L_tot; + m_opt_derate_ave = m_opt_derate_ave + m_opt_derate[CT] * m_L_actSCA[CT] / m_L_tot; m_RowShadow_ave = m_RowShadow_ave + m_RowShadow[CT] * m_L_actSCA[CT] / m_L_tot; m_EndLoss_ave = m_EndLoss_ave + m_EndLoss(CT, i)*m_L_actSCA[CT] / m_L_tot; @@ -2118,7 +2225,7 @@ void C_csp_trough_collector_receiver::set_output_value() { mc_reported_outputs.value(E_THETA_AVE, m_Theta_ave*m_r2d); //[deg], convert from rad mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] - mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] + mc_reported_outputs.value(E_OPT_DERATE_AVE, m_opt_derate_ave); //[-] mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] @@ -2346,7 +2453,7 @@ void C_csp_trough_collector_receiver::startup(const C_csp_weatherreader::S_outpu double m_dot_htf_loop = m_m_dot_htfmin; if( weather.m_beam > 50.0 && m_T_htf_out_t_end_converged[m_nSCA - 1] > (0.5*m_T_fp + 0.5*m_T_startup) ) { - double m_dot_ss = (weather.m_beam * m_CosTh_ave * m_IAM_ave * m_RowShadow_ave * m_EndLoss_ave) / + double m_dot_ss = (weather.m_beam * m_CosTh_ave * m_opt_derate_ave * m_RowShadow_ave * m_EndLoss_ave) / (m_I_bn_des * m_opteff_des) * m_m_dot_loop_des; //[kg/s] m_dot_htf_loop = min( m_m_dot_htfmax, max(m_m_dot_htfmin, 0.8*m_dot_ss + 0.2*m_m_dot_htfmin) ); //[kg/s] } @@ -4205,7 +4312,7 @@ double C_csp_trough_collector_receiver::calculate_optical_efficiency(const C_csp // loop_optical_eta() has side-effects. Store affected member variable values for restore after call. double m_costh_ini(m_costh); std::vector m_q_i_ini(m_q_i); - std::vector m_IAM_ini(m_IAM); + std::vector m_opt_derate_ini(m_opt_derate); util::matrix_t m_ColOptEff_ini(m_ColOptEff); double m_EqOpteff_ini(m_EqOpteff); util::matrix_t m_EndGain_ini(m_EndGain); @@ -4214,7 +4321,7 @@ double C_csp_trough_collector_receiver::calculate_optical_efficiency(const C_csp std::vector m_q_SCA_ini(m_q_SCA); double m_Theta_ave_ini(m_Theta_ave); double m_CosTh_ave_ini(m_CosTh_ave); - double m_IAM_ave_ini(m_IAM_ave); + double m_opt_derate_ave_ini(m_opt_derate_ave); double m_RowShadow_ave_ini(m_RowShadow_ave); double m_EndLoss_ave_ini(m_EndLoss_ave); double m_dni_costh_ini(m_dni_costh); @@ -4229,7 +4336,7 @@ double C_csp_trough_collector_receiver::calculate_optical_efficiency(const C_csp // Restore member variable values m_costh = m_costh_ini; m_q_i = m_q_i_ini; - m_IAM = m_IAM_ini; + m_opt_derate = m_opt_derate_ini; m_ColOptEff = m_ColOptEff_ini; m_EqOpteff = m_EqOpteff_ini; m_EndGain = m_EndGain_ini; @@ -4238,7 +4345,7 @@ double C_csp_trough_collector_receiver::calculate_optical_efficiency(const C_csp m_q_SCA = m_q_SCA_ini; m_Theta_ave = m_Theta_ave_ini; m_CosTh_ave = m_CosTh_ave_ini; - m_IAM_ave = m_IAM_ave_ini; + m_opt_derate_ave = m_opt_derate_ave_ini; m_RowShadow_ave = m_RowShadow_ave_ini; m_EndLoss_ave = m_EndLoss_ave_ini; m_dni_costh = m_dni_costh_ini; @@ -4448,22 +4555,6 @@ bool C_csp_trough_collector_receiver::design_solar_mult(std::vector trou } } - // Max Field Flow Velocity - m_max_field_flow_velocity = 0; - { - double density = m_htfProps.dens(m_T_loop_out_des + 273.15, std::numeric_limits::quiet_NaN()); - - m_max_field_flow_velocity = m_m_dot_htfmax * 4 / (density * M_PI * m_min_inner_diameter * m_min_inner_diameter); - } - - // Min Field Flow Velocity - m_min_field_flow_velocity = 0; - { - double density = m_htfProps.dens(m_T_loop_in_des + 273.15, std::numeric_limits::quiet_NaN()); - - m_min_field_flow_velocity = m_m_dot_htfmin * 4 / (density * M_PI * m_max_inner_diameter * m_max_inner_diameter); - } - // HCE design heat loss m_HCE_heat_loss_des = std::vector(); { @@ -6860,10 +6951,13 @@ double C_csp_trough_collector_receiver::m_dot_runner(double m_dot_field, int nfi switch (irnr_onedir) { case 0: m_dot_rnr = m_dot_rnr_0; + break; case 1: m_dot_rnr = m_dot_rnr_1; + break; default: m_dot_rnr = m_dot_rnr_1 - (irnr_onedir - 1)*m_dot_field / float(nfieldsec) * 2; + break; } return max(m_dot_rnr, 0.0); diff --git a/tcs/csp_solver_trough_collector_receiver.h b/tcs/csp_solver_trough_collector_receiver.h index 62c35e331..e23e80184 100644 --- a/tcs/csp_solver_trough_collector_receiver.h +++ b/tcs/csp_solver_trough_collector_receiver.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include class C_csp_trough_collector_receiver : public C_csp_collector_receiver { @@ -56,7 +57,7 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver { E_THETA_AVE, //[deg] E_COSTH_AVE, //[-] - E_IAM_AVE, //[-] + E_OPT_DERATE_AVE, //[-] E_ROWSHADOW_AVE, //[-] E_ENDLOSS_AVE, //[-] E_DNI_COSTH, //[W/m2] @@ -113,13 +114,14 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver // Parameters calculated in init() int m_n_c_iam_matrix; //[-] Number of columns in the IAM matrix int m_n_r_iam_matrix; //[-] Number of rows in the IAM matrix + std::vector> m_optical_tables; //[-] Optical data table double m_v_hot; //[m^3] Hot piping volume double m_v_cold; //[m^3] Cold piping volume int m_nfsec; //[-] Number of field sections int m_nhdrsec; //[-] Number of header sections int m_nrunsec; //[-] Number of unique runner diameters double m_L_tot; //[m] Total length of collectors in a loop - + double m_m_dot_design; //[kg/s] Total solar field mass flow rate at design double m_m_dot_loop_des;//[kg/s] LOOP design mass flow rate double m_W_dot_sca_tracking_nom; //[MWe] Tracking parasitics when trough is on sun @@ -164,18 +166,18 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver std::vector m_q_SCA; //[W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)*all_defocus) std::vector m_q_SCA_control_df; //[W/m] Total incident irradiation less CONTROL defocus (m_q_sca * control_defocus) - std::vector m_IAM; //[-] Incidence angle modifiers std::vector m_RowShadow; //[-] Row-to-row shadowing losses /*m_nColt, m_nSCA*/ util::matrix_t m_ColOptEff; //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack util::matrix_t m_EndGain; //[-] Light from different collector hitting receiver util::matrix_t m_EndLoss; //[-] Light missing receiver due to length + std::vector m_opt_derate; //[-] Optical derate (default from IAM) double m_Theta_ave; //[rad] Field average m_theta value (but... nothing in our model allows for this to different over SCAs) double m_CosTh_ave; //[-] Field average costheta value - double m_IAM_ave; //[-] Field average incidence angle modifier + double m_opt_derate_ave; //[-] Field average optical derate average double m_RowShadow_ave; //[-] Field average row shadowing loss double m_EndLoss_ave; //[-] Field average end loss @@ -328,6 +330,9 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver void set_output_value(); + double calculate_opt_derate(const int i, const double SolarAz, + const double SolarZenRad, const double theta); + public: // Class to save messages for up stream classes @@ -430,9 +435,11 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver double m_rec_qf_delay; //[-] Energy-based receiver startup delay (fraction of rated thermal power) double m_p_start; //[kWe-hr] Collector startup energy, per SCA - util::matrix_t m_IAM_matrix; //[-] IAM coefficients, matrix for 4 collectors + std::vector m_opt_model; // The optical model (1=Solar position ; 2=Collector incidence table ; 3 = IAM matrix) + std::vector> m_OpticalTables_in; // Values of the optical efficiency table + util::matrix_t m_IAM_matrix; //[-] IAM coefficients, matrix for 4 collectors - util::matrix_t m_GlazingIntact; //[-] Glazing intact (broken glass) flag {1=true, else=false} + util::matrix_t m_GlazingIntact; //[-] Glazing intact (broken glass) flag {1=true, else=false} bool m_calc_design_pipe_vals = std::numeric_limits::quiet_NaN(); //[-] Should the HTF state be calculated at design conditions double m_L_rnr_pb = std::numeric_limits::quiet_NaN(); //[m] Length of hot or cold runner pipe around the power block diff --git a/test/shared_test/lib_csp_trough_test.cpp b/test/shared_test/lib_csp_trough_test.cpp index cf7edd869..3c21f2c12 100644 --- a/test/shared_test/lib_csp_trough_test.cpp +++ b/test/shared_test/lib_csp_trough_test.cpp @@ -198,6 +198,7 @@ std::unique_ptr TroughFactory::MakeTrough(TroughSpecifications* trough_s trough->m_ColperSCA = trough_specifications->ColperSCA; trough->m_Distance_SCA = trough_specifications->Distance_SCA; + trough->m_opt_model = trough_specifications->opt_model; trough->m_IAM_matrix = trough_specifications->IAM_matrix; trough->m_HCE_FieldFrac = trough_specifications->HCE_FieldFrac; trough->m_D_2 = trough_specifications->D_2; @@ -364,6 +365,8 @@ std::unique_ptr DefaultTroughFactory::MakeSpecifications() trough_specifications->ColperSCA = { 8., 8., 8., 8. }; trough_specifications->Distance_SCA = { 1., 1., 1., 1. }; + trough_specifications->opt_model = { 3,3,3,3 }; // Set to IAM poly + double vals2[] = { 1, 0.0327, -0.1351, 1, 0.0327, -0.1351, diff --git a/test/shared_test/lib_csp_trough_test.h b/test/shared_test/lib_csp_trough_test.h index 52536aaf8..145050f2b 100644 --- a/test/shared_test/lib_csp_trough_test.h +++ b/test/shared_test/lib_csp_trough_test.h @@ -146,6 +146,7 @@ namespace csp_trough std::vector ColperSCA; //[-] The number of individual collector sections in an SCA std::vector Distance_SCA; //[m] Piping distance between SCA's in the field + std::vector opt_model; //[-] Optical model 1=Solar position ; 2=Collector incidence table ; 3 = IAM matrix util::matrix_t IAM_matrix; //[-] IAM coefficients, matrix for 4 collectors util::matrix_t HCE_FieldFrac; //[-] Fraction of the field occupied by this HCE type util::matrix_t D_2; //[m] Inner absorber tube diameter diff --git a/test/ssc_test/cmod_trough_physical_iph_test.cpp b/test/ssc_test/cmod_trough_physical_iph_test.cpp index 74df0784e..78046993f 100644 --- a/test/ssc_test/cmod_trough_physical_iph_test.cpp +++ b/test/ssc_test/cmod_trough_physical_iph_test.cpp @@ -57,12 +57,12 @@ TEST(HeatTroughCmod, Default_NoFinancial) double annual_total_water_use = heat_trough.GetOutput("annual_total_water_use"); //EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_gross_energy"), 24267285, kErrorToleranceHi); - EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_energy"), 24142307, kErrorToleranceHi); - EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_electricity_consumption"), 94120, kErrorToleranceHi); - EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_thermal_consumption"), 182.81, kErrorToleranceHi); - EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_tes_freeze_protection"), 182.81, kErrorToleranceHi); - EXPECT_NEAR(heat_trough.GetOutput("annual_field_freeze_protection"), 0., kErrorToleranceHi); - EXPECT_NEAR_FRAC(heat_trough.GetOutput("annual_total_water_use"), 176.3, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_energy, 24142307, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_electricity_consumption, 94120, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_thermal_consumption, 190, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_tes_freeze_protection, 190, kErrorToleranceHi); + EXPECT_NEAR(annual_field_freeze_protection, 0., kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_total_water_use, 176.3, kErrorToleranceHi); } } diff --git a/test/ssc_test/cmod_trough_physical_test.cpp b/test/ssc_test/cmod_trough_physical_test.cpp index 5c754b7a9..c512e41d9 100644 --- a/test/ssc_test/cmod_trough_physical_test.cpp +++ b/test/ssc_test/cmod_trough_physical_test.cpp @@ -68,25 +68,25 @@ TEST(PowerTroughCmod, Default_NoFinancial) double n_op_modes = power_trough.GetOutputSum("n_op_modes"); double is_rec_su_allowed = power_trough.GetOutputSum("is_rec_su_allowed"); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_energy"), 377677449, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_thermal_consumption"), 872331, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_tes_freeze_protection"), 692200, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_field_freeze_protection"), 180131, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("capacity_factor"), 43.16, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_W_cycle_gross"), 430999201, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("kwh_per_kw"), 3781, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("conversion_factor"), 87.63, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutput("annual_total_water_use"), 82009, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_energy, 377677449, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_thermal_consumption, 882888, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_tes_freeze_protection, 701511, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_field_freeze_protection, 180131, kErrorToleranceHi); + EXPECT_NEAR_FRAC(capacity_factor, 43.16, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_W_cycle_gross, 430999201, kErrorToleranceHi); + EXPECT_NEAR_FRAC(kwh_per_kw, 3781, kErrorToleranceHi); + EXPECT_NEAR_FRAC(conversion_factor, 87.63, kErrorToleranceHi); + EXPECT_NEAR_FRAC(annual_total_water_use, 82009, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutputSum("time_hr"), 38373180, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutputSum("month"), 57168, kErrorToleranceHi); - EXPECT_NEAR_FRAC(power_trough.GetOutputSum("beam"), 2687889, kErrorToleranceHi); - 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"), 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(time_hr, 38373180, kErrorToleranceHi); + EXPECT_NEAR_FRAC(month, 57168, kErrorToleranceHi); + EXPECT_NEAR_FRAC(beam, 2687889, kErrorToleranceHi); + EXPECT_NEAR_FRAC(defocus, 8747, kErrorToleranceHi); + EXPECT_NEAR_FRAC(q_dc_tes, 343833, kErrorToleranceHi); + EXPECT_NEAR_FRAC(P_fixed, 5348, kErrorToleranceHi); + EXPECT_NEAR_FRAC(op_mode_1, 54965, kErrorToleranceHi); + EXPECT_NEAR_FRAC(n_op_modes, 9800, kErrorToleranceHi); + EXPECT_NEAR_FRAC(is_rec_su_allowed, 8759, kErrorToleranceHi); //EXPECT_NEAR_FRAC(power_trough.GetOutputSum("operating_modes_a"), 35458021, kErrorToleranceHi); } //ssc_data_t defaults = singleowner_defaults();