diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index e7a1e2ba7..629f887aa 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -39,6 +39,8 @@ set(SSC_SRC cmod_financial_eqns.cpp cmod_financial_eqns.h cmod_fossilgen.cpp + cmod_fresnel_physical.cpp + cmod_fresnel_physical_iph.cpp cmod_fuelcell.cpp cmod_fuelcell.h cmod_generic_system-builder.cpp @@ -70,6 +72,7 @@ set(SSC_SRC cmod_mhk_tidal.cpp cmod_mhk_wave.cpp cmod_mspt_sf_and_rec_isolated.cpp + cmod_mspt_iph.cpp cmod_poacalib.cpp cmod_ptes_design_point.cpp cmod_pv6parmod.cpp diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp new file mode 100644 index 000000000..6e722c57b --- /dev/null +++ b/ssc/cmod_fresnel_physical.cpp @@ -0,0 +1,1872 @@ +/* +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 "common.h" + +#include "csp_solver_fresnel_collector_receiver.h" +#include "csp_solver_pc_Rankine_indirect_224.h" +#include "csp_solver_tou_block_schedules.h" +#include "csp_dispatch.h" +#include "csp_system_costs.h" + +#include +#include +#include + +static var_info _cm_vtab_fresnel_physical[] = { + + { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, + + + // Weather Reader + + { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, + + + // System Design + + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System_Design", "", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System_Design", "", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System_Design", "", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "P_ref", "Design Turbine Net Output", "MWe", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Equivalent full-load thermal storage hours", "hr", "", "System_Design", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "System_Design", "*", "", "" }, + + + // Solar Field + + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "Solar_Field", "*", "INTEGER", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "Solar_Field", "*", "INTEGER", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "water_per_wash", "Water usage per wash", "L/m2_aper", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "washes_per_year", "Mirror washing frequency", "none", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_htf_vol", "Volume of HTF in a single collector unit per unit aperture area", "L/m2-ap", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_su_delay", "Fixed startup delay time for the receiver", "hr", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_qf_delay", "Energy-based receiver startup delay (fraction of rated thermal power)", "-", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "p_start", "Collector startup energy, per SCA", "kWe-hr", "", "Solar_Field", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "Solar_Field", "*", "", "" }, + + // Collector and Receiver + + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "Col_Rec", "*", "INTEGER", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "Col_Rec", "*", "", "" }, + + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "Col_Rec", "*", "INTEGER", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "Col_Rec", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "Col_Rec", "?=4", "INTEGER", "" }, + + + // Power Cycle Inputs + + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "Powerblock", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "Powerblock", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "Powerblock", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Powerblock", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "Powerblock", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "Powerblock", "*", "", "" }, + /*X*/ /*startup script*/{ SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "Powerblock", "", "INTEGER", "" }, + + + + // Steam Rankine Cycle + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet T diff", "C", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "CT", "Flag for using dry cooling or wet cooling system", "none", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "none", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction ", "none", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_ARRAY, "F_wc", "Fraction indicating wet cooling use for hybrid system", "none", "constant=[0,0,0,0,0,0,0,0,0]", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control flag (sliding=user, fixed=fresnel)", "1/2/3", "tower/trough/user", "Powerblock", "pc_config=0", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "Powerblock", "*", "", "" }, + + + // User Defined cycle + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "ud_f_W_dot_cool_des", "Percent of user-defined power cycle design gross output consumed by cooling", "%", "", "user_defined_PC", "pc_config=1", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "ud_m_dot_water_cool_des", "Mass flow rate of water required at user-defined power cycle design point", "kg/s", "", "user_defined_PC", "pc_config=1", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_MATRIX, "ud_ind_od", "Off design user-defined power cycle performance as function of T_htf, m_dot_htf [ND], and T_amb", "", "", "user_defined_PC", "pc_config=1", "", "" }, + + // TES + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "store_fluid", "Storage HTF ID", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Storage user-defined HTF Properties", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Height of HTF when tank is full", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Loss coefficient from tank", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Number of equivalent tank pairs", "", "", "Storage", "*", "INTEGER", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Hot tank heater set point", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Cold tank heater set point", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Hot side HX approach temp", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Minimum tank fluid height", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Cold side HX approach temp", "", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Pumping power to move 1kg of HTF through tes loop", "kW/(kg/s)", "", "Storage", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "Storage", "*", "", "" }, + + /*Storage NOT in UI*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "Storage", "?=1.85", "", "SIMULATION_PARAMETER" }, + + + // System Control + + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Fixed parasitic load - runs at all times", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Balance of plant parasitic power fraction", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Aux heater, boiler parasitic", "", "", "Sys_Control", "*", "", "" }, + + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "Sys_Control", "?=0", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "Sys_Control", "?=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "Sys_Control", "", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "Sys_Control", "?=0.99", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "Sys_Control", "is_dispatch=1", "", "" }, + + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "SIMULATION_PARAMETER" }, + + + + // Financials + /*Sys Design*/{SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", ""}, + + + /*Dipatch*/{ SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "Sys_Control", "*", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "Sys_Control", "*", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_NUMBER, "is_tod_pc_target_also_pc_max","Is the TOD target cycle heat input also the max cycle heat input?", "", "", "tou", "?=0", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, + + /*Startup Script*/{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "SIMULATION_PARAMETER" }, + + + /*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "PPA pricing weekday schedule, 12x24", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "PPA pricing weekend schedule, 12x24", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + { SSC_INPUT, SSC_ARRAY, "dispatch_tod_factors", "TOD factors for periods 1 through 9", "", + "We added this array input after SAM 2022.12.21 to replace the functionality of former single value inputs dispatch_factor1 through dispatch_factor9", "Time of Delivery Factors","ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1","", "SIMULATION_PARAMETER" }, + + + + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor1", "Dispatch payment factor 1", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor2", "Dispatch payment factor 2", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor3", "Dispatch payment factor 3", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor4", "Dispatch payment factor 4", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor5", "Dispatch payment factor 5", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor6", "Dispatch payment factor 6", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor7", "Dispatch payment factor 7", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor8", "Dispatch payment factor 8", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + ///*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "dispatch_factor9", "Dispatch payment factor 9", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + + /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_NUMBER, "ppa_soln_mode", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + + /*Fin Merc Plant Energy*/{ SSC_INPUT, SSC_MATRIX, "mp_energy_market_revenue", "Energy market revenue input", "", "Lifetime x 2[Cleared Capacity(MW),Price($ / MWh)]", "Revenue", "csp_financial_model=6&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + + // Capital Costs + + // Direct Capital Costs + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "site_improvements_spec_cost", "Site Improvement Cost per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "solar_field_spec_cost", "Solar Field Cost per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "htf_system_spec_cost", "HTF System Cost Per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "storage_spec_cost", "Storage cost per kWht", "$/kWht", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "fossil_spec_cost", "Fossil Backup Cost per kWe", "$/kWe", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "power_plant_spec_cost", "Power Plant Cost per kWe", "$/kWe", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "bop_spec_cost", "Balance of Plant Cost per kWe", "$/kWe", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "contingency_percent", "Contingency Percent", "%", "", "Capital_Costs", "?=0", "", "" }, + + // Indirect Capital Costs + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_per_acre", "EPC Costs per acre", "$/acre", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_percent_direct", "EPC Costs % direct", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_per_watt", "EPC Cost Wac", "$/Wac", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_fixed", "Fixed EPC Cost", "$", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_per_acre", "Land Cost per acre", "$/acre", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_percent_direct", "Land Cost % direct", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_per_watt", "Land Cost Wac", "$/Wac", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_fixed", "Fixed Land Cost", "$", "", "Capital_Costs", "?=0", "", "" }, + + + // Sales Tax + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "sales_tax_percent", "Sales Tax Percentage of Direct Cost", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Fin Tax and Insurace*/{ SSC_INPUT, SSC_NUMBER, "sales_tax_rate", "Sales Tax Rate", "%", "", "Capital_Costs", "?=0", "", "" }, + + + // OUTPUTS + // Design Point Outputs + + // System Design + { SSC_OUTPUT, SSC_NUMBER, "solar_mult", "Actual solar multiple", "", "", "System Design Calc", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_Ap", "Actual field aperture", "m2", "", "System Design Calc", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "nameplate", "Nameplate capacity", "MWe", "", "System Design Calc", "*", "", "" }, + + + // Solar Field + { SSC_OUTPUT, SSC_NUMBER, "q_dot_rec_des", "Receiver thermal output at design", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "A_loop", "Aperture of a single loop", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_opt_eff", "Loop optical efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sm1_aperture", "Total required aperture, SM=1", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sm1_nLoops", "Required number of loops, SM=1", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_tracking_power", "Design tracking power", "MW", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "A_field", "Total field aperture", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "q_field_des", "Design field power output", "MW", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_area", "Solar field area", "acres", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_land_area", "Total land area", "acres", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_htf_min_temp", "Minimum field htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_htf_max_temp", "Maximum field htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "mdot_field_des", "Field design HTF mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + + { SSC_OUTPUT, SSC_NUMBER, "dP_field_des_SS", "Steady State Field design total pressure drop", "bar", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_field_des_SS", "Steady State Field design thermal power", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "T_field_out_des_SS", "Steady State Field design outlet temperature", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_des_SS", "Steady State Field mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_loop_des_SS", "Steady State Loop mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "V_hdr_min_des_SS", "Steady State min header velocity", "m/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "V_hdr_max_des_SS", "Steady State max header velocity", "m/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eta_optical_des_SS", "Steady State optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "therm_eff_des_SS", "Steady State field optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eff_des_SS", "Steady State field total efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_pump_des_SS", "Steady State field pumping power", "MWe", "", "Receiver", "*", "", "" }, + + + { SSC_OUTPUT, SSC_NUMBER, "T_loop_out_des_SS", "Steady State loop design outlet temperature", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loop_des_SS", "Steady State loop design thermal power", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "therm_eff_loop_des_SS", "Steady State loop optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eff_loop_des_SS", "Steady State loop total efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_pump_des_SS", "Steady State field pumping power", "MWe", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loss_receiver_des_SS", "Steady State field heat loss from receiver", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loss_hdr_rnr_des_SS", "Steady State field heat loss from headers and runners", "MWt", "", "Receiver", "*", "", "" }, + + + // Collector and Receiver + { SSC_OUTPUT, SSC_NUMBER, "DP_pressure_loss", "Total loop pressure loss at design", "bar", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "avg_dt_des", "Average field temp difference at design", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "hl_des", "Heat loss at design", "W/m", "", "Receiver", "*", "", "" }, + { 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", "*", "", "" }, + + // Thermal Storage + { SSC_OUTPUT, SSC_NUMBER, "vol_tank", "Total tank volume", "m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_tes_des", "TES design capacity", "MWt-hr", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "d_tank", "Tank diameter", "m", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "vol_min", "Minimum Fluid Volume", "m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "q_dot_loss_tes_des", "Estimated TES Heat Loss", "MW", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_min_temp", "Minimum storage htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_max_temp", "Maximum storage htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_dens", "Storage htf density", "kg/m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_cp", "Storage htf specific heat", "kJ/kg-K", "", "Power Cycle", "*", "", "" }, + + // System Control + { SSC_OUTPUT, SSC_NUMBER, "W_dot_bop_design", "BOP parasitics at design", "MWe", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_fixed", "Fixed parasitic at design", "MWe", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "aux_design", "Aux parasitics at design", "MWe", "", "System Control", "*", "", "" }, + + // Capital Costs + + // Direct Capital Costs + { SSC_OUTPUT, SSC_NUMBER, "site_improvements_cost", "Site improvements cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "solar_field_cost", "Solar field cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "htf_system_cost", "HTF system cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "ts_cost", "Thermal storage cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "fossil_backup_cost", "Fossil backup cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "power_plant_cost", "Power plant cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "bop_cost", "Balance of plant cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "contingency_cost", "Contingency cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_direct_cost", "Total direct cost", "$", "", "Capital Costs", "", "", "" }, + + // Indirect Capital Costs + { SSC_OUTPUT, SSC_NUMBER, "epc_total_cost", "EPC total cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "plm_total_cost", "Total land cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_indirect_cost", "Total direct cost", "$", "", "Capital Costs", "", "", "" }, + + // Sales Tax + { SSC_OUTPUT, SSC_NUMBER, "sales_tax_total", "Sales tax total", "$", "", "Capital Costs", "", "", "" }, + + // Total Installed Costs + { SSC_OUTPUT, SSC_NUMBER, "total_installed_cost", "Total installed cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "installed_per_capacity", "Estimated total installed cost per net capacity ($/kW)", "$/kW", "", "Capital Costs", "", "", "" }, + + + + + + + + // Simulation outputs + + + + // Simulation Kernel + { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "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", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solazi", "Resource Solar Azimuth", "deg", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solzen", "Resource Solar Zenith", "deg", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "beam", "Resource Beam normal irradiance", "W/m2", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tdry", "Resource Dry bulb temperature", "C", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "twet", "Resource Wet bulb temperature", "C", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "rh", "Resource Relative Humidity", "%", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "wspd", "Resource Wind Speed", "m/s", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pres", "Resource Pressure", "mbar", "", "weather", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "weather", "sim_type=1", "", "" }, + + + + // Solar Field (from Trough) + { SSC_OUTPUT, SSC_ARRAY, "EqOpteff", "Field optical efficiency before defocus", "", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "SCAs_def", "Field fraction of focused SCAs", "", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_inc", "Receiver thermal power incident", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_thermal_loss", "Receiver thermal losses", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_abs", "Receiver thermal power absorbed", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "rec_thermal_eff", "Receiver thermal efficiency", "", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "q_dot_piping_loss", "Field piping thermal losses", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_dot_field_int_energy", "Field change in material/htf internal energy", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_htf_sf_out", "Field thermal power leaving in HTF", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_freeze_prot", "Field freeze protection required", "MWt", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "m_dot_loop", "Receiver mass flow rate", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_recirc", "Field total mass flow recirculated", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_delivered", "Field total mass flow delivered", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_cold_in", "Field timestep-averaged inlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_cold_in", "Loop timestep-averaged inlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_hot_out", "Loop timestep-averaged outlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_hot_out", "Field timestep-averaged outlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "deltaP_field", "Field pressure drop", "bar", "", "solar_field", "sim_type=1", "", "" }, + + { 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", "", "" }, + + + // 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", "", "" }, + + // 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", "MWe", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_hot", "TES hot temperature", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_cold", "TES cold temperature", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_cold", "TES cold tank mass (end)", "kg", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_hot", "TES hot tank mass (end)", "kg", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dc_tes", "TES discharge thermal power", "MWt", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_ch_tes", "TES charge thermal power", "MWt", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_ch_tes", "TES charge state", "MWht", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cr_to_tes_hot", "Mass flow: field to hot TES", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_hot_out", "Mass flow: TES hot out", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc_to_tes_cold", "Mass flow: cycle to cold TES", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_cold_out", "Mass flow: TES cold out", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_to_cycle", "Mass flow: field to cycle", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cycle_to_field", "Mass flow: cycle to field", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cold_tank_to_hot_tank", "Mass flow: cold tank to hot tank", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tes_htf_pump_power", "TES HTF pump power", "MWe", "", "TES", "sim_type=1", "", "" }, + + // Controller + { SSC_OUTPUT, SSC_ARRAY, "op_mode_1", "1st operating mode", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_2", "2nd op. mode, if applicable", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_3", "3rd op. mode, if applicable", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_balance", "Relative mass flow balance error", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_balance", "Relative energy balance error", "", "", "solver", "sim_type=1", "", "" }, + + // Monthly Outputs + { SSC_OUTPUT, SSC_ARRAY, "monthly_energy", "Monthly Energy", "kWh", "", "Post-process", "sim_type=1", "LENGTH=12", "" }, + + // Annual Outputs + { SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual Net Electrical Energy Production w/ avail derate", "kWe-hr", "", "Post-process", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "annual_gross_energy", "Annual Gross Electrical Energy Production w/ avail derate", "kWe-hr", "", "Post-process", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_thermal_consumption", "Annual thermal freeze protection required", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "annual_electricity_consumption", "Annual electricity consumptoin w/ avail derate", "kWe-hr", "", "Post-process", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total Annual Water Usage", "m^3", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_field_freeze_protection", "Annual thermal power for field freeze protection", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_tes_freeze_protection", "Annual thermal power for TES freeze protection", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + + // Newly added + { SSC_OUTPUT, SSC_ARRAY, "n_op_modes", "Operating modes in reporting timestep", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tou_value", "CSP operating Time-of-use value", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pricing_mult", "PPA price multiplier", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_sb", "Thermal power for PC standby", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_min", "Thermal power for PC min operation", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_target", "Target thermal power to PC", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_max", "Max thermal power to PC", "MWt", "", "solver", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "is_rec_su_allowed", "is receiver startup allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_su_allowed", "is power cycle startup allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_sb_allowed", "is power cycle standby allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_su", "Estimate rec. startup thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_on", "Estimate rec. thermal power TO HTF", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_dc", "Estimate max TES discharge thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_ch", "Estimate max TES charge thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_a", "First 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_b", "Next 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_c", "Final 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "disp_rel_mip_gap", "Dispatch relative MIP gap", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_state", "Dispatch solver state", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_subopt_flag", "Dispatch suboptimal solution flag", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_iter", "Dispatch iterations count", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_objective", "Dispatch objective function value", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_obj_relax", "Dispatch objective function - relaxed max", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsf_expected", "Dispatch expected solar field available energy", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfprod_expected", "Dispatch expected solar field generation", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfsu_expected", "Dispatch expected solar field startup enegy", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_tes_expected", "Dispatch expected TES charge level", "MWht", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_pceff_expected", "Dispatch expected power cycle efficiency adj.", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_thermeff_expected", "Dispatch expected SF thermal efficiency adj.", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qpbsu_expected", "Dispatch expected power cycle startup energy", "MWht", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_wpb_expected", "Dispatch expected power generation", "MWe", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_rev_expected", "Dispatch expected revenue factor", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nconstr", "Dispatch number of constraints in problem", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nvar", "Dispatch number of variables in problem", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_time", "Dispatch solver time", "sec", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "tou", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "P_fixed", "Parasitic power fixed load", "MWe", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "P_plant_balance_tot", "Parasitic power generation-dependent load", "MWe", "", "system", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "P_out_net", "Total electric power to grid", "MWe", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "gen", "Total electric power to grid w/ avail. derate", "kWe", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - Power cycle gross output", "kWhe", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "conversion_factor", "Gross to Net Conversion Factor", "%", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sim_duration", "Computational time of timeseries simulation", "s", "", "system", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "W_dot_par_tot_haf", "Adjusted parasitic power", "kWe", "", "system", "*", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "q_dot_defocus_est", "Thermal energy intentionally lost by defocusing", "MWt", "", "system", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "recirculating", "Field recirculating (bypass valve open)", "-", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_diams", "Pipe diameters in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_wallthk", "Pipe wall thickness in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_lengths", "Pipe lengths in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_mdot_dsn", "Mass flow TES pipes at design conditions", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_vel_dsn", "Velocity in TES pipes at design conditions", "m/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_T_dsn", "Temperature in TES pipes at design conditions", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_P_dsn", "Pressure in TES pipes at design conditions", "bar", "", "TES", "sim_type=1", "", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "solver", "*", "", "" }, + + + var_info_invalid }; + +class cm_fresnel_physical : public compute_module +{ +public: + + cm_fresnel_physical() + { + add_var_info(_cm_vtab_fresnel_physical); + add_var_info(vtab_adjustment_factors); + } + + void exec() + { + // Common Parameters + bool is_dispatch = as_boolean("is_dispatch"); + int sim_type = as_number("sim_type"); + + // Weather reader + C_csp_weatherreader weather_reader; + C_csp_solver::S_sim_setup sim_setup; + int n_steps_fixed; + int steps_per_hour; + { + weather_reader.m_weather_data_provider = std::make_shared(as_string("file_name")); + weather_reader.m_filename = as_string("file_name"); + weather_reader.m_trackmode = 0; + weather_reader.m_tilt = 0.0; + weather_reader.m_azimuth = 0.0; + // Initialize to get weather file info + weather_reader.init(); + if (weather_reader.has_error()) throw exec_error("fresnel_physical", weather_reader.get_error()); + + // Set up ssc output arrays + // Set steps per hour + double nhourssim = 8760.0; //[hr] Number of hours to simulate + + sim_setup.m_sim_time_start = 0.0; //[s] starting first hour of year + sim_setup.m_sim_time_end = nhourssim * 3600.; //[s] full year simulation + + steps_per_hour = 1; //[-] + + int n_wf_records = (int)weather_reader.m_weather_data_provider->nrecords(); + steps_per_hour = n_wf_records / 8760; //[-] + + n_steps_fixed = steps_per_hour * 8760; //[-] + sim_setup.m_report_step = 3600.0 / (double)steps_per_hour; //[s] + } + + // Solar field + C_csp_fresnel_collector_receiver c_fresnel; + { + // Inputs + { + c_fresnel.m_solar_mult_or_Ap = as_integer("solar_mult_or_Ap"); + c_fresnel.m_solar_mult_in = as_double("solar_mult_in"); + c_fresnel.m_total_Ap_in = as_double("total_Ap_in"); + + c_fresnel.m_nMod = as_integer("nMod"); + c_fresnel.m_nRecVar = as_integer("nRecVar"); + + c_fresnel.m_eta_pump = as_number("eta_pump"); + c_fresnel.m_HDR_rough = as_number("HDR_rough"); + c_fresnel.m_theta_stow = as_number("theta_stow"); + c_fresnel.m_theta_dep = as_number("theta_dep"); + c_fresnel.m_FieldConfig = as_integer("FieldConfig"); + c_fresnel.m_T_startup = as_number("T_startup"); + c_fresnel.m_P_ref = as_double("P_ref") * 1e6; + c_fresnel.m_eta_ref = as_double("eta_ref"); + + c_fresnel.m_m_dot_htfmin = as_number("m_dot_htfmin"); + c_fresnel.m_m_dot_htfmax = as_number("m_dot_htfmax"); + c_fresnel.m_T_loop_in_des = as_number("T_loop_in_des"); + + c_fresnel.m_T_loop_out_des = as_number("T_loop_out"); + c_fresnel.m_Fluid = as_integer("Fluid"); + + c_fresnel.m_field_fl_props = as_matrix("field_fl_props"); + c_fresnel.m_T_fp = as_number("T_fp"); + c_fresnel.m_I_bn_des = as_number("I_bn_des"); + c_fresnel.m_V_hdr_max = as_number("V_hdr_max"); + c_fresnel.m_V_hdr_min = as_number("V_hdr_min"); + c_fresnel.m_Pipe_hl_coef = as_number("Pipe_hl_coef"); + c_fresnel.m_SCA_drives_elec = as_number("SCA_drives_elec"); + c_fresnel.m_ColAz = as_number("ColAz"); + + c_fresnel.m_mc_bal_hot = as_number("mc_bal_hot"); + c_fresnel.m_mc_bal_cold = as_number("mc_bal_cold"); + c_fresnel.m_mc_bal_sca = as_number("mc_bal_sca"); + + c_fresnel.m_opt_model = as_integer("opt_model"); + + c_fresnel.m_A_aperture = as_number("A_aperture"); + c_fresnel.m_reflectivity = as_number("reflectivity"); + c_fresnel.m_TrackingError = as_number("TrackingError"); + c_fresnel.m_GeomEffects = as_number("GeomEffects"); + c_fresnel.m_Dirt_mirror = as_number("Dirt_mirror"); + c_fresnel.m_Error = as_number("Error"); + c_fresnel.m_L_mod = as_number("L_mod"); + + c_fresnel.m_IAM_T_coefs = as_vector_double("IAM_T_coefs"); + c_fresnel.m_IAM_L_coefs = as_vector_double("IAM_L_coefs"); + c_fresnel.m_OpticalTable = as_matrix("OpticalTable"); + c_fresnel.m_rec_model = as_integer("rec_model"); + + c_fresnel.m_HCE_FieldFrac = as_vector_double("HCE_FieldFrac"); + c_fresnel.m_D_abs_in = as_vector_double("D_abs_in"); + c_fresnel.m_D_abs_out = as_vector_double("D_abs_out"); + c_fresnel.m_D_glass_in = as_vector_double("D_glass_in"); + c_fresnel.m_D_glass_out = as_vector_double("D_glass_out"); + c_fresnel.m_D_plug = as_vector_double("D_plug"); + c_fresnel.m_Flow_type = as_vector_double("Flow_type"); + c_fresnel.m_Rough = as_vector_double("Rough"); + c_fresnel.m_alpha_env = as_vector_double("alpha_env"); + + c_fresnel.m_epsilon_abs_1 = as_matrix_transpose("epsilon_abs_1"); + c_fresnel.m_epsilon_abs_2 = as_matrix_transpose("epsilon_abs_2"); + c_fresnel.m_epsilon_abs_3 = as_matrix_transpose("epsilon_abs_3"); + c_fresnel.m_epsilon_abs_4 = as_matrix_transpose("epsilon_abs_4"); + + c_fresnel.m_alpha_abs = as_vector_double("alpha_abs"); + c_fresnel.m_Tau_envelope = as_vector_double("Tau_envelope"); + c_fresnel.m_epsilon_glass = as_vector_double("epsilon_glass"); + c_fresnel.m_GlazingIntact = as_vector_bool("GlazingIntactIn"); + + c_fresnel.m_P_a = as_vector_double("P_a"); + + c_fresnel.m_AnnulusGas = as_vector_double("AnnulusGas"); + c_fresnel.m_AbsorberMaterial = as_vector_double("AbsorberMaterial"); + c_fresnel.m_Shadowing = as_vector_double("Shadowing"); + c_fresnel.m_dirt_env = as_vector_double("dirt_env"); + c_fresnel.m_Design_loss = as_vector_double("Design_loss"); + + c_fresnel.m_L_mod_spacing = as_number("L_mod_spacing"); + c_fresnel.m_L_crossover = as_number("L_crossover"); + c_fresnel.m_HL_T_coefs = as_vector_double("HL_T_coefs"); + c_fresnel.m_HL_w_coefs = as_vector_double("HL_w_coefs"); + + c_fresnel.m_DP_nominal = as_number("DP_nominal"); + c_fresnel.m_DP_coefs = as_vector_double("DP_coefs"); + c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); + + c_fresnel.m_L_rnr_pb = as_number("L_rnr_pb"); + c_fresnel.m_rec_su_delay = as_number("rec_su_delay"); + c_fresnel.m_rec_qf_delay = as_number("rec_qf_delay"); + c_fresnel.m_p_start = as_number("p_start"); + + c_fresnel.m_V_wind_des = as_number("V_wind_des"); + c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); + } + + // Calculate solar multiple (needed for other component constructors) + c_fresnel.design_solar_mult(); + + // Allocate Outputs + { + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_THETA_AVE, allocate("Theta_ave", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_COSTH_AVE, allocate("CosTh_ave", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_IAM_AVE, allocate("IAM_ave", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_ROWSHADOW_AVE, allocate("RowShadow_ave", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_ENDLOSS_AVE, allocate("EndLoss_ave", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_DNI_COSTH, allocate("dni_costh", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_EQUIV_OPT_ETA_TOT, allocate("EqOpteff", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_DEFOCUS, allocate("SCAs_def", n_steps_fixed), n_steps_fixed); + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_TOT, allocate("q_inc_sf_tot", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_COSTH, allocate("qinc_costh", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_INC, allocate("q_dot_rec_inc", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_THERMAL_LOSS, allocate("q_dot_rec_thermal_loss", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_ABS, allocate("q_dot_rec_abs", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_REC_THERMAL_EFF, allocate("rec_thermal_eff", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_PIPING_LOSS, allocate("q_dot_piping_loss", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_E_DOT_INTERNAL_ENERGY, allocate("e_dot_field_int_energy", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_HTF_OUT, allocate("q_dot_htf_sf_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_FREEZE_PROT, allocate("q_dot_freeze_prot", n_steps_fixed), n_steps_fixed); + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_LOOP, allocate("m_dot_loop", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_IS_RECIRCULATING, allocate("recirculating", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_RECIRC, allocate("m_dot_field_recirc", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_DELIVERED, allocate("m_dot_field_delivered", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_FIELD_COLD_IN, allocate("T_field_cold_in", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_REC_COLD_IN, allocate("T_rec_cold_in", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_REC_HOT_OUT, allocate("T_rec_hot_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_FIELD_HOT_OUT, allocate("T_field_hot_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_PRESSURE_DROP, allocate("deltaP_field", n_steps_fixed), n_steps_fixed); //[bar] + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_W_DOT_SCA_TRACK, allocate("W_dot_sca_track", n_steps_fixed), n_steps_fixed); //[MWe] + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_W_DOT_PUMP, allocate("W_dot_field_pump", n_steps_fixed), n_steps_fixed); //[MWe] + + //// Fresnel + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_THETA_L, allocate("theta_L", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_PHI_T, allocate("phi_t", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_ETA_OPTICAL, allocate("eta_optical", n_steps_fixed), n_steps_fixed); + ////c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_EQ_OPT_EFF, allocate("EqOptEff", n_steps_fixed), n_steps_fixed); + + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_SF_DEF, allocate("sf_def", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_INC_SF_TOT, allocate("q_inc_sf_tot", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_ABS_TOT, allocate("q_abs_tot", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DUMP, allocate("q_dump", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_LOSS_TOT, allocate("q_loss_tot", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_PIPE_HL, allocate("Pipe_hl", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_AVAIL, allocate("q_avail", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_LOSS_SPEC_TOT, allocate("q_loss_spec_tot", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_ETA_THERMAL, allocate("eta_thermal", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_E_BAL_STARTUP, allocate("E_bal_startup", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_AVAIL, allocate("m_dot_avail", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_HTF2, allocate("m_dot_htf2", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_DP_TOT, allocate("DP_tot", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_SYS_C, allocate("T_sys_c", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_SYS_H, allocate("T_sys_h", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_LOOP_OUTLET, allocate("t_loop_outlet", n_steps_fixed), n_steps_fixed); + //c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_I, allocate("q_I", n_steps_fixed), n_steps_fixed); + } + + } + + // Power cycle + C_csp_power_cycle* p_csp_power_cycle; + C_pc_Rankine_indirect_224 rankine_pc; // Steam Rankine and User Defined power cycle classes + { + int pb_tech_type = as_integer("pc_config"); + if (!(pb_tech_type == 0 || pb_tech_type == 1)) // 0 = Rankine, 1 = UDPC + { + throw exec_error("fresnel_physical", "unsupported power cycle"); + } + else + { + C_pc_Rankine_indirect_224::S_params* pc = &rankine_pc.ms_params; + pc->m_P_ref = as_double("P_ref"); + pc->m_eta_ref = as_double("eta_ref"); + pc->m_T_htf_hot_ref = as_double("T_loop_out"); + pc->m_T_htf_cold_ref = as_double("T_loop_in_des"); + pc->m_cycle_max_frac = as_double("cycle_max_frac"); + pc->m_cycle_cutoff_frac = as_double("cycle_cutoff_frac"); + pc->m_q_sby_frac = as_double("q_sby_frac"); + pc->m_startup_time = as_double("startup_time"); + pc->m_startup_frac = as_double("startup_frac"); + pc->m_htf_pump_coef = as_double("pb_pump_coef"); + pc->m_pc_fl = as_integer("Fluid"); // power cycle HTF is same as receiver HTF + pc->m_pc_fl_props = as_matrix("field_fl_props"); + pc->DP_SGS = as_double("DP_SGS"); + + if (pb_tech_type == 0) + { + pc->m_dT_cw_ref = as_double("dT_cw_ref"); + pc->m_T_amb_des = as_double("T_amb_des"); + //pc->m_P_boil = as_double("P_boil"); + pc->m_P_boil_des = 100.0; //[bar] + pc->m_CT = as_integer("CT"); // cooling tech type: 1=evaporative, 2=air, 3=hybrid + pc->m_tech_type = as_integer("tech_type"); // turbine inlet pressure: 1: Fixed, 3: Sliding + if (pc->m_tech_type == 1) { pc->m_tech_type = 2; }; // changing fixed pressure for the tower to fixed pressure for the fresnel + if (pc->m_tech_type == 3) { pc->m_tech_type = 8; }; // changing sliding pressure for the tower to sliding pressure for the fresnel + if (!(pc->m_tech_type == 2 || pc->m_tech_type == 5 || pc->m_tech_type == 6 || pc->m_tech_type == 8)) + { + std::string tech_msg = util::format("tech_type must be either 2 (fixed pressure) or 8 (sliding). Input was %d." + " Simulation proceeded with fixed pressure", pc->m_tech_type); + pc->m_tech_type = 2; + } + pc->m_T_approach = as_double("T_approach"); + pc->m_T_ITD_des = as_double("T_ITD_des"); + pc->m_P_cond_ratio = as_double("P_cond_ratio"); + pc->m_pb_bd_frac = as_double("pb_bd_frac"); + pc->m_P_cond_min = as_double("P_cond_min"); + pc->m_n_pl_inc = as_integer("n_pl_inc"); + + size_t n_F_wc = 0; + ssc_number_t* p_F_wc = as_array("F_wc", &n_F_wc); + pc->m_F_wc.resize(n_F_wc, 0.0); + for (size_t i = 0; i < n_F_wc; i++) + pc->m_F_wc[i] = (double)p_F_wc[i]; + + pc->m_is_user_defined_pc = false; + pc->m_W_dot_cooling_des = std::numeric_limits::quiet_NaN(); + } + else if (pb_tech_type == 1) + { + pc->m_is_user_defined_pc = true; + + // User-Defined Cycle Parameters + pc->m_W_dot_cooling_des = as_double("ud_f_W_dot_cool_des") / 100.0 * as_double("P_ref"); //[MWe] + pc->m_m_dot_water_des = as_double("ud_m_dot_water_cool_des"); //[kg/s] + + // User-Defined Cycle Off-Design Tables + pc->mc_combined_ind = as_matrix("ud_ind_od"); + } + + // Set pointer to parent class + p_csp_power_cycle = &rankine_pc; + + // Set power cycle outputs common to all power cycle technologies + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_Q_DOT_HTF, allocate("q_pb", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_DOT_HTF, allocate("m_dot_pc", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_Q_DOT_STARTUP, allocate("q_dot_pc_startup", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_W_DOT, allocate("P_cycle", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_HTF_IN, allocate("T_pc_in", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_HTF_OUT, allocate("T_pc_out", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_DOT_WATER, allocate("m_dot_water_pc", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_W_DOT_HTF_PUMP, allocate("cycle_htf_pump_power", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_W_DOT_COOLER, allocate("P_cooling_tower_tot", n_steps_fixed), n_steps_fixed); + + // Dependent reported variable + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_ETA_THERMAL, allocate("eta", n_steps_fixed), n_steps_fixed); + } + + } + + // TES + C_csp_two_tank_tes storage; + { + double V_tes_des = as_double("V_tes_des"); + /*double eta_pump = 0.85; + bool has_hot_tank_bypass = false; + double T_tank_hot_inlet_min = 200; + bool custom_tes_p_loss = false; + bool custom_tes_pipe_sizes = false; + + double k_tes_loss_coeffs_val[11] = { 0,0,0,0,0,0,0,0,0,0,0 }; + util::matrix_t k_tes_loss_coeffs; + k_tes_loss_coeffs.assign(k_tes_loss_coeffs_val, 11); + + double tes_diams_val[1] = { -1 }; + util::matrix_t tes_diams; + tes_diams.assign(tes_diams_val, 1); + + double tes_wallthicks_val[1] = { -1 }; + util::matrix_t tes_wallthicks; + tes_wallthicks.assign(tes_wallthicks_val, 1); + + double pipe_rough = 4.5700000000000000e-05; + double dP_discharge = 0; + + double tes_length_vals[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; + util::matrix_t tes_lengths; + tes_lengths.assign(tes_length_vals, 11);*/ + + storage = C_csp_two_tank_tes( + as_integer("Fluid"), + as_matrix("field_fl_props"), + as_integer("store_fluid"), + as_matrix("store_fl_props"), + as_double("P_ref") / as_double("eta_ref"), + c_fresnel.m_solar_mult, + as_double("P_ref") / as_double("eta_ref") * as_double("tshours"), + as_double("h_tank"), + as_double("u_tank"), + as_integer("tank_pairs"), + as_double("hot_tank_Thtr"), + as_double("hot_tank_max_heat"), + as_double("cold_tank_Thtr"), + as_double("cold_tank_max_heat"), + as_double("dt_hot"), + as_double("T_loop_in_des"), + as_double("T_loop_out"), + as_double("T_loop_out"), + as_double("T_loop_in_des"), + as_double("h_tank_min"), + as_double("init_hot_htf_percent"), + as_double("pb_pump_coef"), + as_boolean("tanks_in_parallel"), + V_tes_des, + false, + as_double("tes_pump_coef") + ); + /*eta_pump, + has_hot_tank_bypass, + T_tank_hot_inlet_min, + custom_tes_p_loss, + custom_tes_pipe_sizes, + k_tes_loss_coeffs, + tes_diams, + tes_wallthicks, + tes_lengths, + pipe_rough, + dP_discharge + );*/ + + + //as_double("eta_pump"), + //as_boolean("has_hot_tank_bypass"), + //as_double("T_tank_hot_inlet_min"), + //as_boolean("custom_tes_p_loss"), + //custom_tes_pipe_sizes, + //k_tes_loss_coeffs_mat, + //tes_diams, + //tes_wallthicks, + //tes_lengths, + //as_double("HDR_rough"), + //as_double("DP_SGS") + //); + + // Set storage outputs + int n_wf_records = (int)weather_reader.m_weather_data_provider->nrecords(); + double steps_per_hour = n_wf_records / 8760; + int n_steps_fixed = steps_per_hour * 8760; + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_Q_DOT_LOSS, allocate("tank_losses", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HEATER, allocate("q_tes_heater", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_HOT, allocate("T_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_COLD, allocate("T_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_M_DOT_TANK_TO_TANK, allocate("m_dot_cold_tank_to_hot_tank", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_COLD_TANK, allocate("mass_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_HOT_TANK, allocate("mass_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HTF_PUMP, allocate("tes_htf_pump_power", n_steps_fixed), n_steps_fixed); + + } + + // TOU + C_csp_tou_block_schedules tou; + C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); + tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); + + tou.mc_dispatch_params.m_is_tod_pc_target_also_pc_max = as_boolean("is_tod_pc_target_also_pc_max"); + tou.mc_dispatch_params.m_is_block_dispatch = !is_dispatch; //mw + tou.mc_dispatch_params.m_use_rule_1 = true; + tou.mc_dispatch_params.m_standby_off_buffer = 2.0; + tou.mc_dispatch_params.m_use_rule_2 = false; + tou.mc_dispatch_params.m_q_dot_rec_des_mult = -1.23; + tou.mc_dispatch_params.m_f_q_dot_pc_overwrite = -1.23; + + size_t n_f_turbine = 0; + ssc_number_t* p_f_turbine = as_array("f_turb_tou_periods", &n_f_turbine); + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC].resize(n_f_turbine, 0.0); + //tou_params->mv_t_frac.resize(n_f_turbine, 0.0); + for (size_t i = 0; i < n_f_turbine; i++) + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC][i] = (double)p_f_turbine[i]; + + // Load fraction by time step: + bool is_load_fraction_by_timestep = is_assigned("timestep_load_fractions"); + tou_params->mc_csp_ops.mv_is_diurnal = !(is_load_fraction_by_timestep); + if (is_load_fraction_by_timestep) { + size_t N_load_fractions; + ssc_number_t* load_fractions = as_array("timestep_load_fractions", &N_load_fractions); + std::copy(load_fractions, load_fractions + N_load_fractions, std::back_inserter(tou_params->mc_csp_ops.timestep_load_fractions)); + } + + double ppa_price_year1 = std::numeric_limits::quiet_NaN(); + int csp_financial_model = as_integer("csp_financial_model"); + + if(sim_type == 1){ + + + if (tou_params->mc_pricing.mc_weekdays.ncells() == 1) { + // Resize default value from var table to proper dimensions + tou_params->mc_pricing.mc_weekdays = util::matrix_t(12, 24, 1.0); + } + if (tou_params->mc_pricing.mc_weekends.ncells() == 1) { + // Resize default value from var table to proper dimensions + tou_params->mc_pricing.mc_weekends = util::matrix_t(12, 24, 1.0); + } + + if (csp_financial_model > 0 && csp_financial_model < 5) { // Single Owner financial models + + // Get first year base ppa price + bool is_ppa_price_input_assigned = is_assigned("ppa_price_input"); + if (is_dispatch && !is_ppa_price_input_assigned) { + throw exec_error("fresnel_physical", "\n\nYou selected dispatch optimization which requires that the array input ppa_price_input is defined\n"); + } + + if (is_ppa_price_input_assigned) { + size_t count_ppa_price_input; + ssc_number_t* ppa_price_input_array = as_array("ppa_price_input", &count_ppa_price_input); + ppa_price_year1 = (double)ppa_price_input_array[0]; // [$/kWh] + } + else { + ppa_price_year1 = 1.0; //[-] don't need ppa multiplier if not optimizing + } + + int ppa_soln_mode = as_integer("ppa_soln_mode"); // PPA solution mode (0=Specify IRR target, 1=Specify PPA price) + if (ppa_soln_mode == 0 && is_dispatch) { + throw exec_error("fresnel_physical", "\n\nYou selected dispatch optimization and the Specify IRR Target financial solution mode, " + "but dispatch optimization requires known absolute electricity prices. Dispatch optimization requires " + "the Specify PPA Price financial solution mode. You can continue using dispatch optimization and iteratively " + "solve for the PPA that results in a target IRR by running a SAM Parametric analysis or script.\n"); + } + + int en_electricity_rates = as_integer("en_electricity_rates"); // 0 = Use PPA, 1 = Use Retail + if (en_electricity_rates == 1 && is_dispatch) { + throw exec_error("fresnel_physical", "\n\nYou selected dispatch optimization and the option to Use Retail Electricity Rates on the Electricity Purchases page, " + "but the dispatch optimization model currently does not accept separate buy and sell prices. Please use the Use PPA or Market Prices option " + "on the Electricity Purchases page.\n"); + } + + // Time-of-Delivery factors by time step: + int ppa_mult_model = as_integer("ppa_multiplier_model"); + if (ppa_mult_model == 1) // use dispatch_ts input + { + tou_params->mc_pricing.mv_is_diurnal = false; + + if (is_assigned("dispatch_factors_ts")) { + size_t nmultipliers; + ssc_number_t* multipliers = as_array("dispatch_factors_ts", &nmultipliers); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(nmultipliers, 0.0); + for (size_t ii = 0; ii < nmultipliers; ii++) + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][ii] = multipliers[ii]; + } + else { + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + } + else if (ppa_mult_model == 0) // standard diuranal input + { + tou_params->mc_pricing.mv_is_diurnal = true; + + // Most likely use case is to use schedules and TOD. So assume if at least one is provided, then user intended to use this approach + // the 'else' option applies non-feasible electricity prices, so we want to guard against selecting that it appears users + // are trying to use the schedules. + bool is_one_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") || is_assigned("dispatch_tod_factors"); + + if (is_one_assigned || is_dispatch) { + + tou_params->mc_pricing.mc_weekdays = as_matrix("dispatch_sched_weekday"); + tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); + + auto dispatch_tod_factors = as_vector_double("dispatch_tod_factors"); + if (dispatch_tod_factors.size() != 9) + throw exec_error("fresnel_physical", util::format("\n\nDispatch TOD factors has %d periods instead of the expected 9.\n", (int)dispatch_tod_factors.size())); + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); + for (size_t i = 0; i < 9; i++) + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_tod_factors[i]; + + } + else { + tou_params->mc_pricing.mc_weekdays.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mc_weekends.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, -1.0); + } + } + } + else if (csp_financial_model == 5) { // Commercial + if (is_dispatch) { + throw exec_error("fresnel_physical", "\nDispatch optimization current not enabled for the Commercial financial model\n"); + // need to add pricing lookup for Commercial financial model + } + if (false) {} + else { + tou_params->mc_pricing.mv_is_diurnal = false; + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + } + else if (csp_financial_model == 6) { // use 'mp_energy_market_revenue' -> from Merchant Plant model + + tou_params->mc_pricing.mv_is_diurnal = false; + + if (is_dispatch) { + util::matrix_t mp_energy_market_revenue = as_matrix("mp_energy_market_revenue"); // col 0 = cleared capacity, col 1 = $/MWh + size_t n_rows = mp_energy_market_revenue.nrows(); + if (n_rows < n_steps_fixed) { + + // Todo?: This throws error even if "Time series cleared capacity and price" is not set (ie 'Fixed cleared capacity and time series price' is set') + string ppa_msg = util::format("mp_energy_market_revenue input has %d rows but there are %d number of timesteps", n_rows, n_steps_fixed); + throw exec_error("fresnel_physical", ppa_msg); + } + + double conv_dolmwh_to_centkwh = 0.1; + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, 0.0); + for (size_t ii = 0; ii < n_steps_fixed; ii++) { + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][ii] = mp_energy_market_revenue(ii, 1) * conv_dolmwh_to_centkwh; //[cents/kWh] + } + } + if (false) {} + else { // if no dispatch optimization, don't need an input pricing schedule + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + } + else if (csp_financial_model == 7 || csp_financial_model == 8) { // LCOE (7) and None (8) + + tou_params->mc_pricing.mv_is_diurnal = false; + + // No hourly electricity pricing in these financial models + // However, may still want to solve with dispatch optimization to avoid rapid startup/shutdown, so set to uniform schedule + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, 1.0); + } + else { + throw exec_error("fresnel_physical", "csp_financial_model must be 1-8"); + } + } + else if (sim_type == 2){ + tou_params->mc_pricing.mv_is_diurnal = false; + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + + + + // System Parameters + C_csp_solver::S_csp_system_params system; + { + system.m_pb_fixed_par = as_double("pb_fixed_par"); + size_t nval_bop_array = 0; + ssc_number_t* bop_array = as_array("bop_array", &nval_bop_array); + if (nval_bop_array != 5) throw exec_error("fresnel_physical", "Should be 5 elements in bop_array, has " + util::to_string((int)nval_bop_array) + "."); + system.m_bop_par = bop_array[0]; //as_double("bop_par"); + system.m_bop_par_f = bop_array[1]; //as_double("bop_par_f"); + system.m_bop_par_0 = bop_array[2]; //as_double("bop_par_0"); + system.m_bop_par_1 = bop_array[3]; //as_double("bop_par_1"); + system.m_bop_par_2 = bop_array[4]; //as_double("bop_par_2"); + } + + // System Dispatch + csp_dispatch_opt dispatch; + { + if(is_dispatch){ + + // System Design Parameters + double W_dot_cycle_des = as_double("P_ref"); //[MWe] + double eta_cycle = as_double("eta_ref"); //[-] + + // System Design Calcs + double q_dot_cycle_des = W_dot_cycle_des / eta_cycle; //[MWt] + double q_dot_rec_des = q_dot_cycle_des * c_fresnel.m_solar_mult; //[MWt] + + dispatch.solver_params.set_user_inputs(is_dispatch, as_integer("disp_steps_per_hour"), as_integer("disp_frequency"), as_integer("disp_horizon"), + as_integer("disp_max_iter"), as_double("disp_mip_gap"), as_double("disp_timeout"), + as_integer("disp_spec_presolve"), as_integer("disp_spec_bb"), as_integer("disp_spec_scaling"), as_integer("disp_reporting"), + as_boolean("is_write_ampl_dat"), as_boolean("is_ampl_engine"), as_string("ampl_data_dir"), as_string("ampl_exec_call")); + + double disp_csu_cost_calc = as_double("disp_csu_cost_rel") * W_dot_cycle_des; //[$/start] + double disp_rsu_cost_calc = as_double("disp_rsu_cost_rel") * q_dot_rec_des; //[$/start] + dispatch.params.set_user_params(as_boolean("can_cycle_use_standby"), as_double("disp_time_weighting"), + disp_rsu_cost_calc, 0.0, disp_csu_cost_calc, as_double("disp_pen_ramping"), + as_double("disp_inventory_incentive"), as_double("q_rec_standby"), as_double("q_rec_heattrace"), ppa_price_year1); + } + else { + dispatch.solver_params.dispatch_optimize = false; + } + } + + + // Instantiate Solver + C_csp_solver csp_solver(weather_reader, + c_fresnel, + *p_csp_power_cycle, + storage, + tou, + dispatch, + system, + NULL, + nullptr, + ssc_cmod_update, + (void*)(this)); + + // Set solver reporting outputs + { + // 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); + // 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); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLAZ, allocate("solazi", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLZEN, allocate("solzen", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::BEAM, allocate("beam", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TDRY, allocate("tdry", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TWET, allocate("twet", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::RH, allocate("RH", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::WSPD, allocate("wspd", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PRES, allocate("pres", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CR_DEFOCUS, allocate("defocus", n_steps_fixed), n_steps_fixed); + // TES + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_DC, allocate("q_dc_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_CH, allocate("q_ch_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_E_CH_STATE, allocate("e_ch_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CR_TO_TES_HOT, allocate("m_dot_cr_to_tes_hot", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_HOT_OUT, allocate("m_dot_tes_hot_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_PC_TO_TES_COLD, allocate("m_dot_pc_to_tes_cold", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_COLD_OUT, allocate("m_dot_tes_cold_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_FIELD_TO_CYCLE, allocate("m_dot_field_to_cycle", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CYCLE_TO_FIELD, allocate("m_dot_cycle_to_field", n_steps_fixed), n_steps_fixed); + // System + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_FIXED, allocate("P_fixed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_BOP, allocate("P_plant_balance_tot", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::W_DOT_NET, allocate("P_out_net", n_steps_fixed), n_steps_fixed); + // Controller + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_1, allocate("op_mode_1", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_2, allocate("op_mode_2", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_3, allocate("op_mode_3", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::ERR_M_DOT, allocate("m_dot_balance", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::ERR_Q_DOT, allocate("q_balance", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::N_OP_MODES, allocate("n_op_modes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TOU_PERIOD, allocate("tou_value", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PRICING_MULT, allocate("pricing_mult", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_SB, allocate("q_dot_pc_sb", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MIN, allocate("q_dot_pc_min", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_TARGET, allocate("q_dot_pc_target", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MAX, allocate("q_dot_pc_max", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_REC_SU, allocate("is_rec_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SU, allocate("is_pc_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SB, allocate("is_pc_sb_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_SU, allocate("q_dot_est_cr_su", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_ON, allocate("q_dot_est_cr_on", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_DC, allocate("q_dot_est_tes_dc", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CH, allocate("q_dot_est_tes_ch", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_A, allocate("operating_modes_a", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_B, allocate("operating_modes_b", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_C, allocate("operating_modes_c", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REL_MIP_GAP, allocate("disp_rel_mip_gap", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_STATE, allocate("disp_solve_state", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SUBOPT_FLAG, allocate("disp_subopt_flag", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_ITER, allocate("disp_solve_iter", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ, allocate("disp_objective", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ_RELAX, allocate("disp_obj_relax", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSF_EXPECT, allocate("disp_qsf_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFPROD_EXPECT, allocate("disp_qsfprod_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFSU_EXPECT, allocate("disp_qsfsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_TES_EXPECT, allocate("disp_tes_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PCEFF_EXPECT, allocate("disp_pceff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SFEFF_EXPECT, allocate("disp_thermeff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QPBSU_EXPECT, allocate("disp_qpbsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_WPB_EXPECT, allocate("disp_wpb_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REV_EXPECT, allocate("disp_rev_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NCONSTR, allocate("disp_presolve_nconstr", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NVAR, allocate("disp_presolve_nvar", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_TIME, allocate("disp_solve_time", n_steps_fixed), n_steps_fixed); + } + + update("Initialize physical fresnel model...", 0.0); + + // Initialize Solver + int out_type = -1; + std::string out_msg = ""; + try + { + // Initialize Solver + csp_solver.init(); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + throw exec_error("fresnel_physical", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + //if the pricing schedule is provided as hourly, overwrite the tou schedule + if(as_boolean("is_dispatch_series")) + { + size_t n_dispatch_series; + ssc_number_t* dispatch_series = as_array("dispatch_series", &n_dispatch_series); + + //if( n_dispatch_series != n_steps_fixed) + // throw exec_error("trough_physical", "Invalid dispatch pricing series dimension. Array length must match number of simulation time steps ("+my_to_string(n_steps_fixed)+")."); + + //resize the m_hr_tou array + if (tou_params->mc_pricing.m_hr_tou != 0) + delete[] tou_params->mc_pricing.m_hr_tou; + tou_params->mc_pricing.m_hr_tou = new double[n_steps_fixed]; + //set the tou period as unique for each time step + for (int i = 0; i < n_steps_fixed; i++) + tou_params->mc_pricing.m_hr_tou[i] = i + 1; + //allocate reported arrays + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed); + for (int i = 0; i < n_steps_fixed; i++) + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_series[i]; + } + + // Design point is complete, assign technology design outputs + double Q_tes; + double total_land_area; + double nameplate_des; + { + // System Design Calcs + double eta_ref = as_double("eta_ref"); //[-] + double W_dot_cycle_des = as_double("P_ref"); //[MWe] + double tshours = as_double("tshours"); //[-] + double solar_mult_des = c_fresnel.m_solar_mult; + + double q_dot_pc_des = W_dot_cycle_des / eta_ref; //[MWt] + Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + + double mdot_field_des = c_fresnel.m_m_dot_design; // [kg/s] + + double avg_T_des = (c_fresnel.m_T_loop_in_des + c_fresnel.m_T_loop_out_des) / 2.0; + + // Solar Field + + double W_dot_col_tracking_des = c_fresnel.get_tracking_power(); // [MWe] + double q_dot_rec_des = c_fresnel.m_q_design / 1e6; // [MWt] + double A_loop = c_fresnel.m_A_loop; // [m2] + double loop_opt_eff = c_fresnel.m_loop_opt_eff; + double loop_therm_eff = c_fresnel.m_loop_therm_eff; + double loop_eff = c_fresnel.m_loop_eff; + double sm1_aperture = c_fresnel.m_Ap_sm1; // [m2] + double sm1_nLoops = c_fresnel.m_nLoops_sm1; + double total_tracking_power = c_fresnel.m_W_dot_sca_tracking_nom; // [MW] + double A_field = c_fresnel.m_Ap_tot; // [m2] + double q_field_des = c_fresnel.m_q_design / 1e6; // [MW] + + double field_area = A_field / 4046.85642; // [acres] (convert m2 to acre) + double land_mult = as_double("land_mult"); + total_land_area = field_area * land_mult; // [acres] + + double field_htf_min_temp = c_fresnel.m_htfProps.min_temp() - 273.15; // [C] + double field_htf_max_temp = c_fresnel.m_htfProps.max_temp() - 273.15; // [C] + + // steady state results + double dP_field_des_SS = c_fresnel.m_dP_des_SS; // [bar] + double Q_field_des_SS = c_fresnel.m_Q_field_des_SS / 1e6; // [MW] + double T_field_out_des_SS = c_fresnel.m_T_field_out_des_SS; // [C] + double m_dot_des_SS = c_fresnel.m_m_dot_des_SS; // [kg/s] + double m_dot_loop_des_SS = c_fresnel.m_m_dot_loop_des_SS; // [kg/s] + double V_hdr_min_des_SS = c_fresnel.m_V_hdr_min_des_SS; // [m/s] + double V_hdr_max_des_SS = c_fresnel.m_V_hdr_max_des_SS; // [m/s] + double eta_optical_des_SS = c_fresnel.m_eta_optical_des_SS; + double therm_eff_des_SS = c_fresnel.m_therm_eff_des_SS; + double eff_des_SS = c_fresnel.m_eff_des_SS; + double W_dot_pump_des_SS = c_fresnel.m_W_dot_pump_des_SS; // [MWe] + + double T_loop_out_des_SS = c_fresnel.m_T_loop_out_des_SS; // [C] + double Q_loop_des_SS = c_fresnel.m_Q_loop_des_SS / 1e6; // [MW] + double therm_eff_loop_des_SS = c_fresnel.m_therm_eff_loop_des_SS; + double eff_loop_des_SS = c_fresnel.m_eff_loop_des_SS; + double Q_loss_receiver_des_SS = c_fresnel.m_Q_loss_receiver_des_SS; // [MWt] + double Q_loss_hdr_rnr_des_SS = c_fresnel.m_Q_loss_hdr_rnr_des_SS; // [MWt] + + // Assign + { + assign("q_dot_rec_des", q_dot_rec_des); + assign("A_loop", A_loop); + assign("loop_opt_eff", loop_opt_eff); + assign("loop_therm_eff", loop_therm_eff); + assign("loop_eff", loop_eff); + assign("sm1_aperture", sm1_aperture); + assign("sm1_nLoops", sm1_nLoops); + assign("total_tracking_power", total_tracking_power); + assign("A_field", A_field); + assign("q_field_des", q_field_des); + assign("field_area", field_area); + assign("total_land_area", total_land_area); + assign("field_htf_min_temp", field_htf_min_temp); + assign("field_htf_max_temp", field_htf_max_temp); + assign("mdot_field_des", mdot_field_des); + + assign("dP_field_des_SS", dP_field_des_SS); + assign("Q_field_des_SS", Q_field_des_SS); + assign("T_field_out_des_SS", T_field_out_des_SS); + assign("m_dot_des_SS", m_dot_des_SS); + assign("m_dot_loop_des_SS", m_dot_loop_des_SS); + assign("V_hdr_min_des_SS", V_hdr_min_des_SS); + assign("V_hdr_max_des_SS", V_hdr_max_des_SS); + assign("eta_optical_des_SS", eta_optical_des_SS); + assign("therm_eff_des_SS", therm_eff_des_SS); + assign("eff_des_SS", eff_des_SS); + assign("W_dot_pump_des_SS", W_dot_pump_des_SS); + + assign("T_loop_out_des_SS", T_loop_out_des_SS); + assign("Q_loop_des_SS", Q_loop_des_SS); + assign("therm_eff_loop_des_SS", therm_eff_loop_des_SS); + assign("eff_loop_des_SS", eff_loop_des_SS); + + assign("Q_loss_receiver_des_SS", Q_loss_receiver_des_SS); + assign("Q_loss_hdr_rnr_des_SS", Q_loss_hdr_rnr_des_SS); + } + + // Collector and Receiver + double DP_pressure_loss = c_fresnel.m_nMod * c_fresnel.m_DP_nominal; // [bar] + double avg_dt_des = c_fresnel.m_dT_des; // [C] + double hl_des = c_fresnel.m_hl_des; // [W/m] + double opt_derate = c_fresnel.m_opt_derate; + double opt_normal = c_fresnel.m_opt_normal; + + // Assign + { + assign("DP_pressure_loss", DP_pressure_loss); + assign("avg_dt_des", avg_dt_des); + assign("hl_des", hl_des); + assign("opt_derate", opt_derate); + assign("opt_normal", opt_normal); + } + + // Storage + double V_tes_htf_avail_calc /*m3*/, V_tes_htf_total_calc /*m3*/, + d_tank_calc /*m*/, q_dot_loss_tes_des_calc /*MWt*/, dens_store_htf_at_T_ave_calc /*kg/m3*/, + Q_tes_des_calc /*MWt-hr*/; + + storage.get_design_parameters(V_tes_htf_avail_calc, V_tes_htf_total_calc, + d_tank_calc, q_dot_loss_tes_des_calc, dens_store_htf_at_T_ave_calc, Q_tes_des_calc); + + double vol_min = V_tes_htf_total_calc * (storage.m_h_tank_min / storage.m_h_tank); + double tes_htf_min_temp = storage.get_min_storage_htf_temp() - 273.15; + double tes_htf_max_temp = storage.get_max_storage_htf_temp() - 273.15; + double tes_htf_dens = storage.get_storage_htf_density(); + double tes_htf_cp = storage.get_storage_htf_cp(); + + // Assign + { + assign("vol_tank", V_tes_htf_total_calc); + assign("Q_tes_des", Q_tes_des_calc); + assign("d_tank", d_tank_calc); + assign("vol_min", vol_min); + assign("q_dot_loss_tes_des", q_dot_loss_tes_des_calc); + assign("tes_htf_min_temp", tes_htf_min_temp); + assign("tes_htf_max_temp", tes_htf_max_temp); + assign("tes_htf_dens", tes_htf_dens); + 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] + } + + // System Design + double W_dot_bop_design, W_dot_fixed_parasitic_design; //[MWe] + csp_solver.get_design_parameters(W_dot_bop_design, W_dot_fixed_parasitic_design); + + double gross_net_conversion_des = as_number("gross_net_conversion_factor"); + nameplate_des = W_dot_cycle_des * gross_net_conversion_des; + + // Assign + { + assign("nameplate", nameplate_des); + assign("W_dot_bop_design", W_dot_bop_design); + assign("W_dot_fixed", W_dot_fixed_parasitic_design); + + assign("solar_mult", c_fresnel.m_solar_mult); + assign("nLoops", c_fresnel.m_nLoops); + assign("total_Ap", c_fresnel.m_Ap_tot); + } + + // System Control + // temporary fix + vector aux_vec = as_vector_double("aux_array"); + double aux_design = aux_vec[0] * aux_vec[1] * (aux_vec[2] + aux_vec[3] + aux_vec[4]) * W_dot_cycle_des; + + // Assign + { + assign("aux_design", aux_design); + } + } + + // Calculate Costs and assign outputs + if(csp_financial_model != 8) + { + + // Collect dedicated cost inputs + double site_improvements_spec_cost = as_double("site_improvements_spec_cost"); + double solar_field_spec_cost = as_double("solar_field_spec_cost"); + double htf_system_spec_cost = as_double("htf_system_spec_cost"); + double storage_spec_cost = as_double("storage_spec_cost"); + double fossil_spec_cost = as_double("fossil_spec_cost"); + double power_plant_spec_cost = as_double("power_plant_spec_cost"); + double bop_spec_cost = as_double("bop_spec_cost"); + double contingency_percent = as_double("contingency_percent"); + + double epc_cost_per_acre = as_double("epc_cost_per_acre"); + double epc_cost_percent_direct = as_double("epc_cost_percent_direct"); + double epc_cost_per_watt = as_double("epc_cost_per_watt"); + double epc_cost_fixed = as_double("epc_cost_fixed"); + double plm_cost_per_acre = as_double("plm_cost_per_acre"); + double plm_cost_percent_direct = as_double("plm_cost_percent_direct"); + double plm_cost_per_watt = as_double("plm_cost_per_watt"); + double plm_cost_fixed = as_double("plm_cost_fixed"); + + double sales_tax_percent = as_double("sales_tax_percent"); + + // Collect necessary variables defined in other tabs + double site_improvements_area = c_fresnel.m_Ap_tot; + double solar_field_area = c_fresnel.m_Ap_tot; + double htf_system_area = c_fresnel.m_Ap_tot; + // Q_tes + double P_ref = as_double("P_ref"); // MWe + double fossil_backup_mwe = P_ref; // MWe + double power_plant_mwe = P_ref; // MWe + double bop_mwe = P_ref; // MWe + // total_land_area // m2 + // nameplate_des // MWe + double sales_tax_rate = as_double("sales_tax_rate"); + + // Define outputs + double power_plant_cost_out, ts_cost_out, site_improvements_cost_out, bop_cost_out, solar_field_cost_out, htf_system_cost_out, fossil_backup_cost_out, contingency_cost_out, + total_direct_cost_out, epc_total_cost_out, plm_total_cost_out, total_indirect_cost_out, sales_tax_total_out, total_installed_cost_out, installed_per_capacity_out; + + // Calculate Costs + N_mspt::calculate_mslf_costs(site_improvements_area, site_improvements_spec_cost, solar_field_area, solar_field_spec_cost, htf_system_area, htf_system_spec_cost, Q_tes, storage_spec_cost, fossil_backup_mwe, + fossil_spec_cost, power_plant_mwe, power_plant_spec_cost, bop_mwe, bop_spec_cost, contingency_percent, total_land_area, nameplate_des, epc_cost_per_acre, epc_cost_percent_direct, epc_cost_per_watt, + epc_cost_fixed, plm_cost_per_acre, plm_cost_percent_direct, plm_cost_per_watt, plm_cost_fixed, sales_tax_rate, sales_tax_percent, + + power_plant_cost_out, ts_cost_out, site_improvements_cost_out, bop_cost_out, solar_field_cost_out, htf_system_cost_out, fossil_backup_cost_out, contingency_cost_out, + total_direct_cost_out, epc_total_cost_out, plm_total_cost_out, total_indirect_cost_out, sales_tax_total_out, total_installed_cost_out, installed_per_capacity_out); + + // Assign Outputs + { + assign("site_improvements_cost", site_improvements_cost_out); + assign("solar_field_cost", solar_field_cost_out); + assign("htf_system_cost", htf_system_cost_out); + assign("ts_cost", ts_cost_out); + assign("fossil_backup_cost", fossil_backup_cost_out); + assign("power_plant_cost", power_plant_cost_out); + assign("bop_cost", bop_cost_out); + assign("contingency_cost", contingency_cost_out); + assign("total_direct_cost", total_direct_cost_out); + + assign("epc_total_cost", epc_total_cost_out); + assign("plm_total_cost", plm_total_cost_out); + assign("total_indirect_cost", total_indirect_cost_out); + + assign("sales_tax_total", sales_tax_total_out); + assign("total_installed_cost", total_installed_cost_out); + assign("installed_per_capacity", installed_per_capacity_out); + } + } + + + // Return if only called for design point + if (sim_type != 1) + return; + + update("Begin timeseries simulation...", 0.0); + std::clock_t clock_start = std::clock(); + + // Run Simulation + try + { + // Simulate + csp_solver.Ssimulate(sim_setup); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg); + } + + throw exec_error("fresnel_physical", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + + std::clock_t clock_end = std::clock(); + double sim_duration = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + assign("sim_duration", (ssc_number_t)sim_duration); + + // Do unit post-processing here + double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); + size_t count_pc_su = 0; + ssc_number_t* p_q_dot_pc_startup = as_array("q_dot_pc_startup", &count_pc_su); + if ((int)count_pc_su != n_steps_fixed) + { + log("q_dot_pc_startup array is a different length than 'n_steps_fixed'.", SSC_WARNING); + return; + } + for (int i = 0; i < n_steps_fixed; i++) + { + p_q_pc_startup[i] = (float)(p_q_dot_pc_startup[i] * (sim_setup.m_report_step / 3600.0)); //[MWh] + } + + // Convert mass flow rates from [kg/hr] to [kg/s] + size_t count_m_dot_pc, count_m_dot_water_pc; //count_m_dot_rec, count_m_dot_tes_dc, count_m_dot_tes_ch; + count_m_dot_pc = count_m_dot_water_pc = 0; //count_m_dot_rec = count_m_dot_tes_dc = count_m_dot_tes_ch = 0; + ssc_number_t* p_m_dot_pc = as_array("m_dot_pc", &count_m_dot_pc); + ssc_number_t* p_m_dot_water_pc = as_array("m_dot_water_pc", &count_m_dot_water_pc); + //ssc_number_t *p_m_dot_tes_dc = as_array("m_dot_tes_dc", &count_m_dot_tes_dc); + //ssc_number_t *p_m_dot_tes_ch = as_array("m_dot_tes_ch", &count_m_dot_tes_ch); + if ((int)count_m_dot_pc != n_steps_fixed || (int)count_m_dot_water_pc != n_steps_fixed) + //|| count_m_dot_rec != n_steps_fixed || count_m_dot_tes_dc != n_steps_fixed || count_m_dot_tes_ch != n_steps_fixed) + { + log("At least one m_dot array is a different length than 'n_steps_fixed'.", SSC_WARNING); + return; + } + for (int i = 0; i < n_steps_fixed; i++) + { + //p_m_dot_rec[i] = (ssc_number_t)(p_m_dot_rec[i] / 3600.0); //[kg/s] convert from kg/hr + p_m_dot_pc[i] = (ssc_number_t)(p_m_dot_pc[i] / 3600.0); //[kg/s] convert from kg/hr + p_m_dot_water_pc[i] = (ssc_number_t)(p_m_dot_water_pc[i] / 3600.0); //[kg/s] convert from kg/hr + //p_m_dot_tes_dc[i] = (ssc_number_t)(p_m_dot_tes_dc[i] / 3600.0); //[kg/s] convert from kg/hr + //p_m_dot_tes_ch[i] = (ssc_number_t)(p_m_dot_tes_ch[i] / 3600.0); //[kg/s] convert from kg/hr + } + + + size_t count; + ssc_number_t* p_W_dot_net = as_array("P_out_net", &count); + ssc_number_t* p_time_final_hr = as_array("time_hr", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays"); + + // 'adjustment_factors' class stores factors in hourly array, so need to index as such + adjustment_factors haf(this, "adjust"); + if (!haf.setup(n_steps_fixed)) + throw exec_error("fresnel_physical", "failed to setup adjustment factors: " + haf.error()); + + ssc_number_t* p_gen = allocate("gen", n_steps_fixed); + //ssc_number_t *p_W_dot_par_tot_haf = allocate("W_dot_par_tot_haf", n_steps_fixed); + ssc_number_t* p_q_dot_defocus_est = allocate("q_dot_defocus_est", n_steps_fixed); + + //ssc_number_t *p_W_dot_parasitic_tot = as_array("W_dot_parasitic_tot", &count); + //if (count != n_steps_fixed) + // throw exec_error("trough_physical", "The number of fixed steps does not match the length of output data arrays1"); + + ssc_number_t* p_SCAs_def = as_array("SCAs_def", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays2"); + + ssc_number_t* p_q_dot_htf_sf_out = as_array("q_dot_htf_sf_out", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays3"); + + //ssc_number_t *p_m_dot_tes_dc = as_array("m_dot_tes_dc", &count); + //if ((int)count != n_steps_fixed) + // throw exec_error("trough_physical", "The number of fixed steps for 'm_dot_tes_dc' does not match the length of output data arrays"); + // + //ssc_number_t *p_m_dot_tes_ch = as_array("m_dot_tes_ch", &count); + //if ((int)count != n_steps_fixed) + // throw exec_error("trough_physical", "The number of fixed steps for 'm_dot_tes_ch' does not match the length of output data arrays"); + for (int i = 0; i < n_steps_fixed; i++) + { + size_t hour = (size_t)ceil(p_time_final_hr[i]); + p_gen[i] = (ssc_number_t)(p_W_dot_net[i] * haf(hour) * 1.E3); //[kWe] + //p_W_dot_parasitic_tot[i] *= -1.0; //[kWe] Label is total parasitics, so change to a positive value + //p_W_dot_par_tot_haf[i] = (ssc_number_t)(p_W_dot_parasitic_tot[i] * haf(hour) * 1.E3); //[kWe] + p_q_dot_defocus_est[i] = (ssc_number_t)(1.0 - p_SCAs_def[i]) * p_q_dot_htf_sf_out[i]; //[MWt] + //p_m_dot_tes_dc[i] = (ssc_number_t)(p_m_dot_tes_dc[i] / 3600.0); //[kg/s] convert from kg/hr + //p_m_dot_tes_ch[i] = (ssc_number_t)(p_m_dot_tes_ch[i] / 3600.0); //[kg/s] convert from kg/hr + + } + ssc_number_t* p_annual_energy_dist_time = gen_heatmap(this, steps_per_hour); + // Non-timeseries array outputs + double P_adj = storage.P_in_des; // slightly adjust all field design pressures to account for pressure drop in TES before hot tank + transform(c_fresnel.m_P_rnr_dsn.begin(), c_fresnel.m_P_rnr_dsn.end(), c_fresnel.m_P_rnr_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + transform(c_fresnel.m_P_hdr_dsn.begin(), c_fresnel.m_P_hdr_dsn.end(), c_fresnel.m_P_hdr_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + transform(c_fresnel.m_P_loop_dsn.begin(), c_fresnel.m_P_loop_dsn.end(), c_fresnel.m_P_loop_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + + ssc_number_t* p_pipe_runner_diams = allocate("pipe_runner_diams", c_fresnel.m_D_runner.size()); + std::copy(c_fresnel.m_D_runner.begin(), c_fresnel.m_D_runner.end(), p_pipe_runner_diams); + ssc_number_t* p_pipe_runner_wallthk = allocate("pipe_runner_wallthk", c_fresnel.m_WallThk_runner.size()); + std::copy(c_fresnel.m_WallThk_runner.begin(), c_fresnel.m_WallThk_runner.end(), p_pipe_runner_wallthk); + ssc_number_t* p_pipe_runner_lengths = allocate("pipe_runner_lengths", c_fresnel.m_L_runner.size()); + std::copy(c_fresnel.m_L_runner.begin(), c_fresnel.m_L_runner.end(), p_pipe_runner_lengths); + ssc_number_t* p_pipe_runner_expansions = allocate("pipe_runner_expansions", c_fresnel.m_N_rnr_xpans.size()); + std::copy(c_fresnel.m_N_rnr_xpans.begin(), c_fresnel.m_N_rnr_xpans.end(), p_pipe_runner_expansions); + ssc_number_t* p_pipe_runner_mdot_dsn = allocate("pipe_runner_mdot_dsn", c_fresnel.m_m_dot_rnr_dsn.size()); + std::copy(c_fresnel.m_m_dot_rnr_dsn.begin(), c_fresnel.m_m_dot_rnr_dsn.end(), p_pipe_runner_mdot_dsn); + ssc_number_t* p_pipe_runner_vel_dsn = allocate("pipe_runner_vel_dsn", c_fresnel.m_V_rnr_dsn.size()); + std::copy(c_fresnel.m_V_rnr_dsn.begin(), c_fresnel.m_V_rnr_dsn.end(), p_pipe_runner_vel_dsn); + ssc_number_t* p_pipe_runner_T_dsn = allocate("pipe_runner_T_dsn", c_fresnel.m_T_rnr_dsn.size()); + std::copy(c_fresnel.m_T_rnr_dsn.begin(), c_fresnel.m_T_rnr_dsn.end(), p_pipe_runner_T_dsn); + ssc_number_t* p_pipe_runner_P_dsn = allocate("pipe_runner_P_dsn", c_fresnel.m_P_rnr_dsn.size()); + std::copy(c_fresnel.m_P_rnr_dsn.begin(), c_fresnel.m_P_rnr_dsn.end(), p_pipe_runner_P_dsn); + + ssc_number_t* p_pipe_header_diams = allocate("pipe_header_diams", c_fresnel.m_D_hdr.size()); + std::copy(c_fresnel.m_D_hdr.begin(), c_fresnel.m_D_hdr.end(), p_pipe_header_diams); + ssc_number_t* p_pipe_header_wallthk = allocate("pipe_header_wallthk", c_fresnel.m_WallThk_hdr.size()); + std::copy(c_fresnel.m_WallThk_hdr.begin(), c_fresnel.m_WallThk_hdr.end(), p_pipe_header_wallthk); + //ssc_number_t* p_pipe_header_lengths = allocate("pipe_header_lengths", c_fresnel.m_L_hdr.size()); + //std::copy(c_fresnel.m_L_hdr.begin(), c_fresnel.m_L_hdr.end(), p_pipe_header_lengths); + ssc_number_t* p_pipe_header_expansions = allocate("pipe_header_expansions", c_fresnel.m_N_hdr_xpans.size()); + std::copy(c_fresnel.m_N_hdr_xpans.begin(), c_fresnel.m_N_hdr_xpans.end(), p_pipe_header_expansions); + ssc_number_t* p_pipe_header_mdot_dsn = allocate("pipe_header_mdot_dsn", c_fresnel.m_m_dot_hdr_dsn.size()); + std::copy(c_fresnel.m_m_dot_hdr_dsn.begin(), c_fresnel.m_m_dot_hdr_dsn.end(), p_pipe_header_mdot_dsn); + ssc_number_t* p_pipe_header_vel_dsn = allocate("pipe_header_vel_dsn", c_fresnel.m_V_hdr_dsn.size()); + std::copy(c_fresnel.m_V_hdr_dsn.begin(), c_fresnel.m_V_hdr_dsn.end(), p_pipe_header_vel_dsn); + ssc_number_t* p_pipe_header_T_dsn = allocate("pipe_header_T_dsn", c_fresnel.m_T_hdr_dsn.size()); + std::copy(c_fresnel.m_T_hdr_dsn.begin(), c_fresnel.m_T_hdr_dsn.end(), p_pipe_header_T_dsn); + ssc_number_t* p_pipe_header_P_dsn = allocate("pipe_header_P_dsn", c_fresnel.m_P_hdr_dsn.size()); + std::copy(c_fresnel.m_P_hdr_dsn.begin(), c_fresnel.m_P_hdr_dsn.end(), p_pipe_header_P_dsn); + + ssc_number_t* p_pipe_loop_T_dsn = allocate("pipe_loop_T_dsn", c_fresnel.m_T_loop_dsn.size()); + std::copy(c_fresnel.m_T_loop_dsn.begin(), c_fresnel.m_T_loop_dsn.end(), p_pipe_loop_T_dsn); + ssc_number_t* p_pipe_loop_P_dsn = allocate("pipe_loop_P_dsn", c_fresnel.m_P_loop_dsn.size()); + std::copy(c_fresnel.m_P_loop_dsn.begin(), c_fresnel.m_P_loop_dsn.end(), p_pipe_loop_P_dsn); + + ssc_number_t* p_pipe_tes_diams = allocate("pipe_tes_diams", storage.pipe_diams.ncells()); + std::copy(storage.pipe_diams.data(), storage.pipe_diams.data() + storage.pipe_diams.ncells(), p_pipe_tes_diams); + ssc_number_t* p_pipe_tes_wallthk = allocate("pipe_tes_wallthk", storage.pipe_wall_thk.ncells()); + std::copy(storage.pipe_wall_thk.data(), storage.pipe_wall_thk.data() + storage.pipe_wall_thk.ncells(), p_pipe_tes_wallthk); + ssc_number_t* p_pipe_tes_lengths = allocate("pipe_tes_lengths", storage.pipe_lengths.ncells()); + std::copy(storage.pipe_lengths.data(), storage.pipe_lengths.data() + storage.pipe_lengths.ncells(), p_pipe_tes_lengths); + ssc_number_t* p_pipe_tes_mdot_dsn = allocate("pipe_tes_mdot_dsn", storage.pipe_m_dot_des.ncells()); + std::copy(storage.pipe_m_dot_des.data(), storage.pipe_m_dot_des.data() + storage.pipe_m_dot_des.ncells(), p_pipe_tes_mdot_dsn); + ssc_number_t* p_pipe_tes_vel_dsn = allocate("pipe_tes_vel_dsn", storage.pipe_vel_des.ncells()); + std::copy(storage.pipe_vel_des.data(), storage.pipe_vel_des.data() + storage.pipe_vel_des.ncells(), p_pipe_tes_vel_dsn); + ssc_number_t* p_pipe_tes_T_dsn = allocate("pipe_tes_T_dsn", storage.pipe_T_des.ncells()); + std::copy(storage.pipe_T_des.data(), storage.pipe_T_des.data() + storage.pipe_T_des.ncells(), p_pipe_tes_T_dsn); + ssc_number_t* p_pipe_tes_P_dsn = allocate("pipe_tes_P_dsn", storage.pipe_P_des.ncells()); + std::copy(storage.pipe_P_des.data(), storage.pipe_P_des.data() + storage.pipe_P_des.ncells(), p_pipe_tes_P_dsn); + + + // Monthly outputs + accumulate_monthly_for_year("gen", "monthly_energy", sim_setup.m_report_step / 3600.0, steps_per_hour, 1); + + + // Annual outputs + accumulate_annual_for_year("gen", "annual_energy", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("P_cycle", "annual_W_cycle_gross", 1000.0 * sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); //[kWe-hr] + //accumulate_annual_for_year("W_dot_par_tot_haf", "annual_electricity_consumption", sim_setup.m_report_step/3600.0, steps_per_hour); //[kWe-hr] + accumulate_annual_for_year("disp_objective", "disp_objective_ann", 1000.0 * sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("disp_solve_iter", "disp_iter_ann", 1000.0 * sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("disp_presolve_nconstr", "disp_presolve_nconstr_ann", sim_setup.m_report_step / 3600.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("disp_presolve_nvar", "disp_presolve_nvar_ann", sim_setup.m_report_step / 3600.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("disp_solve_time", "disp_solve_time_ann", sim_setup.m_report_step / 3600. / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("q_dc_tes", "annual_q_dc_tes", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("q_ch_tes", "annual_q_ch_tes", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + + + ssc_number_t annual_field_fp = accumulate_annual_for_year("q_dot_freeze_prot", "annual_field_freeze_protection", sim_setup.m_report_step / 3600.0 * 1.E3, steps_per_hour); //[kWt-hr] + ssc_number_t annual_tes_fp = accumulate_annual_for_year("q_tes_heater", "annual_tes_freeze_protection", sim_setup.m_report_step / 3600.0 * 1.E3, steps_per_hour); //[kWt-hr] + + ssc_number_t annual_thermal_consumption = annual_field_fp + annual_tes_fp; //[kWt-hr] + assign("annual_thermal_consumption", annual_thermal_consumption); + + // Reporting dispatch solution counts + size_t n_flag, n_gap = 0; + ssc_number_t* subopt_flag = as_array("disp_subopt_flag", &n_flag); + ssc_number_t* rel_mip_gap = as_array("disp_rel_mip_gap", &n_gap); + + std::vector flag; + std::vector gap; + flag.resize(n_flag); + gap.resize(n_flag); + for (size_t i = 0; i < n_flag; i++) { + flag[i] = (int)subopt_flag[i]; + gap[i] = (double)rel_mip_gap[i]; + } + + double avg_gap = 0; + if (is_dispatch) { + std::string disp_sum_msg; + dispatch.count_solutions_by_type(flag, (int)as_double("disp_frequency"), disp_sum_msg); + log(disp_sum_msg, SSC_NOTICE); + avg_gap = dispatch.calc_avg_subopt_gap(gap, flag, (int)as_double("disp_frequency")); + } + assign("avg_suboptimal_rel_mip_gap", (ssc_number_t)avg_gap); + + // Calculate water use + // First, sum power cycle water consumption timeseries outputs + accumulate_annual_for_year("m_dot_water_pc", "annual_total_water_use", sim_setup.m_report_step / 1000.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); //[m^3], convert from kg + ssc_number_t V_water_cycle = as_number("annual_total_water_use"); + // Then, add water usage from mirror cleaning + double A_aper_tot = csp_solver.get_cr_aperture_area(); //[m2] + double V_water_mirrors = as_double("water_per_wash") / 1000.0 * A_aper_tot * as_double("washes_per_year"); + assign("annual_total_water_use", (ssc_number_t)(V_water_mirrors + V_water_cycle)); //[m3] + + ssc_number_t ae = as_number("annual_energy"); + ssc_number_t pg = as_number("annual_W_cycle_gross"); + ssc_number_t convfactor = (pg != 0) ? 100 * ae / pg : (ssc_number_t)0.0; + assign("conversion_factor", convfactor); + + double kWh_per_kW = 0.0; + double system_capacity = as_double("P_ref") * as_double("gross_net_conversion_factor") * 1.E3; //[kWe] + double nameplate = system_capacity; //[kWe] + if (nameplate > 0.0) + kWh_per_kW = ae / nameplate; + + assign("capacity_factor", (ssc_number_t)(kWh_per_kW / ((double)n_steps_fixed / (double)steps_per_hour) * 100.)); + assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); + + } +}; + +DEFINE_MODULE_ENTRY(fresnel_physical, "Physical Fresnel applications", 1) diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp new file mode 100644 index 000000000..adffc0d39 --- /dev/null +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -0,0 +1,1480 @@ +/* +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 "common.h" + +#include "csp_solver_fresnel_collector_receiver.h" +#include "csp_solver_pc_heat_sink.h" +#include "csp_solver_tou_block_schedules.h" +#include "csp_dispatch.h" +#include "csp_system_costs.h" +#include "csp_solver_two_tank_tes.h" + +#include +#include +#include + +static var_info _cm_vtab_fresnel_physical_iph[] = { + + { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, + + + // Weather Reader + + { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, + + + // System Design + + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System_Design", "", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System_Design", "", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System_Design", "", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "System_Design", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "System_Design", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "System_Design", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Equivalent full-load thermal storage hours", "hr", "", "System_Design", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Design heat input to power block", "MWt", "", "System_Design", "*", "", "" }, + + + // Solar Field + + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "Solar_Field", "*", "INTEGER", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "Solar_Field", "*", "INTEGER", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "water_per_wash", "Water usage per wash", "L/m2_aper", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "washes_per_year", "Mirror washing frequency", "none", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_htf_vol", "Volume of HTF in a single collector unit per unit aperture area", "L/m2-ap", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_su_delay", "Fixed startup delay time for the receiver", "hr", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "rec_qf_delay", "Energy-based receiver startup delay (fraction of rated thermal power)", "-", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "p_start", "Collector startup energy, per SCA", "kWe-hr", "", "Solar_Field", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "Solar_Field", "*", "", "" }, + + // Collector and Receiver + + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "Col_Rec", "*", "INTEGER", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "Col_Rec", "*", "", "" }, + + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "Col_Rec", "*", "INTEGER", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "Col_Rec", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "Col_Rec", "?=4", "INTEGER", "" }, + + + // Heat Sink + + /*Heat Sink*/{SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Heat Sink", "*", "", ""}, + + // TES + + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "store_fluid", "Storage HTF ID", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Storage user-defined HTF Properties", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Height of HTF when tank is full", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Loss coefficient from tank", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Number of equivalent tank pairs", "", "", "Storage", "*", "INTEGER", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Hot tank heater set point", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Cold tank heater set point", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Hot side HX approach temp", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Minimum tank fluid height", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Cold side HX approach temp", "", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Pumping power to move 1kg of HTF through tes loop", "kW/(kg/s)", "", "Storage", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "Storage", "*", "", "" }, + + /*Storage NOT in UI*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "Storage", "?=1.85", "", "SIMULATION_PARAMETER" }, + + + // System Control + + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Fixed parasitic load - runs at all times", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Balance of plant parasitic power fraction", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Aux heater, boiler parasitic", "", "", "Sys_Control", "*", "", "" }, + + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "Sys_Control", "?=0", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "Sys_Control", "?=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "Sys_Control", "", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "Sys_Control", "?=0.99", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "Sys_Control", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "Sys_Control", "is_dispatch=1", "", "" }, + + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "SIMULATION_PARAMETER" }, + /*LK Only*/{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "SIMULATION_PARAMETER" }, + + + + // Financials + /*Sys Design*/{SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", ""}, + + + /*Dipatch*/{ SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "Sys_Control", "*", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "Sys_Control", "*", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_NUMBER, "is_tod_pc_target_also_pc_max","Is the TOD target cycle heat input also the max cycle heat input?", "", "", "tou", "?=0", "", "" }, + /*Dipatch*/{ SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, + + /*Startup Script*/{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "SIMULATION_PARAMETER" }, + + + /*Financial TOD Factors*/{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "PPA pricing weekday schedule, 12x24", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + /*Financial TOD Factors*/{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "PPA pricing weekend schedule, 12x24", "", "", "Time of Delivery Factors", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + { SSC_INPUT, SSC_ARRAY, "dispatch_tod_factors", "TOD factors for periods 1 through 9", "", + "We added this array input after SAM 2022.12.21 to replace the functionality of former single value inputs dispatch_factor1 through dispatch_factor9", "Time of Delivery Factors","ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1","", "SIMULATION_PARAMETER" }, + + /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_NUMBER, "ppa_soln_mode", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + + /*Fin Merc Plant Energy*/{ SSC_INPUT, SSC_MATRIX, "mp_energy_market_revenue", "Energy market revenue input", "", "Lifetime x 2[Cleared Capacity(MW),Price($ / MWh)]", "Revenue", "csp_financial_model=6&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, + + // Capital Costs + + // Direct Capital Costs + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "site_improvements_spec_cost", "Site Improvement Cost per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "solar_field_spec_cost", "Solar Field Cost per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "htf_system_spec_cost", "HTF System Cost Per m2", "$/m2", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "storage_spec_cost", "Storage cost per kWht", "$/kWht", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "heat_sink_spec_cost", "Heat Sink Cost per kWt", "$/kWt", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "bop_spec_cost", "Balance of Plant Cost per kWe", "$/kWe", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "contingency_percent", "Contingency Percent", "%", "", "Capital_Costs", "?=0", "", "" }, + + // Indirect Capital Costs + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_per_acre", "EPC Costs per acre", "$/acre", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_percent_direct", "EPC Costs % direct", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_per_watt", "EPC Cost Wac", "$/Wac", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "epc_cost_fixed", "Fixed EPC Cost", "$", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_per_acre", "Land Cost per acre", "$/acre", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_percent_direct", "Land Cost % direct", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_per_watt", "Land Cost Wac", "$/Wac", "", "Capital_Costs", "?=0", "", "" }, + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "plm_cost_fixed", "Fixed Land Cost", "$", "", "Capital_Costs", "?=0", "", "" }, + + + // Sales Tax + /*Capital Costs*/{ SSC_INPUT, SSC_NUMBER, "sales_tax_percent", "Sales Tax Percentage of Direct Cost", "%", "", "Capital_Costs", "?=0", "", "" }, + /*Fin Tax and Insurace*/{ SSC_INPUT, SSC_NUMBER, "sales_tax_rate", "Sales Tax Rate", "%", "", "Capital_Costs", "?=0", "", "" }, + + + // OUTPUTS + // Design Point Outputs + + // System Design + { SSC_OUTPUT, SSC_NUMBER, "solar_mult", "Actual solar multiple", "", "", "System Design Calc", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_Ap", "Actual field aperture", "m2", "", "System Design Calc", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "nameplate", "Nameplate capacity", "MWe", "", "System Design Calc", "*", "", "" }, + + + // Solar Field + { SSC_OUTPUT, SSC_NUMBER, "q_dot_rec_des", "Receiver thermal output at design", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "A_loop", "Aperture of a single loop", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_opt_eff", "Loop optical efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency at design", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sm1_aperture", "Total required aperture, SM=1", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sm1_nLoops", "Required number of loops, SM=1", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_tracking_power", "Design tracking power", "MW", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "A_field", "Total field aperture", "m2", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "q_field_des", "Design field power output", "MW", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_area", "Solar field area", "acres", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_land_area", "Total land area", "acres", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_htf_min_temp", "Minimum field htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "field_htf_max_temp", "Maximum field htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "mdot_field_des", "Field design HTF mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + + { SSC_OUTPUT, SSC_NUMBER, "dP_field_des_SS", "Steady State Field design total pressure drop", "bar", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_field_des_SS", "Steady State Field design thermal power", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "T_field_out_des_SS", "Steady State Field design outlet temperature", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_des_SS", "Steady State Field mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_loop_des_SS", "Steady State Loop mass flow rate", "kg/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "V_hdr_min_des_SS", "Steady State min header velocity", "m/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "V_hdr_max_des_SS", "Steady State max header velocity", "m/s", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eta_optical_des_SS", "Steady State optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "therm_eff_des_SS", "Steady State field optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eff_des_SS", "Steady State field total efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_pump_des_SS", "Steady State field pumping power", "MWe", "", "Receiver", "*", "", "" }, + + + { SSC_OUTPUT, SSC_NUMBER, "T_loop_out_des_SS", "Steady State loop design outlet temperature", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loop_des_SS", "Steady State loop design thermal power", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "therm_eff_loop_des_SS", "Steady State loop optical efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "eff_loop_des_SS", "Steady State loop total efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_pump_des_SS", "Steady State field pumping power", "MWe", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loss_receiver_des_SS", "Steady State field heat loss from receiver", "MWt", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_loss_hdr_rnr_des_SS", "Steady State field heat loss from headers and runners", "MWt", "", "Receiver", "*", "", "" }, + + + // Collector and Receiver + { SSC_OUTPUT, SSC_NUMBER, "DP_pressure_loss", "Total loop pressure loss at design", "bar", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "avg_dt_des", "Average field temp difference at design", "C", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "hl_des", "Heat loss at design", "W/m", "", "Receiver", "*", "", "" }, + { 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", "*", "", "" }, + + // Thermal Storage + { SSC_OUTPUT, SSC_NUMBER, "vol_tank", "Total tank volume", "m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "Q_tes_des", "TES design capacity", "MWt-hr", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "d_tank", "Tank diameter", "m", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "vol_min", "Minimum Fluid Volume", "m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "q_dot_loss_tes_des", "Estimated TES Heat Loss", "MW", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_min_temp", "Minimum storage htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_max_temp", "Maximum storage htf temp", "C", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_dens", "Storage htf density", "kg/m3", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "tes_htf_cp", "Storage htf specific heat", "kJ/kg-K", "", "Power Cycle", "*", "", "" }, + + // System Control + { SSC_OUTPUT, SSC_NUMBER, "W_dot_bop_design", "BOP parasitics at design", "MWe", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_fixed", "Fixed parasitic at design", "MWe", "", "Power Cycle", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "aux_design", "Aux parasitics at design", "MWe", "", "System Control", "*", "", "" }, + + // Capital Costs + + // Direct Capital Costs + { SSC_OUTPUT, SSC_NUMBER, "site_improvements_cost", "Site improvements cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "solar_field_cost", "Solar field cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "htf_system_cost", "HTF system cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "ts_cost", "Thermal storage cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "heat_sink_cost", "Heat sink cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "bop_cost", "Balance of plant cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "contingency_cost", "Contingency cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_direct_cost", "Total direct cost", "$", "", "Capital Costs", "", "", "" }, + + // Indirect Capital Costs + { SSC_OUTPUT, SSC_NUMBER, "epc_total_cost", "EPC total cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "plm_total_cost", "Total land cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_indirect_cost", "Total direct cost", "$", "", "Capital Costs", "", "", "" }, + + // Sales Tax + { SSC_OUTPUT, SSC_NUMBER, "sales_tax_total", "Sales tax total", "$", "", "Capital Costs", "", "", "" }, + + // Total Installed Costs + { SSC_OUTPUT, SSC_NUMBER, "total_installed_cost", "Total installed cost", "$", "", "Capital Costs", "", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "installed_per_capacity", "Estimated total installed cost per net capacity ($/kW)", "$/kW", "", "Capital Costs", "", "", "" }, + + + + + + + + // Simulation outputs + + + + // Simulation Kernel + { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "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", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solazi", "Resource Solar Azimuth", "deg", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solzen", "Resource Solar Zenith", "deg", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "beam", "Resource Beam normal irradiance", "W/m2", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tdry", "Resource Dry bulb temperature", "C", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "twet", "Resource Wet bulb temperature", "C", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "rh", "Resource Relative Humidity", "%", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "wspd", "Resource Wind Speed", "m/s", "", "weather", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pres", "Resource Pressure", "mbar", "", "weather", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "weather", "sim_type=1", "", "" }, + + + + // Solar Field + { SSC_OUTPUT, SSC_ARRAY, "EqOpteff", "Field optical efficiency before defocus", "", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "SCAs_def", "Field fraction of focused SCAs", "", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_inc", "Receiver thermal power incident", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_thermal_loss", "Receiver thermal losses", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_abs", "Receiver thermal power absorbed", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "rec_thermal_eff", "Receiver thermal efficiency", "", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "q_dot_piping_loss", "Field piping thermal losses", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_dot_field_int_energy", "Field change in material/htf internal energy", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_htf_sf_out", "Field thermal power leaving in HTF", "MWt", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_freeze_prot", "Field freeze protection required", "MWt", "", "solar_field", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "m_dot_loop", "Receiver mass flow rate", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_recirc", "Field total mass flow recirculated", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_delivered", "Field total mass flow delivered", "kg/s", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_cold_in", "Field timestep-averaged inlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_cold_in", "Loop timestep-averaged inlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_hot_out", "Loop timestep-averaged outlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_hot_out", "Field timestep-averaged outlet temperature", "C", "", "solar_field", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "deltaP_field", "Field pressure drop", "bar", "", "solar_field", "sim_type=1", "", "" }, + + { 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", "", "" }, + { 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", "", "" }, + + + // 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", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_hot", "TES hot temperature", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_cold", "TES cold temperature", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_cold", "TES cold tank mass (end)", "kg", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_hot", "TES hot tank mass (end)", "kg", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dc_tes", "TES discharge thermal power", "MWt", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_ch_tes", "TES charge thermal power", "MWt", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_ch_tes", "TES charge state", "MWht", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cr_to_tes_hot", "Mass flow: field to hot TES", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_hot_out", "Mass flow: TES hot out", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc_to_tes_cold", "Mass flow: cycle to cold TES", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_cold_out", "Mass flow: TES cold out", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_to_cycle", "Mass flow: field to cycle", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cycle_to_field", "Mass flow: cycle to field", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cold_tank_to_hot_tank", "Mass flow: cold tank to hot tank", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tes_htf_pump_power", "TES HTF pump power", "MWe", "", "TES", "sim_type=1", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_diams", "Pipe diameters in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_wallthk", "Pipe wall thickness in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_lengths", "Pipe lengths in TES", "m", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_mdot_dsn", "Mass flow TES pipes at design conditions", "kg/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_vel_dsn", "Velocity in TES pipes at design conditions", "m/s", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_T_dsn", "Temperature in TES pipes at design conditions", "C", "", "TES", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_P_dsn", "Pressure in TES pipes at design conditions", "bar", "", "TES", "sim_type=1", "", "" }, + + + // Parasitics outputs + { SSC_OUTPUT, SSC_ARRAY, "P_fixed", "Parasitic power fixed load", "MWe", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "P_plant_balance_tot", "Parasitic power generation-dependent load", "MWe", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "W_dot_parasitic_tot", "System total electrical parasitic", "MWe", "", "system", "sim_type=1", "", "" }, + + + // Controller + { SSC_OUTPUT, SSC_ARRAY, "tou_value", "CSP operating Time-of-use value", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pricing_mult", "PPA price multiplier", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "n_op_modes", "Operating modes in reporting timestep", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_1", "1st operating mode", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_2", "2nd op. mode, if applicable", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_3", "3rd op. mode, if applicable", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_balance", "Relative mass flow balance error", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_balance", "Relative energy balance error", "", "", "solver", "sim_type=1", "", "" }, + + // Dispatch outputs + { SSC_OUTPUT, SSC_ARRAY, "disp_rel_mip_gap", "Dispatch relative MIP gap", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_state", "Dispatch solver state", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_subopt_flag", "Dispatch suboptimal solution flag", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_iter", "Dispatch iterations count", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_objective", "Dispatch objective function value", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_obj_relax", "Dispatch objective function - relaxed max", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsf_expected", "Dispatch expected solar field available energy", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfprod_expected", "Dispatch expected solar field generation", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfsu_expected", "Dispatch expected solar field startup enegy", "MWt", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_tes_expected", "Dispatch expected TES charge level", "MWht", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_pceff_expected", "Dispatch expected power cycle efficiency adj.", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_thermeff_expected", "Dispatch expected SF thermal efficiency adj.", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qpbsu_expected", "Dispatch expected power cycle startup energy", "MWht", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_wpb_expected", "Dispatch expected power generation", "MWe", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_rev_expected", "Dispatch expected revenue factor", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nconstr", "Dispatch number of constraints in problem", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nvar", "Dispatch number of variables in problem", "", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_time", "Dispatch solver time", "sec", "", "tou", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "tou", "sim_type=1", "", "" }, + + + // These outputs correspond to the first csp-solver timestep in the reporting timestep. + // Subsequent csp-solver timesteps within the same reporting timestep are not tracked + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_sb", "Thermal power for PC standby", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_min", "Thermal power for PC min operation", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_max", "Max thermal power to PC", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_target", "Target thermal power to PC", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_rec_su_allowed", "is receiver startup allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_su_allowed", "is power cycle startup allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_sb_allowed", "is power cycle standby allowed", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_su", "Estimate rec. startup thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_on", "Estimate rec. thermal power TO HTF", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_dc", "Estimate max TES discharge thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_ch", "Estimate max TES charge thermal power", "MWt", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_a", "First 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_b", "Next 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_c", "Final 3 operating modes tried", "", "", "solver", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "gen", "Total thermal power to heat sink with available derate", "kWe", "", "system", "sim_type=1", "", "" }, + + // Monthly Outputs + { SSC_OUTPUT, SSC_ARRAY, "monthly_energy", "Monthly Energy", "kWh", "", "Post-process", "sim_type=1", "LENGTH=12", "" }, + + // Annual Outputs + { SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual Net Electrical Energy Production w/ avail derate", "kWe-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_thermal_consumption", "Annual thermal freeze protection required", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_electricity_consumption", "Annual electricity consumption w/ avail derate", "kWe-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total Annual Water Usage", "m^3", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_field_freeze_protection", "Annual thermal power for field freeze protection", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_tes_freeze_protection", "Annual thermal power for TES freeze protection", "kWt-hr", "", "Post-process", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sim_duration", "Computational time of timeseries simulation", "s", "", "system", "sim_type=1", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_par_tot_haf", "Adjusted parasitic power", "kWe", "", "system", "", "", "" }, + + + + + + var_info_invalid }; + +class cm_fresnel_physical_iph : public compute_module +{ +public: + + cm_fresnel_physical_iph() + { + add_var_info(_cm_vtab_fresnel_physical_iph); + add_var_info(vtab_adjustment_factors); + add_var_info(vtab_technology_outputs); + } + + void exec() + { + // Common Parameters + bool is_dispatch = as_boolean("is_dispatch"); + int sim_type = as_number("sim_type"); + double T_htf_cold_des = as_double("T_loop_in_des"); //[C] + double T_htf_hot_des = as_double("T_loop_out"); //[C] + double tshours = as_double("tshours"); //[-] + double q_dot_pc_des = as_double("q_pb_design"); //[MWt] HEAT SINK design thermal power + double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + + // Weather reader + C_csp_weatherreader weather_reader; + C_csp_solver::S_sim_setup sim_setup; + int n_steps_fixed; + int steps_per_hour; + { + weather_reader.m_weather_data_provider = std::make_shared(as_string("file_name")); + weather_reader.m_filename = as_string("file_name"); + weather_reader.m_trackmode = 0; + weather_reader.m_tilt = 0.0; + weather_reader.m_azimuth = 0.0; + // Initialize to get weather file info + weather_reader.init(); + if (weather_reader.has_error()) throw exec_error("fresnel_physical", weather_reader.get_error()); + + // Set up ssc output arrays + // Set steps per hour + double nhourssim = 8760.0; //[hr] Number of hours to simulate + + sim_setup.m_sim_time_start = 0.0; //[s] starting first hour of year + sim_setup.m_sim_time_end = nhourssim * 3600.; //[s] full year simulation + + steps_per_hour = 1; //[-] + + int n_wf_records = (int)weather_reader.m_weather_data_provider->nrecords(); + steps_per_hour = n_wf_records / 8760; //[-] + + n_steps_fixed = steps_per_hour * 8760; //[-] + sim_setup.m_report_step = 3600.0 / (double)steps_per_hour; //[s] + } + + // Solar field + C_csp_fresnel_collector_receiver c_fresnel; + { + // Inputs + { + c_fresnel.m_solar_mult_or_Ap = as_integer("solar_mult_or_Ap"); + c_fresnel.m_solar_mult_in = as_double("solar_mult_in"); + c_fresnel.m_total_Ap_in = as_double("total_Ap_in"); + + c_fresnel.m_nMod = as_integer("nMod"); + c_fresnel.m_nRecVar = as_integer("nRecVar"); + + c_fresnel.m_eta_pump = as_number("eta_pump"); + c_fresnel.m_HDR_rough = as_number("HDR_rough"); + c_fresnel.m_theta_stow = as_number("theta_stow"); + c_fresnel.m_theta_dep = as_number("theta_dep"); + c_fresnel.m_FieldConfig = as_integer("FieldConfig"); + c_fresnel.m_T_startup = as_number("T_startup"); + + // Set P_ref = q_pb_design and eta_ref = 1 + c_fresnel.m_P_ref = as_double("q_pb_design") * 1e6; + c_fresnel.m_eta_ref = 1; + + c_fresnel.m_m_dot_htfmin = as_number("m_dot_htfmin"); + c_fresnel.m_m_dot_htfmax = as_number("m_dot_htfmax"); + c_fresnel.m_T_loop_in_des = as_number("T_loop_in_des"); + + c_fresnel.m_T_loop_out_des = as_number("T_loop_out"); + c_fresnel.m_Fluid = as_integer("Fluid"); + + c_fresnel.m_field_fl_props = as_matrix("field_fl_props"); + c_fresnel.m_T_fp = as_number("T_fp"); + c_fresnel.m_I_bn_des = as_number("I_bn_des"); + c_fresnel.m_V_hdr_max = as_number("V_hdr_max"); + c_fresnel.m_V_hdr_min = as_number("V_hdr_min"); + c_fresnel.m_Pipe_hl_coef = as_number("Pipe_hl_coef"); + c_fresnel.m_SCA_drives_elec = as_number("SCA_drives_elec"); + c_fresnel.m_ColAz = as_number("ColAz"); + + c_fresnel.m_mc_bal_hot = as_number("mc_bal_hot"); + c_fresnel.m_mc_bal_cold = as_number("mc_bal_cold"); + c_fresnel.m_mc_bal_sca = as_number("mc_bal_sca"); + + c_fresnel.m_opt_model = as_integer("opt_model"); + + c_fresnel.m_A_aperture = as_number("A_aperture"); + c_fresnel.m_reflectivity = as_number("reflectivity"); + c_fresnel.m_TrackingError = as_number("TrackingError"); + c_fresnel.m_GeomEffects = as_number("GeomEffects"); + c_fresnel.m_Dirt_mirror = as_number("Dirt_mirror"); + c_fresnel.m_Error = as_number("Error"); + c_fresnel.m_L_mod = as_number("L_mod"); + + c_fresnel.m_IAM_T_coefs = as_vector_double("IAM_T_coefs"); + c_fresnel.m_IAM_L_coefs = as_vector_double("IAM_L_coefs"); + c_fresnel.m_OpticalTable = as_matrix("OpticalTable"); + c_fresnel.m_rec_model = as_integer("rec_model"); + + c_fresnel.m_HCE_FieldFrac = as_vector_double("HCE_FieldFrac"); + c_fresnel.m_D_abs_in = as_vector_double("D_abs_in"); + c_fresnel.m_D_abs_out = as_vector_double("D_abs_out"); + c_fresnel.m_D_glass_in = as_vector_double("D_glass_in"); + c_fresnel.m_D_glass_out = as_vector_double("D_glass_out"); + c_fresnel.m_D_plug = as_vector_double("D_plug"); + c_fresnel.m_Flow_type = as_vector_double("Flow_type"); + c_fresnel.m_Rough = as_vector_double("Rough"); + c_fresnel.m_alpha_env = as_vector_double("alpha_env"); + + c_fresnel.m_epsilon_abs_1 = as_matrix_transpose("epsilon_abs_1"); + c_fresnel.m_epsilon_abs_2 = as_matrix_transpose("epsilon_abs_2"); + c_fresnel.m_epsilon_abs_3 = as_matrix_transpose("epsilon_abs_3"); + c_fresnel.m_epsilon_abs_4 = as_matrix_transpose("epsilon_abs_4"); + + c_fresnel.m_alpha_abs = as_vector_double("alpha_abs"); + c_fresnel.m_Tau_envelope = as_vector_double("Tau_envelope"); + c_fresnel.m_epsilon_glass = as_vector_double("epsilon_glass"); + c_fresnel.m_GlazingIntact = as_vector_bool("GlazingIntactIn"); + + c_fresnel.m_P_a = as_vector_double("P_a"); + + c_fresnel.m_AnnulusGas = as_vector_double("AnnulusGas"); + c_fresnel.m_AbsorberMaterial = as_vector_double("AbsorberMaterial"); + c_fresnel.m_Shadowing = as_vector_double("Shadowing"); + c_fresnel.m_dirt_env = as_vector_double("dirt_env"); + c_fresnel.m_Design_loss = as_vector_double("Design_loss"); + + c_fresnel.m_L_mod_spacing = as_number("L_mod_spacing"); + c_fresnel.m_L_crossover = as_number("L_crossover"); + c_fresnel.m_HL_T_coefs = as_vector_double("HL_T_coefs"); + c_fresnel.m_HL_w_coefs = as_vector_double("HL_w_coefs"); + + c_fresnel.m_DP_nominal = as_number("DP_nominal"); + c_fresnel.m_DP_coefs = as_vector_double("DP_coefs"); + c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); + + c_fresnel.m_L_rnr_pb = as_number("L_rnr_pb"); // No power block line length + c_fresnel.m_rec_su_delay = as_number("rec_su_delay"); + c_fresnel.m_rec_qf_delay = as_number("rec_qf_delay"); + c_fresnel.m_p_start = as_number("p_start"); + + c_fresnel.m_V_wind_des = as_number("V_wind_des"); + c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); + } + + // Calculate solar multiple (needed for other component constructors) + c_fresnel.design_solar_mult(); + + // Allocate Outputs + { + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_EQUIV_OPT_ETA_TOT, allocate("EqOpteff", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_DEFOCUS, allocate("SCAs_def", n_steps_fixed), n_steps_fixed); + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_TOT, allocate("q_inc_sf_tot", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_COSTH, allocate("qinc_costh", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_INC, allocate("q_dot_rec_inc", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_THERMAL_LOSS, allocate("q_dot_rec_thermal_loss", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_REC_ABS, allocate("q_dot_rec_abs", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_REC_THERMAL_EFF, allocate("rec_thermal_eff", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_PIPING_LOSS, allocate("q_dot_piping_loss", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_E_DOT_INTERNAL_ENERGY, allocate("e_dot_field_int_energy", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_HTF_OUT, allocate("q_dot_htf_sf_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_Q_DOT_FREEZE_PROT, allocate("q_dot_freeze_prot", n_steps_fixed), n_steps_fixed); + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_LOOP, allocate("m_dot_loop", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_IS_RECIRCULATING, allocate("recirculating", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_RECIRC, allocate("m_dot_field_recirc", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_DELIVERED, allocate("m_dot_field_delivered", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_FIELD_COLD_IN, allocate("T_field_cold_in", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_REC_COLD_IN, allocate("T_rec_cold_in", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_REC_HOT_OUT, allocate("T_rec_hot_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_T_FIELD_HOT_OUT, allocate("T_field_hot_out", n_steps_fixed), n_steps_fixed); + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_PRESSURE_DROP, allocate("deltaP_field", n_steps_fixed), n_steps_fixed); //[bar] + + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_W_DOT_SCA_TRACK, allocate("W_dot_sca_track", n_steps_fixed), n_steps_fixed); //[MWe] + c_fresnel.mc_reported_outputs.assign(C_csp_fresnel_collector_receiver::E_W_DOT_PUMP, allocate("W_dot_field_pump", n_steps_fixed), n_steps_fixed); //[MWe] + } + + } + + // Heat Sink + C_pc_heat_sink c_heat_sink; + { + 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.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_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.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.ms_params.m_pc_fl = as_integer("Fluid"); + c_heat_sink.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); + } + + // TES + C_csp_two_tank_tes storage; + { + double V_tes_des = as_double("V_tes_des"); + + storage = C_csp_two_tank_tes( + as_integer("Fluid"), + as_matrix("field_fl_props"), + as_integer("store_fluid"), + as_matrix("store_fl_props"), + q_dot_pc_des, + c_fresnel.m_solar_mult, + Q_tes, + as_double("h_tank"), + as_double("u_tank"), + as_integer("tank_pairs"), + as_double("hot_tank_Thtr"), + as_double("hot_tank_max_heat"), + as_double("cold_tank_Thtr"), + as_double("cold_tank_max_heat"), + as_double("dt_hot"), + as_double("T_loop_in_des"), + as_double("T_loop_out"), + as_double("T_loop_out"), + as_double("T_loop_in_des"), + as_double("h_tank_min"), + as_double("init_hot_htf_percent"), + as_double("pb_pump_coef"), + as_boolean("tanks_in_parallel"), + V_tes_des, + false, + as_double("tes_pump_coef") + ); + + + // Set storage outputs + int n_wf_records = (int)weather_reader.m_weather_data_provider->nrecords(); + double steps_per_hour = n_wf_records / 8760; + int n_steps_fixed = steps_per_hour * 8760; + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_Q_DOT_LOSS, allocate("tank_losses", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HEATER, allocate("q_tes_heater", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_HOT, allocate("T_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_COLD, allocate("T_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_M_DOT_TANK_TO_TANK, allocate("m_dot_cold_tank_to_hot_tank", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_COLD_TANK, allocate("mass_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_HOT_TANK, allocate("mass_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HTF_PUMP, allocate("tes_htf_pump_power", n_steps_fixed), n_steps_fixed); + + } + + // TOU + C_csp_tou_block_schedules tou; + C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); + tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); + + tou.mc_dispatch_params.m_is_tod_pc_target_also_pc_max = as_boolean("is_tod_pc_target_also_pc_max"); + tou.mc_dispatch_params.m_is_block_dispatch = !is_dispatch; //mw + tou.mc_dispatch_params.m_use_rule_1 = true; + tou.mc_dispatch_params.m_standby_off_buffer = 2.0; + tou.mc_dispatch_params.m_use_rule_2 = false; + tou.mc_dispatch_params.m_q_dot_rec_des_mult = -1.23; + tou.mc_dispatch_params.m_f_q_dot_pc_overwrite = -1.23; + + size_t n_f_turbine = 0; + ssc_number_t* p_f_turbine = as_array("f_turb_tou_periods", &n_f_turbine); + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC].resize(n_f_turbine, 0.0); + //tou_params->mv_t_frac.resize(n_f_turbine, 0.0); + for (size_t i = 0; i < n_f_turbine; i++) + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC][i] = (double)p_f_turbine[i]; + + // Load fraction by time step: + bool is_load_fraction_by_timestep = is_assigned("timestep_load_fractions"); + tou_params->mc_csp_ops.mv_is_diurnal = !(is_load_fraction_by_timestep); + if (is_load_fraction_by_timestep) { + size_t N_load_fractions; + ssc_number_t* load_fractions = as_array("timestep_load_fractions", &N_load_fractions); + std::copy(load_fractions, load_fractions + N_load_fractions, std::back_inserter(tou_params->mc_csp_ops.timestep_load_fractions)); + } + + double ppa_price_year1 = std::numeric_limits::quiet_NaN(); + int csp_financial_model = as_integer("csp_financial_model"); + + if (sim_type == 1) { + if (csp_financial_model == 8 || csp_financial_model == 7) { // No Financial Model or LCOH + if (is_dispatch) { + throw exec_error("fresnel_physical_iph", "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 + tou_params->mc_pricing.mc_weekdays.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mc_weekends.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, -1.0); + } + } + else { + throw exec_error("fresnel_physical_iph", "csp_financial_model must 8"); + } + + + } + else if (sim_type == 2) { + tou_params->mc_pricing.mv_is_diurnal = false; + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + + + + // System Parameters + C_csp_solver::S_csp_system_params system; + { + system.m_pb_fixed_par = as_double("pb_fixed_par"); + size_t nval_bop_array = 0; + ssc_number_t* bop_array = as_array("bop_array", &nval_bop_array); + if (nval_bop_array != 5) throw exec_error("fresnel_physical", "Should be 5 elements in bop_array, has " + util::to_string((int)nval_bop_array) + "."); + system.m_bop_par = bop_array[0]; //as_double("bop_par"); + system.m_bop_par_f = bop_array[1]; //as_double("bop_par_f"); + system.m_bop_par_0 = bop_array[2]; //as_double("bop_par_0"); + system.m_bop_par_1 = bop_array[3]; //as_double("bop_par_1"); + system.m_bop_par_2 = bop_array[4]; //as_double("bop_par_2"); + } + + // System Dispatch + csp_dispatch_opt dispatch; + dispatch.solver_params.dispatch_optimize = false; + + + // Instantiate Solver + C_csp_solver csp_solver(weather_reader, + c_fresnel, + c_heat_sink, + storage, + tou, + dispatch, + system, + NULL, + nullptr, + ssc_cmod_update, + (void*)(this)); + + // Set solver reporting outputs + { + // 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); + // 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); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLAZ, allocate("solazi", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLZEN, allocate("solzen", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::BEAM, allocate("beam", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TDRY, allocate("tdry", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TWET, allocate("twet", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::RH, allocate("RH", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::WSPD, allocate("wspd", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PRES, allocate("pres", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CR_DEFOCUS, allocate("defocus", n_steps_fixed), n_steps_fixed); + // TES + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_DC, allocate("q_dc_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_CH, allocate("q_ch_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_E_CH_STATE, allocate("e_ch_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CR_TO_TES_HOT, allocate("m_dot_cr_to_tes_hot", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_HOT_OUT, allocate("m_dot_tes_hot_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_PC_TO_TES_COLD, allocate("m_dot_pc_to_tes_cold", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_COLD_OUT, allocate("m_dot_tes_cold_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_FIELD_TO_CYCLE, allocate("m_dot_field_to_cycle", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CYCLE_TO_FIELD, allocate("m_dot_cycle_to_field", n_steps_fixed), n_steps_fixed); + // System + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_FIXED, allocate("P_fixed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_BOP, allocate("P_plant_balance_tot", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::W_DOT_NET, allocate("W_dot_parasitic_tot", n_steps_fixed), n_steps_fixed); + // Controller + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_1, allocate("op_mode_1", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_2, allocate("op_mode_2", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_3, allocate("op_mode_3", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::ERR_M_DOT, allocate("m_dot_balance", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::ERR_Q_DOT, allocate("q_balance", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::N_OP_MODES, allocate("n_op_modes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TOU_PERIOD, allocate("tou_value", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PRICING_MULT, allocate("pricing_mult", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_SB, allocate("q_dot_pc_sb", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MIN, allocate("q_dot_pc_min", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_TARGET, allocate("q_dot_pc_target", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MAX, allocate("q_dot_pc_max", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_REC_SU, allocate("is_rec_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SU, allocate("is_pc_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SB, allocate("is_pc_sb_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_SU, allocate("q_dot_est_cr_su", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_ON, allocate("q_dot_est_cr_on", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_DC, allocate("q_dot_est_tes_dc", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CH, allocate("q_dot_est_tes_ch", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_A, allocate("operating_modes_a", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_B, allocate("operating_modes_b", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_C, allocate("operating_modes_c", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REL_MIP_GAP, allocate("disp_rel_mip_gap", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_STATE, allocate("disp_solve_state", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SUBOPT_FLAG, allocate("disp_subopt_flag", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_ITER, allocate("disp_solve_iter", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ, allocate("disp_objective", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ_RELAX, allocate("disp_obj_relax", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSF_EXPECT, allocate("disp_qsf_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFPROD_EXPECT, allocate("disp_qsfprod_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFSU_EXPECT, allocate("disp_qsfsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_TES_EXPECT, allocate("disp_tes_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PCEFF_EXPECT, allocate("disp_pceff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SFEFF_EXPECT, allocate("disp_thermeff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QPBSU_EXPECT, allocate("disp_qpbsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_WPB_EXPECT, allocate("disp_wpb_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REV_EXPECT, allocate("disp_rev_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NCONSTR, allocate("disp_presolve_nconstr", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NVAR, allocate("disp_presolve_nvar", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_TIME, allocate("disp_solve_time", n_steps_fixed), n_steps_fixed); + } + + update("Initialize physical fresnel model...", 0.0); + + // Initialize Solver + int out_type = -1; + std::string out_msg = ""; + try + { + // Initialize Solver + csp_solver.init(); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + throw exec_error("fresnel_physical", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + // Design point is complete, assign technology design outputs + double total_land_area; + double nameplate_des; + { + // System Design Calcs + //double eta_ref = as_double("eta_ref"); //[-] + //double W_dot_cycle_des = as_double("P_ref"); //[MWe] + double tshours = as_double("tshours"); //[-] + double solar_mult_des = c_fresnel.m_solar_mult; + double q_pb_design = as_double("q_pb_design"); + + double q_dot_pc_des = q_pb_design; //[MWt] + Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + + double mdot_field_des = c_fresnel.m_m_dot_design; // [kg/s] + + double avg_T_des = (c_fresnel.m_T_loop_in_des + c_fresnel.m_T_loop_out_des) / 2.0; + + // Solar Field + + double W_dot_col_tracking_des = c_fresnel.get_tracking_power(); // [MWe] + double q_dot_rec_des = c_fresnel.m_q_design / 1e6; // [MWt] + double A_loop = c_fresnel.m_A_loop; // [m2] + double loop_opt_eff = c_fresnel.m_loop_opt_eff; + double loop_therm_eff = c_fresnel.m_loop_therm_eff; + double loop_eff = c_fresnel.m_loop_eff; + double sm1_aperture = c_fresnel.m_Ap_sm1; // [m2] + double sm1_nLoops = c_fresnel.m_nLoops_sm1; + double total_tracking_power = c_fresnel.m_W_dot_sca_tracking_nom; // [MW] + double A_field = c_fresnel.m_Ap_tot; // [m2] + double q_field_des = c_fresnel.m_q_design / 1e6; // [MW] + + double field_area = A_field / 4046.85642; // [acres] (convert m2 to acre) + double land_mult = as_double("land_mult"); + total_land_area = field_area * land_mult; // [acres] + + double field_htf_min_temp = c_fresnel.m_htfProps.min_temp() - 273.15; // [C] + double field_htf_max_temp = c_fresnel.m_htfProps.max_temp() - 273.15; // [C] + + // steady state results + double dP_field_des_SS = c_fresnel.m_dP_des_SS; // [bar] + double Q_field_des_SS = c_fresnel.m_Q_field_des_SS / 1e6; // [MW] + double T_field_out_des_SS = c_fresnel.m_T_field_out_des_SS; // [C] + double m_dot_des_SS = c_fresnel.m_m_dot_des_SS; // [kg/s] + double m_dot_loop_des_SS = c_fresnel.m_m_dot_loop_des_SS; // [kg/s] + double V_hdr_min_des_SS = c_fresnel.m_V_hdr_min_des_SS; // [m/s] + double V_hdr_max_des_SS = c_fresnel.m_V_hdr_max_des_SS; // [m/s] + double eta_optical_des_SS = c_fresnel.m_eta_optical_des_SS; + double therm_eff_des_SS = c_fresnel.m_therm_eff_des_SS; + double eff_des_SS = c_fresnel.m_eff_des_SS; + double W_dot_pump_des_SS = c_fresnel.m_W_dot_pump_des_SS; // [MWe] + + double T_loop_out_des_SS = c_fresnel.m_T_loop_out_des_SS; // [C] + double Q_loop_des_SS = c_fresnel.m_Q_loop_des_SS / 1e6; // [MW] + double therm_eff_loop_des_SS = c_fresnel.m_therm_eff_loop_des_SS; + double eff_loop_des_SS = c_fresnel.m_eff_loop_des_SS; + double Q_loss_receiver_des_SS = c_fresnel.m_Q_loss_receiver_des_SS; // [MWt] + double Q_loss_hdr_rnr_des_SS = c_fresnel.m_Q_loss_hdr_rnr_des_SS; // [MWt] + + // Assign + { + assign("q_dot_rec_des", q_dot_rec_des); + assign("A_loop", A_loop); + assign("loop_opt_eff", loop_opt_eff); + assign("loop_therm_eff", loop_therm_eff); + assign("loop_eff", loop_eff); + assign("sm1_aperture", sm1_aperture); + assign("sm1_nLoops", sm1_nLoops); + assign("total_tracking_power", total_tracking_power); + assign("A_field", A_field); + assign("q_field_des", q_field_des); + assign("field_area", field_area); + assign("total_land_area", total_land_area); + assign("field_htf_min_temp", field_htf_min_temp); + assign("field_htf_max_temp", field_htf_max_temp); + assign("mdot_field_des", mdot_field_des); + + assign("dP_field_des_SS", dP_field_des_SS); + assign("Q_field_des_SS", Q_field_des_SS); + assign("T_field_out_des_SS", T_field_out_des_SS); + assign("m_dot_des_SS", m_dot_des_SS); + assign("m_dot_loop_des_SS", m_dot_loop_des_SS); + assign("V_hdr_min_des_SS", V_hdr_min_des_SS); + assign("V_hdr_max_des_SS", V_hdr_max_des_SS); + assign("eta_optical_des_SS", eta_optical_des_SS); + assign("therm_eff_des_SS", therm_eff_des_SS); + assign("eff_des_SS", eff_des_SS); + assign("W_dot_pump_des_SS", W_dot_pump_des_SS); + + assign("T_loop_out_des_SS", T_loop_out_des_SS); + assign("Q_loop_des_SS", Q_loop_des_SS); + assign("therm_eff_loop_des_SS", therm_eff_loop_des_SS); + assign("eff_loop_des_SS", eff_loop_des_SS); + + assign("Q_loss_receiver_des_SS", Q_loss_receiver_des_SS); + assign("Q_loss_hdr_rnr_des_SS", Q_loss_hdr_rnr_des_SS); + } + + // Collector and Receiver + double DP_pressure_loss = c_fresnel.m_nMod * c_fresnel.m_DP_nominal; // [bar] + double avg_dt_des = c_fresnel.m_dT_des; // [C] + double hl_des = c_fresnel.m_hl_des; // [W/m] + double opt_derate = c_fresnel.m_opt_derate; + double opt_normal = c_fresnel.m_opt_normal; + + // Assign + { + assign("DP_pressure_loss", DP_pressure_loss); + assign("avg_dt_des", avg_dt_des); + assign("hl_des", hl_des); + assign("opt_derate", opt_derate); + assign("opt_normal", opt_normal); + } + + // Storage + double V_tes_htf_avail_calc /*m3*/, V_tes_htf_total_calc /*m3*/, + d_tank_calc /*m*/, q_dot_loss_tes_des_calc /*MWt*/, dens_store_htf_at_T_ave_calc /*kg/m3*/, + Q_tes_des_calc /*MWt-hr*/; + + storage.get_design_parameters(V_tes_htf_avail_calc, V_tes_htf_total_calc, + d_tank_calc, q_dot_loss_tes_des_calc, dens_store_htf_at_T_ave_calc, Q_tes_des_calc); + + double vol_min = V_tes_htf_total_calc * (storage.m_h_tank_min / storage.m_h_tank); + double tes_htf_min_temp = storage.get_min_storage_htf_temp() - 273.15; + double tes_htf_max_temp = storage.get_max_storage_htf_temp() - 273.15; + double tes_htf_dens = storage.get_storage_htf_density(); + double tes_htf_cp = storage.get_storage_htf_cp(); + + // Assign + { + assign("vol_tank", V_tes_htf_total_calc); + assign("Q_tes_des", Q_tes_des_calc); + assign("d_tank", d_tank_calc); + assign("vol_min", vol_min); + assign("q_dot_loss_tes_des", q_dot_loss_tes_des_calc); + assign("tes_htf_min_temp", tes_htf_min_temp); + assign("tes_htf_max_temp", tes_htf_max_temp); + assign("tes_htf_dens", tes_htf_dens); + 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] + //} + + // System Design + double W_dot_bop_design, W_dot_fixed_parasitic_design; //[MWe] + csp_solver.get_design_parameters(W_dot_bop_design, W_dot_fixed_parasitic_design); + + nameplate_des = q_dot_pc_des * 1.E3; // [kWt] + + // Assign + { + assign("nameplate", nameplate_des * 1.E-3); // [MWt] + assign("W_dot_bop_design", W_dot_bop_design); + assign("W_dot_fixed", W_dot_fixed_parasitic_design); + + assign("solar_mult", c_fresnel.m_solar_mult); + assign("nLoops", c_fresnel.m_nLoops); + assign("total_Ap", c_fresnel.m_Ap_tot); + } + + // System Control + // temporary fix + vector aux_vec = as_vector_double("aux_array"); + double W_dot_cycle_des = 0; + double aux_design = aux_vec[0] * aux_vec[1] * (aux_vec[2] + aux_vec[3] + aux_vec[4]) * W_dot_cycle_des; + + // Assign + { + assign("aux_design", aux_design); + } + } + + // Calculate Costs and assign outputs + if (true) + { + + // Collect dedicated cost inputs + double site_improvements_spec_cost = as_double("site_improvements_spec_cost"); + double solar_field_spec_cost = as_double("solar_field_spec_cost"); + double htf_system_spec_cost = as_double("htf_system_spec_cost"); + double storage_spec_cost = as_double("storage_spec_cost"); + double heat_sink_spec_cost = as_double("heat_sink_spec_cost"); + double bop_spec_cost = as_double("bop_spec_cost"); + double contingency_percent = as_double("contingency_percent"); + + double epc_cost_per_acre = as_double("epc_cost_per_acre"); + double epc_cost_percent_direct = as_double("epc_cost_percent_direct"); + double epc_cost_per_watt = as_double("epc_cost_per_watt"); + double epc_cost_fixed = as_double("epc_cost_fixed"); + double plm_cost_per_acre = as_double("plm_cost_per_acre"); + double plm_cost_percent_direct = as_double("plm_cost_percent_direct"); + double plm_cost_per_watt = as_double("plm_cost_per_watt"); + double plm_cost_fixed = as_double("plm_cost_fixed"); + + double sales_tax_percent = as_double("sales_tax_percent"); + + // Collect necessary variables defined in other tabs + double site_improvements_area = c_fresnel.m_Ap_tot; + double solar_field_area = c_fresnel.m_Ap_tot; + double htf_system_area = c_fresnel.m_Ap_tot; + // Q_tes + double heat_sink_mwt = as_double("q_pb_design"); + double power_plant_mwt = heat_sink_mwt; // MWe + double bop_mwt = heat_sink_mwt; // MWe + // total_land_area // m2 + // nameplate_des // MWe + double sales_tax_rate = as_double("sales_tax_rate"); + + // Define outputs + double heat_sink_cost_out, ts_cost_out, site_improvements_cost_out, bop_cost_out, solar_field_cost_out, htf_system_cost_out, contingency_cost_out, + total_direct_cost_out, epc_total_cost_out, plm_total_cost_out, total_indirect_cost_out, sales_tax_total_out, total_installed_cost_out, installed_per_capacity_out; + double dummy; + + // Calculate Costs + N_mspt::calculate_mslf_costs(site_improvements_area, site_improvements_spec_cost, solar_field_area, solar_field_spec_cost, htf_system_area, htf_system_spec_cost, Q_tes, storage_spec_cost,0,0, + heat_sink_mwt, heat_sink_spec_cost, bop_mwt, bop_spec_cost, contingency_percent, total_land_area, nameplate_des, epc_cost_per_acre, epc_cost_percent_direct, epc_cost_per_watt, + epc_cost_fixed, plm_cost_per_acre, plm_cost_percent_direct, plm_cost_per_watt, plm_cost_fixed, sales_tax_rate, sales_tax_percent, + + heat_sink_cost_out, ts_cost_out, site_improvements_cost_out, bop_cost_out, solar_field_cost_out, htf_system_cost_out, dummy, contingency_cost_out, + total_direct_cost_out, epc_total_cost_out, plm_total_cost_out, total_indirect_cost_out, sales_tax_total_out, total_installed_cost_out, installed_per_capacity_out); + + // Assign Outputs + { + assign("site_improvements_cost", site_improvements_cost_out); + assign("solar_field_cost", solar_field_cost_out); + assign("htf_system_cost", htf_system_cost_out); + assign("ts_cost", ts_cost_out); + assign("heat_sink_cost", heat_sink_cost_out); + assign("bop_cost", bop_cost_out); + assign("contingency_cost", contingency_cost_out); + assign("total_direct_cost", total_direct_cost_out); + + assign("epc_total_cost", epc_total_cost_out); + assign("plm_total_cost", plm_total_cost_out); + assign("total_indirect_cost", total_indirect_cost_out); + + assign("sales_tax_total", sales_tax_total_out); + assign("total_installed_cost", total_installed_cost_out); + assign("installed_per_capacity", installed_per_capacity_out); + } + } + + + // Return if only called for design point + if (sim_type != 1) + return; + + update("Begin timeseries simulation...", 0.0); + std::clock_t clock_start = std::clock(); + + // Run Simulation + try + { + // Simulate + csp_solver.Ssimulate(sim_setup); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg); + } + + throw exec_error("fresnel_physical", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + + std::clock_t clock_end = std::clock(); + double sim_duration = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + assign("sim_duration", (ssc_number_t)sim_duration); + + // Outputs + + + size_t count; + ssc_number_t* p_q_dot_heat_sink = as_array("q_dot_to_heat_sink", &count); + //ssc_number_t* p_W_dot_net = as_array("P_out_net", &count); + ssc_number_t* p_time_final_hr = as_array("time_hr", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays"); + + // 'adjustment_factors' class stores factors in hourly array, so need to index as such + adjustment_factors haf(this, "adjust"); + if (!haf.setup(n_steps_fixed)) + throw exec_error("fresnel_physical", "failed to setup adjustment factors: " + haf.error()); + + ssc_number_t* p_gen = allocate("gen", n_steps_fixed); + ssc_number_t* p_q_dot_defocus_est = allocate("q_dot_defocus_est", n_steps_fixed); + ssc_number_t* p_SCAs_def = as_array("SCAs_def", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays2"); + + ssc_number_t* p_W_dot_parasitic_tot = as_array("W_dot_parasitic_tot", &count); + ssc_number_t* p_W_dot_par_tot_haf = allocate("W_dot_par_tot_haf", n_steps_fixed); + + ssc_number_t* p_q_dot_htf_sf_out = as_array("q_dot_htf_sf_out", &count); + if ((int)count != n_steps_fixed) + throw exec_error("fresnel_physical", "The number of fixed steps does not match the length of output data arrays3"); + for (int i = 0; i < n_steps_fixed; i++) + { + size_t hour = (size_t)ceil(p_time_final_hr[i]); + p_gen[i] = (ssc_number_t)(p_q_dot_heat_sink[i] * haf(hour) * 1.E3); //[kWe] + p_q_dot_defocus_est[i] = (ssc_number_t)(1.0 - p_SCAs_def[i]) * p_q_dot_htf_sf_out[i]; //[MWt] + p_W_dot_parasitic_tot[i] *= -1.0; //[MWe] Label is total parasitics, so change to a positive value + p_W_dot_par_tot_haf[i] = (ssc_number_t)(p_W_dot_parasitic_tot[i] * haf(hour) * 1.E3); //[kWe] apply availability derate and convert from MWe + } + + // Monthly outputs + accumulate_monthly_for_year("gen", "monthly_energy", sim_setup.m_report_step / 3600.0, steps_per_hour, 1); + + // Annual outputs + accumulate_annual_for_year("gen", "annual_energy", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + + // This term currently includes TES freeze protection + accumulate_annual_for_year("W_dot_par_tot_haf", "annual_electricity_consumption", sim_setup.m_report_step / 3600.0, steps_per_hour); //[kWe-hr] + + double V_water_mirrors = as_double("water_per_wash") / 1000.0 * c_fresnel.m_Ap_tot * as_double("washes_per_year"); + assign("annual_total_water_use", (ssc_number_t)V_water_mirrors); + + ssc_number_t annual_field_fp = accumulate_annual_for_year("q_dot_freeze_prot", "annual_field_freeze_protection", sim_setup.m_report_step / 3600.0 * 1.E3, steps_per_hour); //[kWt-hr] + ssc_number_t annual_tes_fp = accumulate_annual_for_year("q_tes_heater", "annual_tes_freeze_protection", sim_setup.m_report_step / 3600.0 * 1.E3, steps_per_hour); //[kWt-hr] + + ssc_number_t annual_thermal_consumption = annual_field_fp + annual_tes_fp; //[kWt-hr] + assign("annual_thermal_consumption", annual_thermal_consumption); + + // Reporting dispatch solution counts + size_t n_flag, n_gap = 0; + ssc_number_t* subopt_flag = as_array("disp_subopt_flag", &n_flag); + ssc_number_t* rel_mip_gap = as_array("disp_rel_mip_gap", &n_gap); + + std::vector flag; + std::vector gap; + flag.resize(n_flag); + gap.resize(n_flag); + for (size_t i = 0; i < n_flag; i++) { + flag[i] = (int)subopt_flag[i]; + gap[i] = (double)rel_mip_gap[i]; + } + + double avg_gap = 0; + if (is_dispatch) { + std::string disp_sum_msg; + dispatch.count_solutions_by_type(flag, (int)as_double("disp_frequency"), disp_sum_msg); + log(disp_sum_msg, SSC_NOTICE); + avg_gap = dispatch.calc_avg_subopt_gap(gap, flag, (int)as_double("disp_frequency")); + } + assign("avg_suboptimal_rel_mip_gap", (ssc_number_t)avg_gap); + + + ssc_number_t ae = as_number("annual_energy"); //[kWt-hr] + double nameplate = q_dot_pc_des * 1.E3; //[kWt] + double kWh_per_kW = ae / nameplate; + assign("capacity_factor", (ssc_number_t)(kWh_per_kW / 8760. * 100.)); + assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); + + + + + + + + // Do unit post-processing here + + ssc_number_t* p_annual_energy_dist_time = gen_heatmap(this, steps_per_hour); + // Non-timeseries array outputs + double P_adj = storage.P_in_des; // slightly adjust all field design pressures to account for pressure drop in TES before hot tank + transform(c_fresnel.m_P_rnr_dsn.begin(), c_fresnel.m_P_rnr_dsn.end(), c_fresnel.m_P_rnr_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + transform(c_fresnel.m_P_hdr_dsn.begin(), c_fresnel.m_P_hdr_dsn.end(), c_fresnel.m_P_hdr_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + transform(c_fresnel.m_P_loop_dsn.begin(), c_fresnel.m_P_loop_dsn.end(), c_fresnel.m_P_loop_dsn.begin(), [P_adj](double x) {return x + P_adj; }); + + ssc_number_t* p_pipe_runner_diams = allocate("pipe_runner_diams", c_fresnel.m_D_runner.size()); + std::copy(c_fresnel.m_D_runner.begin(), c_fresnel.m_D_runner.end(), p_pipe_runner_diams); + ssc_number_t* p_pipe_runner_wallthk = allocate("pipe_runner_wallthk", c_fresnel.m_WallThk_runner.size()); + std::copy(c_fresnel.m_WallThk_runner.begin(), c_fresnel.m_WallThk_runner.end(), p_pipe_runner_wallthk); + ssc_number_t* p_pipe_runner_lengths = allocate("pipe_runner_lengths", c_fresnel.m_L_runner.size()); + std::copy(c_fresnel.m_L_runner.begin(), c_fresnel.m_L_runner.end(), p_pipe_runner_lengths); + ssc_number_t* p_pipe_runner_expansions = allocate("pipe_runner_expansions", c_fresnel.m_N_rnr_xpans.size()); + std::copy(c_fresnel.m_N_rnr_xpans.begin(), c_fresnel.m_N_rnr_xpans.end(), p_pipe_runner_expansions); + ssc_number_t* p_pipe_runner_mdot_dsn = allocate("pipe_runner_mdot_dsn", c_fresnel.m_m_dot_rnr_dsn.size()); + std::copy(c_fresnel.m_m_dot_rnr_dsn.begin(), c_fresnel.m_m_dot_rnr_dsn.end(), p_pipe_runner_mdot_dsn); + ssc_number_t* p_pipe_runner_vel_dsn = allocate("pipe_runner_vel_dsn", c_fresnel.m_V_rnr_dsn.size()); + std::copy(c_fresnel.m_V_rnr_dsn.begin(), c_fresnel.m_V_rnr_dsn.end(), p_pipe_runner_vel_dsn); + ssc_number_t* p_pipe_runner_T_dsn = allocate("pipe_runner_T_dsn", c_fresnel.m_T_rnr_dsn.size()); + std::copy(c_fresnel.m_T_rnr_dsn.begin(), c_fresnel.m_T_rnr_dsn.end(), p_pipe_runner_T_dsn); + ssc_number_t* p_pipe_runner_P_dsn = allocate("pipe_runner_P_dsn", c_fresnel.m_P_rnr_dsn.size()); + std::copy(c_fresnel.m_P_rnr_dsn.begin(), c_fresnel.m_P_rnr_dsn.end(), p_pipe_runner_P_dsn); + + ssc_number_t* p_pipe_header_diams = allocate("pipe_header_diams", c_fresnel.m_D_hdr.size()); + std::copy(c_fresnel.m_D_hdr.begin(), c_fresnel.m_D_hdr.end(), p_pipe_header_diams); + ssc_number_t* p_pipe_header_wallthk = allocate("pipe_header_wallthk", c_fresnel.m_WallThk_hdr.size()); + std::copy(c_fresnel.m_WallThk_hdr.begin(), c_fresnel.m_WallThk_hdr.end(), p_pipe_header_wallthk); + //ssc_number_t* p_pipe_header_lengths = allocate("pipe_header_lengths", c_fresnel.m_L_hdr.size()); + //std::copy(c_fresnel.m_L_hdr.begin(), c_fresnel.m_L_hdr.end(), p_pipe_header_lengths); + ssc_number_t* p_pipe_header_expansions = allocate("pipe_header_expansions", c_fresnel.m_N_hdr_xpans.size()); + std::copy(c_fresnel.m_N_hdr_xpans.begin(), c_fresnel.m_N_hdr_xpans.end(), p_pipe_header_expansions); + ssc_number_t* p_pipe_header_mdot_dsn = allocate("pipe_header_mdot_dsn", c_fresnel.m_m_dot_hdr_dsn.size()); + std::copy(c_fresnel.m_m_dot_hdr_dsn.begin(), c_fresnel.m_m_dot_hdr_dsn.end(), p_pipe_header_mdot_dsn); + ssc_number_t* p_pipe_header_vel_dsn = allocate("pipe_header_vel_dsn", c_fresnel.m_V_hdr_dsn.size()); + std::copy(c_fresnel.m_V_hdr_dsn.begin(), c_fresnel.m_V_hdr_dsn.end(), p_pipe_header_vel_dsn); + ssc_number_t* p_pipe_header_T_dsn = allocate("pipe_header_T_dsn", c_fresnel.m_T_hdr_dsn.size()); + std::copy(c_fresnel.m_T_hdr_dsn.begin(), c_fresnel.m_T_hdr_dsn.end(), p_pipe_header_T_dsn); + ssc_number_t* p_pipe_header_P_dsn = allocate("pipe_header_P_dsn", c_fresnel.m_P_hdr_dsn.size()); + std::copy(c_fresnel.m_P_hdr_dsn.begin(), c_fresnel.m_P_hdr_dsn.end(), p_pipe_header_P_dsn); + + ssc_number_t* p_pipe_loop_T_dsn = allocate("pipe_loop_T_dsn", c_fresnel.m_T_loop_dsn.size()); + std::copy(c_fresnel.m_T_loop_dsn.begin(), c_fresnel.m_T_loop_dsn.end(), p_pipe_loop_T_dsn); + ssc_number_t* p_pipe_loop_P_dsn = allocate("pipe_loop_P_dsn", c_fresnel.m_P_loop_dsn.size()); + std::copy(c_fresnel.m_P_loop_dsn.begin(), c_fresnel.m_P_loop_dsn.end(), p_pipe_loop_P_dsn); + + ssc_number_t* p_pipe_tes_diams = allocate("pipe_tes_diams", storage.pipe_diams.ncells()); + std::copy(storage.pipe_diams.data(), storage.pipe_diams.data() + storage.pipe_diams.ncells(), p_pipe_tes_diams); + ssc_number_t* p_pipe_tes_wallthk = allocate("pipe_tes_wallthk", storage.pipe_wall_thk.ncells()); + std::copy(storage.pipe_wall_thk.data(), storage.pipe_wall_thk.data() + storage.pipe_wall_thk.ncells(), p_pipe_tes_wallthk); + ssc_number_t* p_pipe_tes_lengths = allocate("pipe_tes_lengths", storage.pipe_lengths.ncells()); + std::copy(storage.pipe_lengths.data(), storage.pipe_lengths.data() + storage.pipe_lengths.ncells(), p_pipe_tes_lengths); + ssc_number_t* p_pipe_tes_mdot_dsn = allocate("pipe_tes_mdot_dsn", storage.pipe_m_dot_des.ncells()); + std::copy(storage.pipe_m_dot_des.data(), storage.pipe_m_dot_des.data() + storage.pipe_m_dot_des.ncells(), p_pipe_tes_mdot_dsn); + ssc_number_t* p_pipe_tes_vel_dsn = allocate("pipe_tes_vel_dsn", storage.pipe_vel_des.ncells()); + std::copy(storage.pipe_vel_des.data(), storage.pipe_vel_des.data() + storage.pipe_vel_des.ncells(), p_pipe_tes_vel_dsn); + ssc_number_t* p_pipe_tes_T_dsn = allocate("pipe_tes_T_dsn", storage.pipe_T_des.ncells()); + std::copy(storage.pipe_T_des.data(), storage.pipe_T_des.data() + storage.pipe_T_des.ncells(), p_pipe_tes_T_dsn); + ssc_number_t* p_pipe_tes_P_dsn = allocate("pipe_tes_P_dsn", storage.pipe_P_des.ncells()); + std::copy(storage.pipe_P_des.data(), storage.pipe_P_des.data() + storage.pipe_P_des.ncells(), p_pipe_tes_P_dsn); + + + + + + + + } +}; + +DEFINE_MODULE_ENTRY(fresnel_physical_iph, "Physical Fresnel IPH applications", 1) diff --git a/ssc/cmod_lcoefcr.cpp b/ssc/cmod_lcoefcr.cpp index 3db0eee64..6e253b8cf 100644 --- a/ssc/cmod_lcoefcr.cpp +++ b/ssc/cmod_lcoefcr.cpp @@ -33,6 +33,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "core.h" #include "lib_financial.h" +#include + static var_info vtab_lcoefcr[] = { /* VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS*/ @@ -77,3 +79,183 @@ class cm_lcoefcr : public compute_module }; DEFINE_MODULE_ENTRY( lcoefcr, "Calculate levelized cost of energy using fixed charge rate method.", 1 ) + + +static var_info vtab_lcoefcr_design[] = +{ + /* VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS*/ + { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, + + { SSC_INPUT, SSC_NUMBER, "ui_fcr_input_option", "0: fixed charge rate; 1: calculate", "", "", "Simple LCOE", "*", "", ""}, + + // FCR Input Option = 0: Fixed fixed charge rate + { SSC_INPUT, SSC_NUMBER, "ui_fixed_charge_rate", "Input fixed charge rate", "", "", "Simple LCOE", "ui_fcr_input_option=0", "", ""}, + + // FCR Input Option = 1: Calculated fixed charge rate + { SSC_INPUT, SSC_NUMBER, "c_inflation", "Input fixed charge rate", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_equity_return", "IRR (nominal)", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_debt_percent", "Project term debt (% of capital)", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_nominal_interest_rate", "Nominal debt interest rate", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_tax_rate", "Effective tax rate", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_lifetime", "Analysis period", "years", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_ARRAY, "c_depreciation_schedule", "Depreciation schedule", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_construction_interest", "Nominal construction interest rate", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + { SSC_INPUT, SSC_ARRAY, "c_construction_cost", "Construction cost schedule", "%", "", "Simple LCOE", "ui_fcr_input_option=1", "", ""}, + + // General Inputs + + // "Performance" Inputs + { SSC_INPUT, SSC_NUMBER, "total_installed_cost", "Total installed cost", "$", "", "System Costs", "sim_type=1", "", "SIMULATION_PARAMETER" }, + + { SSC_INPUT, SSC_NUMBER, "annual_electricity_consumption","Annual electricity consumption with avail derate", "kWe-hr", "", "IPH LCOH", "sim_type=1", "", "SIMULATION_PARAMETER" }, + { SSC_INPUT, SSC_NUMBER, "electricity_rate", "Cost of electricity used to operate pumps and trackers", "$/kWe-hr","", "IPH LCOH", "sim_type=1", "", "SIMULATION_PARAMETER" }, + { SSC_INPUT, SSC_NUMBER, "fixed_operating_cost", "Annual fixed operating cost", "$", "", "Simple LCOE", "sim_type=1", "", "SIMULATION_PARAMETER" }, + + { SSC_INPUT, SSC_NUMBER, "variable_operating_cost", "Annual variable operating cost", "$/kWh", "", "Simple LCOE", "sim_type=1", "", "SIMULATION_PARAMETER" }, + { SSC_INPUT, SSC_NUMBER, "annual_energy", "Annual energy production", "kWh", "", "Simple LCOE", "sim_type=1", "", "SIMULATION_PARAMETER" }, + + // "Design" outputs + { SSC_OUTPUT, SSC_NUMBER, "crf", "Capital recovery factor", "", "", "Simple LCOE", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "pfin", "Project financing factor", "", "", "Simple LCOE", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "cfin", "Construction financing factor", "", "", "Simple LCOE", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "wacc", "WACC", "", "", "Simple LCOE", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "fixed_charge_rate_calc", "Calculated fixed charge rate", "", "", "Simple LCOE", "*", "", "" }, + + { SSC_OUTPUT, SSC_NUMBER, "lcoe_fcr", "LCOE Levelized cost of energy", "$/kWh", "", "Simple LCOE", "sim_type=1", "", "" }, + + // "Performance" outputs + + var_info_invalid }; + +class cm_lcoefcr_design : public compute_module +{ +private: +public: + cm_lcoefcr_design() + { + add_var_info(vtab_lcoefcr_design); + } + + void exec() + { + int fcr_input_option = as_integer("ui_fcr_input_option"); + + double fixed_charge_rate = std::numeric_limits::quiet_NaN(); + double crf = std::numeric_limits::quiet_NaN(); + double pfin = std::numeric_limits::quiet_NaN(); + double cfin = std::numeric_limits::quiet_NaN(); + double wacc = std::numeric_limits::quiet_NaN(); + if (fcr_input_option == 0) { + fixed_charge_rate = as_double("ui_fixed_charge_rate"); + } + else { + double i = as_double("c_inflation") / 100.0; + double nroe = as_double("c_equity_return") / 100.0; + double rroe = (1. + nroe) / (1. + i) - 1.0; // real return on equity + double df = as_double("c_debt_percent") / 100.0; + double inom = as_double("c_nominal_interest_rate") / 100.0; + double ireal = (1. + inom) / (1. + i) - 1.0; // real interest rate + double tr = as_double("c_tax_rate") / 100.0; + wacc = ((1. + ((1. - df) * ((1. + rroe) * (1. + i) - 1.)) + + (df * ((1. + ireal) * (1. + i) - 1.) * (1. - tr))) / (1. + i)) - 1.; + + double t = as_double("c_lifetime"); + crf = wacc / (1.0 - (1.0 / std::pow((1.0 + wacc), t))); // real crf + + std::vector dep = as_vector_double("c_depreciation_schedule"); + int n_dep = dep.size(); + std::vector arr_dep(n_dep); + for (int iii = 0; iii < n_dep; iii++) { + arr_dep[iii] = dep[iii] / 100.0 * (1.0 / std::pow((1.+wacc)*(1.+i), iii+1.0)); + } + + double pvd = std::accumulate(arr_dep.begin(), arr_dep.end(), 0.0); + pfin = (1.0 - tr*pvd) / (1.0 - tr); + + double cint = as_double("c_construction_interest") / 100.0; + std::vector ccon = as_vector_double("c_construction_cost"); + int n_con = ccon.size(); + std::vector arr_con(n_con); + for (int iii = 0; iii < n_con; iii++) { + arr_con[iii] = ccon[iii] / 100.0 * (1.0 + (1.0 - tr)*(std::pow(1. + cint, iii + 0.5)-1.0)); + } + + cfin = std::accumulate(arr_con.begin(), arr_con.end(), 0.0); + fixed_charge_rate = crf * pfin * cfin; + + } + + assign("fixed_charge_rate_calc", fixed_charge_rate); + assign("crf", crf); + assign("pfin", pfin); + assign("cfin", cfin); + assign("wacc", wacc); + + // ***************************************************** + // If calling cmod to run design only, return here + if (as_integer("sim_type") != 1) { + return; + } + // ***************************************************** + // ***************************************************** + + double aep = as_double("annual_energy"); // kWh annual output, get from performance model + double foc_in = as_double("fixed_operating_cost"); // $ + double voc = as_double("variable_operating_cost"); // $/kWh + double icc = as_double("total_installed_cost"); // $ + + double electricity_rate = as_number("electricity_rate"); //[$/kWe-hr] + double annual_electricity_consumption = as_number("annual_electricity_consumption"); //[kWe-hr] + double annual_electricity_cost = electricity_rate*annual_electricity_consumption; //[$] + + double foc = foc_in + annual_electricity_cost; //[$] + + double lcoe = (fixed_charge_rate * icc + foc) / aep + voc; //$/kWh + + assign("lcoe_fcr", var_data((ssc_number_t)lcoe)); + + // For reference: code from UI page 7/25/23 + /* + if (${ ui_fcr_input_option } == 0) + ${ fixed_charge_rate } = ${ ui_fixed_charge_rate }; + else { + i = ${ c_inflation } / 100; + nroe = ${ c_equity_return } / 100; + rroe = (1 + nroe) / (1 + i) - 1; // real return on equity + df = ${ c_debt_percent } / 100; + inom = ${ c_nominal_interest_rate } / 100; + ireal = (1 + inom) / (1 + i) - 1; // real interest rate + tr = ${ c_tax_rate } / 100; + wacc = ((1 + ((1 - df) * ((1 + rroe) * (1 + i) - 1)) + + (df * ((1 + ireal) * (1 + i) - 1) * (1 - tr))) / (1 + i)) - 1; // real wacc + ${ ui_wacc } = wacc; + t = ${ c_lifetime }; + crf = wacc / (1 - (1 / (1 + wacc) ^ t)); // real crf + dep = ${ c_depreciation_schedule }; + arr = alloc(#dep); + for (n = 0; n < #arr; n++) + arr[n] = dep[n] / 100 * (1 / ((1 + wacc) * (1 + i)) ^ (n + 1)); + pvd = sum(arr); + pfin = (1 - tr * pvd) / (1 - tr); + ccon = ${ c_construction_cost }; + cint = ${ c_construction_interest } / 100; + arr = alloc(#ccon); + for (n = 0; n < #arr; n++) + arr[n] = ccon[n] / 100 * (1 + (1 - tr) * ((1 + cint) ^ (n + 0.5) - 1)); + cfin = sum(arr); + fcr = crf * pfin * cfin; + ${ ui_crf } = crf; + ${ ui_pfin } = pfin; + ${ ui_cfin } = cfin; + ${ ui_wacc } = wacc; + ${ ui_ireal } = ireal; + ${ fixed_charge_rate } = fcr; + } + + */ + + } + +}; + +DEFINE_MODULE_ENTRY(lcoefcr_design, "Calculate levelized cost of energy using fixed charge rate method.", 1) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp new file mode 100644 index 000000000..02500fec1 --- /dev/null +++ b/ssc/cmod_mspt_iph.cpp @@ -0,0 +1,2297 @@ +/* +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" + +// for adjustment factors +#include "common.h" + +// solarpilot header files +#include "AutoPilot_API.h" +#include "SolarField.h" +#include "IOUtil.h" +#include "csp_common.h" + +// Can probably delete these headers later... +#include "csp_solver_util.h" +#include "csp_solver_core.h" +#include "csp_solver_pt_sf_perf_interp.h" +#include "csp_solver_mspt_receiver_222.h" +#include "csp_solver_mspt_receiver.h" +#include "csp_solver_mspt_collector_receiver.h" +#include "csp_solver_pc_heat_sink.h" +#include "csp_solver_two_tank_tes.h" +#include "csp_solver_tou_block_schedules.h" + +#include "csp_dispatch.h" + +#include "csp_solver_cr_electric_resistance.h" +#include "csp_solver_cavity_receiver.h" + +#include "csp_system_costs.h" + +#include + +static var_info _cm_vtab_mspt_iph[] = { + + // VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS + { SSC_INPUT, SSC_STRING, "solar_resource_file", "Local weather file path", "", "", "Solar Resource", "?", "LOCAL_FILE", ""}, + { SSC_INPUT, SSC_TABLE, "solar_resource_data", "Weather resource data in memory", "", "", "Solar Resource", "?", "", "SIMULATION_PARAMETER"}, + + // Simulation parameters + { SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", "", "", "System Control", "?=0", "", ""}, + { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", ""}, + { SSC_INPUT, SSC_NUMBER, "time_start", "Simulation start time", "s", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "time_stop", "Simulation stop time", "s", "", "System Control", "?=31536000", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "time_steps_per_hour", "Number of simulation time steps per hour", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "vacuum_arrays", "Allocate arrays for only the required number of steps", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, + + // System Design + { SSC_INPUT, SSC_NUMBER, "is_parallel_htr", "Does plant include a HTF heater parallel to solar field?", "", "", "System Control", "?=0", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_htf_cold_des", "Cold HTF inlet temperature at design conditions", "C", "", "System Design", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_htf_hot_des", "Hot HTF outlet temperature at design conditions", "C", "", "System Design", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "q_pb_design", "Design heat input to power block", "MWt", "", "System Design", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tshours", "Equivalent full-load thermal storage hours", "hr", "", "System Design", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "solarm", "Solar multiple", "-", "", "System Design", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "dni_des", "Design-point DNI", "W/m2", "", "System Design", "*", "", ""}, + + // Solar field + { SSC_INPUT, SSC_NUMBER, "field_model_type", "0=design field and tower/receiver geometry, 1=design field, 2=user specified field, 3=user flux and eta map, pass heliostat_positions to SolarPILOT for layout, 4=user flux and eta maps, no SolarPILOT, input A_sf_in, total_land_area_before_rad_cooling_in, and N_hel", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "helio_width", "Heliostat width", "m", "", "Heliostat Field", "field_model_type<4", "", ""}, + { SSC_INPUT, SSC_NUMBER, "helio_height", "Heliostat height", "m", "", "Heliostat Field", "field_model_type<4", "", ""}, + { SSC_INPUT, SSC_NUMBER, "helio_optical_error_mrad", "Heliostat optical error", "mrad", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "helio_active_fraction", "Heliostat active fraction", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "dens_mirror", "Ratio of heliostat reflective area to profile", "", "", "Heliostat Field", "field_model_type<4", "", ""}, + { SSC_INPUT, SSC_NUMBER, "helio_reflectance", "Heliostat reflectance", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_absorptance", "Receiver absorptance", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_hl_perm2", "Receiver design heatloss", "kW/m2", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "land_max", "Land max boundary", "-ORm", "", "Heliostat Field", "?=7.5", "", ""}, + { SSC_INPUT, SSC_NUMBER, "land_min", "Land min boundary", "-ORm", "", "Heliostat Field", "?=0.75", "", ""}, + { SSC_INPUT, SSC_MATRIX, "land_bound_table", "Land boundary table", "m", "", "Heliostat Field", "?", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_ARRAY, "land_bound_list", "Land boundary table listing", "", "", "Heliostat Field", "?", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "p_start", "Heliostat startup energy", "kWe-hr", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "p_track", "Heliostat tracking energy", "kWe", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "hel_stow_deploy", "Stow/deploy elevation angle", "deg", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "v_wind_max", "Heliostat max wind velocity", "m/s", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "interp_nug", "Interpolation nugget", "-", "", "Heliostat Field", "?=0", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "interp_beta", "Interpolation beta coef.", "-", "", "Heliostat Field", "?=1.99", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_MATRIX, "helio_aim_points", "Heliostat aim point table", "m", "", "Heliostat Field", "?", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_MATRIX, "eta_map", "Field efficiency array", "", "", "Heliostat Field", "field_model_type>2", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "eta_map_aod_format", "Use 3D AOD format field efficiency array", "", "heliostat", "Heliostat Field", "field_model_type>2", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_MATRIX, "flux_maps", "Flux map intensities", "", "", "Heliostat Field", "field_model_type>2", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "c_atm_0", "Attenuation coefficient 0", "", "", "Heliostat Field", "?=0.006789", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_atm_1", "Attenuation coefficient 1", "", "", "Heliostat Field", "?=0.1046", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_atm_2", "Attenuation coefficient 2", "", "", "Heliostat Field", "?=-0.0107", "", ""}, + { SSC_INPUT, SSC_NUMBER, "c_atm_3", "Attenuation coefficient 3", "", "", "Heliostat Field", "?=0.002845", "", ""}, + { SSC_INPUT, SSC_NUMBER, "n_facet_x", "Number of heliostat facets - X", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "n_facet_y", "Number of heliostat facets - Y", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "focus_type", "Heliostat focus method", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "cant_type", "Heliostat canting method", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "n_flux_days", "Number of days in flux map lookup", "", "", "Tower and Receiver", "?=8", "", ""}, + { SSC_INPUT, SSC_NUMBER, "delta_flux_hrs", "Hourly frequency in flux map lookup", "", "", "Tower and Receiver", "?=1", "", ""}, + { SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "none", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "check_max_flux", "Check max flux at design point", "", "", "Heliostat Field", "?=0", "", ""}, + { SSC_INPUT, SSC_NUMBER, "sf_excess", "Heliostat field multiple", "", "", "System Design", "?=1.0", "", ""}, + + // Inputs required for user defined SF performance when field_model_type = 4 + // Values can be defined by mapping to equivalent _calc output for simulation results with field_model_type < 3 + { SSC_INPUT, SSC_NUMBER, "A_sf_in", "Solar field area", "m^2", "", "Heliostat Field", "field_model_type>3", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "total_land_area_before_rad_cooling_in", "Total land area not including radiative cooling - in", "acre", "", "Heliostat Field", "field_model_type>3", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "N_hel", "Number of heliostats - in", "", "", "Heliostat Field", "field_model_type>3", "", "SIMULATION_PARAMETER"}, + + { SSC_INPUT, SSC_NUMBER, "flux_max", "Maximum allowable flux", "", "", "Tower and Receiver", "?=1000", "", ""}, + { SSC_INPUT, SSC_NUMBER, "opt_init_step", "Optimization initial step size", "", "", "Heliostat Field", "?=0.05", "", ""}, + { SSC_INPUT, SSC_NUMBER, "opt_max_iter", "Max number iteration steps", "", "", "Heliostat Field", "?=200", "", ""}, + { SSC_INPUT, SSC_NUMBER, "opt_conv_tol", "Optimization convergence tolerance", "", "", "Heliostat Field", "?=0.001", "", ""}, + { SSC_INPUT, SSC_NUMBER, "opt_flux_penalty", "Optimization flux overage penalty", "", "", "Heliostat Field", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "opt_algorithm", "Optimization algorithm", "", "", "Heliostat Field", "?=1", "", ""}, + + + // Receiver (type 222) parameters + { SSC_INPUT, SSC_NUMBER, "receiver_type", "0: external (default), 1; cavity", "", "", "Heliostat Field", "?=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "N_panels", "Number of individual panels on the receiver", "", "", "Tower and Receiver", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "d_tube_out", "The outer diameter of an individual receiver tube", "mm", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "th_tube", "The wall thickness of a single receiver tube", "mm", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "mat_tube", "Receiver tube material, 2=Stainless AISI316", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_htf", "Receiver HTF, 17=Salt (60% NaNO3, 40% KNO3) 10=Salt (46.5% LiF 11.5% NaF 42% KF) 50=Lookup tables", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "field_fl_props", "User defined field fluid property data", "-", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "Flow_type", "Receiver flow pattern: see figure on SAM Receiver page", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "crossover_shift", "Number of panels shift in receiver crossover position", "", "", "Tower and Receiver", "?=0", "", "SIMULATION_PARAMETER"}, + { SSC_INPUT, SSC_NUMBER, "epsilon", "The emissivity of the receiver surface coating", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "hl_ffact", "The heat loss factor (thermal loss fudge factor)", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "f_rec_min", "Minimum receiver mass flow rate turn down fraction", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_su_delay", "Fixed startup delay time for the receiver", "hr", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_qf_delay", "Energy-based receiver startup delay (fraction of rated thermal power)", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "csp.pt.rec.max_oper_frac", "Maximum receiver mass flow rate fraction", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "eta_pump", "Receiver HTF pump efficiency", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "piping_length_mult", "Piping length multiplier", "", "", "Tower and Receiver", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "piping_length_const", "Piping constant length", "m", "", "Tower and Receiver", "*", "", ""}, + + // Cavity inputs that should *not* be reset during call to this cmod +{ SSC_INPUT, SSC_NUMBER, "n_cav_rec_panels", "Cavity receiver number of panels", "", "", "Tower and Receiver", "receiver_type=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cav_rec_span", "Cavity receiver span angle", "deg", "", "Tower and Receiver", "receiver_type=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cav_rec_passive_abs", "Cavity receiver passive surface solar absorptance", "", "", "Tower and Receiver", "receiver_type=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cav_rec_passive_eps", "Cavity receiver passive surface thermal emissivity", "", "", "Tower and Receiver", "receiver_type=1", "", "" }, + + +// New variables replacing deprecated variable "piping_loss". Variable currently not required so exec() can check if assigned and throw a more detailed error +{ SSC_INPUT, SSC_NUMBER, "piping_loss_coefficient", "Thermal loss per meter of piping", "Wt/m2-K", "", "Tower and Receiver", "", "", ""}, + +{ SSC_INPUT, SSC_NUMBER, "rec_clearsky_model", "Clearsky model: None = -1, User-defined data = 0, Meinel = 1; Hottel = 2; Allen = 3; Moon = 4", "", "", "Tower and Receiver", "?=-1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_ARRAY, "rec_clearsky_dni", "User-defined clear-sky DNI", "W/m2", "", "Tower and Receiver", "rec_clearsky_model=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "rec_clearsky_fraction", "Weighting fraction on clear-sky DNI for receiver flow control", "", "", "Tower and Receiver", "?=0.0", "", "SIMULATION_PARAMETER"}, + +// Transient receiver parameters +{ SSC_INPUT, SSC_NUMBER, "is_rec_model_trans", "Formulate receiver model as transient?", "", "", "Tower and Receiver", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "is_rec_startup_trans", "Formulate receiver startup model as transient?", "", "", "Tower and Receiver", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "rec_tm_mult", "Receiver thermal mass multiplier", "", "", "Tower and Receiver", "?=1.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "riser_tm_mult", "Riser thermal mass multiplier", "", "", "Tower and Receiver", "?=1.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "downc_tm_mult", "Downcomer thermal mass multiplier", "", "", "Tower and Receiver", "?=1.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "u_riser", "Design point HTF velocity in riser", "m/s", "", "Tower and Receiver", "?=4.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "th_riser", "Riser or downcomer tube wall thickness", "mm", "", "Tower and Receiver", "?=15.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "heat_trace_power", "Riser/downcomer heat trace power during startup", "kW/m", "", "Tower and Receiver", "?=500.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "preheat_flux", "Tube absorbed solar flux during preheat", "kW/m2", "", "Tower and Receiver", "?=50.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "min_preheat_time", "Minimum time required in preheat startup stage", "hr", "", "Tower and Receiver", "?=0.0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "min_fill_time", "Startup time delay for filling the receiver/piping", "hr", "", "Tower and Receiver", "?=0.1333", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "startup_ramp_time", "Time required to reach full flux during receiver startup", "hr", "", "Tower and Receiver", "?=0.1333", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "startup_target_Tdiff", "Target HTF T at end of startup - steady state hot HTF temperature", "C", "", "Tower and Receiver", "?=-5.0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "is_rec_startup_from_T_soln", "Begin receiver startup from solved temperature profiles?", "", "", "Tower and Receiver", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "is_rec_enforce_min_startup", "Always enforce minimum startup time", "", "", "Tower and Receiver", "?=1", "", ""}, + + +// Field layout and tower/receiver dimensions +// If field_model_type = 1, tower/receiver dimensions are used as guess values +// and optimized values are reported as _calc outputs +{ SSC_INPUT, SSC_MATRIX, "helio_positions", "Heliostat position table - in", "", "", "Heliostat Field", "field_model_type=2|field_model_type=3", "", "COL_LABEL=XY_POSITION" }, +{ SSC_INPUT, SSC_NUMBER, "rec_height", "Receiver height - in", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "D_rec", "The overall outer diameter of the receiver - in", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "h_tower", "Tower height - in", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cav_rec_height", "Cavity receiver height - in", "m", "", "Tower and Receiver", "receiver_type=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cav_rec_width", "Cavity receiver aperture width - in", "m", "", "Tower and Receiver", "receiver_type=1", "", "" }, + +// Parallel heater parameters +{ SSC_INPUT, SSC_NUMBER, "heater_mult", "Heater multiple relative to design cycle thermal power", "-", "", "Parallel Heater", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "heater_efficiency", "Heater electric to thermal efficiency", "%", "", "Parallel Heater", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "f_q_dot_des_allowable_su", "Fraction of design power allowed during startup", "-", "", "Parallel Heater", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "hrs_startup_at_max_rate", "Duration of startup at max startup power", "hr", "", "Parallel Heater", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "f_q_dot_heater_min", "Minimum allowable heater output as fraction of design", "", "", "Parallel Heater", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "disp_hsu_cost_rel", "Heater startup cost", "$/MWt/start", "", "System Control", "is_dispatch=1&is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "heater_spec_cost", "Heater specific cost", "$/kWht", "", "System Costs", "is_parallel_htr=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "allow_heater_no_dispatch_opt", "Allow heater with no dispatch optimization? SAM UI relies on cmod default", "", "", "System Costs", "?=0", "", "SIMULATION_PARAMETER" }, + +// TES parameters - general +{ SSC_INPUT, SSC_NUMBER, "tes_init_hot_htf_percent", "Initial fraction of available volume that is hot", "%", "", "Thermal Storage", "", /*not required because replacing deprecated var and checked in cmod*/ "", ""}, +{ SSC_INPUT, SSC_NUMBER, "h_tank", "Total height of tank (height of HTF when tank is full)", "m", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MW", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "u_tank", "Loss coefficient from the tank", "W/m2-K", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Number of equivalent tank pairs", "", "", "Thermal Storage", "*", "INTEGER", ""}, +{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Minimum allowable cold tank HTF temperature", "C", "", "Thermal Storage", "*", "", ""}, +// TES parameters - 2 tank +{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Minimum allowable HTF height in storage tank", "m", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Minimum allowable hot tank HTF temperature", "C", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MW", "", "Thermal Storage", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "Thermal Storage", "*", "", "" }, + +// Heat Sink +{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Heat Sink", "*", "", "" }, + +// Aux and Balance of Plant +{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Fixed parasitic load - runs at all times", "MWe/MWcap", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "aux_par", "Aux heater, boiler parasitic", "MWe/MWcap", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "aux_par_f", "Aux heater, boiler parasitic - multiplying fraction", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "aux_par_0", "Aux heater, boiler parasitic - constant coefficient", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "aux_par_1", "Aux heater, boiler parasitic - linear coefficient", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "aux_par_2", "Aux heater, boiler parasitic - quadratic coefficient", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_par", "Balance of plant parasitic power fraction", "MWe/MWcap", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_par_f", "Balance of plant parasitic power fraction - mult frac", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_par_0", "Balance of plant parasitic power fraction - const coeff", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_par_1", "Balance of plant parasitic power fraction - linear coeff", "", "", "System Control", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_par_2", "Balance of plant parasitic power fraction - quadratic coeff", "", "", "System Control", "*", "", "" }, + +// System Control +{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "System Control", "?", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "", "", "System Control", "*", "", ""}, +{ SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 CSP operation Time-of-Use Weekday schedule", "", "", "System Control", "*", "", ""}, +{ SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 CSP operation Time-of-Use Weekend schedule", "", "", "System Control", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "is_tod_pc_target_also_pc_max", "Is the TOD target cycle heat input also the max cycle heat input?", "", "", "System Control", "?=0", "", ""}, + +// Receiver control +{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "System Control", "?=9e99", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER"}, + +// Costs +{ SSC_INPUT, SSC_NUMBER, "tower_fixed_cost", "Tower fixed cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "tower_exp", "Tower cost scaling exponent", "", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "rec_ref_cost", "Receiver reference cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "rec_ref_area", "Receiver reference area for cost scale", "", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "rec_cost_exp", "Receiver cost scaling exponent", "", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "site_spec_cost", "Site improvement cost", "$/m2", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "heliostat_spec_cost", "Heliostat field cost", "$/m2", "", "System Costs", "*", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "plant_spec_cost", "Power cycle specific cost", "$/kWe", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "bop_spec_cost", "BOS specific cost", "$/kWe", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "tes_spec_cost", "Thermal energy storage cost", "$/kWht", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "land_spec_cost", "Total land area cost", "$/acre", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "contingency_rate", "Contingency for cost overrun", "%", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "sales_tax_rate", "Sales tax rate", "%", "", "Financial Parameters", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "sales_tax_frac", "Percent of cost to which sales tax applies", "%", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "cost_sf_fixed", "Solar field fixed cost", "$", "", "System Costs", "*", "", "" }, +//{ SSC_INPUT, SSC_NUMBER, "fossil_spec_cost", "Fossil system specific cost", "$/kWe", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.epc.per_acre", "EPC cost per acre", "$/acre", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.epc.percent", "EPC cost percent of direct", "%", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.epc.per_watt", "EPC cost per watt", "$/W", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.epc.fixed", "EPC fixed", "$", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.plm.percent", "PLM cost percent of direct", "%", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.plm.per_watt", "PLM cost per watt", "$/W", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.cost.plm.fixed", "PLM fixed", "$", "", "System Costs", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.sf.fixed_land_area", "Fixed land area", "acre", "", "Heliostat Field", "*", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.sf.land_overhead_factor", "Land overhead factor", "", "", "Heliostat Field", "*", "", "" }, + +// Construction financing inputs/outputs (SSC variable table from cmod_cb_construction_financing) +{ SSC_INPUT, SSC_NUMBER, "const_per_interest_rate1", "Interest rate, loan 1", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_interest_rate2", "Interest rate, loan 2", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_interest_rate3", "Interest rate, loan 3", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_interest_rate4", "Interest rate, loan 4", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_interest_rate5", "Interest rate, loan 5", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_months1", "Months prior to operation, loan 1", "", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_months2", "Months prior to operation, loan 2", "", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_months3", "Months prior to operation, loan 3", "", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_months4", "Months prior to operation, loan 4", "", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_months5", "Months prior to operation, loan 5", "", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_percent1", "Percent of total installed cost, loan 1", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_percent2", "Percent of total installed cost, loan 2", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_percent3", "Percent of total installed cost, loan 3", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_percent4", "Percent of total installed cost, loan 4", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_percent5", "Percent of total installed cost, loan 5", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_upfront_rate1", "Upfront fee on principal, loan 1", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_upfront_rate2", "Upfront fee on principal, loan 2", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_upfront_rate3", "Upfront fee on principal, loan 3", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_upfront_rate4", "Upfront fee on principal, loan 4", "%", "", "Financial Parameters", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "const_per_upfront_rate5", "Upfront fee on principal, loan 5", "%", "", "Financial Parameters", "*", "", ""}, + +// **************************************************************************************************************************************** +// DEPRECATED INPUTS -- exec() checks if a) variable is assigned and b) if replacement variable is assigned. throws exception if a=true and b=false +// **************************************************************************************************************************************** +{ SSC_INPUT, SSC_NUMBER, "piping_loss", "Thermal loss per meter of piping", "Wt/m", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost", "Cycle startup cost", "$", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost", "Receiver startup cost", "$", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "disp_pen_delta_w", "Dispatch cycle production change penalty", "$/kWe-change", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "P_boil", "Boiler operating pressure", "bar", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_NUMBER, "csp.pt.tes.init_hot_htf_percent", "Initial fraction of available volume that is hot", "%", "", "Deprecated", "", "", "SIMULATION_PARAMETER" }, + +// **************************************************************************************************************************************** +// Design Outputs here: +// **************************************************************************************************************************************** + + // land area with variable name required by downstream financial model +{ SSC_OUTPUT, SSC_NUMBER, "total_land_area", "Total land area", "acre", "", "System Costs", "*", "", "" }, +// System capacity required by downstream financial model +{ SSC_OUTPUT, SSC_NUMBER, "system_capacity", "System capacity", "kWt", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cp_system_nameplate", "System capacity for capacity payments", "MWt", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cp_battery_nameplate", "Battery nameplate", "MWe", "", "System Costs", "*", "", "" }, + +// Solar Field +{ SSC_OUTPUT, SSC_NUMBER, "N_hel_calc", "Number of heliostats - out", "", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "refl_image_error", "Reflected image error", "mrad", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "heliostat_area", "Active area of heliostat", "m^2", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "average_attenuation", "Average solar field attenuation", "%", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_MATRIX, "helio_positions_calc", "Heliostat position table - out", "", "", "Heliostat Field", "*", "", "COL_LABEL=XY_POSITION" }, +{ SSC_OUTPUT, SSC_NUMBER, "A_sf", "Solar field area", "m^2", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "land_min_abs", "Min distance from tower to heliostat", "m", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "land_max_abs", "Max distance from tower to heliostat", "m", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "land_area_base_calc", "Land area occupied by heliostats", "acre", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "total_land_area_before_rad_cooling_calc", "Total land area not including radiative cooling - out", "acre", "", "Heliostat Field", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_col_tracking_des", "Collector tracking power at design", "MWe", "", "Heliostat Field", "*", "", "" }, + +// Receiver Geometry +{ SSC_OUTPUT, SSC_NUMBER, "rec_height_calc", "Receiver height - out", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "D_rec_calc", "The overall outer diameter of the receiver - out", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "h_tower_calc", "Tower height - out", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "ext_rec_area", "External receiver area - out", "m2", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "ext_rec_aspect", "External receiver aspect ratio - out", "", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cav_rec_height_calc", "Cavity receiver height - out", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cav_rec_width_calc", "Cavity receiver aperture width - out", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cav_rec_area", "Cavity receiver area", "m2", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cav_panel_width", "Cavity panel width", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cav_radius", "Cavity radius", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "A_rec", "Receiver area - planar", "m2", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "L_tower_piping_calc", "Tower piping length", "m", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "od_tube_calc", "Receiver tube outer diameter - out", "mm", "", "Tower and Receiver", "*", "", "" }, + +// Receiver Performance +{ SSC_OUTPUT, SSC_NUMBER, "q_dot_rec_des", "Receiver thermal output at design", "MWt", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "eta_rec_thermal_des", "Receiver estimated thermal efficiency at design", "", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_rec_pump_des", "Receiver estimated pump power at design", "MWe", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_rec_pump_tower_share_des", "Receiver estimated pump power due to tower height at design", "MWe", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_rec_pump_rec_share_des", "Receiver estimated pump power due to rec tubes at design", "MWe", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "vel_rec_htf_des", "Receiver estimated tube HTF velocity at design", "m/s", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_rec_des", "Receiver HTF mass flow rate at design", "kg/s", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_rec_max", "Receiver max HTF mass flow rate", "kg/s", "", "Tower and Receiver", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "q_dot_piping_loss_des", "Receiver estimated piping loss at design", "MWt", "", "Tower and Receiver", "*", "", "" }, + +// Heater +{ SSC_OUTPUT, SSC_NUMBER, "q_dot_heater_des", "Heater design thermal power", "MWt", "", "Heater", "*", "", "" }, +{ 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", "MWt-hr", "", "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", "*", "", "" }, + +// TES +{ SSC_OUTPUT, SSC_NUMBER, "Q_tes_des", "TES design capacity", "MWt-hr", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "V_tes_htf_avail_des", "TES volume of HTF available for heat transfer", "m3", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "V_tes_htf_total_des", "TES total HTF volume", "m3", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "d_tank_tes", "TES tank diameter", "m", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "q_dot_loss_tes_des", "TES thermal loss at design", "MWt", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "tshours_rec", "TES duration at receiver design output", "hr", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "tshours_heater", "TES duration at heater design output", "hr", "", "TES Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "dens_store_htf_at_T_ave", "TES density of HTF at avg temps", "kg/m3", "", "TES Design Calc", "*", "", "" }, + +// Balance of Plant +{ SSC_OUTPUT, SSC_NUMBER, "nameplate", "Nameplate capacity", "MWt", "", "System Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_bop_design", "BOP parasitics at design", "MWe", "", "Balance of Plant", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_fixed", "Fixed parasitic at design", "MWe", "", "Balance of Plant", "*", "", "" }, + +// Costs +{ SSC_OUTPUT, SSC_NUMBER, "h_rec_input_to_cost_model", "Receiver height for cost model selected from receiver type", "m", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.site_improvements", "Site improvement cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.heliostats", "Heliostat cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.tower", "Tower cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.receiver", "Receiver cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.storage", "TES cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.power_block", "Power cycle cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "heater_cost", "Heater cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.bop", "BOP cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.fossil", "Fossil backup cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "ui_direct_subtotal", "Direct capital precontingency cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.contingency", "Contingency cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "total_direct_cost", "Total direct cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.epc.total", "EPC and owner cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.plm.total", "Total land cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.sales_tax.total", "Sales tax cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "total_indirect_cost", "Total indirect cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "total_installed_cost", "Total installed cost", "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.installed_per_capacity", "Estimated installed cost per cap", "$", "", "System Costs", "*", "", "" }, + +// Financing +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal1", "Principal, loan 1", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal2", "Principal, loan 2", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal3", "Principal, loan 3", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal4", "Principal, loan 4", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal5", "Principal, loan 5", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest1", "Interest cost, loan 1", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest2", "Interest cost, loan 2", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest3", "Interest cost, loan 3", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest4", "Interest cost, loan 4", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest5", "Interest cost, loan 5", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_total1", "Total financing cost, loan 1", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_total2", "Total financing cost, loan 2", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_total3", "Total financing cost, loan 3", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_total4", "Total financing cost, loan 4", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_total5", "Total financing cost, loan 5", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_percent_total", "Total percent of installed costs, all loans", "%", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_principal_total", "Total principal, all loans", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "const_per_interest_total", "Total interest costs, all loans", "$", "", "Financial Parameters", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "construction_financing_cost", "Total construction financing cost", "$", "", "Financial Parameters", "*", "", "" }, + + + +// **************************************************************************************************************************************** +// Timeseries Simulation Outputs here: +// **************************************************************************************************************************************** + // Simulation outputs +{ SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "solzen", "Resource solar zenith", "deg", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "solaz", "Resource solar azimuth", "deg", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "beam", "Resource beam normal irradiance", "W/m2", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "tdry", "Resource dry Bulb temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "twet", "Resource wet Bulb temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "rh", "Resource relative humidity", "%", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "wspd", "Resource wind velocity", "m/s", "", "", "sim_type=1", "", ""}, + +// Collector-receiver outputs +{ SSC_OUTPUT, SSC_MATRIX, "eta_map_out", "Solar field optical efficiencies", "", "", "", "sim_type=1", "", "COL_LABEL=OPTICAL_EFFICIENCY,ROW_LABEL=NO_ROW_LABEL"}, +{ SSC_OUTPUT, SSC_MATRIX, "flux_maps_for_import", "Flux map for import", "", "", "", "sim_type=1", "", "COL_LABEL=FLUX_MAPS,ROW_LABEL=NO_ROW_LABEL" }, +{ SSC_OUTPUT, SSC_MATRIX, "flux_maps_out", "Flux map intensities", "", "", "", "sim_type=1", "", "COL_LABEL=FLUX_MAPS,ROW_LABEL=NO_ROW_LABEL"}, + +{ SSC_OUTPUT, SSC_ARRAY, "q_sf_inc", "Field incident thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "eta_field", "Field optical efficiency", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "sf_adjust_out", "Field availability adjustment factor", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "rec_defocus", "Receiver component defocus", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_inc", "Receiver incident thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "eta_therm", "Receiver thermal efficiency", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "Q_thermal", "Receiver thermal power to HTF less piping loss", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "pparasi", "Field tracking power", "MWe", "", "", "sim_type=1", "", "" }, + +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_rec", "Receiver mass flow rate", "kg/s", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_startup", "Receiver startup thermal energy consumed", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_rec_in", "Receiver HTF inlet temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_rec_out", "Receiver HTF outlet temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_piping_losses", "Receiver header/tower piping losses", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_thermal_loss", "Receiver convection and emission losses", "MWt", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_reflection_loss", "Receiver reflection losses", "MWt", "", "", "sim_type=1&receiver_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_tower_pump", "Receiver and tower HTF pumping power", "MWe", "", "", "sim_type=1", "", "" }, + +{ SSC_OUTPUT, SSC_ARRAY, "T_rec_out_end", "Receiver HTF outlet temperature at end of timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_rec_out_max", "Receiver maximum HTF outlet temperature during timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_panel_out_max", "Receiver panel maximum HTF outlet temperature during timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_wall_rec_inlet", "Receiver inlet panel wall temperature at end of timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_wall_rec_outlet", "Receiver outlet panel wall temperature at end of timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_wall_riser", "Receiver riser wall temperature at end of timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_wall_downcomer", "Receiver downcomer wall temperature at end of timestep", "C", "", "CR", "sim_type=1&is_rec_model_trans=1", "", ""}, + +{ SSC_OUTPUT, SSC_ARRAY, "clearsky", "Predicted clear-sky beam normal irradiance", "W/m2", "", "CR", "sim_type=1&rec_clearsky_fraction>0", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "Q_thermal_ss", "Receiver thermal power to HTF less piping loss (steady state)", "MWt", "", "CR", "sim_type=1&is_rec_model_trans=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "Q_thermal_ss_csky", "Receiver thermal power to HTF less piping loss under clear-sky conditions (steady state)", "MWt", "", "CR", "sim_type=1&rec_clearsky_fraction>0", "", "" }, + +// Heater outputs is_parallel_htr +{ SSC_OUTPUT, SSC_ARRAY, "W_dot_heater", "Parallel heater electricity consumption", "MWe", "", "Parallel Heater", "sim_type=1&is_parallel_htr=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_heater_to_htf", "Parallel heater thermal power to HTF", "MWt", "", "Parallel Heater", "sim_type=1&is_parallel_htr=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_heater_startup", "Parallel heater thermal power consumed during startup", "MWt", "", "Parallel Heater", "sim_type=1&is_parallel_htr=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_htf_heater", "Parallel heater HTF mass flow rate", "kg/s", "", "Parallel Heater", "sim_type=1&is_parallel_htr=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "T_htf_heater_in", "Parallel heater HTF inlet temperature", "C", "", "Parallel Heater", "sim_type=1&is_parallel_htr=1", "", "" }, +{ 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", "", "" }, + +// Thermal energy storage outputs +{ SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_tes_heater", "TES freeze protection power", "MWt", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "T_tes_hot", "TES hot temperature", "C", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "T_tes_cold", "TES cold temperature", "C", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "mass_tes_cold", "TES cold tank mass (end)", "kg", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "mass_tes_hot", "TES hot tank mass (end)", "kg", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_dc_tes", "TES discharge thermal power", "MWt", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "q_ch_tes", "TES charge thermal power", "MWt", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "e_ch_tes", "TES charge state", "MWht", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_cr_to_tes_hot", "Mass flow: field to hot TES", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_hot_out", "Mass flow: TES hot out", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_pc_to_tes_cold", "Mass flow: cycle to cold TES", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_cold_out", "Mass flow: TES cold out", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_field_to_cycle", "Mass flow: field to cycle", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_cycle_to_field", "Mass flow: cycle to field", "kg/s", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "tes_htf_pump_power", "TES HTF pump power", "MWe", "", "", "sim_type=1", "", "" }, + + +// Parasitics outputs +{ SSC_OUTPUT, SSC_ARRAY, "P_fixed", "Parasitic power fixed load", "MWe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_plant_balance_tot", "Parasitic power generation-dependent load", "MWe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_rec_heattrace", "Receiver heat trace parasitic load", "MWe", "", "System", "sim_type=1&is_rec_model_trans=1", "", ""}, + +// System outputs +{ SSC_OUTPUT, SSC_ARRAY, "W_dot_parasitic_tot", "System total electrical parasitic", "MWe", "", "", "sim_type=1", "", ""}, + +// Controller outputs +{ SSC_OUTPUT, SSC_ARRAY, "tou_value", "CSP operating time-of-use value", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "pricing_mult", "PPA price multiplier", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "n_op_modes", "Operating modes in reporting timestep", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "op_mode_1", "1st operating mode", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "op_mode_2", "2nd operating mode, if applicable", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "op_mode_3", "3rd operating mode, if applicable", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_balance", "Relative mass flow balance error", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_balance", "Relative energy balance error", "", "", "", "sim_type=1", "", ""}, + +// Dispatch outputs +{ SSC_OUTPUT, SSC_ARRAY, "disp_rel_mip_gap", "Dispatch relative MIP gap", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_solve_state", "Dispatch solver state", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_subopt_flag", "Dispatch suboptimal solution flag", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_solve_iter", "Dispatch iterations count", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_objective", "Dispatch objective function value", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_obj_relax", "Dispatch objective function - relaxed max", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_qsf_expected", "Dispatch expected solar field available energy", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_qsfprod_expected", "Dispatch expected solar field generation", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_qsfsu_expected", "Dispatch expected solar field startup enegy", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_tes_expected", "Dispatch expected TES charge level", "MWht", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_pceff_expected", "Dispatch expected power cycle efficiency adj.", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_thermeff_expected", "Dispatch expected SF thermal efficiency adj.", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_qpbsu_expected", "Dispatch expected power cycle startup energy", "MWht", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_wpb_expected", "Dispatch expected power generation", "MWe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_rev_expected", "Dispatch expected revenue factor", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nconstr", "Dispatch number of constraints in problem", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nvar", "Dispatch number of variables in problem", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "disp_solve_time", "Dispatch solver time", "sec", "", "", "sim_type=1", "", ""}, + + +// These outputs correspond to the first csp-solver timestep in the reporting timestep. +// Subsequent csp-solver timesteps within the same reporting timestep are not tracked +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_sb", "Thermal power for PC standby", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_min", "Thermal power for PC min operation", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_max", "Max thermal power to PC", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_target", "Target thermal power to PC", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "is_rec_su_allowed", "Is receiver startup allowed", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "is_pc_su_allowed", "Is power cycle startup allowed", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "is_pc_sb_allowed", "Is power cycle standby allowed", "", "", "", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_ARRAY, "is_PAR_HTR_allowed", "Is parallel electric heater operation allowed", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_elec_to_PAR_HTR", "Electric heater thermal power target", "MWt", "", "", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_su", "Estimated receiver startup thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_on", "Estimated receiver thermal power TO HTF", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_dc", "Estimated max TES discharge thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_ch", "Estimated max TES charge thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "operating_modes_a", "First 3 operating modes tried", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "operating_modes_b", "Next 3 operating modes tried", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "operating_modes_c", "Final 3 operating modes tried", "", "", "", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_ARRAY, "gen", "Total thermal power to heat sink with available derate", "kWt", "", "", "sim_type=1", "", ""}, + +// Annual single-value outputs +{ SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual Thermal Energy to Heat Sink w/ avail derate", "kWt-hr", "", "Post-process", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_rec_htf", "Annual receiver power delivered to HTF", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", ""}, + + +{ SSC_OUTPUT, SSC_NUMBER, "annual_electricity_consumption", "Annual electricity consumption w/ avail derate", "kWe-hr", "", "Post-process", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "Post-process", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWth/kWt", "", "Post-process", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total annual water usage from mirror washing", "m3", "", "Post-process", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_rec_inc", "Annual receiver incident thermal power after reflective losses", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_rec_loss", "Annual receiver convective and radiative losses", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_piping_loss", "Annual tower piping losses", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_rec_startup", "Annual receiver startup energy", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "annual_E_tower_pump", "Annual tower pumping power", "MWe-hr", "", "Tower and Receiver", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "annual_eta_rec_th", "Annual receiver thermal efficiency ignoring rec reflective loss", "", "", "Tower and Receiver", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_eta_rec_th_incl_refl", "Annual receiver thermal efficiency including reflective loss", "", "", "Tower and Receiver", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_q_defocus_est", "Annual defocus loss estimate", "MWt-hr", "", "Tower and Receiver", "sim_type=1", "", "" }, + +{ SSC_OUTPUT, SSC_NUMBER, "sim_cpu_run_time", "Simulation duration clock time", "s", "", "", "sim_type=1", "", ""}, + +var_info_invalid }; + +class cm_mspt_iph : public compute_module +{ +public: + + cm_mspt_iph() + { + add_var_info(_cm_vtab_mspt_iph); + add_var_info(vtab_adjustment_factors); + add_var_info(vtab_sf_adjustment_factors); + add_var_info(vtab_technology_outputs); + } + + bool relay_message(string& msg, double percent) + { + log(msg); + return update(msg, (float)percent); + } + + void exec() override + { + std::clock_t clock_start = std::clock(); + + int sim_type = as_integer("sim_type"); // 1 (default): timeseries, 2: design only + + bool is_dispatch = as_boolean("is_dispatch"); + + // ***************************************************** + // ***************************************************** + + + // ***************************************************** + // System Design Parameters + double T_htf_cold_des = as_double("T_htf_cold_des"); //[C] + double T_htf_hot_des = as_double("T_htf_hot_des"); //[C] + double tshours = as_double("tshours"); //[-] + + // System Design Calcs + double q_dot_pc_des = as_double("q_pb_design"); //[MWt] HEAT SINK design thermal power + double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + double q_dot_rec_des = q_dot_pc_des * as_number("solarm"); //[MWt] + + // Weather reader + C_csp_weatherreader weather_reader; + if (is_assigned("solar_resource_file")) { + weather_reader.m_weather_data_provider = make_shared(as_string("solar_resource_file")); + if (weather_reader.m_weather_data_provider->has_message()) log(weather_reader.m_weather_data_provider->message(), SSC_WARNING); + } + if (is_assigned("solar_resource_data")) { + weather_reader.m_weather_data_provider = make_shared(lookup("solar_resource_data")); + if (weather_reader.m_weather_data_provider->has_message()) log(weather_reader.m_weather_data_provider->message(), SSC_WARNING); + } + + size_t n_steps_full = weather_reader.m_weather_data_provider->nrecords(); //steps_per_hour * 8760; + weather_reader.m_trackmode = 0; + weather_reader.m_tilt = 0.0; + weather_reader.m_azimuth = 0.0; + // Initialize to get weather file info + weather_reader.init(); + if (weather_reader.has_error()) throw exec_error("tcsmolten_salt", weather_reader.get_error()); + + // Get info from the weather reader initialization + double site_elevation = weather_reader.ms_solved_params.m_elev; //[m] + + int tes_type = 1; + + // 'sf_model_type' + // 0 = design field and tower/receiver geometry + // 1 = design field + // 2 = user field, calculate performance + // 3 = user performance maps vs solar position + int field_model_type = as_integer("field_model_type"); + + if (sim_type == 2 && field_model_type < 2) { + field_model_type = 2; + } + + int rec_type = as_integer("receiver_type"); + + // Run solarpilot right away to update values as needed + util::matrix_t mt_eta_map; + util::matrix_t mt_flux_maps; + + int N_hel = -999; + double A_sf = std::numeric_limits::quiet_NaN(); + double THT = std::numeric_limits::quiet_NaN(); + double rec_height = std::numeric_limits::quiet_NaN(); + double cav_rec_height = std::numeric_limits::quiet_NaN(); + double rec_aspect = std::numeric_limits::quiet_NaN(); + double D_rec = std::numeric_limits::quiet_NaN(); + double cav_rec_width = std::numeric_limits::quiet_NaN(); + double land_area_base = std::numeric_limits::quiet_NaN(); + double total_land_area_before_rad_cooling = std::numeric_limits::quiet_NaN(); + double heliostat_area = std::numeric_limits::quiet_NaN(); + double h_helio = std::numeric_limits::quiet_NaN(); //[m] + double average_attenuation = std::numeric_limits::quiet_NaN(); + double refl_image_error = std::numeric_limits::quiet_NaN(); + double land_max_abs = std::numeric_limits::quiet_NaN(); + double land_min_abs = std::numeric_limits::quiet_NaN(); + + util::matrix_t helio_pos; + + assign("is_optimize", 0); + bool is_optimize = false; + int n_flux_y = 1; + + if (field_model_type < 4) { + + h_helio = as_double("helio_height"); //[m] + + // Field types 0-3 require solar pilot + solarpilot_invoke spi(this); + + assign("q_design", q_dot_rec_des); //[MWt] + + // Set up "cmod_solarpilot.cpp" conversions as necessary + double helio_optical_error_mrad = as_number("helio_optical_error_mrad"); //[mrad] + refl_image_error = std::sqrt(2. * helio_optical_error_mrad * 2. * helio_optical_error_mrad * 2.); //[mrad] + assign("helio_optical_error", (ssc_number_t)(helio_optical_error_mrad * 1.E-3)); + + // Set 'n_flux_x' and 'n_flux_y' here, for now + assign("n_flux_y", n_flux_y); + + if (rec_type == 0) { + assign("rec_aspect", as_number("rec_height") / as_number("D_rec")); + int n_ext_rec_panels = as_integer("N_panels"); + assign("n_flux_x", (ssc_number_t)max(12, n_ext_rec_panels)); + } + else if (rec_type == 1) { + assign("n_flux_x", 2); // n_flux_x represents *per panel* the number subsurfaces in x direction + } + else { + throw exec_error("tcsmolten_salt", "receiver_type must be 1 (external) or 0 (cavity)"); + } + + if ((field_model_type == 0 || field_model_type == 1) && sim_type == 1) // Auto-design. Generate a new system (is_optimize = true) or field layout + { + // What if sim_type = 2? + // If from the UI, then probably never want to actually layout the field and geometry + // because there are macros for that + // If called from a script.... then maybe? + // So.. maybe add optional input like "is_layout_field_in_design_only" and default to False? + + if (field_model_type == 0) + { + assign("is_optimize", 1); + is_optimize = true; + } + + assign("calc_fluxmaps", 1); + + spi.run(weather_reader.m_weather_data_provider); + + if (is_optimize) + { + //Optimization iteration history + vector > steps; + vector obj, flux; + spi.getOptimizationSimulationHistory(steps, obj, flux); + size_t nr = steps.size(); + if (nr > 0) + { + size_t nc = steps.front().size() + 2; + ssc_number_t* ssc_hist = allocate("opt_history", nr, nc); + for (size_t i = 0; i < nr; i++) { + + for (size_t j = 0; j < steps.front().size(); j++) + ssc_hist[i * nc + j] = (ssc_number_t)steps.at(i).at(j); + ssc_hist[i * nc + nc - 2] = (ssc_number_t)obj.at(i); + ssc_hist[i * nc + nc - 1] = (ssc_number_t)flux.at(i); + } + } + } + + // receiver calculations + double H_rec = spi.recs.front().rec_height.val; + + //collect the optical efficiency data and sun positions + if (spi.fluxtab.zeniths.size() > 0 && spi.fluxtab.azimuths.size() > 0 + && spi.fluxtab.efficiency.size() > 0) + { + size_t nvals = spi.fluxtab.efficiency.size(); + mt_eta_map.resize(nvals, 3); + + for (size_t i = 0; i < nvals; i++) + { + mt_eta_map(i, 0) = spi.fluxtab.azimuths[i] * 180. / CSP::pi; //Convention is usually S=0, E<0, W>0 + mt_eta_map(i, 1) = spi.fluxtab.zeniths[i] * 180. / CSP::pi; //Provide zenith angle + mt_eta_map(i, 2) = spi.fluxtab.efficiency[i]; + } + } + else + throw exec_error("solarpilot", "failed to calculate a correct optical efficiency table"); + + //collect the flux map data + block_t* flux_data = &spi.fluxtab.flux_surfaces.front().flux_data; //there should be only one flux stack for SAM + if (flux_data->ncols() > 0 && flux_data->nlayers() > 0) + { + if (rec_type == 0) { + + int nflux_y = (int)flux_data->nrows(); + int nflux_x = (int)flux_data->ncols(); + + mt_flux_maps.resize(nflux_y * flux_data->nlayers(), nflux_x); + + int cur_row = 0; + + for (size_t i = 0; i < flux_data->nlayers(); i++) + { + for (int j = 0; j < nflux_y; j++) + { + for (int k = 0; k < nflux_x; k++) + { + mt_flux_maps(cur_row, k) = flux_data->at(j, k, i); + //fluxdata[cur_row * nflux_x + k] = (float)flux_data->at(j, k, i); + } + cur_row++; + } + } + } + else if (rec_type == 1) { + + int nflux_y = (int)flux_data->nrows(); + int nflux_x = (int)flux_data->ncols(); + + int n_panels_cav = as_integer("n_cav_rec_panels"); //[-] + int n_sp_surfaces = spi.fluxtab.flux_surfaces.size(); + int n_panels_cav_sp = n_sp_surfaces - 1; + + if (nflux_y > 1) { + throw exec_error("solarpilot", "cavity flux maps currently only work for nflux_y = 1"); + } + + mt_flux_maps.resize(nflux_y * flux_data->nlayers(), n_panels_cav_sp); + + int cur_row = 0; + + // nlayers is number of solar positions (i.e. flux maps) + for (size_t i = 0; i < flux_data->nlayers(); i++) { + + int j = 0; + + double flux_receiver = 0.0; + + // Start at k=1 because the first surface in flux_surfaces is the aperture, which we don't want + for (int k = 1; k <= n_panels_cav_sp; k++) { + + block_t* flux_data = &spi.fluxtab.flux_surfaces[k].flux_data; //.front().flux_data; //there should be only one flux stack for SAM + + double flux_local = 0.0; + for (int l = 0; l < nflux_x; l++) { + //double flux_local0 = flux_data->at(j, 0, i); + //double flux_local1 = flux_data->at(j, 1, i); + //double flux_local2 = flux_data->at(j, 2, i); + //double flux_local3 = flux_data->at(j, 3, i); + + flux_local += flux_data->at(j, l, i); + } + + // Adjust k to start flux maps with first receiver surface + mt_flux_maps(cur_row, k - 1) = flux_local; + flux_receiver += flux_local; + double abc = 1.23; + } + + cur_row++; + } + } + } + else + throw exec_error("solarpilot", "failed to calculate a correct flux map table"); + } + else if (field_model_type == 2 || field_model_type == 3) + { + // only calculates a flux map, so need to "assign" 'helio_positions_in' for SolarPILOT cmod + util::matrix_t helio_pos_temp = as_matrix("helio_positions"); + size_t n_h_rows = helio_pos_temp.nrows(); + ssc_number_t* p_helio_positions_in = allocate("helio_positions_in", n_h_rows, 2); + + // Try to determine whether heliostat positions represent surround or cavity field + double y_h_min = 1.E5; + double y_h_max = -1.E5; + for (size_t i = 0; i < n_h_rows; i++) + { + p_helio_positions_in[i * 2] = (ssc_number_t)helio_pos_temp(i, 0); //[m] x + p_helio_positions_in[i * 2 + 1] = (ssc_number_t)helio_pos_temp(i, 1); //[m] y + + y_h_min = min(y_h_min, p_helio_positions_in[i * 2 + 1]); + y_h_max = max(y_h_max, p_helio_positions_in[i * 2 + 1]); + } + + bool is_cavity_field = false; + if ((y_h_max - y_h_min) / max(std::abs(y_h_max), std::abs(y_h_min)) < 1.25) { + is_cavity_field = true; + } + + // Check determined field type against user-specified receiver type + if (sim_type == 1) { + if (is_cavity_field && rec_type == 0) { + throw exec_error("mspt compute module", "\nExternal receiver specified, but cavity field detected. Try one of the following options:\n" + "1) Run field layout macro on Heliostat Field page\n" + "2) Select option for simulation to layout field and tower/receiver design\n" + "3) Enter new heliostat positions\n"); + } + + if (!is_cavity_field && rec_type == 1) { + throw exec_error("mspt compute module", "\nCavity receiver specified, but surround field detected. Try one of the following options:\n" + "1) Run field layout macro on Heliostat Field page\n" + "2) Select option for simulation to layout field and tower/receiver design\n" + "3) Enter new heliostat positions\n"); + } + } + + // 'calc_fluxmaps' defaults to false in solarpilot cmod, so overwrite here if we want flux maps + if (sim_type == 1 && field_model_type == 2) { + assign("calc_fluxmaps", 1); + } + else if (sim_type == 2 || field_model_type == 3) { + assign("calc_fluxmaps", 0); + } + + spi.run(weather_reader.m_weather_data_provider); + + if (sim_type == 1 && field_model_type == 2) { + //collect the optical efficiency data and sun positions + if (spi.fluxtab.zeniths.size() > 0 && spi.fluxtab.azimuths.size() > 0 + && spi.fluxtab.efficiency.size() > 0) + { + size_t nvals = spi.fluxtab.efficiency.size(); + mt_eta_map.resize(nvals, 3); + + for (size_t i = 0; i < nvals; i++) + { + mt_eta_map(i, 0) = spi.fluxtab.azimuths[i] * 180. / CSP::pi; //Convention is usually S=0, E<0, W>0 + mt_eta_map(i, 1) = spi.fluxtab.zeniths[i] * 180. / CSP::pi; //Provide zenith angle + mt_eta_map(i, 2) = spi.fluxtab.efficiency[i]; + } + } + else + throw exec_error("solarpilot", "failed to calculate a correct optical efficiency table"); + + //collect the flux map data + block_t* flux_data = &spi.fluxtab.flux_surfaces.front().flux_data; //there should be only one flux stack for SAM + if (flux_data->ncols() > 0 && flux_data->nlayers() > 0) + { + if (rec_type == 0) { + + int nflux_y = (int)flux_data->nrows(); + int nflux_x = (int)flux_data->ncols(); + + mt_flux_maps.resize(nflux_y * flux_data->nlayers(), nflux_x); + + int cur_row = 0; + + for (size_t i = 0; i < flux_data->nlayers(); i++) + { + for (int j = 0; j < nflux_y; j++) + { + for (int k = 0; k < nflux_x; k++) + { + mt_flux_maps(cur_row, k) = flux_data->at(j, k, i); + //fluxdata[cur_row * nflux_x + k] = (float)flux_data->at(j, k, i); + } + cur_row++; + } + } + } + else if (rec_type == 1) { + + int nflux_y = (int)flux_data->nrows(); + int nflux_x = (int)flux_data->ncols(); + + int n_panels_cav = as_integer("n_cav_rec_panels"); //[-] + int n_sp_surfaces = spi.fluxtab.flux_surfaces.size(); + int n_panels_cav_sp = n_sp_surfaces - 1; + + if (nflux_y > 1) { + throw exec_error("solarpilot", "cavity flux maps currently only work for nflux_y = 1"); + } + + mt_flux_maps.resize(nflux_y * flux_data->nlayers(), n_panels_cav_sp); + + int cur_row = 0; + + // nlayers is number of solar positions (i.e. flux maps) + for (size_t i = 0; i < flux_data->nlayers(); i++) { + + int j = 0; + + double flux_receiver = 0.0; + + // Start at k=1 because the first surface in flux_surfaces is the aperture, which we don't want + for (int k = 1; k <= n_panels_cav_sp; k++) { + + block_t* flux_data = &spi.fluxtab.flux_surfaces[k].flux_data; //.front().flux_data; //there should be only one flux stack for SAM + + double flux_local = 0.0; + for (int l = 0; l < nflux_x; l++) { + flux_local += flux_data->at(j, l, i); + } + + // Adjust k to start flux maps with first receiver surface + mt_flux_maps(cur_row, k - 1) = flux_local; + flux_receiver += flux_local; + } + // flux_receiver should equal 1 after each panel is added + + cur_row++; + } + } + } + else + throw exec_error("solarpilot", "failed to calculate a correct flux map table"); + + } + else if (field_model_type == 3) { + + mt_eta_map = as_matrix("eta_map"); + mt_flux_maps = as_matrix("flux_maps"); + } + else if (field_model_type == 2 && sim_type == 2) { + + mt_eta_map.resize_fill(1, 3, std::numeric_limits::quiet_NaN()); + mt_flux_maps.resize_fill(1, 12, std::numeric_limits::quiet_NaN()); + + } + else { + string msg = util::format("Invalid combination of field_model_type and sim_type"); + + throw exec_error("MSPT CSP Solver", msg); + } + } + + N_hel = (int)spi.layout.heliostat_positions.size(); + + helio_pos.resize(N_hel, 2); + for (int i = 0; i < N_hel; i++) { + helio_pos(i, 0) = (ssc_number_t)spi.layout.heliostat_positions.at(i).location.x; + helio_pos(i, 0) = (ssc_number_t)spi.layout.heliostat_positions.at(i).location.y; + } + + THT = spi.sf.tht.val; + + if (rec_type == 0) { + rec_height = spi.recs.front().rec_height.val; + rec_aspect = spi.recs.front().rec_aspect.Val(); + D_rec = rec_height / rec_aspect; //[-] + } + else if (rec_type == 1) { + cav_rec_height = spi.recs.front().rec_height.val; + + // copied from cmod_solarpilot, would be nice to consolidate there + double cav_rec_height_spout, cav_radius_spout, f_offset_spout; + cav_rec_height_spout = cav_radius_spout = f_offset_spout = std::numeric_limits::quiet_NaN(); + int n_panels_spout = -1; + + cav_rec_height_spout = spi.recs.front().rec_height.val; //[m] + cav_radius_spout = spi.recs.front().rec_cav_rad.val; //[m] + f_offset_spout = spi.recs.front().rec_cav_cdepth.val; //[-] + n_panels_spout = spi.recs.front().n_panels.val; //[-] + + double theta0_calc, panelSpan_calc, panel_width_calc, rec_area_calc, + rec_span_calc, offset_calc; + + cavity_receiver_helpers::calc_receiver_macro_geometry_sp_inputs(cav_rec_height_spout, cav_radius_spout, + f_offset_spout, n_panels_spout, + theta0_calc, panelSpan_calc, panel_width_calc, + rec_area_calc, cav_rec_width, rec_span_calc, offset_calc); + + } + + A_sf = spi.CalcSolarFieldArea(N_hel); + heliostat_area = spi.CalcHeliostatArea(); + average_attenuation = spi.CalcAveAttenuation(); + + land_min_abs = as_double("land_min") * THT; //[m] + land_max_abs = as_double("land_max") * THT; //[m] + + total_land_area_before_rad_cooling = spi.GetTotalLandArea(); // [acres] Total land area + land_area_base = spi.GetBaseLandArea(); // [acres] Land area occupied by heliostats + + } + else if (field_model_type == 4) + { + // Use input flux and efficiency maps + mt_eta_map = as_matrix("eta_map"); + mt_flux_maps = as_matrix("flux_maps"); + + // Need to specify: + // 1) reflective area (scale flux map) + // 2) number heliostats for heliostats (tracking parasitics) + // 3) total land area before radiative cooling + // 4) tower and receiver dimensions + N_hel = as_number("N_hel"); + A_sf = as_number("A_sf_in"); //[m2] + total_land_area_before_rad_cooling = as_double("total_land_area_before_rad_cooling_in"); + + // Get tower/receiver dimensions through cmod + THT = as_double("h_tower"); //[m] + h_helio = 0.0; //[m] Need a finite value for cost model + + if (rec_type == 0) { + rec_height = as_double("rec_height"); //[m] + D_rec = as_double("D_rec"); //[m] + rec_aspect = rec_height / D_rec; //[-] + } + else if (rec_type == 1) { + cav_rec_height = as_double("cav_rec_height"); + cav_rec_width = as_double("cav_rec_width"); + } + + helio_pos.resize_fill(1, 2, std::numeric_limits::quiet_NaN()); + + // Don't define land_area_base because we're not requiring it as an input in this field model type + land_area_base = std::numeric_limits::quiet_NaN(); + } + else + { + string msg = util::format("One field performance modeling option must be set to True"); + + throw exec_error("MSPT CSP Solver", msg); + } + + + if (tes_type != 1) + { + throw exec_error("MSPT CSP Solver", "Thermocline thermal energy storage is not yet supported by the new CSP Solver and Dispatch Optimization models.\n"); + } + + + // Set steps per hour + C_csp_solver::S_sim_setup sim_setup; + sim_setup.m_sim_time_start = as_double("time_start"); //[s] time at beginning of first time step + sim_setup.m_sim_time_end = as_double("time_stop"); //[s] time at end of last time step + + int steps_per_hour = (int)as_double("time_steps_per_hour"); //[-] + + //if the number of steps per hour is not provided (=-1), then assign it based on the weather file step + if (steps_per_hour < 0) + { + double sph_d = 3600. / weather_reader.m_weather_data_provider->step_sec(); + steps_per_hour = (int)(sph_d + 1.e-5); + if ((double)steps_per_hour != sph_d) + throw spexception("The time step duration must be evenly divisible within an hour."); + } + + size_t n_steps_fixed = (size_t)steps_per_hour * 8760; //[-] + if (as_boolean("vacuum_arrays")) + { + n_steps_fixed = steps_per_hour * (size_t)((sim_setup.m_sim_time_end - sim_setup.m_sim_time_start) / 3600.); + } + //int n_steps_fixed = (int)( (sim_setup.m_sim_time_end - sim_setup.m_sim_time_start) * steps_per_hour / 3600. ) ; + sim_setup.m_report_step = 3600.0 / (double)steps_per_hour; //[s] + + // *********************************************** + // *********************************************** + // Heat Sink + // *********************************************** + // *********************************************** + + 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_pc_heat_sink c_heat_sink; + 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_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.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.ms_params.m_pc_fl = as_integer("rec_htf"); + c_heat_sink.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); + + //// ********************************************************* + //// ********************************************************* + //// ********************************************************* + //// Now set Type 222 parameters + //// ********************************************************* + //// ********************************************************* + //// ********************************************************* + + double A_rec = std::numeric_limits::quiet_NaN(); + double ext_rec_area = std::numeric_limits::quiet_NaN(); + double ext_rec_aspect = std::numeric_limits::quiet_NaN(); + double cav_rec_area = std::numeric_limits::quiet_NaN(); + double cav_panel_width = std::numeric_limits::quiet_NaN(); + double cav_radius = std::numeric_limits::quiet_NaN(); + + // Calculate external receiver area, height, diameter here + // Calculate cavity receiver area and height below. Don't set diameter or aspect ratio for cavity receiver + if (rec_type == 0) { + A_rec = rec_height * D_rec * 3.1415926; //[m2] + ext_rec_area = A_rec; //[m2] + ext_rec_aspect = rec_height / D_rec; //[-] + } + + std::unique_ptr receiver; + bool is_rec_model_trans = as_boolean("is_rec_model_trans"); + bool is_rec_model_clearsky = as_double("rec_clearsky_fraction") > 0.0; + + if (rec_type == 1) { // Cavity receiver + + // No transient model available for cavity receiver + is_rec_model_trans = false; + + // No clear sky model available for cavity receiver + is_rec_model_clearsky = false; + + double od_rec_tube = as_double("d_tube_out"); //[mm] + double th_rec_tube = as_double("th_tube"); //[mm] + double rec_span = as_double("cav_rec_span") * CSP::pi / 180.0; //[rad] convert from cmod units of deg + + // lips must be 0 for now due to solarpilot limitations + size_t nPanels = as_integer("n_cav_rec_panels"); //[-] + double topLipHeight = 0; //[m] Height of top lip in meters + double botLipHeight = 0; //[m] Height of bottom lip in meters + + // surface radiative properties + double e_act_sol = as_double("rec_absorptance"); //[-] Absorptivity in short wave - active surfaces + double e_act_therm = as_double("epsilon"); //[-] Emissivity in long wave - active surfaces + double e_pass_sol = as_double("cav_rec_passive_abs"); //[-] Absorptivity in short wave (solar) - passive surfaces + double e_pass_therm = as_double("cav_rec_passive_eps"); //[-] Emissivity in long wave - passive surfaces + + C_cavity_receiver::E_mesh_types active_surface_mesh_type = C_cavity_receiver::E_mesh_types::no_mesh; // quad; // no_mesh; // quad; + C_cavity_receiver::E_mesh_types floor_and_cover_mesh_type = C_cavity_receiver::E_mesh_types::no_mesh; + C_cavity_receiver::E_mesh_types lips_mesh_type = C_cavity_receiver::E_mesh_types::no_mesh; // quad; + + // Default values to test against matlab code + // ************************************************************************************* + if (false) { + // Method sets up, initializes, and runs the steady state model w/ inputs corresponding to matlab code + cavity_receiver_helpers::test_cavity_case(); + } + // *************************************************************************************** + // *************************************************************************************** + + std::unique_ptr c_cav_rec = std::unique_ptr(new C_cavity_receiver(as_double("dni_des"), + as_integer("rec_htf"), as_matrix("field_fl_props"), + od_rec_tube, th_rec_tube, as_integer("mat_tube"), + nPanels, cav_rec_height, cav_rec_width, + rec_span, topLipHeight, botLipHeight, + e_act_sol, e_pass_sol, e_act_therm, e_pass_therm, + active_surface_mesh_type, floor_and_cover_mesh_type, lips_mesh_type, + as_double("piping_loss_coefficient"), as_double("piping_length_const"), as_double("piping_length_mult"), + THT, as_double("T_htf_hot_des"), + as_double("T_htf_cold_des"), as_double("f_rec_min"), q_dot_rec_des, + as_double("rec_su_delay"), as_double("rec_qf_delay"), as_double("csp.pt.rec.max_oper_frac"), + as_double("eta_pump"))); + + receiver = std::move(c_cav_rec); + + // Calculate receiver area + double theta0, panelspan, panelwidth, radius, offset; + theta0 = panelspan = panelwidth = radius = offset = A_rec = std::numeric_limits::quiet_NaN(); + cavity_receiver_helpers::calc_receiver_macro_geometry(cav_rec_height, cav_rec_width, + rec_span, nPanels, + theta0, panelspan, panelwidth, A_rec, radius, offset); + + cav_rec_area = A_rec; //[m2] + cav_panel_width = panelwidth; //[m] + cav_radius = radius; //[m] + } + else if (rec_type == 0) { + + int rec_night_recirc = 0; + int rec_clearsky_model = as_integer("rec_clearsky_model"); + bool is_calc_od_tube = false; + double W_dot_rec_target = std::numeric_limits::quiet_NaN(); + + if (rec_clearsky_model > 4) + throw exec_error("tcsmolten_salt", "Invalid specification for 'rec_clearsky_model'"); + if (rec_clearsky_model == -1 && as_double("rec_clearsky_fraction") >= 0.0001) + throw exec_error("tcsmolten_salt", "'rec_clearsky_model' must be specified when 'rec_clearsky_fraction' > 0.0."); + + if (!as_boolean("is_rec_model_trans") && !as_boolean("is_rec_startup_trans")) { + //std::unique_ptr ss_receiver = std::make_unique(); // new to C++14 + std::unique_ptr ss_receiver = std::unique_ptr(new C_mspt_receiver_222( + THT, as_double("epsilon"), + as_double("T_htf_hot_des"), as_double("T_htf_cold_des"), + as_double("f_rec_min"), q_dot_rec_des, + as_double("rec_su_delay"), as_double("rec_qf_delay"), + as_double("csp.pt.rec.max_oper_frac"), as_double("eta_pump"), + as_double("d_tube_out"), as_double("th_tube"), + as_double("piping_loss_coefficient"), as_double("piping_length_const"), + as_double("piping_length_mult"), + as_integer("rec_htf"), as_matrix("field_fl_props"), + as_integer("mat_tube"), + rec_night_recirc, + as_integer("N_panels"), D_rec, rec_height, + as_integer("Flow_type"), as_integer("crossover_shift"), as_double("hl_ffact"), + as_double("T_htf_hot_des"), as_double("rec_clearsky_fraction"), + is_calc_od_tube, W_dot_rec_target + )); // steady-state receiver + + receiver = std::move(ss_receiver); + } + else { + + bool is_enforce_min_startup = as_boolean("is_rec_enforce_min_startup"); + + //trans_receiver->m_is_startup_from_solved_profile = as_boolean("is_rec_startup_from_T_soln"); + if (as_boolean("is_rec_startup_trans") && as_boolean("is_rec_startup_from_T_soln")) + throw exec_error("tcsmolten_salt", "Receiver startup from solved temperature profiles is only available when receiver transient startup model is enabled"); + + //trans_receiver->m_is_enforce_min_startup = as_boolean("is_rec_enforce_min_startup"); + if (as_boolean("is_rec_startup_trans") && !as_boolean("is_rec_startup_from_T_soln") && !is_enforce_min_startup) + { + log("Both 'is_rec_enforce_min_startup' and 'is_rec_startup_from_T_soln' were set to 'false'. Minimum startup time will always be enforced unless 'is_rec_startup_from_T_soln' is set to 'true'", SSC_WARNING); + is_enforce_min_startup = true; + } + + std::unique_ptr trans_receiver = std::unique_ptr(new C_mspt_receiver( + THT, as_double("epsilon"), + as_double("T_htf_hot_des"), as_double("T_htf_cold_des"), + as_double("f_rec_min"), q_dot_rec_des, + as_double("rec_su_delay"), as_double("rec_qf_delay"), + as_double("csp.pt.rec.max_oper_frac"), as_double("eta_pump"), + as_double("d_tube_out"), as_double("th_tube"), + as_double("piping_loss_coefficient"), as_double("piping_length_const"), + as_double("piping_length_mult"), + as_integer("rec_htf"), as_matrix("field_fl_props"), + as_integer("mat_tube"), + rec_night_recirc, + as_integer("N_panels"), D_rec, rec_height, + as_integer("Flow_type"), as_integer("crossover_shift"), as_double("hl_ffact"), + as_double("T_htf_hot_des"), as_double("rec_clearsky_fraction"), + is_calc_od_tube, W_dot_rec_target, + as_boolean("is_rec_model_trans"), as_boolean("is_rec_startup_trans"), + as_double("rec_tm_mult"), as_double("u_riser"), + as_double("th_riser"), as_double("riser_tm_mult"), + as_double("downc_tm_mult"), as_double("heat_trace_power"), + as_double("preheat_flux"), as_double("min_preheat_time"), + as_double("min_fill_time"), as_double("startup_ramp_time"), + as_double("T_htf_cold_des"), min(0.0, as_double("startup_target_Tdiff")), + 5.0, + as_boolean("is_rec_startup_from_T_soln"), is_enforce_min_startup + )); // transient receiver + + receiver = std::move(trans_receiver); + } + } + + // Test mspt_receiver initialization + //receiver.init(); + + // ******************************* + // ******************************* + // Construct heliostat field class after receiver + // so it can use the active receiver area + C_pt_sf_perf_interp heliostatfield(A_rec); + + heliostatfield.ms_params.m_p_start = as_double("p_start"); //[kWe-hr] Heliostat startup energy + heliostatfield.ms_params.m_p_track = as_double("p_track"); //[kWe] Heliostat tracking power + heliostatfield.ms_params.m_hel_stow_deploy = as_double("hel_stow_deploy"); // N/A + heliostatfield.ms_params.m_v_wind_max = as_double("v_wind_max"); // N/A + heliostatfield.ms_params.m_eta_map = mt_eta_map; + heliostatfield.ms_params.m_flux_maps = mt_flux_maps; + heliostatfield.ms_params.m_n_flux_x = mt_flux_maps.ncols(); // for multi-surface cav receiver, these values need to match + heliostatfield.ms_params.m_n_flux_y = n_flux_y; + heliostatfield.ms_params.m_N_hel = N_hel; + heliostatfield.ms_params.m_A_sf = A_sf; //[m2] + if (field_model_type < 3) + { + heliostatfield.ms_params.m_eta_map_aod_format = false; + } + else + { + heliostatfield.ms_params.m_eta_map_aod_format = as_boolean("eta_map_aod_format"); + } + heliostatfield.ms_params.m_clearsky_model = as_integer("rec_clearsky_model"); + + std::vector clearsky_data; + if (heliostatfield.ms_params.m_clearsky_model == 0) + { + size_t n_csky = 0; + ssc_number_t* csky = as_array("rec_clearsky_dni", &n_csky); + if (n_csky != n_steps_full) + throw exec_error("tcsmolten_salt", "Invalid clear-sky DNI data. Array must have " + util::to_string((int)n_steps_full) + " rows."); + + clearsky_data.resize(n_steps_full); + for (size_t i = 0; i < n_steps_full; i++) + clearsky_data.at(i) = (double)csky[i]; + } + heliostatfield.ms_params.mv_clearsky_data = clearsky_data; + + //Load the solar field adjustment factors + adjustment_factors sf_haf(this, "sf_adjust"); + if (!sf_haf.setup((int)n_steps_full)) + throw exec_error("tcsmolten_salt", "failed to setup sf adjustment factors: " + sf_haf.error()); + //allocate array to pass to tcs + heliostatfield.ms_params.m_sf_adjust.resize(sf_haf.size()); + for (int i = 0; i < sf_haf.size(); i++) + heliostatfield.ms_params.m_sf_adjust.at(i) = sf_haf(i); + + // Set callback information + heliostatfield.mf_callback = ssc_cmod_solarpilot_callback; + heliostatfield.m_cdata = (void*)this; + + // Try running pt heliostat init() call just for funsies + // What happens when no callback to reference? + //heliostatfield.init(); + + + // Now try to instantiate mspt_collector_receiver + C_csp_mspt_collector_receiver collector_receiver(heliostatfield, *receiver); + // Then try init() call here, which should call inits from both classes + //collector_receiver.init(); + + // ******************************************************* + // ******************************************************* + // Set receiver outputs + //float *p_q_thermal_copy = allocate("Q_thermal_123", n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_FIELD_Q_DOT_INC, allocate("q_sf_inc", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_FIELD_ETA_OPT, allocate("eta_field", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_FIELD_ADJUST, allocate("sf_adjust_out", n_steps_fixed), n_steps_fixed); + + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_REC_DEFOCUS, allocate("rec_defocus", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_INC, allocate("q_dot_rec_inc", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_ETA_THERMAL, allocate("eta_therm", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_THERMAL, allocate("Q_thermal", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_M_DOT_HTF, allocate("m_dot_rec", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_STARTUP, allocate("q_startup", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_HTF_IN, allocate("T_rec_in", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_HTF_OUT, allocate("T_rec_out", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_PIPE_LOSS, allocate("q_piping_losses", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_LOSS, allocate("q_thermal_loss", n_steps_fixed), n_steps_fixed); + // Cavity-specific outputs + if (rec_type == 1) { + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_REFL_LOSS, allocate("q_dot_reflection_loss", n_steps_fixed), n_steps_fixed); + } + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_W_DOT_TRACKING, allocate("pparasi", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_W_DOT_PUMP, allocate("P_tower_pump", n_steps_fixed), n_steps_fixed); + + // Transient model specific outputs + if (is_rec_model_trans) { + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_P_HEATTRACE, allocate("P_rec_heattrace", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_HTF_OUT_END, allocate("T_rec_out_end", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_HTF_OUT_MAX, allocate("T_rec_out_max", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_HTF_PANEL_OUT_MAX, allocate("T_panel_out_max", n_steps_fixed), n_steps_fixed); + + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_WALL_INLET, allocate("T_wall_rec_inlet", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_WALL_OUTLET, allocate("T_wall_rec_outlet", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_RISER, allocate("T_wall_riser", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_T_DOWNC, allocate("T_wall_downcomer", n_steps_fixed), n_steps_fixed); + + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_THERMAL_SS, allocate("Q_thermal_ss", n_steps_fixed), n_steps_fixed); + } + if (is_rec_model_clearsky) { + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_CLEARSKY, allocate("clearsky", n_steps_fixed), n_steps_fixed); + collector_receiver.mc_reported_outputs.assign(C_csp_mspt_collector_receiver::E_Q_DOT_THERMAL_CSKY_SS, allocate("Q_thermal_ss_csky", n_steps_fixed), n_steps_fixed); + } + + // 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; + bool is_parallel_heater = as_boolean("is_parallel_htr"); // defaults to false + double q_dot_heater_des = 0.0; //[MWt] + double heater_spec_cost = 0.0; + if (is_parallel_heater) { + + if (!is_dispatch && sim_type == 1) { + if (!as_boolean("allow_heater_no_dispatch_opt")) { + throw exec_error("tcsmolten_salt", "When the molten salt power tower case has an electric HTF charger, dispatch optimization must be selected"); + } + } + + double heater_mult = as_double("heater_mult"); //[-] + heater_spec_cost = as_double("heater_spec_cost"); //[$/kWt] + + q_dot_heater_des = q_dot_pc_des * heater_mult; //[MWt] + //double q_dot_heater_des = receiver->m_q_rec_des * 2.0; // / 4.0; //[MWt] + + double heater_efficiency = as_double("heater_efficiency") / 100.0; //[-] convert from % input + double f_q_dot_des_allowable_su = as_double("f_q_dot_des_allowable_su"); //[-] fraction of design power allowed during startup + double hrs_startup_at_max_rate = as_double("hrs_startup_at_max_rate"); //[hr] duration of startup at max startup power + double f_heater_min = as_double("f_q_dot_heater_min"); //[-] minimum allowable heater output as fraction of design + + p_electric_resistance = new C_csp_cr_electric_resistance(as_double("T_htf_cold_des"), as_double("T_htf_hot_des"), + q_dot_heater_des, heater_efficiency, f_heater_min, + f_q_dot_des_allowable_su, hrs_startup_at_max_rate, + as_integer("rec_htf"), as_matrix("field_fl_props"), C_csp_cr_electric_resistance::E_elec_resist_startup_mode::INSTANTANEOUS_NO_MAX_ELEC_IN); + + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_W_DOT_HEATER, allocate("W_dot_heater", n_steps_fixed), n_steps_fixed); + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_Q_DOT_HTF, allocate("q_dot_heater_to_htf", n_steps_fixed), n_steps_fixed); + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_Q_DOT_STARTUP, allocate("q_dot_heater_startup", n_steps_fixed), n_steps_fixed); + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_M_DOT_HTF, allocate("m_dot_htf_heater", n_steps_fixed), n_steps_fixed); + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_T_HTF_IN, allocate("T_htf_heater_in", n_steps_fixed), n_steps_fixed); + p_electric_resistance->mc_reported_outputs.assign(C_csp_cr_electric_resistance::E_T_HTF_OUT, allocate("T_htf_heater_out", n_steps_fixed), n_steps_fixed); + } + p_heater = p_electric_resistance; + + // Thermal energy storage + C_csp_two_tank_tes storage( + as_integer("rec_htf"), + as_matrix("field_fl_props"), + as_integer("rec_htf"), + as_matrix("field_fl_props"), + q_dot_pc_des, //[MWt] + as_double("solarm"), //[-] + Q_tes, + as_double("h_tank"), + as_double("u_tank"), + as_integer("tank_pairs"), + as_double("hot_tank_Thtr"), + as_double("hot_tank_max_heat"), + as_double("cold_tank_Thtr"), + as_double("cold_tank_max_heat"), + 0.0, // MSPT assumes direct storage, so no user input here: hardcode = 0.0 + as_double("T_htf_cold_des"), + as_double("T_htf_hot_des"), + as_double("T_htf_hot_des"), + as_double("T_htf_cold_des"), + as_double("h_tank_min"), + as_double("tes_init_hot_htf_percent"), + as_double("pb_pump_coef"), + as_boolean("tanks_in_parallel"), //[-] + 1.85, //[m/s] + false // for now, to get 'tanks_in_parallel' to work + ); + + // Set storage outputs + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_Q_DOT_LOSS, allocate("tank_losses", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HEATER, allocate("q_dot_tes_heater", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_HOT, allocate("T_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_TES_T_COLD, allocate("T_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_COLD_TANK, allocate("mass_tes_cold", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_MASS_HOT_TANK, allocate("mass_tes_hot", n_steps_fixed), n_steps_fixed); + storage.mc_reported_outputs.assign(C_csp_two_tank_tes::E_W_DOT_HTF_PUMP, allocate("tes_htf_pump_power", n_steps_fixed), n_steps_fixed); + + // TOU parameters + C_csp_tou_block_schedules tou; + C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); + tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); + + tou.mc_dispatch_params.m_is_tod_pc_target_also_pc_max = as_boolean("is_tod_pc_target_also_pc_max"); + tou.mc_dispatch_params.m_is_block_dispatch = !as_boolean("is_dispatch"); //mw + tou.mc_dispatch_params.m_use_rule_1 = true; + tou.mc_dispatch_params.m_standby_off_buffer = 2.0; + tou.mc_dispatch_params.m_use_rule_2 = false; + tou.mc_dispatch_params.m_q_dot_rec_des_mult = -1.23; + tou.mc_dispatch_params.m_f_q_dot_pc_overwrite = -1.23; + + size_t n_f_turbine = 0; + ssc_number_t* p_f_turbine = as_array("f_turb_tou_periods", &n_f_turbine); + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC].resize(n_f_turbine, 0.0); + //tou_params->mv_t_frac.resize(n_f_turbine, 0.0); + for (size_t i = 0; i < n_f_turbine; i++) + tou_params->mc_csp_ops.mvv_tou_arrays[C_block_schedule_csp_ops::TURB_FRAC][i] = (double)p_f_turbine[i]; + + // Load fraction by time step: + bool is_load_fraction_by_timestep = is_assigned("timestep_load_fractions"); + tou_params->mc_csp_ops.mv_is_diurnal = !(is_load_fraction_by_timestep); + if (is_load_fraction_by_timestep) { + size_t N_load_fractions; + ssc_number_t* load_fractions = as_array("timestep_load_fractions", &N_load_fractions); + std::copy(load_fractions, load_fractions + N_load_fractions, std::back_inserter(tou_params->mc_csp_ops.timestep_load_fractions)); + } + + int csp_financial_model = as_integer("csp_financial_model"); + + double ppa_price_year1 = std::numeric_limits::quiet_NaN(); + if (sim_type == 1) { + if (csp_financial_model == 8 || csp_financial_model == 7) { // No Financial Model or LCOH + if (is_dispatch) { + throw exec_error("tcsmolten_salt", "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 + tou_params->mc_pricing.mc_weekdays.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mc_weekends.resize_fill(12, 24, 1.); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, -1.0); + } + } + else { + throw exec_error("mspt_iph", "csp_financial_model must 8"); + } + } + else if (sim_type == 2) { + tou_params->mc_pricing.mv_is_diurnal = false; + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + } + // ***************************************************** + // + + // System parameters + C_csp_solver::S_csp_system_params system; + system.m_pb_fixed_par = as_double("pb_fixed_par"); + system.m_bop_par = as_double("bop_par"); + system.m_bop_par_f = as_double("bop_par_f"); + system.m_bop_par_0 = as_double("bop_par_0"); + system.m_bop_par_1 = as_double("bop_par_1"); + system.m_bop_par_2 = as_double("bop_par_2"); + + // ***************************************************** + // System dispatch + csp_dispatch_opt dispatch; + dispatch.solver_params.dispatch_optimize = false; + + // Instantiate Solver + C_csp_solver csp_solver(weather_reader, + collector_receiver, + c_heat_sink, + storage, + tou, + dispatch, + system, + p_heater, + nullptr, + ssc_cmod_update, + (void*)(this)); + + + // Set solver reporting outputs + 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::ERR_M_DOT, allocate("m_dot_balance", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::ERR_Q_DOT, allocate("q_balance", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::N_OP_MODES, allocate("n_op_modes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_1, allocate("op_mode_1", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_2, allocate("op_mode_2", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::OP_MODE_3, allocate("op_mode_3", n_steps_fixed), n_steps_fixed); + + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TOU_PERIOD, allocate("tou_value", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PRICING_MULT, allocate("pricing_mult", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_SB, allocate("q_dot_pc_sb", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MIN, allocate("q_dot_pc_min", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_TARGET, allocate("q_dot_pc_target", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PC_Q_DOT_MAX, allocate("q_dot_pc_max", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_REC_SU, allocate("is_rec_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SU, allocate("is_pc_su_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PC_SB, allocate("is_pc_sb_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_SU, allocate("q_dot_est_cr_su", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CR_ON, allocate("q_dot_est_cr_on", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_DC, allocate("q_dot_est_tes_dc", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::EST_Q_DOT_CH, allocate("q_dot_est_tes_ch", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_IS_PAR_HTR_SU, allocate("is_PAR_HTR_allowed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::PAR_HTR_Q_DOT_TARGET, allocate("q_dot_elec_to_PAR_HTR", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_A, allocate("operating_modes_a", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_B, allocate("operating_modes_b", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CTRL_OP_MODE_SEQ_C, allocate("operating_modes_c", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REL_MIP_GAP, allocate("disp_rel_mip_gap", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_STATE, allocate("disp_solve_state", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SUBOPT_FLAG, allocate("disp_subopt_flag", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_ITER, allocate("disp_solve_iter", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ, allocate("disp_objective", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_OBJ_RELAX, allocate("disp_obj_relax", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSF_EXPECT, allocate("disp_qsf_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFPROD_EXPECT, allocate("disp_qsfprod_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QSFSU_EXPECT, allocate("disp_qsfsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_TES_EXPECT, allocate("disp_tes_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PCEFF_EXPECT, allocate("disp_pceff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SFEFF_EXPECT, allocate("disp_thermeff_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_QPBSU_EXPECT, allocate("disp_qpbsu_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_WPB_EXPECT, allocate("disp_wpb_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_REV_EXPECT, allocate("disp_rev_expected", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NCONSTR, allocate("disp_presolve_nconstr", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_PRES_NVAR, allocate("disp_presolve_nvar", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::DISPATCH_SOLVE_TIME, allocate("disp_solve_time", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLZEN, allocate("solzen", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SOLAZ, allocate("solaz", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::BEAM, allocate("beam", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TDRY, allocate("tdry", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TWET, allocate("twet", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::RH, allocate("RH", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::WSPD, allocate("wspd", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::CR_DEFOCUS, allocate("defocus", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_DC, allocate("q_dc_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_Q_DOT_CH, allocate("q_ch_tes", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::TES_E_CH_STATE, allocate("e_ch_tes", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CR_TO_TES_HOT, allocate("m_dot_cr_to_tes_hot", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_HOT_OUT, allocate("m_dot_tes_hot_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_PC_TO_TES_COLD, allocate("m_dot_pc_to_tes_cold", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_TES_COLD_OUT, allocate("m_dot_tes_cold_out", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_FIELD_TO_CYCLE, allocate("m_dot_field_to_cycle", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::M_DOT_CYCLE_TO_FIELD, allocate("m_dot_cycle_to_field", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_FIXED, allocate("P_fixed", n_steps_fixed), n_steps_fixed); + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::SYS_W_DOT_BOP, allocate("P_plant_balance_tot", n_steps_fixed), n_steps_fixed); + + csp_solver.mc_reported_outputs.assign(C_csp_solver::C_solver_outputs::W_DOT_NET, allocate("W_dot_parasitic_tot", n_steps_fixed), n_steps_fixed); + + + update("Initialize MSPT model...", 0.0); + + int out_type = -1; + std::string out_msg = ""; + try + { + // Initialize Solver + csp_solver.init(); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + throw exec_error("tcsmolten_salt", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + // ***************************************************** + // System design is complete, get design parameters from component models as necessary + + // ************************* + // Solar field + assign("N_hel_calc", N_hel); //[-] + assign("refl_image_error", refl_image_error); //[mrad] + assign("heliostat_area", heliostat_area); //[m2] + assign("average_attenuation", average_attenuation); //[%] + assign("A_sf", (ssc_number_t)A_sf); //[m2] + assign("land_min_abs", (ssc_number_t)land_min_abs); //[m] + assign("land_max_abs", (ssc_number_t)land_max_abs); //[m] + assign("land_area_base_calc", (ssc_number_t)land_area_base); //[acre] + assign("total_land_area_before_rad_cooling_calc", (ssc_number_t)total_land_area_before_rad_cooling); //[acre] + + size_t n_helio_pos_rows = helio_pos.nrows(); + ssc_number_t* p_helio_positions_calc = allocate("helio_positions_calc", n_helio_pos_rows, 2); + // Try to determine whether heliostat positions represent surround or cavity field + for (size_t i = 0; i < n_helio_pos_rows; i++) + { + p_helio_positions_calc[i * 2] = (ssc_number_t)helio_pos(i, 0); //[m] x + p_helio_positions_calc[i * 2 + 1] = (ssc_number_t)helio_pos(i, 1); //[m] y + } + + double W_dot_col_tracking_des = collector_receiver.get_tracking_power(); //[MWe] + assign("W_dot_col_tracking_des", W_dot_col_tracking_des); //[MWe] + + // ************************* + // Tower and receiver + assign("h_tower_calc", (ssc_number_t)THT); //[m] + // External receiver + assign("rec_height_calc", (ssc_number_t)rec_height); //[m] + assign("D_rec_calc", (ssc_number_t)D_rec); //[m] + assign("ext_rec_area", (ssc_number_t)ext_rec_area); //[m2] + assign("ext_rec_aspect", (ssc_number_t)ext_rec_aspect); //[-] + // Cavity receiver + assign("cav_rec_height_calc", (ssc_number_t)cav_rec_height); + assign("cav_rec_width_calc", (ssc_number_t)cav_rec_width); + assign("cav_rec_area", (ssc_number_t)cav_rec_area); + assign("cav_panel_width", (ssc_number_t)cav_panel_width); + assign("cav_radius", (ssc_number_t)cav_radius); + // Both + assign("A_rec", A_rec); //[m2] + + double L_tower_piping = std::numeric_limits::quiet_NaN(); + double od_tube_calc = std::numeric_limits::quiet_NaN(); + receiver->get_design_geometry(L_tower_piping, od_tube_calc); + assign("L_tower_piping_calc", L_tower_piping); //[m] + assign("od_tube_calc", od_tube_calc * 1.E3); //[mm] convert from m + + double eta_rec_thermal_des; //[-] + double W_dot_rec_pump_des; //[MWe] + double W_dot_rec_pump_tower_share_des; //[MWe] + double W_dot_rec_pump_rec_share_des; //[MWe] + double rec_pump_coef_des; //[MWe/MWt] + double rec_vel_htf_des; //[m/s] + double m_dot_htf_rec_des; //[kg/s] + double q_dot_piping_loss_des; //[MWt] + double m_dot_htf_rec_max; //[kg/s] + receiver->get_design_performance(eta_rec_thermal_des, + W_dot_rec_pump_des, W_dot_rec_pump_tower_share_des, W_dot_rec_pump_rec_share_des, + rec_pump_coef_des, rec_vel_htf_des, m_dot_htf_rec_des, m_dot_htf_rec_max, q_dot_piping_loss_des); + assign("q_dot_rec_des", q_dot_rec_des); //[MWt] + assign("eta_rec_thermal_des", eta_rec_thermal_des); //[-] + assign("W_dot_rec_pump_des", W_dot_rec_pump_des); //[MWe] + assign("W_dot_rec_pump_tower_share_des", W_dot_rec_pump_tower_share_des); //[MWe] + assign("W_dot_rec_pump_rec_share_des", W_dot_rec_pump_rec_share_des); //[MWe] + assign("vel_rec_htf_des", rec_vel_htf_des); //[m/s] + assign("m_dot_htf_rec_des", m_dot_htf_rec_des); //[kg/s] + assign("q_dot_piping_loss_des", q_dot_piping_loss_des); //[MWt] + assign("m_dot_htf_rec_max", m_dot_htf_rec_max); //[kg/s] + + // ************************* + // Heater + assign("q_dot_heater_des", q_dot_heater_des); //[MWt] + double W_dot_heater_des_calc = 0.0; //[MWe] + double E_heater_su_des = 0.0; //[MWt-hr] + if (is_parallel_heater) { + p_electric_resistance->get_design_parameters(E_heater_su_des, W_dot_heater_des_calc); + } + assign("W_dot_heater_des", (ssc_number_t)W_dot_heater_des_calc); //[MWe] + assign("E_heater_su_des", (ssc_number_t)E_heater_su_des); //[MWt-hr] + + // ************************* + // Thermal Energy Storage + double V_tes_htf_avail_calc /*m3*/, V_tes_htf_total_calc /*m3*/, + d_tank_calc /*m*/, q_dot_loss_tes_des_calc /*MWt*/, dens_store_htf_at_T_ave_calc /*kg/m3*/, + Q_tes_des_calc /*MWt-hr*/; + + storage.get_design_parameters(V_tes_htf_avail_calc, V_tes_htf_total_calc, + d_tank_calc, q_dot_loss_tes_des_calc, dens_store_htf_at_T_ave_calc, Q_tes_des_calc); + + assign("Q_tes_des", Q_tes_des_calc); //[MWt-hr] + assign("V_tes_htf_avail_des", V_tes_htf_avail_calc); //[m3] + assign("V_tes_htf_total_des", V_tes_htf_total_calc); //[m3] + assign("d_tank_tes", d_tank_calc); //[m] + assign("q_dot_loss_tes_des", q_dot_loss_tes_des_calc); //[MWt] + assign("tshours_rec", Q_tes_des_calc / q_dot_rec_des); //[hr] + assign("dens_store_htf_at_T_ave", dens_store_htf_at_T_ave_calc); //[kg/m3] + + double tshours_heater = 0.0; + if (q_dot_heater_des > 0.0) { + tshours_heater = Q_tes_des_calc / q_dot_heater_des; //[hr] + } + + 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); + + // ************************* + // System + double W_dot_bop_design, W_dot_fixed_parasitic_design; //[MWe] + csp_solver.get_design_parameters(W_dot_bop_design, W_dot_fixed_parasitic_design); + + // Calculate net system *generation* capacity including HTF pumps and system parasitics + //double plant_net_capacity_calc = W_dot_cycle_des - W_dot_col_tracking_des - W_dot_rec_pump_des - + // W_dot_pc_pump_des - W_dot_pc_cooling_des - W_dot_bop_design - W_dot_fixed_parasitic_design; //[MWe] + + //double plant_net_conv_calc = plant_net_capacity_calc / W_dot_cycle_des; //[-] + + //double system_capacity = plant_net_capacity_calc * 1.E3; //[kWe], convert from MWe + double system_capacity = q_dot_pc_des * 1.E3; //[kWt] + + assign("W_dot_bop_design", W_dot_bop_design); //[MWe] + assign("W_dot_fixed", W_dot_fixed_parasitic_design); //[MWe] + // Calculate system capacity instead of pass in + assign("system_capacity", system_capacity); //[kWt] + assign("nameplate", system_capacity * 1.E-3); //[MWt] + assign("cp_system_nameplate", system_capacity * 1.E-3); //[MWt] + assign("cp_battery_nameplate", 0.0); //[MWe] + + // ******* Costs ************ + double A_sf_refl = A_sf; + double site_improv_spec_cost = as_double("site_spec_cost"); + double heliostat_spec_cost = as_double("heliostat_spec_cost"); + double heliostat_fixed_cost = as_double("cost_sf_fixed"); + + double h_rec_cost_in = std::numeric_limits::quiet_NaN(); + if (rec_type == 0) { + h_rec_cost_in = rec_height; //[m] + } + else if (rec_type == 1) { + h_rec_cost_in = cav_rec_height; //[m] + } + double tower_fixed_cost = as_double("tower_fixed_cost"); + double tower_cost_scaling_exp = as_double("tower_exp"); + + double rec_ref_cost = as_double("rec_ref_cost"); + double A_rec_ref = as_double("rec_ref_area"); + double rec_cost_scaling_exp = as_double("rec_cost_exp"); + + double tes_spec_cost = as_double("tes_spec_cost"); + + // no Cold Temp TES, so set those cost model inputs to 0 + double Q_CT_tes = 0.0; + double CT_tes_spec_cost = 0.0; + + //double W_dot_design = as_double("P_ref"); + double power_cycle_spec_cost = 0.0; // as_double("plant_spec_cost"); + + // Set heater thermal power and cost above, because they're dependent on is_heater boolean + + double bop_spec_cost = as_double("bop_spec_cost"); + + double fossil_backup_spec_cost = 0.0; // as_double("fossil_spec_cost"); + + double contingency_rate = as_double("contingency_rate"); + + // land area + double total_land_area = total_land_area_before_rad_cooling; + assign("total_land_area", (ssc_number_t)total_land_area); + + double plant_net_capacity = system_capacity / 1000.0; //[MWe], convert from kWe + double EPC_land_spec_cost = as_double("csp.pt.cost.epc.per_acre"); + double EPC_land_perc_direct_cost = as_double("csp.pt.cost.epc.percent"); + double EPC_land_per_power_cost = as_double("csp.pt.cost.epc.per_watt"); + double EPC_land_fixed_cost = as_double("csp.pt.cost.epc.fixed"); + double total_land_spec_cost = as_double("land_spec_cost"); + double total_land_perc_direct_cost = as_double("csp.pt.cost.plm.percent"); + double total_land_per_power_cost = as_double("csp.pt.cost.plm.per_watt"); + double total_land_fixed_cost = as_double("csp.pt.cost.plm.fixed"); + double sales_tax_basis = as_double("sales_tax_frac"); + double sales_tax_rate = as_double("sales_tax_rate"); + + double site_improvement_cost, heliostat_cost, tower_cost, receiver_cost, tes_cost, CT_tes_cost, power_cycle_cost, + heater_cost, rad_field_totcost, rad_fluid_totcost, rad_storage_totcost, bop_cost, fossil_backup_cost, + direct_capital_precontingency_cost, contingency_cost, total_direct_cost, epc_and_owner_cost, total_land_cost, + sales_tax_cost, total_indirect_cost, total_installed_cost, estimated_installed_cost_per_cap; + + site_improvement_cost = heliostat_cost = tower_cost = receiver_cost = tes_cost = CT_tes_cost = power_cycle_cost = + heater_cost = rad_field_totcost = rad_fluid_totcost = rad_storage_totcost = bop_cost = fossil_backup_cost = + direct_capital_precontingency_cost = contingency_cost = total_direct_cost = epc_and_owner_cost = total_land_cost = + sales_tax_cost = total_indirect_cost = total_installed_cost = estimated_installed_cost_per_cap = std::numeric_limits::quiet_NaN(); + + // Still need to set radiative cooling inputs to 0 until/if we remove radiative cooling from cost model + double rad_fluidcost = 0.0; + double rad_installcost = 0.0; + double rad_unitcost = 0.0; + double rad_volmulti = 0.0; + double coldstorage_unitcost = 0.0; + double radfield_area = 0.0; + double coldstorage_vol = 0.0; + double radfield_vol = 0.0; + + N_mspt::calculate_mspt_etes_costs( + A_sf_refl, + site_improv_spec_cost, + heliostat_spec_cost, + heliostat_fixed_cost, + + THT, + h_rec_cost_in, + h_helio, + tower_fixed_cost, + tower_cost_scaling_exp, + + A_rec, + rec_ref_cost, + A_rec_ref, + rec_cost_scaling_exp, + + Q_tes, + tes_spec_cost, + + Q_CT_tes, + CT_tes_spec_cost, + + q_dot_pc_des, + power_cycle_spec_cost, + + q_dot_heater_des, //[MWt] + heater_spec_cost, + + radfield_area, + coldstorage_vol, + radfield_vol, + rad_unitcost, + rad_installcost, + rad_volmulti, + rad_fluidcost, + coldstorage_unitcost, + + bop_spec_cost, + + fossil_backup_spec_cost, + + contingency_rate, + + total_land_area, + plant_net_capacity, + EPC_land_spec_cost, + EPC_land_perc_direct_cost, + EPC_land_per_power_cost, + EPC_land_fixed_cost, + total_land_spec_cost, + total_land_perc_direct_cost, + total_land_per_power_cost, + total_land_fixed_cost, + sales_tax_basis, + sales_tax_rate, + + site_improvement_cost, + heliostat_cost, + tower_cost, + receiver_cost, + tes_cost, + CT_tes_cost, + power_cycle_cost, + heater_cost, + rad_field_totcost, + rad_fluid_totcost, + rad_storage_totcost, + bop_cost, + fossil_backup_cost, + direct_capital_precontingency_cost, + contingency_cost, + total_direct_cost, + total_land_cost, + epc_and_owner_cost, + sales_tax_cost, + total_indirect_cost, + total_installed_cost, + estimated_installed_cost_per_cap + ); + + // 1.5.2016 twn: financial model needs an updated total_installed_cost, remaining are for reporting only + assign("total_installed_cost", (ssc_number_t)total_installed_cost); + + assign("h_rec_input_to_cost_model", (ssc_number_t)h_rec_cost_in); //[m] + assign("csp.pt.cost.site_improvements", (ssc_number_t)site_improvement_cost); + assign("csp.pt.cost.heliostats", (ssc_number_t)heliostat_cost); + assign("csp.pt.cost.tower", (ssc_number_t)tower_cost); + assign("csp.pt.cost.receiver", (ssc_number_t)receiver_cost); + assign("csp.pt.cost.storage", (ssc_number_t)tes_cost); + assign("csp.pt.cost.power_block", (ssc_number_t)power_cycle_cost); + assign("heater_cost", (ssc_number_t)heater_cost); + + assign("csp.pt.cost.bop", (ssc_number_t)bop_cost); + assign("csp.pt.cost.fossil", (ssc_number_t)fossil_backup_cost); + assign("ui_direct_subtotal", (ssc_number_t)direct_capital_precontingency_cost); + assign("csp.pt.cost.contingency", (ssc_number_t)contingency_cost); + assign("total_direct_cost", (ssc_number_t)total_direct_cost); + assign("csp.pt.cost.epc.total", (ssc_number_t)epc_and_owner_cost); + assign("csp.pt.cost.plm.total", (ssc_number_t)total_land_cost); + assign("csp.pt.cost.sales_tax.total", (ssc_number_t)sales_tax_cost); + assign("total_indirect_cost", (ssc_number_t)total_indirect_cost); + assign("csp.pt.cost.installed_per_capacity", (ssc_number_t)estimated_installed_cost_per_cap); + + // Update construction financing costs, specifically, update: "construction_financing_cost" + double const_per_interest_rate1 = as_double("const_per_interest_rate1"); + double const_per_interest_rate2 = as_double("const_per_interest_rate2"); + double const_per_interest_rate3 = as_double("const_per_interest_rate3"); + double const_per_interest_rate4 = as_double("const_per_interest_rate4"); + double const_per_interest_rate5 = as_double("const_per_interest_rate5"); + double const_per_months1 = as_double("const_per_months1"); + double const_per_months2 = as_double("const_per_months2"); + double const_per_months3 = as_double("const_per_months3"); + double const_per_months4 = as_double("const_per_months4"); + double const_per_months5 = as_double("const_per_months5"); + double const_per_percent1 = as_double("const_per_percent1"); + double const_per_percent2 = as_double("const_per_percent2"); + double const_per_percent3 = as_double("const_per_percent3"); + double const_per_percent4 = as_double("const_per_percent4"); + double const_per_percent5 = as_double("const_per_percent5"); + double const_per_upfront_rate1 = as_double("const_per_upfront_rate1"); + double const_per_upfront_rate2 = as_double("const_per_upfront_rate2"); + double const_per_upfront_rate3 = as_double("const_per_upfront_rate3"); + double const_per_upfront_rate4 = as_double("const_per_upfront_rate4"); + double const_per_upfront_rate5 = as_double("const_per_upfront_rate5"); + + double const_per_principal1, const_per_principal2, const_per_principal3, const_per_principal4, const_per_principal5; + double const_per_interest1, const_per_interest2, const_per_interest3, const_per_interest4, const_per_interest5; + double const_per_total1, const_per_total2, const_per_total3, const_per_total4, const_per_total5; + double const_per_percent_total, const_per_principal_total, const_per_interest_total, construction_financing_cost; + + const_per_principal1 = const_per_principal2 = const_per_principal3 = const_per_principal4 = const_per_principal5 = + const_per_interest1 = const_per_interest2 = const_per_interest3 = const_per_interest4 = const_per_interest5 = + const_per_total1 = const_per_total2 = const_per_total3 = const_per_total4 = const_per_total5 = + const_per_percent_total = const_per_principal_total = const_per_interest_total = construction_financing_cost = + std::numeric_limits::quiet_NaN(); + + N_financial_parameters::construction_financing_total_cost(total_installed_cost, + const_per_interest_rate1, const_per_interest_rate2, const_per_interest_rate3, const_per_interest_rate4, const_per_interest_rate5, + const_per_months1, const_per_months2, const_per_months3, const_per_months4, const_per_months5, + const_per_percent1, const_per_percent2, const_per_percent3, const_per_percent4, const_per_percent5, + const_per_upfront_rate1, const_per_upfront_rate2, const_per_upfront_rate3, const_per_upfront_rate4, const_per_upfront_rate5, + const_per_principal1, const_per_principal2, const_per_principal3, const_per_principal4, const_per_principal5, + const_per_interest1, const_per_interest2, const_per_interest3, const_per_interest4, const_per_interest5, + const_per_total1, const_per_total2, const_per_total3, const_per_total4, const_per_total5, + const_per_percent_total, const_per_principal_total, const_per_interest_total, construction_financing_cost); + + assign("const_per_principal1", (ssc_number_t)const_per_principal1); + assign("const_per_principal2", (ssc_number_t)const_per_principal2); + assign("const_per_principal3", (ssc_number_t)const_per_principal3); + assign("const_per_principal4", (ssc_number_t)const_per_principal4); + assign("const_per_principal5", (ssc_number_t)const_per_principal5); + assign("const_per_interest1", (ssc_number_t)const_per_interest1); + assign("const_per_interest2", (ssc_number_t)const_per_interest2); + assign("const_per_interest3", (ssc_number_t)const_per_interest3); + assign("const_per_interest4", (ssc_number_t)const_per_interest4); + assign("const_per_interest5", (ssc_number_t)const_per_interest5); + assign("const_per_total1", (ssc_number_t)const_per_total1); + assign("const_per_total2", (ssc_number_t)const_per_total2); + assign("const_per_total3", (ssc_number_t)const_per_total3); + assign("const_per_total4", (ssc_number_t)const_per_total4); + assign("const_per_total5", (ssc_number_t)const_per_total5); + assign("const_per_percent_total", (ssc_number_t)const_per_percent_total); + assign("const_per_principal_total", (ssc_number_t)const_per_principal_total); + assign("const_per_interest_total", (ssc_number_t)const_per_interest_total); + assign("construction_financing_cost", (ssc_number_t)construction_financing_cost); + + + + // ***************************************************** + // If calling cmod to run design only, return here + if (as_integer("sim_type") != 1) { + return; + } + // ***************************************************** + // ***************************************************** + + + update("Begin timeseries simulation...", 0.0); + + try + { + // Simulate ! + csp_solver.Ssimulate(sim_setup); + } + catch (C_csp_exception& csp_exception) + { + // Report warning before exiting with error + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg); + } + + throw exec_error("tcsmolten_salt", csp_exception.m_error_message); + } + + // If no exception, then report messages + while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) + { + log(out_msg, out_type); + } + + size_t count; + ssc_number_t* p_q_dot_heat_sink = as_array("q_dot_to_heat_sink", &count); + + // 'adjustment_factors' class stores factors in hourly array, so need to index as such + adjustment_factors haf(this, "adjust"); + if (!haf.setup(count)) + throw exec_error("tcsmolten_salt", "failed to setup adjustment factors: " + haf.error()); + + ssc_number_t* p_gen = allocate("gen", count); + ssc_number_t* p_W_dot_parasitic_tot = as_array("W_dot_parasitic_tot", &count); + ssc_number_t* p_W_dot_par_tot_haf = allocate("W_dot_par_tot_haf", n_steps_fixed); + + + ssc_number_t* p_time_final_hr = as_array("time_hr", &count); + + for (size_t i = 0; i < count; i++) + { + size_t hour = (size_t)ceil(p_time_final_hr[i]); + p_gen[i] = (ssc_number_t)(p_q_dot_heat_sink[i] * 1.E3 * haf(hour)); //[kWt] + p_W_dot_parasitic_tot[i] *= -1.0; //[MWe] Label is total parasitics, so change to a positive value + p_W_dot_par_tot_haf[i] = (ssc_number_t)(p_W_dot_parasitic_tot[i] * haf(hour) * 1.E3); //[kWe] apply availability derate and convert from MWe + + } + + ssc_number_t* p_annual_energy_dist_time = gen_heatmap(this, steps_per_hour); + + accumulate_annual_for_year("gen", "annual_energy", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); //[kWt-hr] + + // This term currently includes TES freeze protection + accumulate_annual_for_year("W_dot_par_tot_haf", "annual_electricity_consumption", sim_setup.m_report_step / 3600.0, steps_per_hour); //[kWe-hr] + + double V_water_mirrors = as_double("water_usage_per_wash") / 1000.0 * A_sf * as_double("washing_frequency"); + assign("annual_total_water_use", (ssc_number_t) V_water_mirrors); + + ssc_number_t ae = as_number("annual_energy"); //[kWt-hr] + double nameplate = q_dot_pc_des * 1.E3; //[kWt] + double kWh_per_kW = ae / nameplate; + assign("capacity_factor", (ssc_number_t)(kWh_per_kW / 8760. * 100.)); + assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); + + + // Do unit post-processing here + + // Convert mass flow rates from [kg/hr] to [kg/s] + size_t count_m_dot_rec = 0; + ssc_number_t* p_m_dot_rec = as_array("m_dot_rec", &count_m_dot_rec); + if (count_m_dot_rec != n_steps_fixed) + { + log("At least one m_dot array is a different length than 'n_steps_fixed'.", SSC_WARNING); + return; + } + for (size_t i = 0; i < n_steps_fixed; i++) + { + p_m_dot_rec[i] = (ssc_number_t)(p_m_dot_rec[i] / 3600.0); //[kg/s] convert from kg/hr + } + + // Set output data from heliostat class + size_t n_rows_eta_map = heliostatfield.ms_params.m_eta_map.nrows(); + ssc_number_t* eta_map_out = allocate("eta_map_out", n_rows_eta_map, 3); + size_t n_rows_flux_maps = heliostatfield.ms_params.m_flux_maps.nrows(); + size_t n_cols_flux_maps = heliostatfield.ms_params.m_flux_maps.ncols() + 2; + ssc_number_t* flux_maps_out = allocate("flux_maps_out", n_rows_eta_map, n_cols_flux_maps); + ssc_number_t* flux_maps_for_import = allocate("flux_maps_for_import", n_rows_eta_map, n_cols_flux_maps); + + if (n_rows_eta_map != n_rows_flux_maps) + { + log("The number of rows in the field efficiency and receiver flux map matrices are not equal. This is unexpected, and the flux maps may be inaccurate."); + } + + // [W/m2 * m2 / (m2_per_panel?)] + double flux_scaling_mult = as_double("dni_des") * heliostatfield.ms_params.m_A_sf / 1000.0 / + (A_rec / double(heliostatfield.ms_params.m_n_flux_x)); + + for (size_t i = 0; i < n_rows_eta_map; i++) + { + flux_maps_out[n_cols_flux_maps * i] = eta_map_out[3 * i] = (ssc_number_t)heliostatfield.ms_params.m_eta_map(i, 0); //[deg] Solar azimuth angle + flux_maps_out[n_cols_flux_maps * i + 1] = eta_map_out[3 * i + 1] = (ssc_number_t)heliostatfield.ms_params.m_eta_map(i, 1); //[deg] Solar zenith angle + flux_maps_for_import[n_cols_flux_maps * i] = eta_map_out[3 * i] = (ssc_number_t)heliostatfield.ms_params.m_eta_map(i, 0); //[deg] Solar azimuth angle + flux_maps_for_import[n_cols_flux_maps * i + 1] = eta_map_out[3 * i + 1] = (ssc_number_t)heliostatfield.ms_params.m_eta_map(i, 1); //[deg] Solar zenith angle + eta_map_out[3 * i + 2] = (ssc_number_t)heliostatfield.ms_params.m_eta_map(i, 2); //[deg] Solar field optical efficiency + for (size_t j = 2; j < n_cols_flux_maps; j++) + { + flux_maps_out[n_cols_flux_maps * i + j] = (ssc_number_t)(heliostatfield.ms_params.m_flux_maps(i, j - 2) * heliostatfield.ms_params.m_eta_map(i, 2) * flux_scaling_mult); //[kW/m^2] + flux_maps_for_import[n_cols_flux_maps * i + j] = (ssc_number_t)heliostatfield.ms_params.m_flux_maps(i, j - 2); + } + } + + accumulate_annual_for_year("Q_thermal", "annual_q_rec_htf", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); //[MWt-hr] + accumulate_annual_for_year("q_dot_rec_inc", "annual_q_rec_inc", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); //[MWt-hr] + accumulate_annual_for_year("q_thermal_loss", "annual_q_rec_loss", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("q_piping_losses", "annual_q_piping_loss", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("q_startup", "annual_q_rec_startup", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("P_tower_pump", "annual_E_tower_pump", sim_setup.m_report_step / 3600.0, steps_per_hour, 1, n_steps_fixed / steps_per_hour); + + assign("annual_eta_rec_th", (ssc_number_t)(1.0 - as_number("annual_q_rec_loss") / as_number("annual_q_rec_inc"))); + assign("annual_eta_rec_th_incl_refl", (ssc_number_t)(as_number("rec_absorptance") * as_number("annual_eta_rec_th"))); + + size_t count_df; + ssc_number_t* p_defocus = as_array("defocus", &count_df); + size_t count_q_rec_in; + ssc_number_t* p_q_rec_in = as_array("q_dot_rec_inc", &count_q_rec_in); + + double q_defocus_sum = 0.0; + double i_defocus; + for (size_t i = 0; i < count_df; i++) { + i_defocus = min(1.0, max(0.0, p_defocus[i])); + q_defocus_sum += p_q_rec_in[i] * (1.0 - i_defocus); //[MWt] + } + q_defocus_sum *= sim_setup.m_report_step / 3600.0; //[MWt-hr] + assign("annual_q_defocus_est", q_defocus_sum); //[MWt-hr] + + std::clock_t clock_end = std::clock(); + double sim_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] + assign("sim_cpu_run_time", sim_cpu_run_time); //[s] + + } +}; + +DEFINE_MODULE_ENTRY(mspt_iph, "CSP molten salt power tower with hierarchical controller and dispatch optimization", 1) diff --git a/ssc/sscapi.cpp b/ssc/sscapi.cpp index ff4d307f0..80cd86218 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -117,8 +117,11 @@ extern module_entry_info cm_entry_iph_to_lcoefcr, cm_entry_tcsgeneric_solar, cm_entry_tcsmolten_salt, + cm_entry_mspt_iph, cm_entry_mspt_sf_and_rec_isolated, cm_entry_ptes_design_point, + cm_entry_fresnel_physical, + cm_entry_fresnel_physical_iph, cm_entry_tcslinear_fresnel, cm_entry_linear_fresnel_dsg_iph, cm_entry_tcsmslf, @@ -151,6 +154,7 @@ extern module_entry_info cm_entry_battwatts, cm_entry_fuelcell, cm_entry_lcoefcr, + cm_entry_lcoefcr_design, cm_entry_pv_get_shade_loss_mpp, cm_entry_inv_cec_cg, cm_entry_thermalrate, @@ -219,7 +223,10 @@ static module_entry_info *module_table[] = { &cm_entry_iph_to_lcoefcr, &cm_entry_tcsgeneric_solar, &cm_entry_tcsmolten_salt, + &cm_entry_mspt_iph, &cm_entry_mspt_sf_and_rec_isolated, + &cm_entry_fresnel_physical, + &cm_entry_fresnel_physical_iph, &cm_entry_ptes_design_point, &cm_entry_tcslinear_fresnel, &cm_entry_linear_fresnel_dsg_iph, @@ -253,6 +260,7 @@ static module_entry_info *module_table[] = { &cm_entry_battwatts, &cm_entry_fuelcell, &cm_entry_lcoefcr, + &cm_entry_lcoefcr_design, &cm_entry_pv_get_shade_loss_mpp, &cm_entry_inv_cec_cg, &cm_entry_thermalrate, diff --git a/tcs/CMakeLists.txt b/tcs/CMakeLists.txt index ad2df2ba3..09a6263b3 100644 --- a/tcs/CMakeLists.txt +++ b/tcs/CMakeLists.txt @@ -17,6 +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_gen_collector_receiver.cpp csp_solver_lf_dsg_collector_receiver.cpp csp_solver_mono_eq_methods.cpp @@ -109,6 +110,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_gen_collector_receiver.h csp_solver_lf_dsg_collector_receiver.h csp_solver_mspt_collector_receiver.h diff --git a/tcs/csp_dispatch.cpp b/tcs/csp_dispatch.cpp index 27991b2f0..3534ea96f 100644 --- a/tcs/csp_dispatch.cpp +++ b/tcs/csp_dispatch.cpp @@ -267,10 +267,12 @@ bool csp_dispatch_opt::predict_performance(int step_start, int ntimeints, int di double q_inc = Asf * opt_eff * dni * 1.e-6; //MW //get thermal efficiency - double therm_eff = pointers.col_rec->calculate_thermal_efficiency_approx(pointers.m_weather.ms_outputs, q_inc); + double therm_eff = pointers.col_rec->calculate_thermal_efficiency_approx(pointers.m_weather.ms_outputs, q_inc, simloc); therm_eff *= params.sf_effadj; therm_eff_ave += therm_eff * ave_weight; + //C_csp_fresnel_collector_receiver x; + //store the predicted field energy output // use the cold tank temperature as a surrogate for the loop inlet temperature, as it // closely follows the loop inlet temperature, and is more representative over the diff --git a/tcs/csp_solver_core.h b/tcs/csp_solver_core.h index f20e931be..148787908 100644 --- a/tcs/csp_solver_core.h +++ b/tcs/csp_solver_core.h @@ -504,7 +504,7 @@ class C_csp_collector_receiver virtual double calculate_optical_efficiency( const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim ) = 0; - virtual double calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_incident ) = 0; //very approximate thermal efficiency for optimization projections + virtual double calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_incident, const C_csp_solver_sim_info& sim) = 0; //very approximate thermal efficiency for optimization projections virtual double get_collector_area() = 0; }; diff --git a/tcs/csp_solver_cr_electric_resistance.cpp b/tcs/csp_solver_cr_electric_resistance.cpp index 84536f71e..ea357fee4 100644 --- a/tcs/csp_solver_cr_electric_resistance.cpp +++ b/tcs/csp_solver_cr_electric_resistance.cpp @@ -435,7 +435,7 @@ double C_csp_cr_electric_resistance::calculate_optical_efficiency(const C_csp_we return std::numeric_limits::quiet_NaN(); } -double C_csp_cr_electric_resistance::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +double C_csp_cr_electric_resistance::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) { throw(C_csp_exception("C_csp_cr_electric_resistance::calculate_thermal_efficiency_approx(...) is not complete")); return std::numeric_limits::quiet_NaN(); diff --git a/tcs/csp_solver_cr_electric_resistance.h b/tcs/csp_solver_cr_electric_resistance.h index a2b8b9da0..7b6d16fcf 100644 --- a/tcs/csp_solver_cr_electric_resistance.h +++ b/tcs/csp_solver_cr_electric_resistance.h @@ -158,7 +158,7 @@ class C_csp_cr_electric_resistance : public C_csp_collector_receiver virtual double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim); - virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/); + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_cr_heat_pump.cpp b/tcs/csp_solver_cr_heat_pump.cpp index efd7e7a9e..9f44d2211 100644 --- a/tcs/csp_solver_cr_heat_pump.cpp +++ b/tcs/csp_solver_cr_heat_pump.cpp @@ -579,7 +579,7 @@ double C_csp_cr_heat_pump::calculate_optical_efficiency(const C_csp_weatherreade throw(C_csp_exception("C_csp_cr_heat_pump::calculate_optical_efficiency() is not complete")); } -double C_csp_cr_heat_pump::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +double C_csp_cr_heat_pump::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) { throw(C_csp_exception("C_csp_cr_heat_pump::calculate_thermal_efficiency() is not complete")); } diff --git a/tcs/csp_solver_cr_heat_pump.h b/tcs/csp_solver_cr_heat_pump.h index 3c7743eeb..8b39e06ef 100644 --- a/tcs/csp_solver_cr_heat_pump.h +++ b/tcs/csp_solver_cr_heat_pump.h @@ -299,7 +299,7 @@ class C_csp_cr_heat_pump : public C_csp_collector_receiver virtual double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim); - virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/); + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp new file mode 100644 index 000000000..969f6ff13 --- /dev/null +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -0,0 +1,4657 @@ +#include "csp_solver_fresnel_collector_receiver.h" +#include "Toolbox.h" + +using namespace std; + +static C_csp_reported_outputs::S_output_info S_output_info[] = +{ + {C_csp_fresnel_collector_receiver::E_EQUIV_OPT_ETA_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_DEFOCUS, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + {C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_INC_SF_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_REC_INC, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_REC_THERMAL_LOSS, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_REC_THERMAL_EFF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_REC_ABS, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_PIPING_LOSS, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_E_DOT_INTERNAL_ENERGY, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_HTF_OUT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DOT_FREEZE_PROT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + {C_csp_fresnel_collector_receiver::E_M_DOT_LOOP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_IS_RECIRCULATING, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_RECIRC, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_M_DOT_FIELD_DELIVERED, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_FIELD_COLD_IN, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_REC_COLD_IN, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_REC_HOT_OUT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_FIELD_HOT_OUT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_PRESSURE_DROP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + {C_csp_fresnel_collector_receiver::E_W_DOT_SCA_TRACK, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_W_DOT_PUMP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + csp_info_invalid +}; + + +// ---------------------------------------------------------------------------- PRIVATE + +int C_csp_fresnel_collector_receiver::freeze_protection(const C_csp_weatherreader::S_outputs& weather, + double& T_cold_in /*K*/, double m_dot_loop /*kg/s*/, + const C_csp_solver_sim_info& sim_info, double& Q_fp /*MJ*/) +{ + C_mono_eq_freeze_prot_E_bal c_freeze_protection_eq(this, weather, m_dot_loop, sim_info); + C_monotonic_eq_solver c_fp_solver(c_freeze_protection_eq); + + // Set upper and lower bounds on T_htf_cold_in + double T_htf_cold_in_lower = T_cold_in; //[K] + double T_htf_cold_in_upper = std::numeric_limits::quiet_NaN(); //[K] + + // Set two initial guess values + double T_htf_guess_lower = (m_Q_field_losses_total_subts / sim_info.ms_ts.m_step) * 1.E6 / + (m_c_htf_ave_ts_ave_temp * m_m_dot_htf_tot) + T_cold_in; //[K] + + double T_htf_guess_upper = T_htf_guess_lower + 10.0; //[K] + + // Set solver settings - relative error on E_balance + c_fp_solver.settings(0.01, 30, T_htf_cold_in_lower, T_htf_cold_in_upper, false); + + int iter_solved = -1; + double tol_solved = std::numeric_limits::quiet_NaN(); + + int fp_code = 0; + double T_cold_in_solved = std::numeric_limits::quiet_NaN(); + + try + { + fp_code = c_fp_solver.solve(T_htf_guess_lower, T_htf_guess_upper, 0.0, T_cold_in_solved, tol_solved, iter_solved); + } + catch (C_csp_exception) + { + throw(C_csp_exception("C_csp_fresnel_collector::off - freeze protection failed")); + } + + if (fp_code != C_monotonic_eq_solver::CONVERGED) + { + throw(C_csp_exception("C_csp_fresnel_collector::off - freeze protection failed to converge")); + } + + T_cold_in = T_cold_in_solved; //[K] + Q_fp = c_freeze_protection_eq.m_Q_htf_fp; //[MJ] + + return fp_code; +} + +double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double m_dot_field, double P_field_in, + const std::vector& T_in_SCA, const std::vector& T_out_SCA) +{ + std::vector DP_tube(m_nMod, 0.); + + double DP_IOCOP = 0; + double DP_loop_tot = 0; + double DP_toField = 0; + double DP_fromField = 0; + double DP_hdr_cold = 0; + double DP_hdr_hot = 0; + double m_dot_hdr_in, m_dot_hdr, m_dot_temp; + double rho_hdr_cold; + + double m_dot_htf = m_dot_field / (double)m_nLoops; + double T_loop_in = T_in_SCA[0]; + double T_loop_out = T_out_SCA[m_nMod - 1]; + + //handle loop pressure drop based on the heat loss model selection + switch (m_rec_model) + { + + //Polynomial heat loss model + case 1: + { + /* + This option doesn't require specific knowledge about absorber geometry, so we cannot calculate + pressure drop from first principles. Instead use coefficients and scaling polynomials provided + by the user. + */ + + DP_loop_tot = (float)m_nMod * m_DP_nominal * CSP::poly_eval(m_dot_htf / m_m_dot_design, &m_DP_coefs[0], m_DP_coefs.size()); + + break; + } + + //Evacuated tube receiver model + case 2: + { + //------Inlet, Outlet, and COP + + DP_IOCOP = PressureDrop(m_dot_htf, (T_loop_in + T_loop_out) / 2.0, 1.0, m_D_h.at(0), + m_HDR_rough, (40. + m_L_crossover), 0.0, 0.0, 2.0, 0.0, 0.0, 2.0, 0.0, 0.0, 2.0, 1.0, 0.0); + + //-------HCE's + DP_tube.resize(m_nMod, 0); + for (int j = 0; j < m_nRecVar; j++) { + for (int i = 0; i < m_nMod; i++) { + + //Account for extra fittings on the first HCE + double x1, x2; + if (i == 0) { + x1 = 10.0; + x2 = 3.0; + } + else { + x1 = 0.0; + x2 = 1.0; + } + + double T_htf_ave = (T_in_SCA[i] + T_out_SCA[i]) / 2; + DP_tube[i] += PressureDrop(m_dot_htf, T_htf_ave, 1.0, m_D_h.at(j), (m_Rough[j] * m_D_h.at(j)), + (m_L_mod + m_L_mod_spacing), 0.0, 0.0, x1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, x2) * m_HCE_FieldFrac[j]; + //if(ErrorFound()) return 1 + } + } + //The pressure drop only across the loop + double DP_loop = 0.; + for (int j = 0; j < m_nMod; j++) + DP_loop += DP_tube[j]; + + DP_loop_tot = DP_IOCOP + DP_loop; + + break; + } + + + default: + //error + //message(TCS_ERROR, "No such heat loss model. Error in loop pressure drop calculations."); + string msg = "No such heat loss model. Error in loop pressure drop calculations."; + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return -1; + } + + + //-------SGS to field section + m_m_dot_htf_tot = m_dot_htf * float(m_nLoops); + double m_dot_run_in = std::numeric_limits::quiet_NaN(); + if (m_nfsec > 2) { //mjw 5.4.11 Correct the mass flow for situations where nfsec/2==odd + m_dot_run_in = m_m_dot_htf_tot / 2.0 * (1. - float(m_nfsec % 4) / float(m_nfsec)); + } + else { + m_dot_run_in = m_m_dot_htf_tot / 2.0; + } + double x3 = float(m_nrunsec) - 1.0; //Number of contractions/expansions + m_dot_temp = m_dot_run_in; + DP_toField = 0.0; + DP_fromField = 0.0; + for (int i = 0; i < m_nrunsec; i++) { + double rnr_toField = PressureDrop(m_dot_temp, T_loop_in, 1.0, m_D_runner[i], m_HDR_rough, m_L_runner[i], 0.0, x3, 0.0, 0.0, + max(float(CSP::nint(m_L_runner[i] / 70.)) * 4., 8.), 1.0, 0.0, 1.0, 0.0, 0.0, 0.0); //Correct for less than all mass flow passing through each section + //if(ErrorFound()) return 1 + //-------SGS from field section + double rnr_fromField = PressureDrop(m_dot_temp, T_loop_out, 1.0, m_D_runner[i], m_HDR_rough, m_L_runner[i], x3, 0.0, 0.0, 0.0, + max(float(CSP::nint(m_L_runner[i] / 70.)) * 4., 8.), 1.0, 0.0, 0.0, 0.0, 0.0, 0.0); //Correct for less than all mass flow passing through each section + //if(ErrorFound()) return 1 + + DP_toField += rnr_toField; + DP_fromField += rnr_fromField; + + m_DP_rnr[i] = rnr_toField; + m_DP_rnr[2 * m_nrunsec - i - 1] = rnr_fromField; + + if (i > 1) m_dot_temp = max(m_dot_temp - 2. * m_m_dot_htf_tot / float(m_nfsec), 0.0); + } + + + + double m_dot_header_in = m_m_dot_htf_tot / float(m_nfsec); + double m_dot_header = m_dot_header_in; + DP_hdr_cold = 0.0; + DP_hdr_hot = 0.0; + for (int i = 0; i < m_nhdrsec; i++) { + //Determine whether the particular section has an expansion valve + double x2 = 0.0; + if (i > 0) { + if (m_D_hdr[i] != m_D_hdr[i - 1]) x2 = 1.; + } + + //Calculate pressure drop in cold header and hot header sections.. both use similar information + + // COLD header + double dp_hdr_cold = PressureDrop(m_dot_header, T_loop_in, 1.0, m_D_hdr[i], m_HDR_rough, + (m_L_crossover + 4.275) * 2., 0.0, x2, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); //*m_dot_header/m_dot_header_in //mjw/tn 1.25.12 already account for m_dot_header in function call //mjw 5.11.11 scale by mass flow passing though + m_DP_hdr[i] = dp_hdr_cold; + DP_hdr_cold += dp_hdr_cold; + + // HOT header + double dp_hdr_hot = PressureDrop(m_dot_header, T_loop_out, 1.0, m_D_hdr[i], m_HDR_rough, + (m_L_crossover + 4.275) * 2., x2, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); //*m_dot_header/m_dot_header_in //mjw 5.11.11 + m_DP_hdr[2 * m_nhdrsec - i - 1] = dp_hdr_hot; + DP_hdr_hot += dp_hdr_hot; + + //Siphon off header mass flow rate at each loop. Multiply by 2 because there are 2 loops per hdr section + m_dot_header = max(m_dot_header - 2. * m_dot_htf, 0.0); + + } + + + + + // The total pressure drop in all of the piping + m_dP_total = (DP_loop_tot + DP_hdr_cold + DP_hdr_hot + DP_fromField + DP_toField); + + // Convert pressure drops to gauge pressures + m_P_rnr[0] = m_dP_total; + for (int i = 1; i < 2 * m_nrunsec; i++) { + m_P_rnr[i] = m_P_rnr[i - 1] - m_DP_rnr[i - 1]; + if (i == m_nrunsec) { m_P_rnr[i] -= (DP_hdr_cold + DP_loop_tot + DP_IOCOP + DP_hdr_hot); } + } + + m_P_hdr[0] = m_P_rnr[m_nrunsec - 1] - m_DP_rnr[m_nrunsec - 1]; // report pressures for farthest subfield + for (int i = 1; i < 2 * m_nhdrsec; i++) { + m_P_hdr[i] = m_P_hdr[i - 1] - m_DP_hdr[i - 1]; + if (i == m_nhdrsec) { m_P_hdr[i] -= (DP_loop_tot + DP_IOCOP); } + } + + // Polynomial does not have this information + //m_P_loop[0] = m_P_hdr[m_nhdrsec - 1] - m_DP_hdr[m_nhdrsec - 1]; // report pressures for farthest loop + //for (int i = 1; i < m_nMod; i++) { + // m_P_loop[i] = m_P_loop[i - 1] - m_DP_loop[i - 1]; + //} + + // The total pumping power consumption + rho_hdr_cold = m_htfProps.dens((T_in_SCA[0] + T_out_SCA[m_nMod - 1]) / 2, P_field_in); + m_W_dot_pump = m_dP_total * m_dot_field / (rho_hdr_cold * m_eta_pump) / 1.e6; //[MW] + + + m_dP_total *= 1.E-5; //[bar], convert from Pa + + return m_dP_total; +} + +void C_csp_fresnel_collector_receiver::set_output_value() +{ + mc_reported_outputs.value(E_EQUIV_OPT_ETA_TOT, m_EqOpteff * m_ftrack); //[-] + mc_reported_outputs.value(E_DEFOCUS, m_control_defocus * m_component_defocus); //[-] + + mc_reported_outputs.value(E_Q_DOT_INC_SF_TOT, m_q_dot_inc_sf_tot); //[MWt] + + mc_reported_outputs.value(E_Q_DOT_REC_INC, m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts + + m_q_dot_sca_refl_summed_fullts); //[MWt] 09.08.2023 tmb: add reflective losses (due to absorber absorptance) to receiver incident power + mc_reported_outputs.value(E_Q_DOT_REC_THERMAL_LOSS, m_q_dot_sca_loss_summed_fullts); //[MWt] + mc_reported_outputs.value(E_Q_DOT_REC_ABS, m_q_dot_sca_abs_summed_fullts); //[MWt] + + double rec_Q_inc = m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts; + double rec_Q_abs = m_q_dot_sca_abs_summed_fullts; + double rec_thermal_eff = rec_Q_inc == 0 ? 0 : rec_Q_abs / rec_Q_inc; + mc_reported_outputs.value(E_REC_THERMAL_EFF, rec_thermal_eff); + + mc_reported_outputs.value(E_Q_DOT_PIPING_LOSS, m_q_dot_xover_loss_summed_fullts + + m_q_dot_HR_cold_loss_fullts + + m_q_dot_HR_hot_loss_fullts); //[MWt] + mc_reported_outputs.value(E_E_DOT_INTERNAL_ENERGY, m_E_dot_sca_summed_fullts + + m_E_dot_xover_summed_fullts + + m_E_dot_HR_cold_fullts + + m_E_dot_HR_hot_fullts); //[MWt] + mc_reported_outputs.value(E_Q_DOT_HTF_OUT, m_q_dot_htf_to_sink_fullts); //[MWt] + mc_reported_outputs.value(E_Q_DOT_FREEZE_PROT, m_q_dot_freeze_protection); //[MWt] + + mc_reported_outputs.value(E_M_DOT_LOOP, m_m_dot_htf_tot / (double)m_nLoops); //[kg/s] + mc_reported_outputs.value(E_IS_RECIRCULATING, m_is_m_dot_recirc); //[-] + if (m_is_m_dot_recirc) + { + mc_reported_outputs.value(E_M_DOT_FIELD_RECIRC, m_m_dot_htf_tot); //[kg/s] + mc_reported_outputs.value(E_M_DOT_FIELD_DELIVERED, 0.0); //[kg/s] + } + else + { + mc_reported_outputs.value(E_M_DOT_FIELD_RECIRC, 0.0); //[kg/s] + mc_reported_outputs.value(E_M_DOT_FIELD_DELIVERED, m_m_dot_htf_tot); //[kg/s] + } + + mc_reported_outputs.value(E_T_FIELD_COLD_IN, m_T_sys_c_t_int_fullts - 273.15); //[C] + mc_reported_outputs.value(E_T_REC_COLD_IN, m_T_htf_c_rec_in_t_int_fullts - 273.15); //[C] + mc_reported_outputs.value(E_T_REC_HOT_OUT, m_T_htf_h_rec_out_t_int_fullts - 273.15); //[C] + mc_reported_outputs.value(E_T_FIELD_HOT_OUT, m_T_sys_h_t_int_fullts - 273.15); //[C] + mc_reported_outputs.value(E_PRESSURE_DROP, m_dP_total); //[bar] + + mc_reported_outputs.value(E_W_DOT_SCA_TRACK, m_W_dot_sca_tracking); //[MWe] + mc_reported_outputs.value(E_W_DOT_PUMP, m_W_dot_pump); //[MWe] + + return; +} + +// ------------------------------------------------------------------- PRIVATE SUPPLEMENTAL + +C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_weatherreader::S_outputs& weather, + double T_htf_cold_in /*K*/, double m_dot_htf_loop /*kg/s*/, + const C_csp_solver_sim_info& sim_info) +{ + // Calculate total field mass flow rate + m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); + + // Helpful Variables + double dt = sim_info.ms_ts.m_step; + double T_db = weather.m_tdry + 273.15; //[K] Dry bulb temperature, convert from C + double T_dp = weather.m_twet + 273.15; //[K] Dew point temperature, convert from C + + // Calculate effective sky temperature + double hour = fmod(sim_info.ms_ts.m_time / 3600.0, 24.0); //[hr] Hour of day + double T_sky; //[K] Effective sky temperature + { + if (T_dp > -300.0) + T_sky = CSP::skytemp(T_db, T_dp, hour); //[K] Effective sky temperature + else + T_sky = T_db - 20.0; + } + + + double q_dot_loss_HR_cold = 0.0; //[W] + double E_HR_cold = 0.0; //[MJ] + double E_HR_cold_htf = 0.0; //[MJ] + double E_HR_cold_losses = 0.0; //[MJ] + double E_HR_cold_bal = 0.0; //[MJ] + + // Header Properties + double rho_hdr_cold = m_htfProps.dens(m_T_sys_c_t_end_last, 1.); //[kg/m^3] + double rho_hdr_hot = m_htfProps.dens(m_T_sys_h_t_end_last, 1.); //[kg/m^3] + double c_hdr_cold_last = m_htfProps.Cp(m_T_sys_c_t_end_last) * 1000.0; //[J/kg-K] mjw 1.6.2011 Adding mc_bal to the cold header inertia + + // BULK Temperature calculations + { + // This value is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) + m_T_sys_c_t_end = (m_T_sys_c_t_end_last - T_htf_cold_in) * exp(-(m_dot_htf_loop * float(m_nLoops)) + / (m_v_cold * rho_hdr_cold + m_mc_bal_cold / c_hdr_cold_last) * dt) + T_htf_cold_in; + + // Try calculating a timestep-average Bulk Temperature (and assume it is the outlet) + // This is from trough (line 1189) + m_T_sys_c_t_int = T_htf_cold_in + ((m_v_cold * rho_hdr_cold + m_mc_bal_cold / c_hdr_cold_last) / (-m_dot_htf_loop * float(m_nLoops))) * + (m_T_sys_c_t_end_last - T_htf_cold_in) * (exp(-(m_dot_htf_loop * float(m_nLoops)) / (m_v_cold * rho_hdr_cold + m_mc_bal_cold / c_hdr_cold_last) * sim_info.ms_ts.m_step) - 1.0) + / sim_info.ms_ts.m_step; + } + + // Current Header Properties + double m_cp_sys_c_t_int = m_htfProps.Cp(m_T_sys_c_t_int) * 1000.0; //[kg/m^3] (c_hdr_cold) + + //Consider heat loss from cold piping + //Runner + { + m_Runner_hl_cold = 0.0; + m_Runner_hl_cold_tot = 0.0; + m_T_rnr[0] = m_T_sys_c_t_int; + for (int i = 0; i < m_nrunsec; i++) + { + if (i != 0) { + m_T_rnr[i] = m_T_rnr[i - 1] - m_Runner_hl_cold / (m_dot_runner(m_m_dot_htf_tot, m_nfsec, i - 1) * m_cp_sys_c_t_int); + } + m_Runner_hl_cold = m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (m_T_sys_c_t_end - T_db); //[W] + m_Runner_hl_cold_tot += m_Runner_hl_cold; + } + } + //Header + { + m_Header_hl_cold = 0.0; + m_Header_hl_cold_tot = 0.0; + + m_T_hdr[0] = m_T_rnr[m_nrunsec - 1] - m_Runner_hl_cold / (m_dot_runner(m_m_dot_htf_tot, m_nfsec, m_nrunsec - 1) * m_cp_sys_c_t_int); // T's for farthest headers + for (int i = 0; i < m_nhdrsec; i++) + { + if (i != 0) { + m_T_hdr[i] = m_T_hdr[i - 1] - m_Header_hl_cold / (m_dot_header(m_m_dot_htf_tot, m_nfsec, m_nLoops, i - 1) * m_cp_sys_c_t_int); + } + m_Header_hl_cold = m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (m_T_sys_c_t_end - T_db); //[W] + m_Header_hl_cold_tot += m_Header_hl_cold; + } + } + double Pipe_hl_cold = m_Header_hl_cold_tot + m_Runner_hl_cold_tot; + + q_dot_loss_HR_cold = m_Header_hl_cold + m_Runner_hl_cold; //[W] + E_HR_cold_losses = q_dot_loss_HR_cold * sim_info.ms_ts.m_step / 1.E6; //[MJ] + + // Internal energy change in cold runners/headers. Positive means it has gained energy (temperature) + E_HR_cold = (m_v_cold * rho_hdr_cold * m_cp_sys_c_t_int + m_mc_bal_cold) * (m_T_sys_c_t_end - m_T_sys_c_t_end_last) * 1.E-6; //[MJ] + E_HR_cold_htf = m_dot_htf_loop * float(m_nLoops) * m_cp_sys_c_t_int * (m_T_htf_in_t_int[0] - T_htf_cold_in) * sim_info.ms_ts.m_step / 1.E6; //[MJ] + E_HR_cold_bal = -E_HR_cold_losses - E_HR_cold_htf - E_HR_cold; //[MJ] + + // Calculate Loop Inlet temperature (follows original fresnel calculation) + double trough_m_T_loop_in = m_T_hdr[m_nhdrsec - 1] - m_Header_hl_cold / (m_dot_header(m_m_dot_htf_tot, m_nfsec, m_nLoops, m_nhdrsec - 1) * m_cp_sys_c_t_int); // trough calculation + m_T_loop_in = m_T_sys_c_t_end - Pipe_hl_cold / (m_dot_htf_loop * m_nLoops * m_cp_sys_c_t_int); // original fresnel calculation + m_T_loop[0] = m_T_loop_in; + m_T_htf_in_t_int[0] = m_T_loop_in; + + // Reset vectors that are populated in following for(i..nSCA) loop + { + m_q_abs_SCAtot.assign(m_q_abs_SCAtot.size(), 0.0); + m_q_loss_SCAtot.assign(m_q_loss_SCAtot.size(), 0.0); + m_q_1abs_tot.assign(m_q_1abs_tot.size(), 0.0); + m_q_reflect_tot.assign(m_q_reflect_tot.size(), 0.0); + m_E_avail.assign(m_E_avail.size(), 0.0); + m_E_accum.assign(m_E_accum.size(), 0.0); + m_E_int_loop.assign(m_E_int_loop.size(), 0.0); + // And single values... + m_EqOpteff = 0.0; + } + + // Vectors storing information for the energy balance + std::vector E_sca, E_sca_htf, E_sca_abs, E_sca_bal; //[MJ] + std::vector E_xover, E_xover_htf, E_xover_abs, E_xover_bal; + { + E_sca.resize(m_nMod); + E_sca_htf.resize(m_nMod); + E_sca_abs.resize(m_nMod); + E_sca_bal.resize(m_nMod); + + E_xover.resize(m_nMod - 1); + E_xover_htf.resize(m_nMod - 1); + E_xover_abs.resize(m_nMod - 1); + E_xover_bal.resize(m_nMod - 1); + } + + std::vector q_dot_loss_xover; //[W] + q_dot_loss_xover.resize(m_nMod - 1); + + double q_inc_total = 0.; + double q_abs_abs_total = 0.; + double q_abs_htf_total = 0.; + std::vector EqOpteffs(m_nMod, 0.); + + // MAIN SCA Temperature Solve + for (int i = 0; i < m_nMod; i++) + { + m_q_loss.assign(m_q_loss.size(), 0.0); //[W/m] + m_q_abs.assign(m_q_abs.size(), 0.0); //[W/m] + m_q_1abs.assign(m_q_1abs.size(), 0.0); //[W/m] + + double c_htf_i = 0.0; //[J/kg-K] + double rho_htf_i = 0.0; //[kg/m^3] + + double dT_loc, m_node, T_node_ave, errhl; + switch (m_rec_model) + { + // Evacuated Receiver Model + case 2: + { + + for (int j = 0; j < m_nRecVar; j++) + { + //Check to see if the field fraction for this HCE is zero. if so, don't bother calculating for this variation + if (m_HCE_FieldFrac[j] == 0.0) continue; + + double c_htf_j, rho_htf_j; + + m_evac_receiver->Calculate_Energy_Balance(m_T_htf_in_t_int[i], m_dot_htf_loop, T_db, T_sky, + weather.m_wspd, weather.m_pres * 100.0, m_q_SCA[i], j, i, false, sim_info.ms_ts.m_time / 3600.0, m_ColOptEff, + //outputs + m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs, m_q_reflect[j]); + + // Check for NaN + if (m_q_abs[j] != m_q_abs[j]) + { + return E_loop_energy_balance_exit::NaN; + } + + //Keep a running sum of all of the absorbed/lost heat for each SCA in the loop + m_q_abs_SCAtot[i] += m_q_abs[j] * m_L_mod * m_HCE_FieldFrac[j]; //[W] Heat absorbed by HTF, weighted, for SCA + m_q_loss_SCAtot[i] += m_q_loss[j] * m_L_mod * m_HCE_FieldFrac[j]; + m_q_1abs_tot[i] += m_q_1abs[j] * m_HCE_FieldFrac[j]; //losses in W/m from the absorber surface + m_q_reflect_tot[i] += m_q_reflect[j] * m_L_mod * m_HCE_FieldFrac[j]; //[W] Total reflective loss + + c_htf_i += c_htf_j * m_HCE_FieldFrac[j]; + rho_htf_i += rho_htf_j * m_HCE_FieldFrac[j]; + + //keep track of the total equivalent optical efficiency + EqOpteffs[i] += m_ColOptEff[i] * m_Shadowing[j] * m_dirt_env[j] * m_alpha_abs[j] * m_Tau_envelope[j] * m_HCE_FieldFrac[j]; + m_EqOpteff += m_ColOptEff[i] * m_Shadowing[j] * m_dirt_env[j] * m_alpha_abs[j] * m_Tau_envelope[j] * (m_L_mod / m_L_tot) * m_HCE_FieldFrac[j]; + + } + + q_inc_total += m_q_SCA[i] * m_L_mod; // [W] + q_abs_abs_total += m_q_SCA[i] * m_L_mod * EqOpteffs[i]; // [W] absorbed by absorber + q_abs_htf_total += m_q_abs_SCAtot[i]; + + //Calculate the specific heat for the node + c_htf_i *= 1000.0; //[J/kg-K] + + //Calculate the mass of HTF associated with this node + m_node = rho_htf_i * m_A_cs[0] * m_L_mod; + + + // 7.8.2016 twn: reformulate the energy balance calculations similar to the runner/headers: + // the outlet HTF temperature is equal to the bulk temperature + // THis is from physical trough (line 1330) NOT type 262 (line 1620) + m_T_htf_out_t_end[i] = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i)) * + exp(-m_dot_htf_loop * c_htf_i * sim_info.ms_ts.m_step / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); + + m_T_htf_out_t_int[i] = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + ((m_node * c_htf_i + m_mc_bal_sca * m_L_mod) / (-m_dot_htf_loop * c_htf_i) * + (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i)) * + (exp(-m_dot_htf_loop * c_htf_i * sim_info.ms_ts.m_step / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)) - 1.0)) / sim_info.ms_ts.m_step; + + //Calculate the mass of HTF associated with this node + m_node = rho_htf_i * m_A_cs[0] * m_L_mod; + + break; + } + + // Polynomial Model + case 1: + { + double V_wind = weather.m_wspd; + double dt = sim_info.ms_ts.m_step; + T_node_ave = m_T_htf_out_t_int[i]; + + //iterate to improve heat loss estimate + double errhl = 999.; + while (std::abs(errhl) > .1) { + dT_loc = T_node_ave - T_db; + m_q_loss_SCAtot[i] = m_L_mod * CSP::poly_eval(dT_loc, &m_HL_T_coefs[0], m_HL_T_coefs.size()) * CSP::poly_eval(V_wind, &m_HL_w_coefs[0], m_HL_w_coefs.size()); //W loss + m_q_abs_SCAtot[i] = m_q_SCA[i] * m_L_mod * m_ColOptEff.at(i) - m_q_loss_SCAtot[i]; + c_htf_i = m_htfProps.Cp(T_node_ave) * 1000.; //specific heat [J/kg-K] + //Calculate the mass of HTF associated with this node + rho_htf_i = m_htfProps.dens(T_node_ave, 1.0); + m_node = m_rec_htf_vol / 1000. * m_A_aperture * rho_htf_i; // [L/m2-ap]*[1 m3/1000 L]*[m2-ap]*[kg/m3] --> kg + // 7.8.2016 twn: reformulate the energy balance calculations similar to the runner/headers: + // the outlet HTF temperature is equal to the bulk temperature + // THis is from physical trough (line 1330) NOT type 262 (line 1620) + + m_T_htf_out_t_end[i] = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i)) * + exp(-m_dot_htf_loop * c_htf_i * sim_info.ms_ts.m_step / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); + + m_T_htf_out_t_int[i] = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + ((m_node * c_htf_i + m_mc_bal_sca * m_L_mod) / (-m_dot_htf_loop * c_htf_i) * + (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i)) * + (exp(-m_dot_htf_loop * c_htf_i * sim_info.ms_ts.m_step / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)) - 1.0)) / sim_info.ms_ts.m_step; + + errhl = T_node_ave - m_T_htf_out_t_int[i]; //iterate until the node temperature does not vary significantly + + T_node_ave = m_T_htf_out_t_int[i]; + } + + m_q_1abs_tot[i] = m_q_loss_SCAtot[i] / m_L_mod; + m_EqOpteff = m_eta_optical; //Use the optical efficiency as it is for this option + + break; + } + } + + //Calculate the actual amount of energy absorbed by the field that doesn't go into changing the SCA's average temperature + //Include the thermal inertia term + if (m_q_abs_SCAtot[i] > 0.0) + { + double x1 = (m_node * c_htf_i + m_L_mod * m_mc_bal_sca); + //x1 = (mass * cp of fluid in section) + (mass * cp of SCA) + m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); // GOOD + + m_E_int_loop[i] = x1 * (m_T_htf_out_t_end[i] - 298.15); //mjw 1.18.2011 energy relative to ambient + + m_E_avail[i] = max(m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step - m_E_accum[i], 0.0); //[J/s]*[hr]*[s/hr]: [J] + } + + //Set the inlet temperature of the next SCA equal to the outlet temperature of the current SCA minus the heat losses in intermediate piping + if (i < m_nMod - 1) + { + //Determine the length between SCA's to use. if halfway down the loop, use the row distance. + double L_int; + if (i == m_nMod / 2 - 1) { + L_int = 2. + m_L_crossover; + } + else { + L_int = m_L_mod_spacing; + } + + //Calculate inlet temperature of the next SCA + m_T_htf_in_t_int[i + 1] = m_T_htf_out_t_int[i] - m_Pipe_hl_coef * m_D_abs_out[0] * CSP::pi * L_int * (m_T_htf_out_t_int[i] - T_db) + / (m_dot_htf_loop * c_htf_i); + + q_dot_loss_xover[i] = m_Pipe_hl_coef * m_D_abs_out[0] * CSP::pi * L_int * (m_T_htf_out_t_int[i] - T_db); + + //Add the internal energy of the crossover piping + //TB Seems to be += (V + m) * dTemp? + //m_E_int_loop[i] = m_E_int_loop[i] + L_int * (pow(m_D_abs_out[0], 2) / 4. * pi + m_mc_bal_sca / c_htf_i) * (m_T_htf_out_t_int[i] - 298.150); + double V_xover = L_int * pow(m_D_abs_out[0], 2) / 4. * CSP::pi; + double mc_xover_fluid = V_xover * rho_htf_i * c_htf_i; + double mc_xover_structure = L_int * m_mc_bal_sca; + m_E_int_loop[i] += (mc_xover_fluid + mc_xover_structure) * (m_T_htf_out_t_int[i] - 298.150); + + E_xover[i] = 0.0; //[MJ] + E_xover_abs[i] = -q_dot_loss_xover[i] * sim_info.ms_ts.m_step / 1.E6; //[MJ] + E_xover_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_in_t_int[i + 1] - m_T_htf_out_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; //[MJ] + E_xover_bal[i] = E_xover_abs[i] - E_xover_htf[i] - E_xover[i]; //[MJ] + + } + + } + + //Set the loop outlet temperature + double T_loop_outX = m_T_htf_out_t_end[m_nMod - 1]; + + // Initialize + double q_dot_loss_HR_hot = 0.0; //[W] + double E_HR_hot = 0.0; //[MJ] + double E_HR_hot_htf = 0.0; //[MJ] + double E_HR_hot_losses = 0.0; //[MJ] + double E_HR_hot_bal = 0.0; //[MJ] + + //Calculation for heat losses from hot piping + //Header + { + m_Header_hl_hot = 0.0; // per piping section in one field subsection + m_Header_hl_hot_tot = 0.0; // total in entire field + m_T_hdr[m_nhdrsec] = T_loop_outX; // loop outlet temp. + m_c_hdr_hot = m_htfProps.Cp(T_loop_outX) * 1000.; //[kJ/kg-K] + int D_index = 0; + for (int i = m_nhdrsec; i < 2 * m_nhdrsec; i++) + { + if (i != m_nhdrsec) { + m_T_hdr[i] = m_T_hdr[i - 1] - m_Header_hl_hot / (m_dot_header(m_m_dot_htf_tot, m_nfsec, m_nLoops, i - 1) * m_c_hdr_hot); + } + + m_Header_hl_hot = m_L_crossover * m_D_hdr[D_index] * CSP::pi * m_Pipe_hl_coef * (T_loop_outX - T_db); + m_Header_hl_hot_tot += m_Header_hl_hot; + + D_index++; + } + } + //Runner + { + m_Runner_hl_hot = 0.0; // per piping section in half the field + m_Runner_hl_hot_tot = 0.0; // total in entire field + m_T_rnr[m_nrunsec] = m_T_hdr[2 * m_nhdrsec - 1] - m_Header_hl_hot / (m_dot_header(m_m_dot_htf_tot, m_nfsec, m_nLoops, 2 * m_nhdrsec - 1) * m_c_hdr_hot); + int D_index = 0; + for (int i = m_nrunsec; i < 2 * m_nrunsec; i++) + { + if (i != m_nrunsec) { + m_T_rnr[i] = m_T_rnr[i - 1] - m_Runner_hl_hot / (m_dot_runner(m_m_dot_htf_tot, m_nfsec, i - 1) * m_c_hdr_hot); + } + m_Runner_hl_hot = m_L_runner[D_index] * CSP::pi * m_D_runner[D_index] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt + m_Runner_hl_hot_tot += m_Runner_hl_hot; + D_index++; + } + } + + q_dot_loss_HR_hot = m_Header_hl_hot + m_Runner_hl_hot; //[W] + + m_T_field_out = m_T_rnr[2 * m_nrunsec - 1] - m_Runner_hl_hot / (m_dot_runner(m_m_dot_htf_tot, m_nfsec, 2 * m_nrunsec - 1) * m_c_hdr_hot); + + // Adjust the loop outlet temperature to account for thermal losses incurred in the hot header and the runner pipe + double T_sys_h_in = T_loop_outX - q_dot_loss_HR_hot / (m_dot_htf_loop * float(m_nLoops) * m_c_hdr_hot); //[C] + + // MAIN End Timestep Calculations + { + // Calculate the hot field/system/runner/header outlet temperature at the end of the timestep + m_T_sys_h_t_end = (m_T_sys_h_t_end_last - T_sys_h_in) * exp(-m_dot_htf_loop * float(m_nLoops) / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * sim_info.ms_ts.m_step) + T_sys_h_in; + + // Calculate the hot field/system/runner/header timestep-integrated-average temperature + // Try calculating a timestep-average Bulk Temperature (and assume it is the outlet) + m_T_sys_h_t_int = T_sys_h_in + ((m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) / (-m_dot_htf_loop * float(m_nLoops))) * + (m_T_sys_h_t_end_last - T_sys_h_in) * (exp(-(m_dot_htf_loop * float(m_nLoops)) / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * sim_info.ms_ts.m_step) - 1.0) + / sim_info.ms_ts.m_step; + } + + // Calculate outputs + { + double E_bal_T_h_t_ave = -(m_dot_htf_loop * float(m_nLoops) * m_c_hdr_hot * (m_T_sys_h_t_int - T_sys_h_in) * sim_info.ms_ts.m_step + + (m_v_hot * rho_hdr_hot * m_c_hdr_hot + m_mc_bal_hot) * (m_T_sys_h_t_end - m_T_sys_h_t_end_last)); //[J] + + E_HR_hot_htf = m_dot_htf_loop * float(m_nLoops) * m_c_hdr_hot * (m_T_sys_h_t_int - T_loop_outX) * sim_info.ms_ts.m_step / 1.E6; //[MJ] + + E_HR_hot = (m_v_hot * rho_hdr_hot * m_c_hdr_hot + m_mc_bal_hot) * (m_T_sys_h_t_end - m_T_sys_h_t_end_last) * 1.E-6; //[MJ] + + E_HR_hot_bal = -E_HR_hot_losses - E_HR_hot_htf - E_HR_hot; //[MJ] + + // Calculate sub-timestep reporting energy (rate) balance metrics + // Loop metrics + m_q_dot_sca_loss_summed_subts = 0.0; //[MWt] + m_q_dot_sca_abs_summed_subts = 0.0; //[MWt] + m_q_dot_sca_refl_summed_subts = 0.0; //[MWt] + m_q_dot_xover_loss_summed_subts = 0.0; //[MWt] + m_E_dot_sca_summed_subts = 0.0; //[MWt] + m_E_dot_xover_summed_subts = 0.0; //[MWt] + + for (int i = 0; i < m_nMod; i++) + { + if (i < m_nMod - 1) + { + m_q_dot_xover_loss_summed_subts += q_dot_loss_xover[i]; //[W] -> convert to MWt and multiply by nLoops below + m_E_dot_xover_summed_subts += E_xover[i]; //[MJ] -> convert to MWt and multiply by nLoops below + } + m_q_dot_sca_loss_summed_subts += m_q_loss_SCAtot[i]; //[W] -> convert to MWT and multiply by nLoops below + m_q_dot_sca_abs_summed_subts += m_q_abs_SCAtot[i]; //[W] -> convert to MWT and multiply by nLoops below + m_q_dot_sca_refl_summed_subts += m_q_reflect_tot[i]; //[W] -> convert to MWT and multiply by nLoops below + m_E_dot_sca_summed_subts += E_sca[i]; //[MJ] -> convert to MWt and multiply by nLoops below + } + m_q_dot_xover_loss_summed_subts *= 1.E-6 * m_nLoops; //[MWt] + m_E_dot_xover_summed_subts *= (m_nLoops / sim_info.ms_ts.m_step); //[MWt] + m_q_dot_sca_loss_summed_subts *= 1.E-6 * m_nLoops; //[MWt] + m_q_dot_sca_abs_summed_subts *= 1.E-6 * m_nLoops; //[MWt] + m_q_dot_sca_refl_summed_subts *= 1.E-6 * m_nLoops; //[MWt] + m_E_dot_sca_summed_subts *= (m_nLoops / sim_info.ms_ts.m_step); //[MWt] + + // Header-runner metrics + m_q_dot_HR_cold_loss_subts = q_dot_loss_HR_cold * 1.E-6; //[MWt] + m_q_dot_HR_hot_loss_subts = q_dot_loss_HR_hot * 1.E-6; //[MWt] + m_E_dot_HR_cold_subts = E_HR_cold / sim_info.ms_ts.m_step; //[MWt] + m_E_dot_HR_hot_subts = E_HR_hot / sim_info.ms_ts.m_step; //[MWt] + + // HTF out of system + m_c_htf_ave_ts_ave_temp = m_htfProps.Cp_ave(T_htf_cold_in, m_T_sys_h_t_int) * 1000.0; //[J/kg-K] + m_q_dot_htf_to_sink_subts = m_m_dot_htf_tot * m_c_htf_ave_ts_ave_temp * (m_T_sys_h_t_int - T_htf_cold_in) * 1.E-6; + + double Q_dot_balance_subts = m_q_dot_sca_abs_summed_subts - m_q_dot_xover_loss_summed_subts - + m_q_dot_HR_cold_loss_subts - m_q_dot_HR_hot_loss_subts - + m_E_dot_sca_summed_subts - m_E_dot_xover_summed_subts - + m_E_dot_HR_cold_subts - m_E_dot_HR_hot_subts - m_q_dot_htf_to_sink_subts; //[MWt] + + // Calculate total field energy balance: + double Q_abs_scas_summed = 0.0; //[MJ] + double Q_loss_xover = 0.0; //[MJ] + double E_scas_summed = 0.0; //[MJ] + double E_xovers_summed = 0.0; //[MJ] + + double E_scas_htf_summed = 0.0; //[MJ] + double E_xovers_htf_summed = 0.0; //[MJ] + + for (int i = 0; i < m_nMod; i++) + { + if (i < m_nMod - 1) + { + Q_loss_xover += q_dot_loss_xover[i]; //[W] -> convert to MJ and multiply nLoops below + E_xovers_summed += E_xover[i]; //[MJ] -> multiply nLoops below + E_xovers_htf_summed += E_xover_htf[i]; //[MJ] -> multiply by nLoops below + } + Q_abs_scas_summed += m_q_abs_SCAtot[i]; //[W] -> convert to MJ and multiply nLoops below + E_scas_summed += E_sca[i]; //[MJ] -> multiply nLoops below + E_scas_htf_summed += E_sca_htf[i]; //[MJ] -> multiply by nLoops below + } + Q_loss_xover *= sim_info.ms_ts.m_step * 1.E-6 * m_nLoops; //[MJ] = [W*s*MW/W*#loops] + Q_abs_scas_summed *= sim_info.ms_ts.m_step * 1.E-6 * m_nLoops; //[MJ] = [W*s*MW/W*#loops] + E_xovers_summed *= m_nLoops; //[MJ] multiply nLoops below + E_scas_summed *= m_nLoops; //[MJ] multiply nLoops below + + E_scas_htf_summed *= m_nLoops; //[MJ] + E_xovers_htf_summed *= m_nLoops; //[MJ] + + + double Q_htf = m_m_dot_htf_tot * m_c_htf_ave_ts_ave_temp * (m_T_sys_h_t_int - T_htf_cold_in) * sim_info.ms_ts.m_step * 1.E-6; //[MJ] + double E_htf_bal = E_HR_cold_htf + E_scas_htf_summed + E_xovers_htf_summed + E_HR_hot_htf - Q_htf; //[MJ] + + double Q_loss_HR_cold = q_dot_loss_HR_cold * sim_info.ms_ts.m_step * 1.E-6; //[MJ] + double Q_loss_HR_hot = q_dot_loss_HR_hot * sim_info.ms_ts.m_step * 1.E-6; //[MJ] + + m_Q_field_losses_total_subts = Q_loss_xover + Q_loss_HR_cold + Q_loss_HR_hot - Q_abs_scas_summed; //[MJ] + } + + + return E_loop_energy_balance_exit::SOLVED; +} + +/************************************************************************************************** + --------------------------------------------------------------------------------- + --Inputs + * nhsec - [-] number of header sections + * nfsec - [-] number of field section + * nrunsec- [-] number of unique runner diameter sections + * rho - [kg/m3] Fluid density + * V_max - [m/s] Maximum fluid velocity at design + * V_min - [m/s] Minimum fluid velocity at design + * m_dot - [kg/s] Mass flow rate at design + --Outputs + * D_hdr - [m] An ARRAY containing the header diameter for each loop section + * D_runner - [m] An ARRAY containing the diameter of the runner pipe sections + * summary - Address of string variable on which summary contents will be written. + --------------------------------------------------------------------------------- */ +void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int nrunsec, double rho, double V_max, double V_min, double m_dot, + vector& D_hdr, vector& D_runner, std::string* summary = NULL) { + + //resize the header matrices if they are incorrect + //real(8),intent(out):: D_hdr(nhsec), D_runner(nrunsec) + if ((int)D_hdr.size() != nhsec) D_hdr.resize(nhsec); + if ((int)D_runner.size() != nrunsec) D_runner.resize(nrunsec); + + //---- + int nst, nend, nd; + double m_dot_max, m_dot_min, m_dot_ts, m_dot_hdr, m_dot_2loops, m_dot_temp; + + for (int i = 0; i < nhsec; i++) { D_hdr[i] = 0.; } + + //mass flow to section is always half of total + m_dot_ts = m_dot / 2.; + //Mass flow into 1 header + m_dot_hdr = 2. * m_dot_ts / (float(nfsec)); + //Mass flow into the 2 loops attached to a single header section + m_dot_2loops = m_dot_hdr / float(nhsec); + + //Runner diameters + //runner pipe needs some length to go from the power block to the headers + D_runner.at(0) = CSP::pipe_sched(sqrt(4. * m_dot_ts / (rho * V_max * CSP::pi))); + //other runner diameters + m_dot_temp = m_dot_ts * (1. - float(nfsec % 4) / float(nfsec)); //mjw 5.4.11 Fix mass flow rate for nfsec/2==odd + if (nrunsec > 1) { + for (int i = 1; i < nrunsec; i++) { + D_runner[i] = CSP::pipe_sched(sqrt(4. * m_dot_temp / (rho * V_max * CSP::pi))); + m_dot_temp = max(m_dot_temp - m_dot_hdr * 2, 0.0); + } + } + + //Calculate each section in the header + nst = 0; nend = 0; nd = 0; + m_dot_max = m_dot_hdr; + + for (int i = 0; i < nhsec; i++) { + if ((i == nst) && (nd <= 10)) { + //If we've reached the point where a diameter adjustment must be made... + //Also, limit the number of diameter reductions to 10 + + nd++; //keep track of the total number of diameter sections + //Calculate header diameter based on max velocity + D_hdr[i] = CSP::pipe_sched(sqrt(4. * m_dot_max / (rho * V_max * CSP::pi))); + //Determine the mass flow corresponding to the minimum velocity at design + m_dot_min = rho * V_min * CSP::pi * D_hdr[i] * D_hdr[i] / 4.; + //Determine the loop after which the current diameter calculation will no longer apply + nend = (int)floor((m_dot_hdr - m_dot_min) / (m_dot_2loops)); //tn 4.12.11 ceiling->floor + //The starting loop for the next diameter section starts after the calculated ending loop + nst = nend; + //Adjust the maximum required flow rate for the next diameter section based on the previous + //section's outlet conditions + m_dot_max = max(m_dot_hdr - m_dot_2loops * float(nend), 0.0); + } + else { + //If we haven't yet reached the point where the minimum flow condition is acheived, just + //set the header diameter for this loop to be equal to the last diameter + D_hdr[i] = D_hdr.at(i - 1); + } + } + + //Print the results to a string + if (summary != NULL) { + summary->clear(); + char tstr[200]; + //Write runner diam + sprintf(tstr, "Piping geometry file\n\nMaximum fluid velocity: %.2f\nMinimum fluid velocity: %.2f\n\n", V_max, V_min); + summary->append(tstr); + + for (int i = 0; i < nrunsec; i++) { + sprintf(tstr, "To section %d header pipe diameter: %.4f m (%.2f in)\n", i + 1, D_runner[i], D_runner[i] * m_mtoinch); + summary->append(tstr); + } + //Write header diams + sprintf(tstr, "Loop No. | Diameter [m] | Diameter [in] | Diam. ID\n--------------------------------------------------\n"); + summary->append(tstr); + + nd = 1; + for (int i = 0; i < nhsec; i++) { + if (i > 1) { + if (D_hdr[i] != D_hdr.at(i - 1)) nd = nd + 1; + } + sprintf(tstr, " %4d | %6.4f | %6.4f | %3d\n", i + 1, D_hdr[i], D_hdr[i] * m_mtoinch, nd); + summary->append(tstr); + } + //110 format(2X,I4,3X,"|",4X,F6.4,4X,"|",4X,F6.3,5X,"|",1X,I3) + } + +} + +void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_sim_info& sim_info) +{ + //if (weather.m_wspd >= m_wind_stow_speed) // no wind stow speed user input + if (false) + { + loop_optical_wind_stow(); + } + else + { + // First, clear all the values calculated below + loop_optical_eta_off(); + + //calculate the m_hour of the day + double time_hr = sim_info.ms_ts.m_time / 3600.; //[hr] + double dt_hr = sim_info.ms_ts.m_step / 3600.; //[hr] + double hour = fmod(time_hr, 24.); //[hr] + + //Time calculations + int day_of_year = (int)ceil(time_hr / 24.); //Day of the year + // Duffie & Beckman 1.5.3b + double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; + // Eqn of time in minutes + double EOT = 229.2 * (0.000075 + 0.001868 * cos(B) - 0.032077 * sin(B) - 0.014615 * cos(B * 2.0) - 0.04089 * sin(B * 2.0)); + // Declination in radians (Duffie & Beckman 1.6.1) + double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; + // Solar Noon and time in hours + double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; + + // Deploy & stow times in hours + // Calculations modified by MJW 11/13/2009 to correct bug + m_theta_dep = max(m_theta_dep, 1.e-6); + double DepHr1 = cos(m_latitude) / tan(m_theta_dep); + double DepHr2 = -tan(Dec) * sin(m_latitude) / tan(m_theta_dep); + double DepHr3 = CSP::sign(tan(CSP::pi - m_theta_dep)) * acos((DepHr1 * DepHr2 + sqrt(DepHr1 * DepHr1 - DepHr2 * DepHr2 + 1.0)) / (DepHr1 * DepHr1 + 1.0)) * 180.0 / CSP::pi / 15.0; + double DepTime = SolarNoon + DepHr3; + + m_theta_stow = max(m_theta_stow, 1.e-6); + double StwHr1 = cos(m_latitude) / tan(m_theta_stow); + double StwHr2 = -tan(Dec) * sin(m_latitude) / tan(m_theta_stow); + double StwHr3 = CSP::sign(tan(CSP::pi - m_theta_stow)) * acos((StwHr1 * StwHr2 + sqrt(StwHr1 * StwHr1 - StwHr2 * StwHr2 + 1.0)) / (StwHr1 * StwHr1 + 1.0)) * 180.0 / CSP::pi / 15.0; + double StwTime = SolarNoon + StwHr3; + + // m_ftrack is the fraction of the time period that the field is tracking. MidTrack is time at midpoint of operation + double HrA = hour - dt_hr; + double HrB = hour; + + double MidTrack; + m_ftrack = std::numeric_limits::quiet_NaN(); + // Solar field operates + if ((HrB > DepTime) && (HrA < StwTime)) + { + // solar field deploys during time period + if (HrA < DepTime) + { + m_ftrack = (HrB - DepTime) / dt_hr; + MidTrack = HrB - m_ftrack * 0.5 * dt_hr; + + // Solar field stows during time period + } + else if (HrB > StwTime) + { + m_ftrack = (StwTime - HrA) / dt_hr; + MidTrack = HrA + m_ftrack * 0.5 * dt_hr; + // solar field operates during entire period + } + else + { + m_ftrack = 1.0; + MidTrack = HrA + 0.5 * dt_hr; + } + // solar field doesn't operate + } + else + { + m_ftrack = 0.0; + MidTrack = HrA + 0.5 * dt_hr; + } + + //// Maximum wind speed value NO max wind speed + //if (V_wind >= m_V_wind_max) + // m_ftrack = 0.0; + + double StdTime = MidTrack; + double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; + // m_hour angle (arc of sun) in radians + double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; + + // Convert other input data as necessary + double SolarAz = weather.m_solazi; //[deg] Solar azimuth angle + SolarAz = (SolarAz - 180.0) * m_d2r; //[rad] convert from [deg] + double SolarAlt; + // B. Stine equation for Solar Altitude angle in radians + SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); + + double SolarZenRad = weather.m_solzen * m_d2r; // Convert from degree to radian + + if (SolarZenRad < CSP::pi / 2.) { + //Convert the solar angles to collector incidence angles + CSP::theta_trans(SolarAz, SolarZenRad, m_ColAz, m_phi_t, m_theta_L); + + switch (m_opt_model) + { + case 1: //sun position + //user provides an optical table as a function of solar position + m_eta_optical = eta_opt_fixed * max(optical_table.interpolate(SolarAz, min(SolarZenRad, CSP::pi / 2.)), 0.0); + break; + case 2: //incidence angle table + //user provides an optical table as a function of collector incidence angles + m_eta_optical = eta_opt_fixed * max(optical_table.interpolate(m_phi_t, max(m_theta_L, 0.0)), 0.0); + break; + case 3: //incidence angle modifier polys + //Otherwise, calculate the collector incidence angles for the IAM equations + m_eta_optical = eta_opt_fixed * + CSP::poly_eval(m_phi_t, &m_IAM_T_coefs[0], m_IAM_T_coefs.size()) * + CSP::poly_eval(m_theta_L, &m_IAM_L_coefs[0], m_IAM_L_coefs.size()); + break; + default: + //error + //message(TCS_ERROR, "No corresponding optical model. Error in solar angle calculation."); + string msg = "No corresponding optical model. Error in solar angle calculation."; + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return; + } + + m_eta_optical *= m_ftrack; + } + else { + m_eta_optical = 0.0; + m_phi_t = CSP::pi / 2.; + m_theta_L = 0.0; + } + + double I_b = weather.m_beam; + m_q_i = I_b * m_A_aperture / m_L_mod; //[W/m] The incoming solar irradiation per aperture length + + //Optical efficiency and incident power values for each SCA + for (int j = 0; j < m_nMod; j++) { + m_ColOptEff.at(j) = m_eta_optical; + m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector + } + + // Assume that whenever fresnel is in STARTUP OR ON, we're using the nominal tracking load + // This is because it takes power to move into and out of defocus, and we'd probably + // just add complexity without any accuracy by trying to capture that + m_W_dot_sca_tracking = m_W_dot_sca_tracking_nom * m_ftrack; //[MWe] + + m_control_defocus = m_component_defocus = 1.0; //[-] + + m_q_dot_inc_sf_tot = m_Ap_tot * weather.m_beam / 1.E6; //[MWt] + + } +} + +void C_csp_fresnel_collector_receiver::loop_optical_eta_off() +{ + // If fresnel is not absorbing any sunlight (night or 100% defocus), then set member data as necessary + + m_q_i = 0; //[W/m] DNI * A_aper / L_sca + m_ColOptEff.fill(0.0); //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack + m_EqOpteff = 0.; + m_q_SCA.assign(m_q_SCA.size(), 0.0); //[W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)) + + m_W_dot_sca_tracking = 0.0; //[MWe] + + m_control_defocus = 1.0; + m_component_defocus = 1.0; + + m_q_dot_inc_sf_tot = 0.0; //[MWt] + + m_eta_optical = 0; + + return; +} + +void C_csp_fresnel_collector_receiver::loop_optical_wind_stow() +{ + // Want to completely defocus fresnel because wind speed is faster than stow speed + // Can use 'loop_optical_eta_off' but then need to reset: + // * tracking power + // * defocus values + + loop_optical_eta_off(); + + m_W_dot_sca_tracking = m_W_dot_sca_tracking_nom; //[MWe] + + m_component_defocus = 0.0; +} + +void C_csp_fresnel_collector_receiver::update_last_temps() +{ + // Update "_last" temperatures + m_T_sys_c_t_end_last = m_T_sys_c_t_end; //[K] + m_T_sys_h_t_end_last = m_T_sys_h_t_end; //[K] + for (int i = 0; i < m_nMod; i++) + { + m_T_htf_out_t_end_last[i] = m_T_htf_out_t_end[i]; //[K] + } + + return; +} + +void C_csp_fresnel_collector_receiver::reset_last_temps() +{ + // Update "_last" temperatures + m_T_sys_c_t_end_last = m_T_sys_c_t_end_converged; //[K] + m_T_sys_h_t_end_last = m_T_sys_h_t_end_converged; //[K] + for (int i = 0; i < m_nMod; i++) + { + m_T_htf_out_t_end_last[i] = m_T_htf_out_t_end_converged[i]; //[K] + } +} + +void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) +{ + // Uses m_q_i, and input defocus to calculate m_q_SCA_control_df + + // Store control defocus + m_control_defocus = defocus; + + for (int i = 0; i < m_nMod; i++) + { + m_q_SCA_control_df[i] = defocus * m_q_i; + } + +} + +void C_csp_fresnel_collector_receiver::apply_component_defocus(double defocus /*-*/) +{ + // Uses m_q_SCA_control_df and input defocus to calculate m_q_SCA + + // Store component defocus + m_component_defocus = defocus; + + for (int i = 0; i < m_nMod; i++) + { + m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; + } +} + +double C_csp_fresnel_collector_receiver::Pump_SGS(double rho, double m_dotsf, double sm) { + + int nl = 8; + double v_dotpb, v_dotsf, m_dotpb, vel_max; + double + * V_dot = new double[nl], + * D = new double[nl], + * V = new double[nl]; + + //Line no. + //1 Expansion vessel or thermal storage tank to pump suction header + //2 Individual pump suction line, from suction header to pump inlet + //3 Individual pump discharge line, from pump discharge to discharge header + //4 Pump discharge header + //5 Collector field outlet header to expansion vessel or thermal storage tank + //6 Steam generator supply header + //7 Inter steam generator piping + //8 Steam generator exit header to expansion vessel or thermal storage + //Assume standard lengths for each line [m] (Kelly & Kearney) + //Assume 3 pumps at 50% each. #3) 3*30. + double L_line[] = { 0.0, 0.0, 90.0, 100.0, 120.0, 80.0, 120.0, 80.0 }; + + //Assume a maximum HTF velocity of 1.85 m/s (based on average from Kelly & Kearney model + vel_max = 1.85; + + //design-point vol. flow rate m3/s + m_dotpb = m_dotsf / sm; + v_dotpb = m_dotpb / rho; + v_dotsf = m_dotsf / rho; + + //Set the volumetric flow rate for each line. + V_dot[0] = v_dotsf; + V_dot[1] = v_dotsf / 2.0; + V_dot[2] = V_dot[1]; + V_dot[3] = v_dotsf; + V_dot[4] = V_dot[3]; + V_dot[5] = v_dotpb; + V_dot[6] = V_dot[5]; + V_dot[7] = V_dot[5]; + + //for each line.. + double psum = 0.; + for (int i = 0; i < nl; i++) { + //Calculate the pipe diameter + D[i] = CSP::pipe_sched(sqrt(4.0 * V_dot[i] / (vel_max * CSP::pi))); + //Calculate the total volume + V[i] = pow(D[i], 2) / 4. * CSP::pi * L_line[i]; + psum += V[i]; + } + + delete[] V_dot; + delete[] D; + delete[] V; + + return psum; + +} + +/* + *************************************************************************************************** + Trough system piping loss model + *************************************************************************************************** + + This piping loss model is derived from the pressure drop calculations presented in the + following document: + + Parabolic Trough Solar System Piping Model + + B. Kelly + Nexant, Inc. San Francisco, California + + D. Kearney + Kearney & Associates + Vashon, Washington + + Subcontract Report + NREL/SR-550-40165 + July 2006 + + ---------------------------- + Note on use of this function + ---------------------------- + The function returns the pressure drop across a given length of pipe, and also accounts for + a variety of possible pressure-loss components. This function should be called multiple times - + once for each section under consideration. For example, separate calls should be made for the + HCE pressure drop, the pressure drop in each section of the header in which flow/geometrical + conditions vary, the section of pipe leading to the header, and so on. + + ---------------------------- + Inputs + ---------------------------- + No | Name | Description | Units | Type + =================================================================================== + 1 | Fluid | Number associated with fluid type | none | float + 2 | m_dot | Mass flow rate of the fluid | kg/s | float + 3 | T | Fluid temperature | K | float + 4 | P | Fluid pressure | Pa | float + 5 | D | Diameter of the contact surface | m | float + 6 | Rough | Pipe roughness | m | float + 7 | L_pipe | Length of pipe for pressure drop | m | float + 8 | Nexp | Number of expansions | none | float + 9 | Ncon | Number of contractions | none | float + 10 | Nels | Number of standard elbows | none | float + 11 | Nelm | Number of medium elbows | none | float + 12 | Nell | Number of long elbows | none | float + 13 | Ngav | Number of gate valves | none | float + 14 | Nglv | Number of globe valves | none | float + 15 | Nchv | Number of check valves | none | float + 16 | Nlw | Number of loop weldolets | none | float + 17 | Nlcv | Number of loop control valves | none | float + 18 | Nbja | Number of ball joint assemblies | none | float + =================================================================================== + ---------------------------- + Outputs + ---------------------------- + 1. PressureDrop (Pa) + */ +double C_csp_fresnel_collector_receiver::PressureDrop(double m_dot, double T, double P, double D, double Rough, double L_pipe, + double Nexp, double Ncon, double Nels, double Nelm, double Nell, double Ngav, double Nglv, + double Nchv, double Nlw, double Nlcv, double Nbja) { + + double rho, v_dot, mu, nu, u_fluid, Re, f, DP_pipe, DP_exp, DP_con, DP_els, DP_elm, DP_ell, DP_gav, + DP_glv, DP_chv, DP_lw, DP_lcv, DP_bja, HL_pm; + + //Calculate fluid properties and characteristics + rho = m_htfProps.dens(T, P); + mu = m_htfProps.visc(T); + nu = mu / rho; + v_dot = m_dot / rho; //fluid volumetric flow rate + u_fluid = v_dot / (CSP::pi * (D / 2.) * (D / 2.)); //Fluid mean velocity + + //Dimensionless numbers + Re = u_fluid * D / nu; + //if(Re<2300.) then + // f = 64./max(Re,1.0) + //else + f = FricFactor(Rough / D, Re); + if (f == 0) return std::numeric_limits::quiet_NaN(); + //} + + //Calculation of pressure loss from pipe length + HL_pm = f * u_fluid * u_fluid / (2. * D * CSP::grav); + DP_pipe = HL_pm * rho * CSP::grav * L_pipe; + + //Calculation of pressure loss from Fittings + DP_exp = 0.25 * rho * u_fluid * u_fluid * Nexp; + DP_con = 0.25 * rho * u_fluid * u_fluid * Ncon; + DP_els = 0.9 * D / f * HL_pm * rho * CSP::grav * Nels; + DP_elm = 0.75 * D / f * HL_pm * rho * CSP::grav * Nelm; + DP_ell = 0.6 * D / f * HL_pm * rho * CSP::grav * Nell; + DP_gav = 0.19 * D / f * HL_pm * rho * CSP::grav * Ngav; + DP_glv = 10.0 * D / f * HL_pm * rho * CSP::grav * Nglv; + DP_chv = 2.5 * D / f * HL_pm * rho * CSP::grav * Nchv; + DP_lw = 1.8 * D / f * HL_pm * rho * CSP::grav * Nlw; + DP_lcv = 10.0 * D / f * HL_pm * rho * CSP::grav * Nlcv; + DP_bja = 8.69 * D / f * HL_pm * rho * CSP::grav * Nbja; + + return DP_pipe + DP_exp + DP_con + DP_els + DP_elm + DP_ell + DP_gav + DP_glv + DP_chv + DP_lw + DP_lcv + DP_bja; + +} + +/************************************************************************************************** + Friction factor (taken from Piping loss model) +*************************************************************************************************** + Uses an iterative method to solve the implicit friction factor function. + For more on this method, refer to Fox, et al., 2006 Introduction to Fluid Mechanics. */ +double C_csp_fresnel_collector_receiver::FricFactor(double Rough, double Reynold) { + + double Test, TestOld, X, Xold, Slope; + double Acc = .01; //0.0001 + int NumTries; + + if (Reynold < 2750.) { + return 64. / max(Reynold, 1.0); + } + + X = 33.33333; //1. / 0.03 + TestOld = X + 2. * log10(Rough / 3.7 + 2.51 * X / Reynold); + Xold = X; + X = 28.5714; //1. / (0.03 + 0.005) + NumTries = 0; + + while (NumTries < 21) { + NumTries++; + Test = X + 2 * log10(Rough / 3.7 + 2.51 * X / Reynold); + if (std::abs(Test - TestOld) <= Acc) { + return 1. / (X * X); + } + + Slope = (Test - TestOld) / (X - Xold); + Xold = X; + TestOld = Test; + X = max((Slope * X - Test) / Slope, 1.e-5); + } + + //call Messages(-1," Could not find friction factor solution",'Warning',0,250) + return 0; +} + +// ******************************************************************** Private STATIC Methods + +// Returns runner mass flow for a given runner index +double C_csp_fresnel_collector_receiver::m_dot_runner(double m_dot_field, int nfieldsec, int irnr) +{ + int nrnrsec = (int)floor(float(nfieldsec) / 4.0) + 1; + + if (irnr < 0 || irnr > 2 * nrnrsec - 1) { throw std::invalid_argument("Invalid runner index"); } + + int irnr_onedir; + double m_dot_rnr; + double m_dot_rnr_0; + double m_dot_rnr_1; + + // convert index to a mass flow equivalent cold runner index + if (irnr > nrnrsec - 1) { + irnr_onedir = 2 * nrnrsec - irnr - 1; + } + else { + irnr_onedir = irnr; + } + + m_dot_rnr_0 = m_dot_field / 2.; + m_dot_rnr_1 = m_dot_rnr_0 * (1. - float(nfieldsec % 4) / float(nfieldsec)); + + switch (irnr_onedir) { + case 0: + m_dot_rnr = m_dot_rnr_0; + case 1: + m_dot_rnr = m_dot_rnr_1; + default: + m_dot_rnr = m_dot_rnr_1 - (irnr_onedir - 1) * m_dot_field / float(nfieldsec) * 2; + } + + return max(m_dot_rnr, 0.0); +} + +// Returns header mass flow for a given header index +double C_csp_fresnel_collector_receiver::m_dot_header(double m_dot_field, int nfieldsec, int nLoopsField, int ihdr) +{ + int nhdrsec = (int)ceil(float(nLoopsField) / float(nfieldsec * 2)); // in the cold or hot headers + + if (ihdr < 0 || ihdr > 2 * nhdrsec - 1) { throw std::invalid_argument("Invalid header index"); } + + int ihdr_onedir; + + // convert index to a mass flow equivalent cold header index + if (ihdr > nhdrsec - 1) { + ihdr_onedir = 2 * nhdrsec - ihdr - 1; + } + else { + ihdr_onedir = ihdr; + } + + double m_dot_oneloop = m_dot_field / float(nLoopsField); + return m_dot_field / float(nfieldsec) - ihdr_onedir * 2 * m_dot_oneloop; +} + +// ------------------------------------------------------------------------------ PUBLIC + +C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() +{ + mc_reported_outputs.construct(S_output_info); + + // Set maximum timestep from parent class member data + m_max_step = 60.0 * 60.0; //[s]: [m] * [s/m] + m_step_recirc = 10.0 * 60.0; //[s] + + m_W_dot_sca_tracking_nom = std::numeric_limits::quiet_NaN(); + + // set initial values for all parameters to prevent possible misuse + m_nMod = -1; + m_nRecVar = -1; + m_nLoops = -1; + m_FieldConfig = -1; + m_eta_pump = std::numeric_limits::quiet_NaN(); + m_HDR_rough = std::numeric_limits::quiet_NaN(); + m_theta_stow = std::numeric_limits::quiet_NaN(); + m_theta_dep = std::numeric_limits::quiet_NaN(); + m_T_startup = std::numeric_limits::quiet_NaN(); + m_m_dot_htfmin = std::numeric_limits::quiet_NaN(); + m_m_dot_htfmax = std::numeric_limits::quiet_NaN(); + m_T_loop_in_des = std::numeric_limits::quiet_NaN(); + m_T_loop_out_des = std::numeric_limits::quiet_NaN(); + m_Fluid = -1; + + m_m_dot_design = std::numeric_limits::quiet_NaN(); + m_m_dot_loop_des = std::numeric_limits::quiet_NaN(); + + m_T_fp = std::numeric_limits::quiet_NaN(); + m_I_bn_des = std::numeric_limits::quiet_NaN(); + m_V_hdr_max = std::numeric_limits::quiet_NaN(); + m_V_hdr_min = std::numeric_limits::quiet_NaN(); + m_Pipe_hl_coef = std::numeric_limits::quiet_NaN(); + m_SCA_drives_elec = std::numeric_limits::quiet_NaN(); + m_ColAz = std::numeric_limits::quiet_NaN(); + + m_solar_mult = std::numeric_limits::quiet_NaN(); + m_mc_bal_hot = std::numeric_limits::quiet_NaN(); + m_mc_bal_cold = std::numeric_limits::quiet_NaN(); + m_mc_bal_sca = std::numeric_limits::quiet_NaN(); + + m_latitude = std::numeric_limits::quiet_NaN(); + m_longitude = std::numeric_limits::quiet_NaN(); + + // ************************************************************************ + // CSP Solver Temperature Tracking + // Temperatures from the most recent converged() operation + m_T_sys_c_t_end_converged = std::numeric_limits::quiet_NaN(); //[K] + m_T_sys_h_t_end_converged = std::numeric_limits::quiet_NaN(); //[K] + + // Temperatures from the most recent timstep (in the event that a method solves multiple, shorter timesteps + m_T_sys_c_t_end_last = std::numeric_limits::quiet_NaN(); //[K] Temperature (bulk) of cold runners & headers at end of previous timestep + m_T_sys_h_t_end_last = std::numeric_limits::quiet_NaN(); //[K] + + // Latest temperature solved during present call to this class + m_T_sys_c_t_end = std::numeric_limits::quiet_NaN(); //[K] + m_T_sys_c_t_int = std::numeric_limits::quiet_NaN(); //[K] + m_T_sys_h_t_end = std::numeric_limits::quiet_NaN(); //[K] + m_T_sys_h_t_int = std::numeric_limits::quiet_NaN(); //[K] + + m_Q_field_losses_total_subts = std::numeric_limits::quiet_NaN(); //[MJ] + m_c_htf_ave_ts_ave_temp = std::numeric_limits::quiet_NaN(); //[J/kg-K] + + m_q_dot_sca_loss_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_sca_abs_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_sca_refl_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_xover_loss_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_HR_cold_loss_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_HR_hot_loss_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_sca_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_xover_summed_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_HR_cold_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_HR_hot_subts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_htf_to_sink_subts = std::numeric_limits::quiet_NaN(); //[MWt] + + // ************************************************************************ + // ************************************************************************ + // Full Timestep Outputs + m_T_sys_c_t_int_fullts = std::numeric_limits::quiet_NaN(); //[K] + m_T_htf_c_rec_in_t_int_fullts = std::numeric_limits::quiet_NaN(); //[K] + m_T_htf_h_rec_out_t_int_fullts = std::numeric_limits::quiet_NaN(); //[K] + m_T_sys_h_t_int_fullts = std::numeric_limits::quiet_NaN(); //[K] + + m_q_dot_sca_loss_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_sca_abs_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_sca_refl_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_xover_loss_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_HR_cold_loss_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_HR_hot_loss_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_sca_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_xover_summed_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_HR_cold_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_E_dot_HR_hot_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_htf_to_sink_fullts = std::numeric_limits::quiet_NaN(); //[MWt] + m_q_dot_freeze_protection = std::numeric_limits::quiet_NaN(); //[MWt] + + m_dP_total = std::numeric_limits::quiet_NaN(); //[bar] + m_W_dot_pump = std::numeric_limits::quiet_NaN(); //[MWe] + + m_is_m_dot_recirc = false; + + m_W_dot_sca_tracking = std::numeric_limits::quiet_NaN(); //[MWe] + + m_EqOpteff = std::numeric_limits::quiet_NaN(); + m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); + m_c_htf_ave = std::numeric_limits::quiet_NaN(); + + m_control_defocus = std::numeric_limits::quiet_NaN(); + m_component_defocus = std::numeric_limits::quiet_NaN(); + + m_q_dot_inc_sf_tot = std::numeric_limits::quiet_NaN(); + + m_AnnulusGasMat.fill(NULL); + m_AbsorberPropMat.fill(NULL); + + mv_HCEguessargs.resize(3); + std::fill(mv_HCEguessargs.begin(), mv_HCEguessargs.end(), std::numeric_limits::quiet_NaN()); +} + +C_csp_fresnel_collector_receiver::~C_csp_fresnel_collector_receiver() +{ + for (int i = 0; i < m_AbsorberPropMat.nrows(); i++) { + for (int j = 0; j < m_AbsorberPropMat.ncols(); j++) { + delete m_AbsorberPropMat(i, j); + delete m_AnnulusGasMat(i, j); + } + } +} + +void C_csp_fresnel_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) +{ + /* + --Initialization call-- + + Do any setup required here. + Get the values of the inputs and parameters + */ + + //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] + } + + // Set HTF properties + { + if (m_Fluid != HTFProperties::User_defined) + { + if (!m_htfProps.SetFluid(m_Fluid)) + { + //message(TCS_ERROR, "Field HTF code is not recognized"); + string msg = "Field HTF code is not recognized"; + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return; + } + } + else if (m_Fluid == HTFProperties::User_defined) + { + int nrows = (int)m_field_fl_props.nrows(); + int ncols = (int)m_field_fl_props.ncols(); + if (nrows > 2 && ncols == 7) + { + + if (!m_htfProps.SetUserDefinedFluid(m_field_fl_props)) + { + //message(TCS_ERROR, m_htfProps.UserFluidErrMessage(), nrows, ncols); + string msg = m_htfProps.UserFluidErrMessage(); + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return; + } + } + else + { + //message(TCS_ERROR, "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)", nrows, ncols); + string msg = "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)"; + m_error_msg = util::format(msg.c_str(), nrows, ncols); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return; + } + } + else + { + //message(TCS_ERROR, "Field HTF code is not recognized"); + string msg = "Field HTF code is not recognized"; + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return; + } + } + + // Set up the optical table object.. + { + /* + 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_OpticalTable.ncols(); + int nrow_OpticalTable = m_OpticalTable.nrows(); + + double* xax = new double[ncol_OpticalTable - 1]; + double* yax = new double[nrow_OpticalTable - 1]; + double* data = new double[(ncol_OpticalTable - 1) * (nrow_OpticalTable - 1)]; + + //get the xaxis data values + for (int i = 1; i < ncol_OpticalTable; i++) { + xax[i - 1] = m_OpticalTable.at(0, i) * m_d2r; + } + //get the yaxis data values + for (int j = 1; j < nrow_OpticalTable; j++) { + yax[j - 1] = m_OpticalTable.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_OpticalTable.at(j, i); + } + } + + optical_table.AddXAxis(xax, ncol_OpticalTable - 1); + optical_table.AddYAxis(yax, nrow_OpticalTable - 1); + optical_table.AddData(data); + delete[] xax; + delete[] yax; + delete[] data; + + } + + // Adjust parameters + m_ColAz = m_ColAz * m_d2r; //[rad] Collector azimuth angle, convert from [deg] + + // Organize the emittance tables + { + m_epsilon_abs.init(4); + m_epsilon_abs.addTable(&m_epsilon_abs_1); //HCE #1 + m_epsilon_abs.addTable(&m_epsilon_abs_2); + m_epsilon_abs.addTable(&m_epsilon_abs_3); + m_epsilon_abs.addTable(&m_epsilon_abs_4); + } + + // Unit Conversions + { + m_theta_stow *= m_d2r; + m_theta_stow = max(m_theta_stow, 1.e-6); + m_theta_dep *= m_d2r; + m_theta_dep = max(m_theta_dep, 1.e-6); + m_T_startup += 273.15; //[K] convert from C + m_T_loop_in_des += 273.15; //[K] convert from C + m_T_loop_out_des += 273.15; //[K] convert from C + m_T_fp += 273.15; //[K] convert from C + m_mc_bal_sca *= 3.6e3; //[Wht/K-m] -> [J/K-m] + } + + /*--- Do any initialization calculations here ---- */ + //Allocate space for the loop simulation objects + { + + // Old Fresnel + m_q_loss.resize(m_nRecVar); + m_q_abs.resize(m_nRecVar); + m_DP_tube.resize(m_nMod); + m_E_int_loop.resize(m_nMod); + m_E_accum.resize(m_nMod); + m_E_avail.resize(m_nMod); + m_q_loss_SCAtot.resize(m_nMod); + m_q_abs_SCAtot.resize(m_nMod); + m_q_SCA.resize(m_nMod); + m_q_1abs_tot.resize(m_nMod); + m_q_1abs.resize(m_nRecVar); + m_q_reflect_tot.resize(m_nMod); + m_q_reflect.resize(m_nRecVar); + m_ColOptEff.resize(m_nMod); + + // Trough + m_q_SCA_control_df.resize(m_nMod); + } + + // Resize CSP Solver Temp Tracking Vectors + { + m_T_htf_out_t_end_converged.resize(m_nMod); + m_T_htf_out_t_end_last.resize(m_nMod); + m_T_htf_in_t_int.resize(m_nMod); + m_T_htf_out_t_end.resize(m_nMod); + m_T_htf_out_t_int.resize(m_nMod); + } + + //Set up annulus gas and absorber property matrices + { + // Old Fresnel Version + //Set up annulus gas and absorber property matrices + m_AnnulusGasMat.resize(m_nRecVar); + m_AbsorberPropMat.resize(m_nRecVar); + for (int j = 0; j < m_nRecVar; j++) { + //Set up a matrix of annulus gas properties + m_AnnulusGasMat.at(j) = new HTFProperties(); + m_AnnulusGasMat.at(j)->SetFluid((int)m_AnnulusGas[j]); + //Set up a matrix of absorber prop materials + m_AbsorberPropMat.at(j) = new AbsorberProps(); + m_AbsorberPropMat.at(j)->setMaterial((int)m_AbsorberMaterial[j]); + } + } + + //Initialize values + + // for test start + init_fieldgeom(); + // for test end + + // Set solved parameters + solved_params.m_T_htf_cold_des = m_T_loop_in_des; //[K] + solved_params.m_q_dot_rec_des = m_q_design / 1.E6; //[MWt] + solved_params.m_A_aper_total = m_Ap_tot; //[m^2] + + // Set previous operating mode + m_operating_mode_converged = C_csp_collector_receiver::OFF; //[-] 0 = requires startup, 1 = starting up, 2 = running + + // Create Evacuated Receiver Model (if necessary) + if (m_rec_model == 2) + { + m_evac_receiver = std::unique_ptr(new EvacReceiverModel(m_D_abs_in, m_D_abs_out, m_D_glass_in, m_D_glass_out, m_D_plug, m_L_mod, m_GlazingIntact, + m_Shadowing, m_dirt_env, m_P_a, m_alpha_abs, m_epsilon_glass, m_Tau_envelope, m_alpha_env, &m_epsilon_abs, + m_htfProps, m_airProps, m_AnnulusGasMat, m_AbsorberPropMat, m_Flow_type, m_A_cs, m_D_h)); + } + + // Run steady state + { + C_csp_weatherreader::S_outputs weatherValues; + weatherValues.m_lat = init_inputs.m_latitude; + weatherValues.m_lon = init_inputs.m_longitude; + weatherValues.m_tz = init_inputs.m_tz; + weatherValues.m_shift = init_inputs.m_shift; + weatherValues.m_elev = init_inputs.m_elev; + weatherValues.m_year = 2009; + weatherValues.m_month = 6; + weatherValues.m_day = 21; + weatherValues.m_hour = 12; + weatherValues.m_minute = 0; + weatherValues.m_beam = m_I_bn_des; + weatherValues.m_tdry = 30; + weatherValues.m_tdew = 30 - 10; + weatherValues.m_wspd = 5; + weatherValues.m_pres = 1013; + weatherValues.m_solazi = m_ColAz; + weatherValues.m_solzen = 0; + + C_csp_solver_htf_1state htfInletState; + //htfInletState.m_m_dot = m_m_dot_design; + //htfInletState.m_pres = 101.3; + //htfInletState.m_qual = 0; + htfInletState.m_temp = m_T_loop_in_des - 273.15; + double defocus = 1; + C_csp_solver_sim_info fresnelInfo; + fresnelInfo.ms_ts.m_time_start = 14817600.; + fresnelInfo.ms_ts.m_step = 15. * 60.; // 5-minute timesteps + fresnelInfo.ms_ts.m_time = fresnelInfo.ms_ts.m_time_start + fresnelInfo.ms_ts.m_step; + fresnelInfo.m_tou = 1.; + C_csp_collector_receiver::S_csp_cr_out_solver fresnelOutputs; + + steady_state(weatherValues, htfInletState, std::numeric_limits::quiet_NaN(), defocus, fresnelOutputs, fresnelInfo); + solved_params.m_T_htf_hot_des = m_T_field_out; + solved_params.m_dP_sf = fresnelOutputs.m_dP_sf; + + + + + // Calculate Steady State Results + { + // Field Results + double temp_initial = htfInletState.m_temp + 273.15; // K + double temp_final = m_T_field_out; // K + double mdot = fresnelOutputs.m_m_dot_salt_tot / 3600.0; // convert from kg/hr to kg/s + double c_htf_ave = m_htfProps.Cp((temp_initial + temp_final) / 2.0); //[kJ/kg-K] + m_Q_field_des_SS = mdot * c_htf_ave * (temp_final - temp_initial) * 1000.0; // convert kW to W + m_T_field_out_des_SS = fresnelOutputs.m_T_salt_hot; // C + m_m_dot_des_SS = mdot; // kg/s field + m_m_dot_loop_des_SS = mdot / float(m_nLoops); + + // Steady State velocities + { + double D_hdr_min = *std::min_element(m_D_hdr.begin(), m_D_hdr.end()); + double D_hdr_max = *std::max_element(m_D_hdr.begin(), m_D_hdr.end()); + double mdot_hdr = mdot / m_nfsec; + double rho_ave = m_htfProps.dens((temp_initial + temp_final) / 2.0, 0.0); //kg/m3 + + double V_min_calc = -1; + double V_max_calc = -1; + std::vector mdot_vec; + for (int i = 0; i < m_nhdrsec; i++) + { + double mdot_hdr_section = this->m_dot_header(mdot, m_nfsec, this->m_nLoops, i); + double D_hdr_section = m_D_hdr[i]; + + double V = (4.0 * mdot_hdr_section) / (rho_ave * CSP::pi * pow(D_hdr_section, 2.0)); + + if (i == 0) + { + V_min_calc = V; + V_max_calc = V; + } + else if (V < V_min_calc) + V_min_calc = V; + else if (V > V_max_calc) + V_max_calc = V; + + + mdot_vec.push_back(mdot_hdr_section); + } + + m_V_hdr_min_des_SS = V_min_calc; + m_V_hdr_max_des_SS = V_max_calc; + + + double max_field_mdot = m_m_dot_htfmax * float(m_nLoops); + double max_hdr_mdot = max_field_mdot / m_nfsec; + double max_velocity_based_on_max_htf_mdot = (4.0 * max_hdr_mdot) / (rho_ave * CSP::pi * pow(0.48895, 2.0)); + + double min_field_mdot = m_m_dot_htfmin * float(m_nLoops); + double min_hdr_mdot = min_field_mdot / m_nfsec; + double min_velocity_based_on_min_htf_mdot = (4.0 * min_hdr_mdot) / (rho_ave * CSP::pi * pow(0.48895, 2.0)); + } + + // SS optical efficiency is collector optical efficiency * receiver OPTICAL efficiency (does not consider heat loss) + m_eta_optical_des_SS = this->m_eta_optical * m_opt_derate; + + // Field Efficiency + double Q_available = m_Ap_tot * weatherValues.m_beam * m_eta_optical_des_SS; // W + m_therm_eff_des_SS = m_Q_field_des_SS / Q_available; + m_eff_des_SS = m_eta_optical_des_SS * m_therm_eff_des_SS; + + // Loop Results + double loop_in = m_T_loop_in; // K + double loop_out = m_T_htf_out_t_int[m_nMod - 1]; // K + double c_htf_loop_ave = m_htfProps.Cp((loop_in + loop_out) / 2.0); //[kJ/kg-K] + m_Q_loop_des_SS = m_m_dot_loop_des_SS * c_htf_loop_ave * (loop_out - loop_in) * 1000.0; // convert kW to W + m_T_loop_out_des_SS = loop_out - 273.15; // Convert from K to C + double Q_loop_available = m_A_loop * weatherValues.m_beam * m_eta_optical_des_SS; // W + m_therm_eff_loop_des_SS = m_Q_loop_des_SS / Q_loop_available; + m_eff_loop_des_SS = m_therm_eff_loop_des_SS * m_eta_optical_des_SS; + + // Pumping Power + m_W_dot_pump_des_SS = m_W_dot_pump; + + // Field Pressure Drop + m_dP_des_SS = fresnelOutputs.m_dP_sf; + + // Thermal Losses + m_Q_loss_receiver_des_SS = m_q_dot_sca_loss_summed_fullts; // MWt + m_Q_loss_hdr_rnr_des_SS = m_q_dot_HR_cold_loss_fullts + m_q_dot_HR_hot_loss_fullts; // MWt + + } + + + } + + return; +} + +bool C_csp_fresnel_collector_receiver::init_fieldgeom() +{ + /* + Call this method once when call() is first invoked. The calculations require location information that + is provided by the weatherreader class and not set until after init() and before the first call(). + */ + + // If solar multiple is not yet calculated + if(m_is_solar_mult_designed == false) + this->design_solar_mult(); + + if (m_rec_model == 2) + { + //Evacuated tube receiver model + //Calculate the cross-sectional flow area of the receiver piping + m_D_h.resize(m_nRecVar); + m_A_cs.resize(m_nRecVar); + for (int i = 0; i < m_nRecVar; i++) { + + if ((int)m_Flow_type[i] == 2) { + m_D_h.at(i) = m_D_abs_in[i] - m_D_plug[i]; + } + else { + m_D_h.at(i) = m_D_abs_in[i]; + m_D_plug[i] = 0.; + } + m_A_cs.at(i) = CSP::pi * (m_D_abs_in[i] * m_D_abs_in[i] - m_D_plug[i] * m_D_plug[i]) / 4.; //[m2] The cross-sectional flow area + } + } + + //Calculate header diameters here based on min/max velocities + //output file with calculated header diameter "header_diam.out" + m_nfsec = m_FieldConfig; + if (m_nfsec % 2 != 0) { + //message(TCS_ERROR, "Number of field subsections must equal an even number"); + string msg = "Number of field subsections must equal an even number"; + m_error_msg = util::format(msg.c_str()); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return false; + } + + /* + The number of header sections per field section is equal to the total number of loops divided + by the number of distinct headers. Since two loops are connected to the same header section, + the total number of header sections is then divided by 2. + */ + m_nhdrsec = (int)ceil(float(m_nLoops) / float(m_nfsec * 2)); + + //We need to determine design information about the field for purposes of header sizing ONLY + m_c_htf_ave = m_htfProps.Cp((m_T_loop_out_des + m_T_loop_in_des) / 2.0) * 1000.; //Specific heat + + //Start by initializing sensitive variables + double x1 = 0.0, loss_tot = 0.0; + m_opteff_des = 0.0; + m_m_dot_design = 0.0; + m_L_tot = (float)m_nMod * m_L_mod; + + //Determine the optical efficiency at design + eta_opt_fixed = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; + //design point solar elevation + double elev_des = asin(sin(0.4092793) * sin(m_latitude) + cos(m_latitude) * cos(0.4092793)); + //translate the solar angles into incidence angles + double phi_t, theta_L, iam_t, iam_l; + CSP::theta_trans(0., CSP::pi / 2. - elev_des, m_ColAz, phi_t, theta_L); //phi_t and theta_L are the translated angles (transverse and longitudinal) + switch (m_opt_model) + { + case 1: //Solar position table + m_opteff_des = eta_opt_fixed * optical_table.interpolate(0., CSP::pi / 2. - elev_des); + break; + case 2: //Collector incidence table + m_opteff_des = eta_opt_fixed * optical_table.interpolate(0., theta_L); + break; + case 3: //IAM polynomials + { + iam_t = 0.; + iam_l = 0.; + int n_IAM_L_coefs = m_IAM_L_coefs.size(); + int n_IAM_T_coefs = m_IAM_T_coefs.size(); + for (int i = 0; i < n_IAM_L_coefs; i++) + iam_l += m_IAM_L_coefs[i] * pow(theta_L, i); + for (int i = 0; i < n_IAM_T_coefs; i++) + iam_t += m_IAM_T_coefs[i] * pow(phi_t, i); + m_opteff_des = eta_opt_fixed * iam_t * iam_l; + break; + } + default: + //message(TCS_ERROR, "The selected optical model (%d) does not exist. Options are 1=Solar position table : 2=Collector incidence table : 3= IAM polynomials", opt_model); + string msg = "The selected optical model (%d) does not exist. Options are 1=Solar position table : 2=Collector incidence table : 3= IAM polynomials"; + m_error_msg = util::format(msg.c_str(), m_opt_model); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return false; + } + + //Determine the heat loss efficiency at design + double dT_loc, c_hl, dTSCA, c_hl_w, hceopt; + switch (m_rec_model) + { + //Polynomial model + case 1: + //evaluate the wind speed polynomial + { + c_hl_w = 0.; + int n_HL_w_coefs = m_HL_w_coefs.size(); + int n_HL_T_coefs = m_HL_T_coefs.size(); + + for (int j = 0; j < n_HL_w_coefs; j++) { + c_hl_w += m_HL_w_coefs[j] * pow(m_V_wind_des, j); + } + + //Assume a linear temperature rise across the field + c_hl = 0.; + dTSCA = (m_T_loop_out_des - m_T_loop_in_des) / (float)(m_nMod + 1); + for (int j = 0; j < m_nMod; j++) { + dT_loc = m_T_loop_in_des + dTSCA * (0.5 + (float)j) - m_T_amb_sf_des; + //evaluate the temperature polynomial + for (int k = 0; k < n_HL_T_coefs; k++) { + c_hl += m_HL_T_coefs[k] * pow(dT_loc, k) * m_L_mod; //Total receiver thermal loss [W/m] for a single loop + } + } + //Calculate the total thermal loss, including temperature and wind loss effects, for the entire loop + loss_tot = c_hl_w * c_hl; + + break; + } + + //Evacuated tube receiver model + case 2: + loss_tot = 0.; + for (int j = 0; j < m_nRecVar; j++) + loss_tot += (float)m_nMod * m_L_mod * m_HCE_FieldFrac[j] * m_Design_loss[j]; + //correct for receiver optical losses + hceopt = 0.; + for (int i = 0; i < m_nRecVar; i++) { + hceopt += m_alpha_abs[i] * m_Tau_envelope[i] * m_HCE_FieldFrac[i]; + } + m_opteff_des *= hceopt; + break; + + default: + //message(TCS_ERROR, "The selected thermal model (%d) does not exist. Options are 1=Regression model : 2=Evacuated tube receiver model", rec_model); + string msg = "The selected thermal model (%d) does not exist. Options are 1=Regression model : 2=Evacuated tube receiver model"; + m_error_msg = util::format(msg.c_str(), m_rec_model); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return false; + } + + //the estimated mass flow rate at design (in solar field) + m_m_dot_design = (m_Ap_tot * m_I_bn_des * m_opteff_des - loss_tot * float(m_nLoops)) / (m_c_htf_ave * (m_T_loop_out_des - m_T_loop_in_des)); //tn 4.25.11 using Ap_tot instead of A_loop. Change location of opteff_des + double m_dot_max = m_m_dot_htfmax * m_nLoops; + double m_dot_min = m_m_dot_htfmin * m_nLoops; + if (m_m_dot_design > m_dot_max) { + const char* msg = "The calculated field design mass flow rate of %.2f kg/s is greater than the maximum defined by the max single loop flow rate and number of loops (%.2f kg/s). " + "The design mass flow rate is reset to the latter."; + m_error_msg = util::format(msg, m_m_dot_design, m_dot_max); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + m_m_dot_design = m_dot_max; + } + else if (m_m_dot_design < m_dot_min) { + const char* msg = "The calculated field design mass flow rate of %.2f kg/s is less than the minimum defined by the min single loop flow rate and number of loops (%.2f kg/s). " + "The design mass flow rate is reset to the latter."; + m_error_msg = util::format(msg, m_m_dot_design, m_dot_min); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + m_m_dot_design = m_dot_min; + } + + + m_m_dot_loop_des = m_m_dot_design / (double)m_nLoops; // [kg/s] + + // Already defined in design_solar_mult (TB) + //mjw 1.16.2011 Design field thermal power + //m_q_design = m_m_dot_design * m_c_htf_ave * (m_T_loop_out_des - m_T_loop_in_des); //[Wt] + + //mjw 1.16.2011 Convert the thermal inertia terms here + m_mc_bal_hot = m_mc_bal_hot * 3.6 * m_q_design; //[J/K] + m_mc_bal_cold = m_mc_bal_cold * 3.6 * m_q_design; //[J/K] + + + + //need to provide fluid density + double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 + //Calculate the header design + m_nrunsec = (int)floor(float(m_nfsec) / 4.0) + 1; //The number of unique runner diameters + m_T_loop.resize(2 * m_nMod + 3); + m_T_rnr.resize(2 * m_nrunsec); + m_T_hdr.resize(2 * m_nhdrsec); + m_D_runner.resize(m_nrunsec); + m_L_runner.resize(m_nrunsec); + m_D_hdr.resize(m_nhdrsec); + m_P_rnr.resize(2 * m_nrunsec); + m_P_hdr.resize(2 * m_nhdrsec); + m_P_rnr_dsn = m_P_rnr; + m_DP_rnr.resize(2 * m_nrunsec); + m_T_rnr_dsn = m_T_rnr; + m_T_hdr_dsn = m_T_hdr; + m_DP_hdr.resize(2 * m_nhdrsec); + m_T_loop_dsn = m_T_loop; + + // Removed (polynomial model does not have pressure across individual receivers) + //m_DP_loop.resize(2 * m_nMod + 3); + //m_P_loop.resize(2 * m_nMod + 3); + + header_design(m_nhdrsec, m_nfsec, m_nrunsec, rho_ave, m_V_hdr_max, m_V_hdr_min, m_m_dot_design, m_D_hdr, m_D_runner, &m_piping_summary); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_piping_summary); + + /* ----- Set initial storage values ------ */ + double T_field_ini = 0.5 * (m_T_fp + m_T_loop_in_des); //[K] + + /* + Do one-time calculations for system geometry. Calculate all HTF volume, set runner piping length + Assume there are two field subsections per span, then if there's an even number of spans in the field, + we count the first header section as half-length. I.e., if a field looks like this: + (1) (2) + ||||||| ||||||| + ----------------- + ||||||| : ||||||| + : + [P] + : + ||||||| : ||||||| + ----------------- + ||||||| ||||||| + (3) (4) + Then the field has 4 subfields and two spans. The runner pipe (:) is half the distance between the two spans. + If the number of subfields were 6 (3 spans), the two runner pipe segments would both be equal to the full + distance between spans. + */ + if (m_nfsec / 2 % 2 == 1) { + x1 = 2.; //the first runners are normal + } + else { + x1 = 1.; //the first runners are short + } + m_L_runner[0] = m_L_rnr_pb; + if (m_nrunsec > 1) { + for (int i = 1; i < m_nrunsec; i++) { + m_L_runner[i] = x1 * (2 * m_L_crossover + (m_L_mod + m_L_mod_spacing) * float(m_nMod) / 2.); + x1 = 2.; //tn 4.25.11 Default to 2 for subsequent runners + } + } + double v_tofrom_sgs = 0.0; + for (int i = 0; i < m_nrunsec; i++) { + v_tofrom_sgs = v_tofrom_sgs + 2. * m_L_runner[i] * CSP::pi * pow(m_D_runner[i], 2) / 4.; //This is the volume of the runner in 1 direction. + } + + //6/14/12, TN: Multiplier for runner heat loss. In main section of code, are only calculating loss for one path. + //Since there will be two symmetric paths (when nrunsec > 1), need to calculate multiplier for heat loss, considering + //that the first 50 meters of runner is assumed shared. + double lsum = 0.; + for (int i = 0; i < m_nrunsec; i++) { lsum += m_L_runner[i]; } + + //Calculate the total HTF volume per loop based on user input. Select method based on heat loss model + double v_loop_tot = 0.; + switch (m_rec_model) + { + case 1: //Polynomial model + v_loop_tot = m_A_loop * m_rec_htf_vol / 1000. * (float)m_nLoops; //[m3] + break; + case 2: + //-------piping from header into and out of the HCE's + for (int j = 0; j < m_nRecVar; j++) { + for (int i = 0; i < m_nMod; i++) { + v_loop_tot += (m_L_mod + m_L_mod_spacing) * m_A_cs.at(j) * m_HCE_FieldFrac[j] * (float)m_nLoops; + } + } + //mjw 1.13.2011 Add on volume for the crossover piping + //v_loop_tot = v_loop_tot + L_crossover*A_cs(SCAInfoArray(nMod/2,1),1)*float(nLoops) + v_loop_tot += m_L_crossover * m_A_cs.at(0) * (float)m_nLoops; //TN 6/20: need to solve for nMod = 1 + break; + default: + //message(TCS_ERROR, "The selected thermal model (%d) does not exist. Options are 1=Regression model : 2=Evacuated tube receiver model", rec_model); + string msg = "The selected thermal model (%d) does not exist. Options are 1=Regression model : 2=Evacuated tube receiver model"; + m_error_msg = util::format(msg.c_str(), m_rec_model); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return false; + } + + //-------field header loop + double v_header = 0.0; + for (int i = 0; i < m_nhdrsec; i++) { + //Also calculate the hot and cold header volume for later use. 4.25 is for header expansion bends + v_header += m_D_hdr[i] * m_D_hdr[i] / 4. * CSP::pi * (m_L_crossover + 4.275) * float(m_nfsec) * 2.0; //tn 4.25.11 The header distance should be multiplied by 2 row spacings + } + //Add on inlet/outlet from the header to the loop. Assume header to loop inlet ~= 10 [m] (Kelley/Kearney) + if (m_rec_model == 2) v_header = v_header + 20. * m_A_cs.at(0) * float(m_nLoops); + + //Calculate the HTF volume associated with pumps and the SGS + double v_sgs = Pump_SGS(rho_ave, m_m_dot_design, m_solar_mult); + + //Calculate the hot and cold balance-of-plant volumes + m_v_hot = v_header + v_tofrom_sgs; + m_v_cold = m_v_hot; + + //Write the volume totals to the piping diameter file + m_piping_summary.append( + "\n----------------------------------------------\n" + "Plant HTF volume information:\n" + "----------------------------------------------\n"); + char tstr[500]; + string fmt = "Cold header pipe volume: %10.4e m3\n" + "Hot header pipe volume: %10.4e m3\n" + "Volume per loop: %10.4e m3\n" + "Total volume in all loops: %10.4e m3\n" + "Total solar field volume: %10.4e m3\n" + "Pump / SGS system volume: %10.4e m3\n" + "---------------------------\n" + "Total plant HTF volume: %10.4e m3\n"; + sprintf(tstr, fmt.c_str(), m_v_cold, m_v_hot, v_loop_tot / float(m_nLoops), v_loop_tot, (m_v_hot * 2. + v_loop_tot), v_sgs, (m_v_hot * 2. + v_loop_tot + v_sgs)); + //piping_summary.append(tstr); + + //Include the pump/SGS volume with the header + m_v_hot = m_v_hot + v_sgs / 2.; + m_v_cold = m_v_cold + v_sgs / 2.; + + // ********************************************* + // CSP Solver Temperature Tracking + m_T_sys_c_t_end_converged = m_T_sys_c_t_end_last = T_field_ini; //[K] + m_T_sys_h_t_end_converged = m_T_sys_h_t_end_last = T_field_ini; //[K] + for (int i = 0; i < m_nMod; i++) + { + m_T_htf_out_t_end_converged[i] = m_T_htf_out_t_end_last[i] = T_field_ini; //[K] + } + // ********************************************* + + // Calculate tracking parasitics for when fresnel is on sun + m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] + + return true; +} + +C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() +{ + return m_operating_mode_converged; +} + +double C_csp_fresnel_collector_receiver::get_startup_time() +{ + // Note: C_csp_fresnel_collector_receiver::startup() is called after this function + return m_rec_su_delay * 3600.; // sec +} + +double C_csp_fresnel_collector_receiver::get_startup_energy() +{ + // Note: C_csp_fresnel_collector_receiver::startup() is called after this function + return m_rec_qf_delay * m_q_design * 1.e-6; // MWh +} + +double C_csp_fresnel_collector_receiver::get_pumping_parasitic_coef() +{ + double T_amb_des = 42. + 273.15; + double T_avg = (m_T_loop_in_des + m_T_loop_out_des) / 2.; + double P_field_in = m_P_rnr_dsn[1]; // hard code? + double dT_avg_SCA = (m_T_loop_out_des - m_T_loop_in_des) / m_nMod; + std::vector T_in_SCA, T_out_SCA; + + for (size_t i = 0; i < m_nMod; i++) { + T_in_SCA.push_back(m_T_loop_in_des + dT_avg_SCA * i); + T_out_SCA.push_back(m_T_loop_in_des + dT_avg_SCA * (i + 1)); + } + + double dP_field = field_pressure_drop(T_amb_des, m_m_dot_design, P_field_in, T_in_SCA, T_out_SCA); + + return m_W_dot_pump / (m_q_design * 1.e-6); +} + +double C_csp_fresnel_collector_receiver::get_min_power_delivery() +{ + double c_htf_ave = m_htfProps.Cp((m_T_startup + m_T_loop_in_des) / 2.0) * 1000.; //[J/kg-K] Specific heat + return m_m_dot_htfmin * m_nLoops * c_htf_ave * (m_T_startup - m_T_loop_in_des) * 1.e-6; // [MWt] +} + +double C_csp_fresnel_collector_receiver::get_max_power_delivery(double T_cold_in /*C*/) +{ + double T_in = T_cold_in + 273.15; // [K] + double T_out = m_T_loop_out_des; // [K] + double c_htf_ave = m_htfProps.Cp((T_out + T_in) / 2.0) * 1000.; // [J/kg-K] + return m_m_dot_htfmax * m_nLoops * c_htf_ave * (T_out - T_in) * 1.e-6; // [MWt] +} + +double C_csp_fresnel_collector_receiver::get_tracking_power() +{ + return m_SCA_drives_elec * 1.e-6 * m_nMod * m_nLoops; //MWe +} + +double C_csp_fresnel_collector_receiver::get_col_startup_power() +{ + return m_p_start * 1.e-3 * m_nMod * m_nLoops; //MWe-hr +} + +void C_csp_fresnel_collector_receiver::get_design_parameters(C_csp_collector_receiver::S_csp_cr_solved_params& solved_params) +{ + return; +} + +void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info) +{ + // Always reset last temps + reset_last_temps(); + + m_is_m_dot_recirc = true; + + // Get optical properties + // Should reflect that the collector is not tracking and probably (but not necessarily) DNI = 0 + loop_optical_eta_off(); + + // Set mass flow rate to minimum allowable + double m_dot_htf_loop = m_m_dot_htfmin; //[kg/s] + + // Set duration for recirculation timestep + if (m_step_recirc != m_step_recirc) + m_step_recirc = 10.0 * 60.0; //[s] + + // Calculate number of steps required given timestep from solver and recirculation step + int n_steps_recirc = (int)std::ceil(sim_info.ms_ts.m_step / m_step_recirc); //[-] Number of recirculation steps required + + // Define a copy of the sim_info structure + double time_start = sim_info.ms_ts.m_time - sim_info.ms_ts.m_step; //[s] + double step_local = sim_info.ms_ts.m_step / (double)n_steps_recirc; //[s] + C_csp_solver_sim_info sim_info_temp = sim_info; + sim_info_temp.ms_ts.m_step = step_local; //[s] + + double Q_fp_sum = 0.0; //[MJ] + + // Zero full timestep outputs + m_T_sys_c_t_int_fullts = m_T_htf_c_rec_in_t_int_fullts = + m_T_htf_h_rec_out_t_int_fullts = m_T_sys_h_t_int_fullts = 0.0; //[K] + + m_q_dot_sca_loss_summed_fullts = m_q_dot_sca_abs_summed_fullts = + m_q_dot_sca_loss_summed_fullts = m_q_dot_xover_loss_summed_fullts = + m_q_dot_HR_cold_loss_fullts = m_q_dot_HR_hot_loss_fullts = + m_E_dot_sca_summed_fullts = m_E_dot_xover_summed_fullts = + m_E_dot_HR_cold_fullts = m_E_dot_HR_hot_fullts = + m_q_dot_htf_to_sink_fullts = 0.0; + + // Simulate through time steps + for (int i = 0; i < n_steps_recirc; i++) + { + sim_info_temp.ms_ts.m_time = time_start + step_local * (i + 1); //[s] + + // Set inlet temperature to previous timestep outlet temperature + double T_cold_in = m_T_sys_h_t_end_last; //[K] + + // Call energy balance with updated info + loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info_temp); + + // Check freeze protection + if (m_T_htf_out_t_end[m_nMod - 1] < m_T_fp + fp_offset) + { + if (m_Q_field_losses_total_subts > 0.0) + { + double Q_fp_i = std::numeric_limits::quiet_NaN(); + double T_cold_in_i = T_cold_in; + int fp_code = freeze_protection(weather, T_cold_in_i, m_dot_htf_loop, sim_info_temp, Q_fp_i); + + T_cold_in = T_cold_in_i; //[K] + Q_fp_sum += Q_fp_i; //[MJ] + } + } + + // Add current temperature to summation + m_T_sys_c_t_int_fullts += T_cold_in; //[K] + m_T_htf_c_rec_in_t_int_fullts += m_T_htf_in_t_int[0]; //[K] + m_T_htf_h_rec_out_t_int_fullts += m_T_htf_out_t_int[m_nMod - 1]; //[K] + m_T_sys_h_t_int_fullts += m_T_sys_h_t_int; //[K] + + // Add subtimestep calcs + m_q_dot_sca_loss_summed_fullts += m_q_dot_sca_loss_summed_subts; //[MWt] + m_q_dot_sca_abs_summed_fullts += m_q_dot_sca_abs_summed_subts; //[MWt] + m_q_dot_sca_refl_summed_fullts += m_q_dot_sca_refl_summed_subts; //[MWt] + m_q_dot_xover_loss_summed_fullts += m_q_dot_xover_loss_summed_subts; //[MWt] + m_q_dot_HR_cold_loss_fullts += m_q_dot_HR_cold_loss_subts; //[MWt] + m_q_dot_HR_hot_loss_fullts += m_q_dot_HR_hot_loss_subts; //[MWt] + m_E_dot_sca_summed_fullts += m_E_dot_sca_summed_subts; //[MWt] + m_E_dot_xover_summed_fullts += m_E_dot_xover_summed_subts; //[MWt] + m_E_dot_HR_cold_fullts += m_E_dot_HR_cold_subts; //[MWt] + m_E_dot_HR_hot_fullts += m_E_dot_HR_hot_subts; //[MWt] + m_q_dot_htf_to_sink_fullts += m_q_dot_htf_to_sink_subts; //[MWt] + + update_last_temps(); + } + + // Now, calculate average value over all subtimesteps + double nd_steps_recirc = (double)n_steps_recirc; + m_T_sys_c_t_int_fullts /= nd_steps_recirc; //[K] + m_T_htf_c_rec_in_t_int_fullts /= nd_steps_recirc; //[K] + m_T_htf_h_rec_out_t_int_fullts /= nd_steps_recirc; //[K] + m_T_sys_h_t_int_fullts /= nd_steps_recirc; //[K] + + m_q_dot_sca_loss_summed_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_sca_abs_summed_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_sca_refl_summed_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_xover_loss_summed_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_HR_cold_loss_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_HR_hot_loss_fullts /= nd_steps_recirc; //[MWt] + m_E_dot_sca_summed_fullts /= nd_steps_recirc; //[MWt] + m_E_dot_xover_summed_fullts /= nd_steps_recirc; //[MWt] + m_E_dot_HR_cold_fullts /= nd_steps_recirc; //[MWt] + m_E_dot_HR_hot_fullts /= nd_steps_recirc; //[MWt] + m_q_dot_htf_to_sink_fullts /= nd_steps_recirc; //[MWt] + + m_q_dot_freeze_protection = Q_fp_sum / sim_info.ms_ts.m_step; //[MWt] + + // Solve for pressure drop and pumping power + m_dP_total = field_pressure_drop(weather.m_tdry, this->m_m_dot_htf_tot, this->m_P_field_in, this->m_T_htf_in_t_int, this->m_T_htf_out_t_int); + + // Are any of these required by the solver for system-level iteration? + cr_out_solver.m_q_startup = 0.0; //[MWt-hr] Receiver thermal output used to warm up the receiver + cr_out_solver.m_time_required_su = sim_info.ms_ts.m_step; //[s] Time required for receiver to startup - at least the entire timestep because it's off + + // 5.8.17, twn: Don't report a component *delivered* mass flow rate if fresnel is recirculating... + // .... and not passing HTF to other components + //cr_out_solver.m_m_dot_salt_tot = m_dot_htf_loop*3600.0*(double)m_nLoops; //[kg/hr] Total HTF mass flow rate + cr_out_solver.m_m_dot_salt_tot = 0.0; //[kg/hr] Total HTF mass flow rate + + cr_out_solver.m_q_thermal = 0.0; //[MWt] No available receiver thermal output + // 7.12.16: Return timestep-end or timestep-integrated-average? + // If multiple recirculation steps, then need to calculate average of timestep-integrated-average + cr_out_solver.m_T_salt_hot = m_T_sys_h_t_int_fullts - 273.15; //[C] + cr_out_solver.m_component_defocus = 1.0; + cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; + + cr_out_solver.m_W_dot_elec_in_tot = m_W_dot_sca_tracking + m_W_dot_pump; //[MWe] + cr_out_solver.m_q_dot_heater = m_q_dot_freeze_protection; //[MWt] + + m_operating_mode = C_csp_collector_receiver::OFF; + + set_output_value(); + + return; +} + +void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info) +{ + + // Always reset last temps + reset_last_temps(); + + m_is_m_dot_recirc = true; + + // Get optical performance + loop_optical_eta(weather, sim_info); + + // Set mass flow rate to what I imagine might be an appropriate value + double m_dot_htf_loop = m_m_dot_htfmin; + if (weather.m_beam > 50.0 && m_T_htf_out_t_end_converged[m_nMod - 1] > (0.5 * m_T_fp + 0.5 * m_T_startup)) + { + double m_dot_ss = (weather.m_beam / m_I_bn_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] + } + + // Set duration for recirculation timestep + if (m_step_recirc != m_step_recirc) + m_step_recirc = 10.0 * 60.0; //[s] + + // Calculate number of steps required given timestep from solver and recirculation step + int n_steps_recirc = (int)std::ceil(sim_info.ms_ts.m_step / m_step_recirc); //[-] Number of recirculation steps required + + // Define a copy of the sim_info structure + double time_start = sim_info.ms_ts.m_time - sim_info.ms_ts.m_step; //[s] + double time_end = sim_info.ms_ts.m_time; //[s] + C_csp_solver_sim_info sim_info_temp = sim_info; + + bool is_T_startup_achieved = false; + + // This code finds the first "Recirculation Step" when the outlet temperature is greater than the Startup Temperature + double time_required_su = sim_info.ms_ts.m_step; //[s] + + double Q_fp_sum = 0.0; //[MJ] + + // Zero full timestep outputs + m_T_sys_c_t_int_fullts = m_T_htf_c_rec_in_t_int_fullts = + m_T_htf_h_rec_out_t_int_fullts = m_T_sys_h_t_int_fullts = 0.0; //[K] + + // Zero full timestep outputs + m_q_dot_sca_loss_summed_fullts = m_q_dot_sca_abs_summed_fullts = + m_q_dot_sca_refl_summed_fullts = m_q_dot_xover_loss_summed_fullts = + m_q_dot_HR_cold_loss_fullts = m_q_dot_HR_hot_loss_fullts = + m_E_dot_sca_summed_fullts = m_E_dot_xover_summed_fullts = + m_E_dot_HR_cold_fullts = m_E_dot_HR_hot_fullts = + m_q_dot_htf_to_sink_fullts = 0.0; + + sim_info_temp.ms_ts.m_time = time_start; + + // Loop through time steps + while (sim_info_temp.ms_ts.m_time < time_end) + { + sim_info_temp.ms_ts.m_time_start = sim_info_temp.ms_ts.m_time; //[s] + sim_info_temp.ms_ts.m_time = std::min(sim_info_temp.ms_ts.m_time_start + m_step_recirc, time_end); //[s] + sim_info_temp.ms_ts.m_step = sim_info_temp.ms_ts.m_time - sim_info_temp.ms_ts.m_time_start; //[s] + + // Set inlet temperature to previous timestep outlet temperature + double T_cold_in = m_T_sys_h_t_end_last; //[K] + + // Call energy balance with updated info + loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info_temp); + + // Check freeze protection + if (m_T_htf_out_t_end[m_nMod - 1] < m_T_fp + fp_offset) + { + if (m_Q_field_losses_total_subts > 0.0) + { + double Q_fp_i = std::numeric_limits::quiet_NaN(); + double T_cold_in_i = T_cold_in; + int fp_code = freeze_protection(weather, T_cold_in_i, m_dot_htf_loop, sim_info_temp, Q_fp_i); + + T_cold_in = T_cold_in_i; //[K] + Q_fp_sum += Q_fp_i; //[MJ] + } + } + + // Add current temperatures + m_T_sys_c_t_int_fullts += T_cold_in * sim_info_temp.ms_ts.m_step; //[K] + m_T_htf_c_rec_in_t_int_fullts += m_T_htf_in_t_int[0] * sim_info_temp.ms_ts.m_step; //[K] + m_T_htf_h_rec_out_t_int_fullts += m_T_htf_out_t_int[m_nMod - 1] * sim_info_temp.ms_ts.m_step; //[K] + m_T_sys_h_t_int_fullts += m_T_sys_h_t_int * sim_info_temp.ms_ts.m_step; //[K] + + // Add subtimestep calcs + m_q_dot_sca_loss_summed_fullts += m_q_dot_sca_loss_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_sca_abs_summed_fullts += m_q_dot_sca_abs_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_sca_refl_summed_fullts += m_q_dot_sca_refl_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_xover_loss_summed_fullts += m_q_dot_xover_loss_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_HR_cold_loss_fullts += m_q_dot_HR_cold_loss_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_HR_hot_loss_fullts += m_q_dot_HR_hot_loss_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_E_dot_sca_summed_fullts += m_E_dot_sca_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_E_dot_xover_summed_fullts += m_E_dot_xover_summed_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_E_dot_HR_cold_fullts += m_E_dot_HR_cold_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_E_dot_HR_hot_fullts += m_E_dot_HR_hot_subts * sim_info_temp.ms_ts.m_step; //[MWt] + m_q_dot_htf_to_sink_fullts += m_q_dot_htf_to_sink_subts * sim_info_temp.ms_ts.m_step; + + // If the *outlet temperature at the end of the timestep* is greater than startup temperature, + if (m_T_sys_h_t_end > m_T_startup) + { + time_required_su = sim_info_temp.ms_ts.m_time - time_start; //[s] + m_operating_mode = C_csp_collector_receiver::ON; //[-] + is_T_startup_achieved = true; + break; + } + + update_last_temps(); + + } + + // Check if startup is achieved in current controller/kernel timestep + if (!is_T_startup_achieved) + { + time_required_su = sim_info.ms_ts.m_step; //[s] + m_operating_mode = C_csp_collector_receiver::STARTUP; //[-] + } + + // Account for time required + { + m_T_sys_c_t_int_fullts /= time_required_su; //[K] + m_T_htf_c_rec_in_t_int_fullts /= time_required_su; //[K] + m_T_htf_h_rec_out_t_int_fullts /= time_required_su; //[K] + m_T_sys_h_t_int_fullts /= time_required_su; //[K] + + m_q_dot_sca_loss_summed_fullts /= time_required_su; //[MWt] + m_q_dot_sca_abs_summed_fullts /= time_required_su; //[MWt] + m_q_dot_sca_refl_summed_fullts /= time_required_su; //[MWt] + m_q_dot_xover_loss_summed_fullts /= time_required_su; //[MWt] + m_q_dot_HR_cold_loss_fullts /= time_required_su; //[MWt] + m_q_dot_HR_hot_loss_fullts /= time_required_su; //[MWt] + m_E_dot_sca_summed_fullts /= time_required_su; //[MWt] + m_E_dot_xover_summed_fullts /= time_required_su; //[MWt] + m_E_dot_HR_cold_fullts /= time_required_su; //[MWt] + m_E_dot_HR_hot_fullts /= time_required_su; //[MWt] + m_q_dot_htf_to_sink_fullts /= time_required_su; //[MWt] + + m_q_dot_freeze_protection = Q_fp_sum / time_required_su; //[MWt] + } + + double Q_dot_balance_subts = m_q_dot_sca_abs_summed_fullts - m_q_dot_xover_loss_summed_fullts - + m_q_dot_HR_cold_loss_fullts - m_q_dot_HR_hot_loss_fullts - + m_E_dot_sca_summed_fullts - m_E_dot_xover_summed_fullts - + m_E_dot_HR_cold_fullts - m_E_dot_HR_hot_fullts - m_q_dot_htf_to_sink_fullts; //[MWt] + + // Solve for pressure drop and pumping power + m_dP_total = field_pressure_drop(weather.m_tdry, this->m_m_dot_htf_tot, this->m_P_field_in, this->m_T_htf_in_t_int, this->m_T_htf_out_t_int); + + // These outputs need some more thought + // For now, just set this > 0.0 so that the controller knows that startup was successful + cr_out_solver.m_q_startup = 1.0; //[MWt-hr] Receiver thermal output used to warm up the receiver + // Startup time is calculated here + cr_out_solver.m_time_required_su = time_required_su; //[s] + // Need to be sure this value is correct..., but controller doesn't use it in CR_SU (confirmed) + + // 5.8.17, twn: Don't report a component *delivered* mass flow rate if fresnel is recirculating... + // .... and not passing HTF to other components + //cr_out_solver.m_m_dot_salt_tot = m_m_dot_htf_tot*3600.0; //[kg/hr] Total HTF mass flow rate + cr_out_solver.m_m_dot_salt_tot = 0.0; //[kg/hr] + + // Should not be available thermal output if receiver is in start up, but controller doesn't use it in CR_SU (confirmed) + cr_out_solver.m_q_thermal = 0.0; //[MWt] No available receiver thermal output + // 7.12.16: Return timestep-end or timestep-integrated-average? + // If multiple recirculation steps, then need to calculate average of timestep-integrated-average + cr_out_solver.m_T_salt_hot = m_T_sys_h_t_int_fullts - 273.15; //[C] + + cr_out_solver.m_component_defocus = 1.0; //[-] + cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; + + cr_out_solver.m_W_dot_elec_in_tot = m_W_dot_sca_tracking + m_W_dot_pump; //[MWe] + // Shouldn't need freeze protection if in startup, but may want a check on this + cr_out_solver.m_q_dot_heater = m_q_dot_freeze_protection; //[MWt] + + set_output_value(); + + return; +} + +void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + double q_dot_elec_to_CR_heat /*MWt*/, double field_control, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + //C_csp_collector_receiver::S_csp_cr_out_report &cr_out_report, + const C_csp_solver_sim_info& sim_info) +{ + + // Always reset last temps + reset_last_temps(); + + m_is_m_dot_recirc = false; + + // Get optical performance (no defocus applied in this method) + // This returns m_q_SCA with NO defocus + loop_optical_eta(weather, sim_info); + + // If Control Defocus: field_control < 1, then apply it here + if (field_control < 1.0) + { + // 1) Calculate m_q_sca_control_df + apply_control_defocus(field_control); + + // 2) Set m_q_sca = m_q_sca_control_df + // m_q_sca_control_df will be baseline for component defocusing downstream in this method + // While loop_energy_balance uses m_q_sca + m_q_SCA = m_q_SCA_control_df; + } + else if (field_control == 1.0) + { + // If no CONTROL defocus, then baseline against the vector returned by 'loop_optical_eta' + m_q_SCA_control_df = m_q_SCA; + } + else + { + throw(C_csp_exception("C_csp_fresnel_collector::on(...) received a CONTROL defocus > 1.0, " + "and that is not ok!")); + } + + // Solve the loop energy balance at the minimum mass flow rate + // Set mass flow rate to minimum allowable + double m_dot_htf_loop = m_m_dot_htfmin; //[kg/s] + // Get inlet condition from input argument + double T_cold_in = htf_state_in.m_temp + 273.15; //[K] + // Call energy balance with updated info + E_loop_energy_balance_exit balance_code = loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info); + + bool on_success = true; + + if (balance_code != E_loop_energy_balance_exit::SOLVED) + { + on_success = false; + } + + // If the outlet temperature (of last SCA!) is greater than the target (considering some convergence tolerance) + // then adjust mass flow rate and see what happens + if ((m_T_htf_out_t_end[m_nMod - 1] - m_T_loop_out_des) / m_T_loop_out_des > 0.001 && on_success) + { + // Try the maximum mass flow rate + m_dot_htf_loop = m_m_dot_htfmax; //[kg/s] + + // We set T_cold_in above, so call loop energy balance + loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info); + + // Is the outlet temperature (of the last SCA!) still greater than the target (considering some convergence tolerance) + // then need to defocus + if ((m_T_htf_out_t_end[m_nMod - 1] - m_T_loop_out_des) / m_T_loop_out_des > 0.001) + { + // The Monotonic Solver will iterate on defocus that achieves the target outlet temperature + // at the maximum HTF mass flow rate + C_mono_eq_defocus c_defocus_function(this, weather, T_cold_in, m_dot_htf_loop, sim_info); + C_monotonic_eq_solver c_defocus_solver(c_defocus_function); + + // Set upper and lower bounds + double defocus_upper = 1.0; //[-] + double defocus_lower = 0.0; //[-] + + // Set guess values... can be smarter about this... + double defocus_guess_upper = min(1.0, (m_T_loop_out_des - m_T_loop_in_des) / (m_T_htf_out_t_end[m_nMod - 1] - m_T_loop_in_des)); + double defocus_guess_lower = 0.9 * defocus_guess_upper; //[-] + + // Set solver settings - relative error on T_htf_out + c_defocus_solver.settings(0.001, 30, defocus_lower, defocus_upper, true); + + int iter_solved = -1; + double tol_solved = std::numeric_limits::quiet_NaN(); + + int defocus_code = 0; + double defocus_solved = 1.0; + try + { + defocus_code = c_defocus_solver.solve(defocus_guess_lower, defocus_guess_upper, m_T_loop_out_des, + defocus_solved, tol_solved, iter_solved); + } + catch (C_csp_exception) + { + throw(C_csp_exception("C_csp_fresnel_collector::on(...) COMPONENT defocus failed.")); + on_success = false; + } + + if (defocus_code != C_monotonic_eq_solver::CONVERGED) + { + throw(C_csp_exception("C_csp_fresnel_collector::on(...) COMPONENT defocus failed.")); + on_success = false; + } + + } + else + { + // Apply 1 var solver to find the mass flow rate that achieves the target outlet temperature + C_mono_eq_T_htf_loop_out c_T_htf_out_calc(this, weather, T_cold_in, sim_info); + C_monotonic_eq_solver c_htf_m_dot_solver(c_T_htf_out_calc); + + // Set upper and lower bounds + double m_dot_upper = m_m_dot_htfmax; //[kg/s] + double m_dot_lower = m_m_dot_htfmin; //[kg/s] + + // Set guess values... can be smarter about this... + double m_dot_guess_upper = 0.75 * m_m_dot_htfmax + 0.25 * m_m_dot_htfmin; //[kg/s] + double m_dot_guess_lower = 0.25 * m_m_dot_htfmax + 0.75 * m_m_dot_htfmin; //[kg/s] + + // Set solver settings + // Relative error + c_htf_m_dot_solver.settings(0.001, 30, m_dot_lower, m_dot_upper, true); + + int iter_solved = -1; + double tol_solved = std::numeric_limits::quiet_NaN(); + + int m_dot_htf_code = 0; + try + { + m_dot_htf_code = c_htf_m_dot_solver.solve(m_dot_guess_lower, m_dot_guess_upper, m_T_loop_out_des, + m_dot_htf_loop, tol_solved, iter_solved); + } + catch (C_csp_exception) + { + throw(C_csp_exception("C_csp_fresnel_collector::on(...) HTF mass flow rate iteration failed.")); + on_success = false; + } + + if (m_dot_htf_code != C_monotonic_eq_solver::CONVERGED) + { + throw(C_csp_exception("C_csp_fresnel_collector::on(...) HTF mass flow rate iteration failed.")); + on_success = false; + } + } + + } + + if (on_success) + { + m_T_sys_c_t_int_fullts = T_cold_in; //[K] + m_T_htf_c_rec_in_t_int_fullts = m_T_htf_in_t_int[0]; //[K] + m_T_htf_h_rec_out_t_int_fullts = m_T_htf_out_t_int[m_nMod - 1]; //[K] + m_T_sys_h_t_int_fullts = m_T_sys_h_t_int; //[K] + + m_q_dot_sca_loss_summed_fullts = m_q_dot_sca_loss_summed_subts; //[MWt] + m_q_dot_sca_abs_summed_fullts = m_q_dot_sca_abs_summed_subts; //[MWt] + m_q_dot_sca_refl_summed_fullts = m_q_dot_sca_refl_summed_subts; //[MWt] + m_q_dot_xover_loss_summed_fullts = m_q_dot_xover_loss_summed_subts; //[MWt] + m_q_dot_HR_cold_loss_fullts = m_q_dot_HR_cold_loss_subts; //[MWt] + m_q_dot_HR_hot_loss_fullts = m_q_dot_HR_hot_loss_subts; //[MWt] + m_E_dot_sca_summed_fullts = m_E_dot_sca_summed_subts; //[MWt] + m_E_dot_xover_summed_fullts = m_E_dot_xover_summed_subts; //[MWt] + m_E_dot_HR_cold_fullts = m_E_dot_HR_cold_subts; //[MWt] + m_E_dot_HR_hot_fullts = m_E_dot_HR_hot_subts; //[MWt] + m_q_dot_htf_to_sink_fullts = m_q_dot_htf_to_sink_subts; //[MWt] + m_q_dot_freeze_protection = 0.0; //[MWt] + + double Q_dot_balance_subts = m_q_dot_sca_abs_summed_fullts - m_q_dot_xover_loss_summed_fullts - + m_q_dot_HR_cold_loss_fullts - m_q_dot_HR_hot_loss_fullts - + m_E_dot_sca_summed_fullts - m_E_dot_xover_summed_fullts - + m_E_dot_HR_cold_fullts - m_E_dot_HR_hot_fullts - m_q_dot_htf_to_sink_fullts; //[MWt] + + // Solve for pressure drop and pumping power + m_dP_total = field_pressure_drop(weather.m_tdry, this->m_m_dot_htf_tot, this->m_P_field_in, this->m_T_htf_in_t_int, this->m_T_htf_out_t_int); + + // Set solver outputs & return + // Receiver is already on, so the controller is not looking for this value + cr_out_solver.m_q_startup = 0.0; //[MWt-hr] + // Receiver is already on, so the controller is not looking for the required startup time + cr_out_solver.m_time_required_su = 0.0; //[s] + // The controller requires the total mass flow rate from the collector-receiver + // This value is set in the most recent call to the loop energy balance + cr_out_solver.m_m_dot_salt_tot = m_m_dot_htf_tot * 3600.0; //[kg/hr] + + // The controller also requires the receiver thermal output + // 7.12.16 Now using the timestep-integrated-average temperature + double c_htf_ave = m_htfProps.Cp((m_T_sys_h_t_int + T_cold_in) / 2.0); //[kJ/kg-K] + cr_out_solver.m_q_thermal = (cr_out_solver.m_m_dot_salt_tot / 3600.0) * c_htf_ave * (m_T_sys_h_t_int - T_cold_in) / 1.E3; //[MWt] + + // Finally, the controller need the HTF outlet temperature from the field + cr_out_solver.m_T_salt_hot = m_T_sys_h_t_int - 273.15; //[C] + + cr_out_solver.m_component_defocus = m_component_defocus; //[-] + cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; + // *********************************************************** + // *********************************************************** + + // For now, set parasitic outputs to 0 + cr_out_solver.m_W_dot_elec_in_tot = m_W_dot_sca_tracking + m_W_dot_pump; //[MWe] + cr_out_solver.m_dP_sf = m_dP_total; //[bar] + cr_out_solver.m_q_dot_heater = m_q_dot_freeze_protection; //[MWt] + } + else + { // Solution failed, so tell controller/solver + + m_T_sys_c_t_int_fullts = 0.0; //[K] + m_T_htf_c_rec_in_t_int_fullts = 0.0; //[K] + m_T_htf_h_rec_out_t_int_fullts = 0.0; //[K] + m_T_sys_h_t_int_fullts = 0.0; //[K] + + m_q_dot_sca_loss_summed_fullts = m_q_dot_sca_abs_summed_fullts = + m_q_dot_sca_loss_summed_fullts = m_q_dot_xover_loss_summed_fullts = + m_q_dot_HR_cold_loss_fullts = m_q_dot_HR_hot_loss_fullts = + m_E_dot_sca_summed_fullts = m_E_dot_xover_summed_fullts = + m_E_dot_HR_cold_fullts = m_E_dot_HR_hot_fullts = + m_q_dot_htf_to_sink_fullts = m_q_dot_freeze_protection = 0.0; + + cr_out_solver.m_q_startup = 0.0; //[MWt-hr] + cr_out_solver.m_time_required_su = 0.0; //[s] + cr_out_solver.m_m_dot_salt_tot = 0.0; //[kg/hr] + cr_out_solver.m_q_thermal = 0.0; //[MWt] + cr_out_solver.m_T_salt_hot = 0.0; //[C] + cr_out_solver.m_component_defocus = 1.0; //[-] + cr_out_solver.m_is_recirculating = false; + m_W_dot_sca_tracking = 0.0; + m_W_dot_pump = 0.0; + cr_out_solver.m_W_dot_elec_in_tot = m_W_dot_sca_tracking + m_W_dot_pump; //[MWe] + cr_out_solver.m_dP_sf = 0.0; //[bar] + + cr_out_solver.m_q_dot_heater = m_q_dot_freeze_protection; //[MWt] + } + + set_output_value(); + + return; +} + +void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + double W_dot_elec_to_CR_heat /*MWe*/, double field_control, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info) +{ + // Original converged values to reset back to + double T_sys_c_t_end_converged_orig = m_T_sys_c_t_end_converged; + double T_sys_h_t_end_converged_orig = m_T_sys_h_t_end_converged; + std::vector T_htf_out_t_end_converged_orig = m_T_htf_out_t_end_converged; + + m_T_sys_c_t_end_converged = htf_state_in.m_temp + 273.15; // this sets m_T_sys_c_t_end_last + m_T_sys_h_t_end_converged = htf_state_in.m_temp + 273.15; // this sets m_T_sys_h_t_end_last + m_T_htf_out_t_end_converged.assign(m_nMod, htf_state_in.m_temp + 273.15); + + // Values for checking whether steady-state + double ss_diff = std::numeric_limits::quiet_NaN(); + const double tol = 0.05; + std::vector T_htf_in_t_int_last = m_T_htf_in_t_int; + std::vector T_htf_out_t_int_last = m_T_htf_out_t_int; + double minutes2SS = 0.; + int count = 0; + int max_iterations = 50; + + do + { + this->on(weather, htf_state_in, W_dot_elec_to_CR_heat, field_control, cr_out_solver, sim_info); + + // Calculate metric for deciding whether steady-state is reached + ss_diff = 0.; + for (int i = 0; i < m_nMod; i++) { + ss_diff += std::abs(m_T_htf_in_t_int[i] - T_htf_in_t_int_last[i]) + + std::abs(m_T_htf_out_t_int[i] - T_htf_out_t_int_last[i]); + } + + // Set converged values so reset_last_temps() propagates the temps in time + m_T_sys_c_t_end_converged = m_T_sys_c_t_end; + m_T_sys_h_t_end_converged = m_T_sys_h_t_end; + m_T_htf_out_t_end_converged = m_T_htf_out_t_end; + + // Update 'last' values + T_htf_in_t_int_last = m_T_htf_in_t_int; + T_htf_out_t_int_last = m_T_htf_out_t_int; + + minutes2SS += sim_info.ms_ts.m_step / 60.; + + + count++; + } while (ss_diff / 200. > tol && count < max_iterations); + + if (count == max_iterations) + { + int x = 0; + } + + // Re-run runner and header pipe sizing using the same diameters to get the actual mass flows and velocities at steady state + double m_dot_ss = cr_out_solver.m_m_dot_salt_tot / 3600.; // [kg/s] + double rho_cold = m_htfProps.dens(T_htf_in_t_int_last[0], 10.e5); // [kg/m3] + double rho_hot = m_htfProps.dens(T_htf_out_t_int_last[m_nMod - 1], 10.e5); // [kg/m3] + std::string summary; + + // Set steady-state outputs + transform(m_T_rnr.begin(), m_T_rnr.end(), m_T_rnr_dsn.begin(), [](double x) {return x - 273.15; }); // K to C + transform(m_P_rnr.begin(), m_P_rnr.end(), m_P_rnr_dsn.begin(), [](double x) {return x / 1.e5; }); // Pa to bar + transform(m_T_hdr.begin(), m_T_hdr.end(), m_T_hdr_dsn.begin(), [](double x) {return x - 273.15; }); // K to C + //transform(m_P_hdr.begin(), m_P_hdr.end(), m_P_hdr_dsn.begin(), [](double x) {return x / 1.e5; }); // Pa to bar + transform(m_T_loop.begin(), m_T_loop.end(), m_T_loop_dsn.begin(), [](double x) {return x - 273.15; }); // K to C + //transform(m_P_loop.begin(), m_P_loop.end(), m_P_loop_dsn.begin(), [](double x) {return x / 1.e5; }); // Pa to bar + + // After steady-state is calculated, reset back to original converged values + m_T_sys_c_t_end_converged = T_sys_c_t_end_converged_orig; + m_T_sys_h_t_end_converged = T_sys_h_t_end_converged_orig; + m_T_htf_out_t_end_converged = T_htf_out_t_end_converged_orig; + + return; +} + + +void C_csp_fresnel_collector_receiver::estimates(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_est_out& est_out, + const C_csp_solver_sim_info& sim_info) +{ + if (m_operating_mode == C_csp_collector_receiver::ON) + { + C_csp_collector_receiver::S_csp_cr_out_solver cr_out_solver; + + on(weather, htf_state_in, std::numeric_limits::quiet_NaN(), 1.0, cr_out_solver, sim_info); + + est_out.m_q_dot_avail = cr_out_solver.m_q_thermal; //[MWt] + est_out.m_m_dot_avail = cr_out_solver.m_m_dot_salt_tot; //[kg/hr] + est_out.m_T_htf_hot = cr_out_solver.m_T_salt_hot; //[C] + est_out.m_q_startup_avail = 0.0; //[MWt] + } + else + { + if (weather.m_beam > 1.0) + { + est_out.m_q_startup_avail = 1.0; //[MWt] fresnel is recirculating, so going into startup isn't significantly different than OFF + } + else + { + est_out.m_q_startup_avail = 0.0; + } + est_out.m_q_dot_avail = 0.0; + est_out.m_m_dot_avail = 0.0; + est_out.m_T_htf_hot = 0.0; + } + + return; +} + +void C_csp_fresnel_collector_receiver::converged() +{ + /* + -- Post-convergence call -- + + Update values that should be transferred to the next time step + */ + + // Check that, if fresnel is ON, if outlet temperature at the end of the timestep is colder than the Startup Temperature + if (m_operating_mode == ON && m_T_sys_h_t_end < m_T_startup) + { + m_operating_mode = OFF; + } + + // CSP Solver Temperature Tracking + m_T_sys_c_t_end_converged = m_T_sys_c_t_end_last = m_T_sys_c_t_end; //[K] + m_T_sys_h_t_end_converged = m_T_sys_h_t_end_last = m_T_sys_h_t_end; //[K] + for (int i = 0; i < m_nMod; i++) + { + m_T_htf_out_t_end_converged[i] = m_T_htf_out_t_end_last[i] = m_T_htf_out_t_end[i]; //[K] + } + + if (m_operating_mode == C_csp_collector_receiver::STEADY_STATE) + { + throw(C_csp_exception("Receiver should only be run at STEADY STATE mode for estimating output. It must be run at a different mode before exiting a timestep", + "fresnel converged method")); + } + + m_operating_mode_converged = m_operating_mode; //[-] + + // Always reset the m_defocus control at the first call of a timestep + //m_defocus_new = 1.0; //[-] + //m_defocus_old = 1.0; //[-] + //m_defocus = 1.0; //[-] + + m_W_dot_sca_tracking = 0.0; //[MWe] + + // Reset the optical efficiency member data + loop_optical_eta_off(); + + mc_reported_outputs.set_timestep_outputs(); + + return; +} + +void C_csp_fresnel_collector_receiver::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); +} + +double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim) +{ + // loop_optical_eta() has side-effects. Store affected member variable values for restore after call. + double m_q_i_ini(m_q_i); + util::matrix_t m_ColOptEff_ini(m_ColOptEff); + double m_EqOpteff_ini(m_EqOpteff); + std::vector m_q_SCA_ini(m_q_SCA); + double m_W_dot_sca_tracking_ini(m_W_dot_sca_tracking); + double m_control_defocus_ini(m_control_defocus); + double m_component_defocus_ini(m_component_defocus); + double m_q_dot_inc_sf_tot_ini(m_q_dot_inc_sf_tot); + + loop_optical_eta(weather, sim); + double eta_optical = m_eta_optical; // m_EqOpteff; + + // Restore member variable values + m_q_i = m_q_i_ini; + m_ColOptEff = m_ColOptEff_ini; + m_EqOpteff = m_EqOpteff_ini; + m_q_SCA = m_q_SCA_ini; + m_W_dot_sca_tracking = m_W_dot_sca_tracking_ini; + m_control_defocus = m_control_defocus_ini; + m_component_defocus = m_component_defocus_ini; + m_q_dot_inc_sf_tot = m_q_dot_inc_sf_tot_ini; + + return eta_optical; +} + +double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) +{ + // q_incident is the power incident (absorbed by the absorber) on all the HCE receivers, calculated using the DNI and optical efficiency + if (q_incident <= 0) return 0.; + + // New Estimate (using steady state) + double q_eff = 0; + { + C_csp_solver_htf_1state htfInletState; + htfInletState.m_temp = m_T_loop_in_des - 273.15; + double defocus = 1; + //C_csp_solver_sim_info fresnelInfo; + //fresnelInfo.ms_ts.m_time_start = 14817600.; + //fresnelInfo.ms_ts.m_step = 15. * 60.; // 5-minute timesteps + //fresnelInfo.ms_ts.m_time = fresnelInfo.ms_ts.m_time_start + fresnelInfo.ms_ts.m_step; + //fresnelInfo.m_tou = 1.; + + C_csp_solver_sim_info fresnelInfo; + fresnelInfo.ms_ts.m_time_start = sim.ms_ts.m_time_start; + fresnelInfo.ms_ts.m_step = 15. * 60.; // 5-minute timesteps + fresnelInfo.ms_ts.m_time = sim.ms_ts.m_time; + fresnelInfo.m_tou = 1.; + + C_csp_collector_receiver::S_csp_cr_out_solver fresnelOutputs; + + steady_state(weather, htfInletState, std::numeric_limits::quiet_NaN(), defocus, fresnelOutputs, fresnelInfo); + double q_thermal = fresnelOutputs.m_q_thermal * 1e6; // [Wt] + + // Optical Efficiency + double optical_eff = m_eta_optical; + double given_optical_eff = (q_incident * 1e6) / (m_Ap_tot * weather.m_beam); + double eff_now = this->calculate_optical_efficiency(weather, sim); + + // Thermal Efficiency + double Q_available = m_Ap_tot * weather.m_beam * optical_eff; // W + q_eff = q_thermal / Q_available; + + if (q_incident == 0) + q_eff = 0; + + else if (q_eff < 0) + q_eff = 0; + + else if (q_eff > 1) + q_eff = 1; + + + } + + return q_eff; +} + +double C_csp_fresnel_collector_receiver::get_collector_area() +{ + return m_Ap_tot; +} + +// ------------------------------------------------------------------- PUBLIC SUPPLEMENTAL + +bool C_csp_fresnel_collector_receiver::design_solar_mult() +{ + if (m_is_solar_mult_designed == true) + return false; + + // Calculate nLoops, depending on designing for solar mult or total field aperture + { + // Optical Derate (Receiver) + m_opt_derate = 0; + for (int i = 0; i < m_nRecVar; i++) + m_opt_derate += m_HCE_FieldFrac[i] * m_Shadowing[i] * m_dirt_env[i]; + + // Optical Normal (Mirror/Collector) + m_opt_normal = 0; + m_opt_normal = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; + + // Loop Optical Efficiency + m_loop_opt_eff = m_opt_derate * m_opt_normal; + + // Loop Aperture + m_A_loop = (float)m_nMod * m_A_aperture; + + // Heat Loss at Design + m_hl_des = 0; + m_dT_des = ((m_T_loop_in_des + m_T_loop_out_des) / 2.0) - (m_T_amb_sf_des + 273.15); // Average temperature difference at design + switch (m_rec_model) + { + // Polynomial + case (1): + { + m_hl_des = CSP::poly_eval(m_dT_des, &m_HL_T_coefs[0], m_HL_T_coefs.size()); + break; + } + // Evacuated Receiver + case (2): + { + for (int i = 0; i < m_nRecVar; i++) + m_hl_des += m_HCE_FieldFrac[i] * m_Design_loss[i]; + break; + } + default: + { + string msg = "The selected thermal model (%d) does not exist. Options are 1=Regression model : 2=Evacuated tube receiver model"; + m_error_msg = util::format(msg.c_str(), m_rec_model); + mc_csp_messages.add_message(C_csp_messages::NOTICE, m_error_msg); + return false; + } + } + + // Loop Thermal Efficiency + m_loop_therm_eff = 1.0 - ((m_hl_des * m_L_mod * m_nMod) / (m_A_loop * m_I_bn_des * m_loop_opt_eff)); + + // Loop Efficiency + m_loop_eff = m_loop_opt_eff * m_loop_therm_eff; + + // Thermal Power at Design + m_q_design = m_P_ref / m_eta_ref; + + // Required Aperture for solar multiple = 1 + m_Ap_sm1 = m_q_design / (m_I_bn_des * m_loop_eff); + + // Calculate actual solar mult, total field aperture, and nLoops + switch (m_solar_mult_or_Ap) + { + // Use Solar Multiple + case 0: + { + m_solar_mult = m_solar_mult_in; + m_Ap_tot = m_solar_mult * m_Ap_sm1; + m_nLoops = std::ceil(m_Ap_tot / m_A_loop); + + // Get 'Actual' Ap_tot + m_Ap_tot = m_nLoops * m_A_loop; + break; + } + case 1: + { + m_Ap_tot = m_total_Ap_in; + m_nLoops = std::ceil(m_Ap_tot / m_A_loop); + + // Get 'Actual' total aperture + m_Ap_tot = m_nLoops * m_A_loop; + m_solar_mult = m_Ap_tot / m_Ap_sm1; + break; + } + default: + { + string msg = "use_solar_mult_or_total_Ap integer should be 0 (solar mult) or 1 (field aperture)"; + mc_csp_messages.add_message(C_csp_messages::NOTICE, msg); + return false; + } + } + + // Update m_q_design with actual aperture + m_q_design = m_I_bn_des * m_Ap_tot * m_loop_eff; + + // Number of Loops necessary for solar mult = 1 + m_nLoops_sm1 = std::ceil(m_Ap_sm1 / m_A_loop); + + } + return true; +} + +// ******************************************************************** Internal Class Methods + +int C_csp_fresnel_collector_receiver::C_mono_eq_defocus::operator()(double defocus /*-*/, double* T_htf_loop_out /*K*/) +{ + // Apply the defocus to calculate a new m_q_SCA + mpc_fresnel->apply_component_defocus(defocus); + + // Solve the loop energy balance at the input mass flow rate + E_loop_energy_balance_exit exit_code = mpc_fresnel->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_m_dot_loop, ms_sim_info); + + if (exit_code != E_loop_energy_balance_exit::SOLVED) + { + *T_htf_loop_out = std::numeric_limits::quiet_NaN(); + return -1; + } + + // Set the outlet temperature at end of timestep + *T_htf_loop_out = mpc_fresnel->m_T_htf_out_t_end[mpc_fresnel->m_nMod - 1]; + + return 0; +} + +int C_csp_fresnel_collector_receiver::C_mono_eq_T_htf_loop_out::operator()(double m_dot_htf_loop /*kg/s*/, double* T_htf_loop_out /*K*/) +{ + // Solve the loop energy balance at the input mass flow rate + E_loop_energy_balance_exit exit_code = mpc_fresnel->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_dot_htf_loop, ms_sim_info); + + if (exit_code != E_loop_energy_balance_exit::SOLVED) + { + *T_htf_loop_out = std::numeric_limits::quiet_NaN(); + return -1; + } + + // Set the outlet temperature at end of timestep + *T_htf_loop_out = mpc_fresnel->m_T_htf_out_t_end[mpc_fresnel->m_nMod - 1]; + + return 0; +} + +int C_csp_fresnel_collector_receiver::C_mono_eq_freeze_prot_E_bal::operator()(double T_htf_cold_in /*K*/, double* E_loss_balance /*-*/) +{ + // Solve the loop energy balance at the input HTF inlet temperature + if (mpc_fresnel->loop_energy_balance_T_t_int(ms_weather, T_htf_cold_in, m_m_dot_loop, ms_sim_info) != E_loop_energy_balance_exit::SOLVED) + { + *E_loss_balance = std::numeric_limits::quiet_NaN(); + return -1; + } + + // Get energy added to the HTF + m_Q_htf_fp = mpc_fresnel->m_m_dot_htf_tot * mpc_fresnel->m_c_htf_ave_ts_ave_temp * + (T_htf_cold_in - mpc_fresnel->m_T_sys_h_t_end_last) / 1.E6 * (ms_sim_info.ms_ts.m_step); //[MJ] + + // Set the normalized difference between the Field Energy Loss and Freeze Protection Energy + *E_loss_balance = (m_Q_htf_fp - mpc_fresnel->m_Q_field_losses_total_subts) / mpc_fresnel->m_Q_field_losses_total_subts; //[-] + + return 0; +} + + + + +// ******************************************************************** Evacuated Receiver Model + +// This subroutine contains the trough detailed plant model. The collector field is modeled +// using an iterative solver. +// This code was written for the National Renewable Energy Laboratory +// Copyright 2009-2010 +// Author: Mike Wagner +// +// Subroutine Inputs (and parameters) +// ---------------------------------------------------------------------------------------------------------------------- +// Nb | Variable | Description | Input Units | Internal Units +// ---|----------------------|---------------------------------------------------------|----------------|---------------- +// 1 | T_1_in | Receiver inlet temperature | | +// 2 | m_dot | Heat transfer fluid mass flow rate | | +// 3 | T_amb | Ambient dry-bulb temperature | | +// 4 | T_sky | Sky temperature | | +// 5 | v_6 | Ambient wind velocity | | +// 6 | P_6 | Ambient atmospheric pressure | | +// 7 | q_i | Total incident irradiation on the receiver | | +// 8 | A_cs | Internal absorber tube cross-sectional area | | +// 9 | m_D_abs_in | Internal absorber tube diameter | | +// 10 | m_D_abs_out | External absorber tube diameter | | +// 11 | m_D_glass_in | Internal glass envelope diameter | | +// 12 | m_D_glass_out | External glass envelope diameter | | +// 13 | m_D_plug | (optional) Plug diameter | | +// 14 | m_D_h | Absorber tube hydraulic diameter | | +// 15 | eps_mode | Interpolation mode for the emissivity (1=table,2=fixed) | | +// 16 | xx | Array of temperature values for emissivity table | | +// 17 | yy | Array of emissivity values for table | | +// 18 | nea | Number of entries in the emissivity table | | +// 19 | m_L_mod | Length of the active receiver surface | | +// 20 | single_point | Logical flag - is the calculation for a single point? | | +// 21 | Epsilon_32 | Constant value for emissivity if table isn't used | | +// 22 | Epsilon_4 | Envelope inner surface emissivity | | +// 23 | epsilon_glass | Envelope outer surface emissivity | | +// 24 | m_alpha_abs | Absorber tube absorptance | | +// 25 | m_alpha_env | Envelope absorptance | | +// 26 | m_ColOptEff | Collector optical efficiency | | +// 27 | m_Tau_envelope | Total envelope transmittance | | +// 28 | m_P_a | Annulus gas pressure | torr | +// 29 | Flow_type | Flag indicating the presence of an internal plug | | +// 30 | AnnulusGas | Annulus gas type | | +// 31 | Fluid | Heat transfer fluid type | | +// 32 | AbsorberMaterial | Absorber material type | | +// 33 | time | Simulation time | | +// +// Subroutine outputs +// ---------------------------------------------------------------------------------------------------------------------- +// Nb | Variable | Description | Input Units | Internal Units +// ---|----------------------|---------------------------------------------------------|----------------|---------------- +// 1 | q_heatloss | Total heat loss from the receiver | W/m | +// 2 | q_12conv | Total heat absorption into the HTF | W/m | +// 3 | q_34tot | Convective and radiative heat loss | | +// 4 | c_1ave | Specific heat of the HTF across the receiver | kJ/kg-K | +// 5 | rho_1ave | Density of the HTF across the receiver | | +// +// ---------------------------------------------------------------------------------------------------------------------- +// Forristall Temperature distribution diagram +// ***************************************************** +// Fluid (1) ----------->(2)<--Absorber-->(3)<-- Annulus -->(4)<--- Glass --->(5)<-- Air (6)/Sky (7) +// +// +// T_1 = Bulk heat transfer fluid (HTF) temperature +// T_2 = Absorber Inside surface temperature +// T_3 = Absorber outside surface temperature +// T_4 = Glass envelope inside surface temperature +// T_5 = Glass envelope outside surface temperature +// T_6 = Ambient temperature +// T_7 = Effective Sky Temperature +// +// q_12conv = Convection heat transfer rate per unit length between the HTF and the inside of the receiver tube +// q_23cond = Conduction heat transfer rate per unit length through the absorber +// q_34conv = Convection heat transfer rate per unit length between the absorber outer surface and the glazing inner surface +// q_34rad = Radiation heat transfer rate per unit length between the absorber outer surface and the glazing inner surface +// q_45cond = Conduction heat transfer rate per unit length through the glazing +// q_56conv = Convection heat transfer rate per unit length between the glazing outer surface and the ambient air +// q_57rad = Radiation heat transfer rate per unit length between the glazing outer surface and the sky +// ---------------------------------------------------------------------------------------------------------------------- +// */ + +/// +/// This subroutine contains the fresnel detailed plant model. The collector field is modeled +/// using an iterative solver. +/// This code was written for the National Renewable Energy Laboratory +/// Copyright 2009-2010 +/// Author: Mike Wagner +/// +/// Internal absorber tube diameter +/// External absorber tube diameter +/// Internal glass envelope diameter +/// External glass envelope diameter +/// (optional) Plug diameter +/// Length of the active receiver surface +/// Is glazing intact +/// +/// +/// Annulus gas pressure +/// Absorber tube absorptance +/// Envelope outer surface emissivity +/// Total envelope transmittance +/// Envelope absorptance +/// +/// HTF Material Properties +/// Air Material Properties +/// Annulus gas type +/// Annulus gas properties +/// Fluid type +/// Cross sectional area +/// Hydraulic diameter +EvacReceiverModel::EvacReceiverModel(vector D_abs_in, vector D_abs_out, vector D_glass_in, vector D_glass_out, vector D_plug, + double L_mod, vector GlazingIntact, vector Shadowing, vector dirt_env, vector P_a, vector alpha_abs, + vector epsilon_glass, vector Tau_envelope, vector alpha_env, emit_table* epsilon_abs, HTFProperties htfProps, HTFProperties airProps, + util::matrix_t AnnulusGasMat, util::matrix_t AbsorberPropMat, vector Flow_type, vector A_cs, vector D_h) + : + m_D_abs_in(D_abs_in), m_D_abs_out(D_abs_out), m_D_glass_in(D_glass_in), m_D_glass_out(D_glass_out), m_D_plug(D_plug), + m_L_mod(L_mod), m_GlazingIntact(GlazingIntact), m_Shadowing(Shadowing), m_dirt_env(dirt_env), m_P_a(P_a), m_alpha_abs(alpha_abs), + m_epsilon_glass(epsilon_glass), m_Tau_envelope(Tau_envelope), m_alpha_env(alpha_env), m_epsilon_abs(epsilon_abs), m_htfProps(htfProps), m_airProps(airProps), + m_AnnulusGasMat(AnnulusGasMat), m_AbsorberPropMat(AbsorberPropMat), m_Flow_type(Flow_type), m_A_cs(A_cs), m_D_h(D_h) +{ +} + +/// +/// Calculate Energy Balance for evacuated receiver +/// +/// Receiver inlet temperature +/// Heat transfer fluid mass flow rate +/// Ambient dry-bulb temperature +/// Sky temperature +/// Ambient wind velocity +/// Ambient atmospheric pressure +/// Total incident irradiation on the receiver +/// HCE variant [0..3] +/// Module index +/// +/// +/// +/// Collector optical efficiency +/// Total heat loss from the receiver +/// Total heat absorption into the HTF +/// Convective and radiative heat loss +/// Specific heat of the HTF across the receiver +/// Density of the HTF across the receiver +/// +/// Absorber reflective losses +void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, double T_amb, double T_sky, double v_6, double P_6, double q_i, + int hv /* HCE variant [0..3] */, int sca_num, bool single_point, double time, util::matrix_t ColOptEff, + //outputs + double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args, double& q_3reflect) +{ + + //---Variable declarations------ + bool reguess; + double T_2, T_3, T_4, T_5, T_6, T_7, v_1, k_23, q_34conv, q_34rad, h_34conv, h_34rad, q_23cond, + k_45, q_45cond, q_56conv, h_56conv, q_57rad, q_3SolAbs, q_5solabs, q_cond_bracket, R_45cond, + T_save[5], T_2g, cp_1, T3_tol, q5_tol, T1_tol, T2_tol, Diff_T3, diff_q5, T_lower, T_upper, + q_5out, T_1_out, diff_T1, T_1_ave, T_1_out1, diff_T2, eps_3, q_in_W, T_upper_max, y_upper, + y_lower, upmult, q5_tol_1, T3_upper, T3_lower, y_T3_upper, y_T3_lower, abs_diffT3; + + bool UPFLAG, LOWFLAG, T3upflag, T3lowflag, is_e_table; + int qq, q5_iter, T1_iter, q_conv_iter; + + double T_save_tot, colopteff_tot; + //cc--> note that xx and yy have size 'nea' + + //---Re-guess criteria:--- + if (time <= 2) goto lab_reguess; + + if (((int)v_reguess_args[0] == 1) != m_GlazingIntact.at(hv)) goto lab_reguess; //m_GlazingIntact state has changed + + if (m_P_a[hv] != v_reguess_args[1]) goto lab_reguess; //Reguess for different annulus pressure + + if (std::abs(v_reguess_args[2] - T_1_in) > 50.) goto lab_reguess; + + for (int i = 0; i < 5; i++) { if (T_save[i] < T_sky - 1.) goto lab_reguess; } + + T_save_tot = 0.; + for (int i = 0; i < 5; i++) { T_save_tot += T_save[i]; } + if (T_save_tot != T_save_tot) goto lab_reguess; //NaN check.. a value is only not equal to itself if it is NaN + + reguess = false; + goto lab_keep_guess; +lab_reguess: + reguess = true; +lab_keep_guess: + + + //------------------------ + + if (reguess) { + if (m_GlazingIntact.at(hv)) { + T_save[0] = T_1_in; + T_save[1] = T_1_in + 2.; + T_save[2] = T_save[1] + 5.; + if (m_P_a[hv] > 1.0) { //Set guess values for different annulus pressures + T_save[3] = T_save[2] - 0.5 * (T_save[2] - T_amb); //If higher pressure, guess higher T4 + T_upper_max = T_save[2] - 0.2 * (T_save[2] - T_amb); //Also, high upper limit for T4 + } + else { + T_save[3] = T_save[2] - 0.9 * (T_save[2] - T_amb); //If lower pressure, guess lower T4 + T_upper_max = T_save[2] - 0.5 * (T_save[2] - T_amb); //Also, low upper limit for T4 + } + T_save[4] = T_save[3] - 2.; + + v_reguess_args[1] = m_P_a[hv]; //Reset previous pressure + v_reguess_args[0] = m_GlazingIntact.at(hv) ? 1. : 0.; //Reset previous glazing logic + v_reguess_args[2] = T_1_in; //Reset previous T_1_in + + } + else { + T_save[0] = T_1_in; + T_save[1] = T_1_in + 2.; + T_save[2] = T_save[1] + 5.; + T_save[3] = T_amb; + T_save[4] = T_amb; + + v_reguess_args[0] = m_GlazingIntact.at(hv) ? 1. : 0.; //Reset previous glazing logic + v_reguess_args[1] = T_1_in; //Reset previous T_1_in + + } + } + + //Set intial guess values + T_2 = T_save[1]; + T_3 = T_save[2]; + T_4 = T_save[3]; + T_5 = T_save[4]; + //Set constant temps + T_6 = T_amb; + T_7 = T_sky; + + qq = 0; //Set iteration counter for T3 loop + + T_2g = T_2; //Initial guess value for T_2 (only used in property lookup) + cp_1 = 1950.; //Initial guess value for cp of WF + + //Tolerances for iteration + T3_tol = 1.5e-3; + q5_tol = 1.0e-3; //Since iterations are nested inside T3, make tolerances a bit tighter + T1_tol = 1.0e-3; + T2_tol = 1.0e-3; + + ////Decreasing the tolerance helps get out of repeating defocus iterations + //if (ncall > 8) { + // T3_tol = 1.5e-4; //1.0 + // q5_tol = 1.0e-4; //max(1.0, 0.001*q_i) + // T1_tol = 1.0e-4; //1.0 + // T2_tol = 1.0e-4; //1.0 + //} + + Diff_T3 = 10.0 + T3_tol; //Set difference > tolerance + + //Constants + k_45 = 1.04; //[W/m-K] Conductivity of glass + R_45cond = log(m_D_glass_out[hv] / m_D_glass_in[hv]) / (2. * pi * k_45); //[K-m/W]Equation of thermal resistance for conduction through a cylinder + + colopteff_tot = ColOptEff.at(sca_num) * m_dirt_env[hv] * m_Shadowing[hv]; //The total optical efficiency + + if (m_GlazingIntact.at(hv)) { //These calculations (q_3SolAbs,q_5solAbs) are not dependent on temperature, so only need to be computed once per call to subroutine + + q_3SolAbs = q_i * colopteff_tot * m_Tau_envelope[hv] * m_alpha_abs[hv]; //[W/m] + //We must account for the radiation absorbed as it passes through the envelope + q_5solabs = q_i * colopteff_tot * m_alpha_env[hv]; //[W/m] + + q_3reflect = q_i * colopteff_tot * m_Tau_envelope[hv] * (1.0 - m_alpha_abs[hv]); // [W/m] + } + else { + //Calculate the absorbed energy + q_3SolAbs = q_i * colopteff_tot * m_alpha_abs[hv]; //[W/m] + //No envelope + q_5solabs = 0.0; //[W/m] + + + q_3reflect = q_i * colopteff_tot * (1.0 - m_alpha_abs[hv]); // [W/m] + } + + is_e_table = false; + if (m_epsilon_abs->getTableSize(hv) < 2) { + eps_3 = m_epsilon_abs->getSingleValue(hv); + } + else { + eps_3 = m_epsilon_abs->interpolate(hv, T_3 - 273.15); //Set epsilon value for case that eps_mode = 1. Will reset inside temp loop if eps_mode > 1. + is_e_table = true; //The emissivity is in tabular form + } + + T3upflag = false; + T3lowflag = false; + + double T3_adjust = 0.0; + double T3_prev_qq = 0.0; + + while (((std::abs(Diff_T3) > T3_tol) && (qq < 100)) || (qq < 2)) { //Outer loop: Find T_3 such than energy balance is satisfied + qq = qq + 1; //loop counter + + T3_prev_qq = T_3; + + if (qq > 1) { + if ((T3upflag) && (T3lowflag)) { + if (Diff_T3 > 0.) { + T3_upper = T_3; + y_T3_upper = Diff_T3; + } + else { + T3_lower = T_3; + y_T3_lower = Diff_T3; + } + T_3 = (y_T3_upper) / (y_T3_upper - y_T3_lower) * (T3_lower - T3_upper) + T3_upper; + + } + else { + if (Diff_T3 > 0.) { + T3_upper = T_3; + y_T3_upper = Diff_T3; + T3upflag = true; + } + else { + T3_lower = T_3; + y_T3_lower = Diff_T3; + T3lowflag = true; + } + + if ((T3upflag) && (T3lowflag)) { + T_3 = (y_T3_upper) / (y_T3_upper - y_T3_lower) * (T3_lower - T3_upper) + T3_upper; + } + else + { + if (Diff_T3 > 0.) + T_3 = T_3 - 50.0; + else + T_3 = T_3 + 50.0; + //T_3 = max(T_7, T_3 - abs_diffT3); //Note that recalculating T_3 using this exact equation, rather than T_3 = T_3 - frac*diff_T3 was found to solve in fewer iterations + } + } + } + + T3_adjust = T_3 - T3_prev_qq; + + //Calculate temperature sensitive emissivity using T_3, if required + if (is_e_table) eps_3 = m_epsilon_abs->interpolate(hv, (T_3 - 273.15)); //call interp((T_3-273.15),eps_mode,xx,yy,eps3old,eps_3) + + //Separate m_GlazingIntact = true and m_GlazingIntact = false If true, T4 must be solved, if false then T4 is explicitly known (or doesn't exist, depending on how you want to look at it) + //Solving for correct T4 as it relates to current T3 value + if (m_GlazingIntact.at(hv)) { + + //********************************************** + //************* SET UP T_4 ITERATION ********************** + //********************************************** + + // if(qq==1){ //If first iteration, set T_4 bounds to phyiscal limits defined by T_3 and T_sky + // T_lower = T_sky; //Lowest possible temperature of T_4 is sky temp + // T_upper = max(T_upper_max,T_amb); //Highest possible temperature is the highest temperature on either side of T_4: either T_3 or ambient + // q5_tol_1= 0.001; //Just get T4 in the ball park. '20' may not be the optimum value..... + // } + // else { //For additional iterations: + // T_lower = T_lower - max(abs_diffT3,0.0); //If diff_T3 is + then new T3 < old T3 so adjust lower limit + // T_upper = T_upper + fabs(min(abs_diffT3,0.0)); //If diff_T3 is (-) then new T3 > old T3 so adjust upper limit + // q5_tol_1= q5_tol; //For remaining T3 iterations, use specified tolerance (note that 2 iterations for T3 are gauranteed) + // } + if (qq == 1) + { + T_lower = T_sky; + T_upper = max(T_3, T_amb); + } + else + { + if (T3_adjust > 0.0) // new T3 > old T3 so adjust upper limit + { + T_upper = min(T_3, T_upper + 1.25 * T3_adjust); + T_lower = T_4; + T_4 = T_4 + 0.5 * T3_adjust; + } + else // T3_adjust negative + { + T_lower = max(T_sky, T_lower + 1.25 * T3_adjust); + T_upper = T_4; + T_4 = T_4 + 0.5 * T3_adjust; + } + } + q5_tol_1 = q5_tol; + + //if( T_4 > T_upper || T_4 < T_lower ) + // T_4 = 0.5*(T_upper + T_lower); + + diff_q5 = q5_tol_1 + 1.0; //Set diff > tolerance + q5_iter = 0; //Set iteration counter + + UPFLAG = false; //Set logic to switch from bisection to false position mode + LOWFLAG = false; //Set logic to switch from bisection to false position mode + //*********************************************************************************** + //************* Begin Bisection/False Position Iteration method ********************* + //*********************************************************************************** + while ((std::abs(diff_q5) > q5_tol_1) && (q5_iter < 100)) { //Determine T_4 such that energy balance from T_3 to surroundings is satisfied + + q5_iter = q5_iter + 1; //Increase iteration counter + + //The convective heat exchange between the absorber and the envelope + // UNITS ( K , K, torr, Pa , m/s, K , -, -, W/m, W/m2-K) + FQ_34CONV_v2(T_3, T_4, P_6, v_6, T_6, hv, q_34conv, h_34conv); + + //The radiative heat exchange between the absorber and the envelope + // Units ( K , K , m , m , K , - , - , logical , W/m , W/m2-K) + FQ_34RAD_v2(T_3, T_4, T_7, eps_3, hv, q_34rad, h_34rad); + //The total heat exchange between absorber and envelope + q_34tot = q_34conv + q_34rad; //[W/m] + + //********************************************** + //************* Calculate T_5 ************* + //********************************************** + //The thermal energy flow across 45 is equal to the energy from the absorber plus + //the thermal energy that is generated by direct heating of the glass envelope + q_45cond = q_34tot + q_5solabs; //[W/m] + + //Knowing heat flow and properties, T_5 can be calculated + T_5 = T_4 - q_45cond * R_45cond; //[K] + + //************************************************************************* + //************* Calculate HT from exterior surface to ambient ************* + //************************************************************************* + //With T_5 and T_6 (amb T) calculate convective and radiative loss from the glass envelope + // units ( K , K , torr, m/s, -, -, W/m, W/m2-K) + FQ_56CONV_v2(T_5, T_6, P_6, v_6, hv, q_56conv, h_56conv); //[W/m] + q_57rad = m_epsilon_glass[hv] * 5.67e-8 * (pow(T_5, 4) - pow(T_7, 4)); + q_5out = q_57rad + q_56conv; //[W/m] + + //*************************************************************************** + //********** Compare q_5out with q_45 cond*********************************** + //*************************************************************************** + diff_q5 = (q_5out - q_45cond) / q_45cond; //[W/m] + + //Determine next guess for T_4. Want to use false position method, but it requires that the *results* at both ends of the bracket are known. We have + //defined a bracket but not the results. Use the guess T_4 to get the results at one end of a new bracket. Then calculate a new T_4 that is highly weighted + //towards the side of the original bracket that the 1st T_4 did not replace. In most cases, this new T_4 will result in the opposite diff_q5, which + //defines both sides of the bracket. If results for both sides are then defined, "LOWFLAG" and "UPFLAG" will be true, and false position method will be applied. + + if (LOWFLAG && UPFLAG) { //False position method + if (diff_q5 > 0.0) { + T_upper = T_4; //If energy leaving T_5 is greater than energy entering T_5, then T_4 guess is too high + y_upper = diff_q5; //so set new upper limit to T_4 + } + else { //If energy leaving T_5 is less than energy entering T_5, then T_4 guess is too low + T_lower = T_4; //so set new lower limit to T_4 + y_lower = diff_q5; //also, set result to go along with lower limit + } + T_4 = (y_upper) / (y_upper - y_lower) * (T_lower - T_upper) + T_upper; + + } + else { //For bisection method... + + if (diff_q5 > 0.0) { //If energy leaving T_5 is greater than energy entering T_5, then T_4 guess is too high + T_upper = T_4; //so set new upper limit to T_4 + y_upper = diff_q5; //also, set result to go along with upper limit + UPFLAG = true; //Upper result is now known + if (qq == 1) { + upmult = 0.1; //Just want to get in ballpark for first iteration of receiver + } + else { + upmult = 0.1; //Weight such that next calculated T_4 (if using bisection method) results in negative diff_q5 + } + + } + else { //If energy leaving T_5 is less than energy entering T_5, then T_4 guess is too low + T_lower = T_4; //so set new lower limit to T_4 + y_lower = diff_q5; //also, set result to go along with lower limit + LOWFLAG = true; //Lower result is now known + if (qq == 1) { + upmult = 0.1; //Just want to get in ballpark for first iteration of receiver + } + else { + upmult = 0.9; //Weight such that next calculated T_4 (if using bisection method) results in positive diff_q5 + } + + } + + if (LOWFLAG && UPFLAG) { //If results of bracket are defined, use false position + T_4 = (y_upper) / (y_upper - y_lower) * (T_lower - T_upper) + T_upper; + } + else { //If not, keep bisection + T_4 = (1. - upmult) * T_lower + upmult * T_upper; + } + + } + + //********************************************************************************************* + //********** END Bisection/False Position Iteration Loop on T_4 ******************************* + //********************************************************************************************* + } + + } + else { //Glazing is not intact + + //Know convection and radiation forcing temps + //----Having guessed the system temperatures, calculate the thermal losses starting from + //----the absorber surface (3) + //The convective heat exchange between the absorber and the envelope + FQ_34CONV_v2(T_3, T_4, P_6, v_6, T_6, hv, q_34conv, h_34conv); + //The radiative heat exchange between the absorber and the envelope + FQ_34RAD_v2(T_3, T_4, T_7, eps_3, hv, q_34rad, h_34rad); + //The total heat exchange between absorber and envelope + q_34tot = q_34conv + q_34rad; //[W/m] + + } //Know heat transfer from outer surface of receiver tube to ambient + + //Bracket Losses + //Bracket conduction losses apply + q_cond_bracket = FQ_COND_BRACKET_v2(T_3, T_6, P_6, v_6); //[W/m] + + q_12conv = q_3SolAbs - (q_34tot + q_cond_bracket); //[W/m] Energy transfer to/from fluid based on energy balance at T_3 + + q_in_W = q_12conv * m_L_mod; //Convert [W/m] to [W] for some calculations + + if (!single_point) { + T_1_out = max(T_sky, q_in_W / (m_dot * cp_1) + T_1_in); //Estimate outlet temperature with previous cp + + diff_T1 = T1_tol + 1.0; //Set diff > tolerance + T1_iter = 0; //Set iteration counter + + while ((std::abs(diff_T1) > T1_tol) && (T1_iter < 100)) { //Find correct cp& rho and solve for T_1_ave + + T1_iter++; //Increase iteration counter + T_1_ave = (T_1_out + T_1_in) / 2.0; //Average fluid temperature + cp_1 = m_htfProps.Cp(T_1_ave) * 1000.; + T_1_out1 = max(T_sky, q_in_W / (m_dot * cp_1) + T_1_in); //Estimate outlet temperature with previous cp + diff_T1 = (T_1_out - T_1_out1) / T_1_out; //Difference between T_1_out used to calc T_ave, and T_1_out calculated with new cp + T_1_out = T_1_out1; //Calculate new T_1_out + + } + } + else { + //If we're only calculating performance for a single point, set the receiver ave/outlet temperature to the inlet. + T_1_out = T_1_in; + T_1_ave = T_1_in; + } + + rho_1ave = m_htfProps.dens(T_1_ave, 0.0); //[kg/m^3] Density + v_1 = m_dot / (rho_1ave * m_A_cs.at(hv)); //HTF bulk velocity + + q_conv_iter = 0; //Set iteration counter + diff_T2 = 1.0 + T2_tol; //Set diff > tolerance + + bool T2upflag = false; + bool T2lowflag = false; + + double y_T2_low = std::numeric_limits::quiet_NaN(); + double y_T2_up = std::numeric_limits::quiet_NaN(); + + double T2_low = min(T_1_ave, T_3); + double T2_up = max(T_1_ave, T_3); + + //Ensure convective calculations are correct (converge on T_2) + while ((std::abs(diff_T2) > T2_tol) && (q_conv_iter < 100)) { + + q_conv_iter++; //Increase iteration counter + + T_2 = fT_2_v2(q_12conv, T_1_ave, T_2g, v_1, hv); //Calculate T_2 (with previous T_2 as input) + diff_T2 = (T_2 - T_2g) / T_2; //T_2 difference + + if (diff_T2 > 0.0) // Calculated > Guessed, set lower limit and increase guessed + { + T2_low = T_2g; + T2lowflag = true; + y_T2_low = diff_T2; + if (T2upflag) + T_2g = y_T2_up / (y_T2_up - y_T2_low) * (T2_low - T2_up) + T2_up; + else + T_2g = T2_up; + } + else // Calculated < Guessed, set upper limit and decrease guessed + { + T2_up = T_2g; + T2upflag = true; + y_T2_up = diff_T2; + if (T2lowflag) + T_2g = y_T2_up / (y_T2_up - y_T2_low) * (T2_low - T2_up) + T2_up; + else + T_2g = T2_low; + } + + if ((T2_up - T2_low) / T2_low < T2_tol / 10.0) + break; + + //T_2g = T_2 - 0.5*(T_2-T_2g); //Reset T_2 + + // if(qq<2){ //For first T3 iteration, do not iterate on T_2 (again, this control is based on observation of solve time and may not be optimal for all simulations) + // break; + // } + + } + + //The conductive heat transfer equals the convective heat transfer (energy balance) + q_23cond = q_12conv; //[W/m] + + //Calculate tube conductivity + k_23 = FK_23_v2(T_2, T_3, hv); //[W/m-K] + + //Update the absorber surface temperature (T_3) according to new heat transfer rate + abs_diffT3 = T_3 - (T_2 + q_23cond * log(m_D_abs_out[hv] / m_D_abs_in[hv]) / (2. * pi * k_23)); + Diff_T3 = abs_diffT3 / T_3; + + + } + + //Warning of convergence failure + //if(qq>99) { //End simulation if loop does not converge + // call messages(-1,"Trough Energy Balance Convergence Error 1",'WARNING',INFO(1),INFO(2)) + // return + //} + // + //if(T1_iter>99) { + // call messages(-1,"Trough Energy Balance Convergence Error 2",'WARNING',INFO(1),INFO(2)) + // return + //} + // + //if(q_conv_iter>99) { + // call messages(-1,"Trough Energy Balance Convergence Error 3",'WARNING',INFO(1),INFO(2)) + // return + //} + // + //if(q5_iter>99) { + // call messages(-1,"Trough Energy Balance Convergence Error 4",'WARNING',INFO(1),INFO(2)) + // return + //} + + //Calculate specific heat in kJ/kg + c_1ave = cp_1 / 1000.; + + q_heatloss = q_34tot + q_cond_bracket + q_5solabs; //[W/m] + + //Save temperatures + T_save[1] = T_2; + T_save[2] = T_3; + T_save[3] = T_4; + T_save[4] = T_5; + +}; + +// ################################################################################################################# +// ################################################################################################################# +// ################################################################################################################# +// +// +// "****************************************************************************************************************************** +// FUNCTION Fq_12conv : Convective heat transfer rate from the HTF to the inside of the receiver tube +// ******************************************************************************************************************************" +// Author: R.E. Forristall (2003, EES) +// Implemented and revised: M.J. Wagner (10/2009) +// Copyright: National Renewable Energy Lab (Golden, CO) 2009 +// note: This function was programmed and tested against the EES original. +// Small variations in output are due to slightly different fluid +// properties used in the two models. +// +// Newton's Law of Cooling. +// +// q' = h * D_i * PI * (T_m - T_s) +// +// h = Nu_Di * k / D_i +// +// Where +// +// q' = convection heat transfer rate per unit length [W/m] +// h = convection heat transfer coefficient [W/m^2-k] +// D_i = inside diameter of absorber pipe [m] +// T_m = mean (bulk) temperature of HTF [C] +// T_s = inside surface temperature of absorber pipe [C] +// Nu_Di = Nusselt number based on inside diameter +// k = conduction heat transfer coefficient of HTF [W/m-K] +// +// The Nusselt number is estimated with the correlation developed by Gnielinski. +// +// Nu# = (f / 8) * (Re_Di - 1000) * Pr / (1 + 12.7 * (f / 8)^(1/2) * (Pr^(2/3) -1)) * (Pr / Pr_w)^0.11 +// f = (1.82 * log10(Re_Di) - 1.64)^(-2) +// Re_Di = Rho * v_m * Di / u +// Pr = Cp * u / k +// +// Where +// +// Nu# = Nusselt number +// Re_Di = Reynolds number for internal pipe flow +// Pr = Prandtl number +// Pr_w = Prandtl number evaluated at the wall temperature +// u = fluid absolute viscosity [kg/m-s] +// Di = inside diameter [m] +// Cp = fluid specific heat [J/kg-K] +// k = fluid thermal conductivity [W/m-K] +// Rho = fluid density [kg/m^3] +// v_m = mean fluid velocity [m/s] +// +// The above correlation is valid for 0.5 < Pr < 2000 and 2300< Re_Di < 5 * 10^6 and can be used for both uniform heat flux and uniform wall temperature cases. With the exception of Pr_w, all properties are evaluated at the mean fluid temperature. +// +// If Re_D <= 2300 and the choice was made from the diagram window to use the laminar flow model, one of the following correlations is used. +// +// for inner tube flow (uniform flux condition) +// Nu# = 4.36 +// +// for inner annulus flow (uniform flux condition -- estimated from table for Nu# with heat fluxes at both surfaces) +// m_D_plug/m_D_abs_in Nu# +// 0 4.364 +// 0.05 4.792 +// 0.10 4.834 +// 0.20 4.833 +// 0.40 4.979 +// 0.60 5.099 +// 0.80 5.24 +// 1.00 5.385 +// +// +// For the "SNL test platform" case the inside diameter in the above correlations is replaced with the following hydraulic diameter definition. +// +// m_D_h = 4 * A_c / P = D_ao - D_ai +// +// Where +// +// m_D_h = hydraulic diameter [m] +// A_c = flow cross sectional area [m^2] +// P = wetted perimeter [m] +// D_ai = inner annulus diameter [m] +// D_ao = outer annulus diameter [m] +// +// (Sources: Incropera, F., DeWitt, D., Fundamentals of Heat and Mass Transfer, Third Edition; John Wiley and Sons, New York, 1981, pp. 489-491, 502-503. Gnielinski, V., "New Equations for Heat and Mass Transfer in Turbulent Pipe and Channel Flow," International Chemical Engineering, Vol. 16, No. 2, April 1976.) +// */ +double EvacReceiverModel::fT_2_v2(double q_12conv, double T_1, double T_2g, double v_1, int hv) +{ + // convection 1->2, HTF temp, guess T2, fluid velocity, HCE #, HCE variant + + // missing variables + //HTFProperties m_htfProps; + //vector m_D_h; + //vector m_Flow_type; + //vector m_D_plug; + //vector m_D_abs_in; + //double pi = 3.14159; + + double Cp_1, Cp_2, f, h_1, k_1, k_2, mu_1, mu_2, Nu_D2, Pr_1, Pr_2, Re_D2, rho_1, DRatio; + bool includelaminar = true; //cc -- this is always set to TRUE in TRNSYS + + T_2g = max(T_2g, m_T_htf_prop_min); + + // Thermophysical properties for HTF + mu_1 = m_htfProps.visc(T_1); //[kg/m-s] + mu_2 = m_htfProps.visc(T_2g); //[kg/m-s] + Cp_1 = m_htfProps.Cp(T_1) * 1000.; //[J/kg-K] + Cp_2 = m_htfProps.Cp(T_2g) * 1000.; //[J/kg-K] + k_1 = max(m_htfProps.cond(T_1), 1.e-4); //[W/m-K] + k_2 = max(m_htfProps.cond(T_2g), 1.e-4); //[W/m-K] + rho_1 = m_htfProps.dens(T_1, 0.0); //[kg/m^3] + + Pr_2 = (Cp_2 * mu_2) / k_2; + Pr_1 = (Cp_1 * mu_1) / k_1; + + if (v_1 > 0.1) { + + Re_D2 = (rho_1 * m_D_h.at(hv) * v_1) / (mu_1); + + // Nusselt Number for laminar flow case if option to include laminar flow model is chosen + if ((includelaminar == true) && (Re_D2 <= 2300.)) { + if (m_Flow_type[hv] == 2.0) { + DRatio = m_D_plug[hv] / m_D_abs_in[hv]; + //Estimate for uniform heat flux case (poly. regression based on lookup table in Forristall EES model) + //---Note that this regression is based on an 8-point table, and is highly non-practical outside of DRatio bounds + //---0 and 1 + if (DRatio > 1.) { + Nu_D2 = 5.385; + } + else if (DRatio < 0.) { + Nu_D2 = 4.364; + } + else { + Nu_D2 = 41.402 * pow(DRatio, 5) - 109.702 * pow(DRatio, 4) + 104.570 * pow(DRatio, 3) - 42.979 * pow(DRatio, 2) + 7.686 * DRatio + 4.411; + } + } + else { + Nu_D2 = 4.36; //uniform heat flux + } + } + else { + // Warning statements if turbulent/transitional flow Nusselt Number correlation is used out of recommended range + // if (Pr_1 <= 0.5) or (2000 <= Pr_1) { CALL WARNING('The result may not be accurate, since 0.5 < Pr_1 < 2000 does not hold. See PROCEDURE Pq_12conv. Pr_1 = XXXA1', Pr_1) + // if (Pr_2 <= 0.5) or (2000 <= Pr_2) { CALL WARNING('The result may not be accurate, since 0.5 < Pr_2 < 2000 does not hold. See PROCEDURE Pq_12conv. Pr_2 = XXXA1', Pr_2) + // If ( Re_D2 <= (2300) ) or (5*10**6 <= Re_D2 ) Then CALL WARNING('The result may not be accurate, since 2300 < Re_D2 < (5 * 10**6) does not hold. See PROCEDURE Pq_12conv. Re_D2 = XXXA1', Re_D2) + + // Turbulent/transitional flow Nusselt Number correlation (modified Gnielinski correlation) + f = pow(1.82 * log10(Re_D2) - 1.64, -2); + Nu_D2 = (f / 8.) * (Re_D2 - 1000.) * Pr_1 / (1. + 12.7 * sqrt(f / 8.) * (pow(Pr_1, 0.6667) - 1.)) * pow(Pr_1 / Pr_2, 0.11); + } + + h_1 = Nu_D2 * k_1 / m_D_h.at(hv); //[W/m**2-K] + return T_1 + q_12conv / (h_1 * m_D_abs_in[hv] * pi); + //q_12conv = h_1 * m_D_abs_in * PI * (T_2 - T_1ave) //[W/m] + } + else { + h_1 = 0.0001; + return T_1; + } + +}; + +// FUNCTION fq_34conv : Convective heat transfer rate between the absorber outer surface and the glazing inner surface +//******************************************************************************************************************************" +// NOTE: Temperatures input in terms of degrees K +// +// Author: R.E. Forristall (2003, EES) +// Implemented and revised: M.J. Wagner (10/2009) +// Copyright: National Renewable Energy Lab (Golden, CO) 2009 +// +//{ Four cases: +// +// 1. Vacuum in annulus: free-molecular heat transfer model for an annulus. +// 2. Low or lost vacuum: natural convection heat transfer model for an annulus. +// 3. No glazing, no wind: natural convection heat transfer model for a horizontal cylinder. +// 4. No glazing, with wind: forced convection heat transfer model for a horizontal cylinder. +// +// +//Case 1: +// +// Free-molecular heat transfer for an annular space between horizontal cylinders. +// +// q' = D_i * PI * h * (T_i - T_o) +// h = k_gas / (D_i / 2 * ln(D_o / D_i) + b * Lambda * (D_i / D_o + 1)) +// b = (2 - a) / a * (9 * Gamma - 5) / (2 * (Gamma + 1)) +// Lambda = 2.331 * 10^(-20) * T_avg / (P * Delta^2) +// +// Where +// +// q' = convection heat transfer rate per unit length [W/m] +// D_i = outer absorber diameter [m] +// D_o = inner glazing diameter [m] +// h = convection heat transfer coefficient for annulus gas [W/m^2-K] +// T_i = outer absorber surface temperature [C] +// T_o = inner glazing surface temperature [C] +// k_gas = thermal conductivity of the annulus fluid at standard temperature and pressure [W/m^2-K] +// b = interaction coefficient [dimensionless] +// Lambda = mean-free-path between collisions of a molecule [cm] +// a = accommodation coefficient [dimensionless] +// Gamma = ratio of specific heats for the annulus fluid [dimensionless] +// T_avg = average temperature of the annulus fluid [K] +// P = pressure of the annulus gas [mm of Hg] +// Delta = molecular diameter of the annulus gas [cm] +// +// The above correlation is valid for Ra_Do < (D_o / (D_o -D_i))^4, but may over estimate q' slightly for large vacuums. +// +//(Source: Ratzel, A., Hickox, C., Gartling, D., "Techniques for Reducing Thermal Conduction and Natural Convection Heat Losses +// in Annular Receiver Geometries," Journal of Heat Transfer, Vol. 101, No. 1, February 1979; pp. 108-113) +// +// +//Case 2: +// +// Modified Raithby and Hollands correlation for natural convection in an annular space between horizontal cylinders. +// +// q' = 2.425 * k * (T_i - T_o) / (1 + (D_i / D_o)^(3/5))^(5/4) * (Pr * Ra_Di / (0.861 + Pr))^(1/4) +// Pr = NU / Alpha +// Ra_Di = g * Beta * (T_i - T_o) * (D_i)^3 / (Alpha * NU) +// Beta = 1 / T_avg "Ideal Gas" +// +// Where +// +// k = conduction heat transfer coefficient for the annulus gas [W/m-K] +// Pr = Prandtl number +// NU = kinematic viscosity [m^2/s] +// Alpha = thermal diffusivity [m^2/s] +// Ra_Di = Rayleigh number based on the annulus inner diameter +// g = local acceleration due to gravity [m/s^2] +// Beta = volumetric thermal expansion coefficient [1/K] +// Rho_o = annulus gas density at the outer surface [kg/m^3] +// Rho_i = annulus gas density at the inner surface [kg/m^3] +// T_avg = average temperature, (T_i + T_o) / 2 [K] +// +// Above correlation is valid for Ra_Do > (D_o / (D_o -D_i))^4. All physical properties are evaluated at the average temperature, (T_i + T_o)/2. +// +//(Source: Bejan, A., Convection Heat Transfer, Second Edition; John Wiley & Son's, New York, 1995, pp. 257-259.) +// +// +//Case 3: +// +// Churchill and Chu correlation for natural convection from a long isothermal horizontal cylinder. +// +// Nu_bar = (0.60 + (0.387 * Ra_D^(1/6)) / (1 + (0.559 / Pr)^(9/16))^(8/27) )^2 +// Ra_D = g * Beta * (T_s - T_inf) * D^3 / (Alpha * NU) +// Beta = 1 / T_f "Ideal Gas" +// Alpha = k / (Cp * Rho) +// Pr = NU / Alpha +// +// h = Nu_bar * k / D +// +// q' = h * PI * D * (T_s - T_inf) +// +// Where +// +// Nu_bar = average Nusselt number +// Ra_D = Rayleigh number based on diameter +// Rho = fluid density [kg/m^3] +// Cp = specific heat at constant pressure [kJ / kg-K] +// T_inf = fluid temperature in the free stream [C] +// T_s = surface temperature [C] +// T_f = film temperature, (T_s + T_inf) / 2 [K] +// T_inf = ambient air temperature [C] +// +// Above correlation is valid for 10^(-5) < Ra_D < 10^12. All physical properties are evaluated at the film temperature, (T_s + T_inf) / 2. +// +//(Source: Incropera, F., DeWitt, D., Fundamentals of Heat and Mass Transfer, Third Edition; John Wiley and Sons, New York, 1981, pp. 550-552.) +// +// +//Case 4: +// +// Zhukauskas's correlation for external forced convection flow normal to an isothermal cylinder. +// +// Nu_bar = C * Re_D^m * Pr^n * (Pr / Pr_s)^(1/4) +// +// Re_D C m +// 1-40 0.75 0.4 +// 40-1000 0.51 0.5 +// 1e3- 2e5 0.26 0.6 +// 2e5-1e6 0.076 0.7 +// +// n = 0.37, Pr <=10 +// n = 0.36, Pr >10 +// +// Re_D = U_inf * D / NU +// Pr = NU / Alpha +// Alpha = k / (Cp * Rho) +// +// Q = h * D * PI * (T_s - T_inf) * L +// +// Where, +// +// Re_D = Reynolds number evaluated at the diameter +// Cp = specific heat at constant pressure of air [W/m-K] +// Rho = density of air [kg/m^3] +// C, m, n = constants +// +// Above correlation is valid for 0.7 < Pr < 500, and 1 < Re_D < 10^6. All physical properties evaluated +// at the free stream temperature, T_inf, except Pr_s. +// +//(Source: Incropera, F., DeWitt, D., Fundamentals of Heat and Mass Transfer, Third Edition; John Wiley and +// Sons, New York, 1981, p. 413.) +//}*/ +void EvacReceiverModel::FQ_34CONV_v2(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34) +{ + // Missing Variables + //vector m_P_a; + //vector m_GlazingIntact; + //HTFProperties m_airProps; + //vector m_D_abs_out; + //double pi = 3.14159; + //util::matrix_t m_AnnulusGasMat; + //vector m_D_glass_in; + + + + double a, Alpha_34, b, Beta_34, C, C1, Cp_34, Cv_34, Delta, Gamma, k_34, Lambda, + m, mu_34, n, nu_34, P, Pr_34, P_A1, Ra_D3, Ra_D4, rho_34, T_34, T_36, + grav, Nu_bar, rho_3, rho_6, mu_36, rho_36, cp_36, + k_36, nu_36, alpha_36, beta_36, Pr_36, h_36, mu_3, mu_6, k_3, k_6, cp_3, Cp_6, nu_6, nu_3, + Alpha_3, alpha_6, Re_D3, Pr_3, Pr_6, Natq_34conv, Kineticq_34conv; + + grav = 9.81; //m/s2 gravitation constant + + P_A1 = m_P_a[hv] * 133.322368; //convert("torr", "Pa") //[Pa] + + T_34 = (T_3 + T_4) / 2.; //[C] + T_36 = (T_3 + T_6) / 2.; //[C] + + if (!m_GlazingIntact.at(hv)) { + + // Thermophysical Properties for air + rho_3 = m_airProps.dens(T_3, P_6); //[kg/m**3], air is fluid 1. + rho_6 = m_airProps.dens(T_6, P_6); //[kg/m**3], air is fluid 1. + + if (v_6 <= 0.1) { + mu_36 = m_airProps.visc(T_36); //[N-s/m**2], AIR + rho_36 = m_airProps.dens(T_36, P_6); //[kg/m**3], AIR + cp_36 = m_airProps.Cp(T_36) * 1000.; //[J/kg-K], AIR + k_36 = m_airProps.cond(T_36); //[W/m-K], AIR + nu_36 = mu_36 / rho_36; //[m**2/s] kinematic viscosity, AIR + alpha_36 = k_36 / (cp_36 * rho_36); //[m**2/s], thermal diffusivity, AIR + beta_36 = 1.0 / T_36; //[1/K] + Ra_D3 = grav * beta_36 * std::abs(T_3 - T_6) * pow(m_D_abs_out[hv], 3) / (alpha_36 * nu_36); + + // Warning Statement if following Nusselt Number correlation is used out of recommended range // + //If ((Ra_D3 <= 1.e-5) || (Ra_D3 >= 1.e12)) continue + //CALL WARNING('The result may not be accurate, since 10**(-5) < Ra_D3 < 10**12 does not hold. See Function fq_34conv. Ra_D3 = XXXA1', Ra_D3) + + // Churchill and Chu correlation for natural convection from a long isothermal horizontal cylinder // + Pr_36 = nu_36 / alpha_36; + Nu_bar = pow(0.60 + (0.387 * pow(Ra_D3, 0.1667)) / pow(1. + pow(0.559 / Pr_36, 0.5625), 0.2963), 2); + h_36 = Nu_bar * k_36 / m_D_abs_out[hv]; //[W/m**2-K]// + q_34conv = h_36 * pi * m_D_abs_out[hv] * (T_3 - T_6); //[W/m]// + h_34 = h_36; //Set output coefficient + } + else { + + // Thermophysical Properties for air + mu_3 = m_airProps.visc(T_3); //[N-s/m**2] + mu_6 = m_airProps.visc(T_6); //[N-s/m**2] + k_3 = m_airProps.cond(T_3); //[W/m-K] + k_6 = m_airProps.cond(T_6); //[W/m-K] + cp_3 = m_airProps.Cp(T_3) * 1000.; //[J/kg-K] + Cp_6 = m_airProps.Cp(T_6) * 1000.; //[J/kg-K] + nu_6 = mu_6 / rho_6; //[m**2/s] + nu_3 = mu_3 / rho_3; //[m**2/s] + Alpha_3 = k_3 / (cp_3 * rho_3); //[m**2/s] + alpha_6 = k_6 / (Cp_6 * rho_6); //[m**2/s] + Re_D3 = v_6 * m_D_abs_out[hv] / nu_6; + Pr_3 = nu_3 / Alpha_3; + Pr_6 = nu_6 / alpha_6; + + // Warning Statements if following Nusselt Number correlation is used out of range // + //if (Re_D3 <= 1) or (Re_D3 >= 10**6) { CALL WARNING('The result may not be accurate, since 1 < Re_D3 < 10**6 does not hold. See Function fq_34conv. Re_D3 = XXXA1', Re_D3) + //If (Pr_6 <= 0.7) or (Pr_6 >= 500) Then CALL WARNING('The result may not be accurate, since 0.7 < Pr_6 < 500 does not hold. See Function fq_34conv. Pr_6 = XXXA1', Pr_6) + + // Coefficients for external forced convection Nusselt Number correlation (Zhukauskas's correlation) // + if (Pr_6 <= 10) { + n = 0.37; + } + else { + n = 0.36; + } + + if (Re_D3 < 40) { + C = 0.75; + m = 0.4; + } + else { + + if ((40 <= Re_D3) && (Re_D3 < 1000.)) { + C = 0.51; + m = 0.5; + } + else { + if ((1.e3 <= Re_D3) && (Re_D3 < 2.e5)) { + C = 0.26; + m = 0.6; + } + else { + if ((2.e5 <= Re_D3) && (Re_D3 < 1.e6)) { + C = 0.076; + m = 0.7; + } + } + } + } + + // Zhukauskas's correlation for external forced convection flow normal to an isothermal cylinder + Nu_bar = C * pow(Re_D3, m) * pow(Pr_6, n) * pow(Pr_6 / Pr_3, 0.25); + h_36 = Nu_bar * k_6 / m_D_abs_out[hv]; //[W/m**2-K] + q_34conv = h_36 * m_D_abs_out[hv] * pi * (T_3 - T_6); //[W/m] + h_34 = h_36; //set output coefficient + } + } + else { + + // Thermophysical Properties for gas in annulus space + mu_34 = m_AnnulusGasMat.at(hv)->visc(T_34); //[kg/m-s] + Cp_34 = m_AnnulusGasMat.at(hv)->Cp(T_34) * 1000.; //[J/kg-K] + Cv_34 = m_AnnulusGasMat.at(hv)->Cv(T_34) * 1000.; //[J/kg-K] + rho_34 = m_AnnulusGasMat.at(hv)->dens(T_34, P_A1); //[kg/m**3] + k_34 = m_AnnulusGasMat.at(hv)->cond(T_34); //[W/m-K] + + // Modified Raithby and Hollands correlation for natural convection in an annular space between horizontal cylinders + Alpha_34 = k_34 / (Cp_34 * rho_34); //[m**2/s]// + nu_34 = mu_34 / rho_34; //[m**2/s]// + Beta_34 = 1. / max(T_34, 1.0); //[1/K]// + Ra_D3 = grav * Beta_34 * std::abs(T_3 - T_4) * pow(m_D_abs_out[hv], 3) / (Alpha_34 * nu_34); + Ra_D4 = grav * Beta_34 * std::abs(T_3 - T_4) * pow(m_D_glass_in[hv], 3) / (Alpha_34 * nu_34); + Pr_34 = nu_34 / Alpha_34; + Natq_34conv = 2.425 * k_34 * (T_3 - T_4) / pow(1 + pow(m_D_abs_out[hv] / m_D_glass_in[hv], 0.6), 1.25) * pow(Pr_34 * Ra_D3 / (0.861 + Pr_34), 0.25); //[W/m]// + P = m_P_a[hv]; //[mmHg] (note that 1 torr = 1 mmHg by definition) + C1 = 2.331e-20; //[mmHg-cm**3/K]// + + // Free-molecular heat transfer for an annular space between horizontal cylinders + if (m_AnnulusGasMat.at(hv)->GetFluid() == HTFProperties::Air) { //AIR + Delta = 3.53e-8; //[cm] + } + + if (m_AnnulusGasMat.at(hv)->GetFluid() == HTFProperties::Hydrogen_ideal) { //H2 + Delta = 2.4e-8; //[cm] + } + + if (m_AnnulusGasMat.at(hv)->GetFluid() == HTFProperties::Argon_ideal) { //Argon + Delta = 3.8e-8; //[cm] + } + + Lambda = C1 * T_34 / (P * Delta * Delta); //[cm] + Gamma = Cp_34 / Cv_34; + a = 1.; + b = (2. - a) / a * (9. * Gamma - 5.) / (2. * (Gamma + 1.)); + h_34 = k_34 / (m_D_abs_out[hv] / 2. * log(m_D_glass_in[hv] / m_D_abs_out[hv]) + b * Lambda / 100. * (m_D_abs_out[hv] / m_D_glass_in[hv] + 1.)); //[W/m**2-K] + Kineticq_34conv = m_D_abs_out[hv] * pi * h_34 * (T_3 - T_4); //[W/m] + + // Following compares free-molecular heat transfer with natural convection heat transfer and uses the largest value for heat transfer in annulus + if (Kineticq_34conv > Natq_34conv) { + q_34conv = Kineticq_34conv; //[W/m] + } + else { + q_34conv = Natq_34conv; //[W/m] + h_34 = q_34conv / (m_D_abs_out[hv] * pi * (T_3 - T_4)); //Recalculate the convection coefficient for natural convection + } + } + +} + +// FUNCTION fq_34rad : Radiation heat transfer rate between the absorber surface and glazing inner surface +//******************************************************************************************************************************" +// NOTE: Temperatures input in terms of degrees K +// +// Author: R.E. Forristall (2003, EES) +// Implemented and revised: M.J. Wagner (10/2009) +// Copyright: National Renewable Energy Lab (Golden, CO) 2009 +// note : Tested against original EES version +// +//{ Radiation heat transfer for a two-surface enclosure. +// +// Two cases, one if the glazing envelope is intact and one if the glazing is missing or damaged. +// +// Case 1: Long (infinite) concentric cylinders. +// +// q' = sigma * PI * D_1 * (T_1^4 - T_2^4) / (1 / EPSILON_1 + (1 - EPSILON_2) / EPSILON_2 * (D_1 / m_D_abs_in)) +// +// Where, +// +// q' = radiation heat transfer per unit length [W/m] +// sigma = Stephan-Boltzmann constant [W/m^2-K^4] +// T_1 = absorber outer surface temperature [K] +// T_2 = glazing inner surface temperature [K] +// D_1 = outer absorber diameter [m] +// m_D_abs_in = inner glazing diameter [m] +// EPSILON_1 = emissivity of inner surface +// EPSILON_2 = emissivity of outer surface +// +// Case 2: Small convex object in a large cavity. +// +// q' = sigma * PI * D_1 * EPSILON_1 * (T_1^4 - T_2^4) +//}*/ +void EvacReceiverModel::FQ_34RAD_v2(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34) +{ + // Missing Variables + //vector m_GlazingIntact; + //double pi = 3.14159; + //vector m_D_abs_out; + //vector m_D_glass_in; + //vector m_epsilon_glass; + + double sigma = 5.67e-8, T_ave; + T_ave = (T_3 + T_4) / 2.; + if (!m_GlazingIntact.at(hv)) { + q_34rad = epsilon_abs_v * pi * m_D_abs_out[hv] * sigma * (pow(T_3, 4) - pow(T_7, 4)); //[W/m] + h_34 = q_34rad / (pi * m_D_abs_out[hv] * (T_3 - T_7)); + } + else { + h_34 = sigma * (T_3 * T_3 + T_4 * T_4) * (T_3 + T_4) / (1.0 / epsilon_abs_v + m_D_abs_out[hv] / m_D_glass_in[hv] * (1.0 / m_epsilon_glass[hv] - 1.0)); + q_34rad = pi * m_D_abs_out[hv] * h_34 * (T_3 - T_4); + } + +} + +// FUNCTION fq_56conv : Convective heat transfer rate between the glazing outer surface and the ambient air +//******************************************************************************************************************************" +// Author: R.E. Forristall (2003, EES) +// Implemented and revised: M.J. Wagner (10/2009) +// Copyright: National Renewable Energy Lab (Golden, CO) 2009 +// note : Tested against original EES version +// +//{ h6 Heat Transfer Coefficient +// +// If no wind, then the Churchill and Chu correlation is used. If wind, then the Zhukauskas's correlation is used. These correlations are described above for q_34conv. +//}*/ +void EvacReceiverModel::FQ_56CONV_v2(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6) +{ + // Missing Variables + //HTFProperties m_airProps; + //vector m_GlazingIntact; + //vector m_D_glass_out; + //double g = 9.8; + //double pi = 3.14159; + + double alpha_5, alpha_6, C, Cp_5, Cp_56, Cp_6, k_5, k_56, k_6, m, mu_5, mu_56, mu_6, n, Nus_6, + nu_5, nu_6, Pr_5, Pr_6, Re_D5, rho_5, rho_56, rho_6, T_56, Nu_bar, + nu_56, alpha_56, beta_56, Ra_D5, Pr_56; + + T_56 = (T_5 + T_6) / 2.0; //[K] + + // Thermophysical Properties for air + mu_5 = m_airProps.visc(T_5); //[kg/m-s] + mu_6 = m_airProps.visc(T_6); //[kg/m-s] + mu_56 = m_airProps.visc(T_56); //[kg/m-s] + k_5 = m_airProps.cond(T_5); //[W/m-K] + k_6 = m_airProps.cond(T_6); //[W/m-K] + k_56 = m_airProps.cond(T_56); //[W/m-K] + Cp_5 = m_airProps.Cp(T_5) * 1000.; //[J/kg-K] + Cp_6 = m_airProps.Cp(T_6) * 1000.; //[J/kg-K] + Cp_56 = m_airProps.Cp(T_56) * 1000.; //[J/kg-K] + rho_5 = m_airProps.dens(T_5, P_6); //[kg/m^3] + rho_6 = m_airProps.dens(T_6, P_6); //[kg/m^3] + rho_56 = m_airProps.dens(T_56, P_6); //[kg/m^3] + + // if the glass envelope is missing then the convection heat transfer from the glass + //envelope is forced to zero by T_5 = T_6 + if (!m_GlazingIntact.at(hv)) { + q_56conv = (T_5 - T_6); //[W/m] + } + else { + if (v_6 <= 0.1) { + + // Coefficients for Churchill and Chu natural convection correlation // + nu_56 = mu_56 / rho_56; //[m^2/s] + alpha_56 = k_56 / (Cp_56 * rho_56); //[m^2/s] + beta_56 = 1.0 / T_56; //[1/K] + Ra_D5 = g * beta_56 * std::abs(T_5 - T_6) * pow(m_D_glass_out[hv], 3) / (alpha_56 * nu_56); + + // Warning Statement if following Nusselt Number correlation is used out of range // + //If (Ra_D5 <= 10**(-5)) or (Ra_D5 >= 10**12) Then CALL WARNING('The result may not be accurate, + //since 10**(-5) < Ra_D5 < 10**12 does not hold. See Function fq_56conv. Ra_D5 = XXXA1', Ra_D5) + + // Churchill and Chu correlation for natural convection for a horizontal cylinder // + Pr_56 = nu_56 / alpha_56; + Nu_bar = pow(0.60 + (0.387 * pow(Ra_D5, 0.1667)) / pow(1.0 + pow(0.559 / Pr_56, 0.5625), 0.2963), 2); + h_6 = Nu_bar * k_56 / m_D_glass_out[hv]; //[W/m**2-K] + q_56conv = h_6 * pi * m_D_glass_out[hv] * (T_5 - T_6); //[W/m] + } + else { + + // Coefficients for Zhukauskas's correlation // + alpha_5 = k_5 / (Cp_5 * rho_5); //[m**2/s] + alpha_6 = k_6 / (Cp_6 * rho_6); //[m**2/s] + nu_5 = mu_5 / rho_5; //[m**2/s] + nu_6 = mu_6 / rho_6; //[m**2/s] + Pr_5 = nu_5 / alpha_5; + Pr_6 = nu_6 / alpha_6; + Re_D5 = v_6 * m_D_glass_out[hv] * rho_6 / mu_6; + + // Warning Statement if following Nusselt Number correlation is used out of range // +// if (Pr_6 <= 0.7) or (Pr_6 >= 500) { CALL WARNING('The result may not be accurate, since 0.7 < Pr_6 < 500 does not hold. See Function fq_56conv. Pr_6 = XXXA1', Pr_6) +// If (Re_D5 <= 1) or (Re_D5 >= 10**6) Then CALL WARNING('The result may not be accurate, since 1 < Re_D5 < 10**6 does not hold. See Function fq_56conv. Re_D5 = XXXA1 ', Re_D5) + + // Zhukauskas's correlation for forced convection over a long horizontal cylinder // + if (Pr_6 <= 10) { + n = 0.37; + } + else { + n = 0.36; + } + + // TB 2023 initialize C and m to be nan + m = std::numeric_limits::quiet_NaN(); + C = std::numeric_limits::quiet_NaN(); + + if (Re_D5 < 40.0) { + C = 0.75; + m = 0.4; + } + else { + if ((40.0 <= Re_D5) && (Re_D5 < 1.e3)) { + C = 0.51; + m = 0.5; + } + else { + if ((1.e3 <= Re_D5) && (Re_D5 < 2.e5)) { + C = 0.26; + m = 0.6; + } + else { + if ((2.e5 <= Re_D5) && (Re_D5 < 1.e6)) { + C = 0.076; + m = 0.7; + } + } + } + } + + Nus_6 = C * pow(Re_D5, m) * pow(Pr_6, n) * pow(Pr_6 / Pr_5, 0.25); + h_6 = Nus_6 * k_6 / m_D_glass_out[hv]; //[W/m**2-K] + q_56conv = h_6 * pi * m_D_glass_out[hv] * (T_5 - T_6); //[W/m] + } + } +} + +// FUNCTION fq_cond_bracket: Heat loss estimate through HCE support bracket +// ******************************************************************************************************************************" +// Author: R.E. Forristall (2003, EES) +// Implemented and revised: M.J. Wagner (10/2009) +// Copyright: National Renewable Energy Lab (Golden, CO) 2009 +// note : Tested against original EES version +//*/ +double EvacReceiverModel::FQ_COND_BRACKET_v2(double T_3, double T_6, double P_6, double v_6) +{ + // Missing Variables + //HTFProperties m_airProps; + //double g = 9.8; + + double P_brac, D_brac, A_CS_brac, k_brac, T_base, T_brac, T_brac6, mu_brac6, rho_brac6, + Cp_brac6, k_brac6, nu_brac6, Alpha_brac6, Beta_brac6, Ra_Dbrac, Pr_brac6, Nu_bar, h_brac6, + mu_brac, mu_6, rho_6, rho_brac, k_6, Cp_brac, nu_6, Cp_6, Nu_brac, Alpha_brac, + Re_Dbrac, Pr_brac, Pr_6, n, C, m, L_HCE, alpha_6; + + + // effective bracket perimeter for convection heat transfer + P_brac = 0.2032; //[m] + + // effective bracket diameter (2 x 1in) + D_brac = 0.0508; //[m] + + // minimum bracket cross-sectional area for conduction heat transfer + A_CS_brac = 0.00016129; //[m**2] + + // conduction coefficient for carbon steel at 600 K + k_brac = 48.0; //[W/m-K] + + // effective bracket base temperature + T_base = T_3 - 10.0; //[C] + + // estimate average bracket temperature + T_brac = (T_base + T_6) / 2.0; //[C] //NOTE: MJW modified from /3 to /2.. believed to be an error + + // estimate film temperature for support bracket + T_brac6 = (T_brac + T_6) / 2.0; //[C] + + // convection coefficient with and without wind + if (v_6 <= 0.1) { + + mu_brac6 = m_airProps.visc(T_brac6); //[N-s/m**2] + rho_brac6 = m_airProps.dens(T_brac6, P_6); //[kg/m**3] + Cp_brac6 = m_airProps.Cp(T_brac6) * 1000.; //[J/kg-K] + k_brac6 = m_airProps.cond(T_brac6); //[W/m-K] + nu_brac6 = mu_brac6 / rho_brac6; //[m**2/s] + Alpha_brac6 = k_brac6 / (Cp_brac6 * rho_brac6); //[m**2/s] + Beta_brac6 = 1.0 / T_brac6; //[1/K] + Ra_Dbrac = g * Beta_brac6 * std::abs(T_brac - T_6) * D_brac * D_brac * D_brac / (Alpha_brac6 * nu_brac6); + + // Warning Statement if following Nusselt Number correlation is used out of recommended range + //If ((Ra_Dbrac <= 1.e-5)) || (Ra_Dbrac >= 1.e12) Then CALL WARNING('The result may not be accurate, + //since 10**(-5) < Ra_Dbrac < 10**12 does not hold. See Function fq_cond_bracket. Ra_Dbrac = XXXA1', Ra_Dbrac) + + // Churchill and Chu correlation for natural convection from a long isothermal horizontal cylinder + Pr_brac6 = nu_brac6 / Alpha_brac6; + Nu_bar = pow(0.60 + (0.387 * pow(Ra_Dbrac, 0.1667)) / pow(1.0 + pow(0.559 / Pr_brac6, 0.5625), 0.2963), 2); + h_brac6 = Nu_bar * k_brac6 / D_brac; //[W/m**2-K] + } + else { + + // Thermophysical Properties for air + mu_brac = m_airProps.visc(T_brac); //[N-s/m**2] + mu_6 = m_airProps.visc(T_6); //[N-s/m**2] + rho_6 = m_airProps.dens(T_6, P_6); //[kg/m**3] + rho_brac = m_airProps.dens(T_brac, P_6); //[kg/m**3] + k_brac = m_airProps.cond(T_brac); //[W/m-K] + k_6 = m_airProps.cond(T_6); //[W/m-K] + k_brac6 = m_airProps.cond(T_brac6); //[W/m-K] + Cp_brac = m_airProps.Cp(T_brac) * 1000.; //[J/kg-K] + Cp_6 = m_airProps.Cp(T_6) * 1000.; //[J/kg-K] + nu_6 = mu_6 / rho_6; //[m**2/s] + Nu_brac = mu_brac / rho_brac; //[m**2/s] + + Alpha_brac = k_brac / (Cp_brac * rho_brac * 1000.0); //[m**2/s] + alpha_6 = k_6 / (Cp_6 * rho_6 * 1000.0); //[m**2/s] + Re_Dbrac = v_6 * D_brac / nu_6; + Pr_brac = Nu_brac / Alpha_brac; + Pr_6 = nu_6 / alpha_6; + + // Warning Statements if following Nusselt Correlation is used out of range +// if (Re_Dbrac <= 1) or (Re_Dbrac >= 10**6) { CALL WARNING('The result may not be accurate, since 1 < Re_Dbrac < 10**6 does not hold. See Function fq_cond_bracket. Re_Dbrac = XXXA1', Re_Dbrac) +// If (Pr_6 <= 0.7) or (Pr_6 >= 500) Then CALL WARNING('The result may not be accurate, since 0.7 < Pr_6 < 500 does not hold. See Function fq_cond_bracket. Pr_6 = XXXA1', Pr_6) + + // Coefficients for external forced convection Nusselt Number correlation (Zhukauskas's correlation) + if (Pr_6 <= 10.) { + n = 0.37; + } + else { + n = 0.36; + } + + if (Re_Dbrac < 40.) { + C = 0.75; + m = 0.4; + } + else { + + if ((40. <= Re_Dbrac) && (Re_Dbrac < 1.e3)) { + C = 0.51; + m = 0.5; + } + else { + if ((1.e3 <= Re_Dbrac) && (Re_Dbrac < 2.e5)) { + C = 0.26; + m = 0.6; + } + else { + if ((2.e5 <= Re_Dbrac) && (Re_Dbrac < 1.e6)) { + C = 0.076; + m = 0.7; + } + } + } + } + + // Zhukauskas's correlation for external forced convection flow normal to an isothermal cylinder + Nu_bar = C * pow(Re_Dbrac, m) * pow(Pr_6, n) * pow(Pr_6 / Pr_brac, 0.25); + h_brac6 = Nu_bar * k_brac6 / D_brac; //[W/m**2-K] + + } + + // estimated conduction heat loss through HCE support brackets / HCE length + L_HCE = 4.06; //[m] + return sqrt(h_brac6 * P_brac * k_brac * A_CS_brac) * (T_base - T_6) / L_HCE; //[W/m] + +} + +// FUNCTION fk_23: Absorber conductance +//******************************************************************************************************************************" +//{ Based on linear fit of data from "Alloy Digest, Sourcebook, Stainless Steels"; ASM International, 2000.} +//*/ +double EvacReceiverModel::FK_23_v2(double T_2, double T_3, int hv) +{ + double T_23; + + //Absorber materials: + // (1) 304L + // (2) 216L + // (3) 321H + // (4) B42 Copper Pipe + + T_23 = (T_2 + T_3) / 2. - 273.15; //[C] + return m_AbsorberPropMat.at(hv)->cond(T_23); + +} diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h new file mode 100644 index 000000000..3a9e3a6ba --- /dev/null +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -0,0 +1,712 @@ +/* +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_fresnel_collector_receiver_ +#define __csp_solver_fresnel_collector_receiver_ + +#include "csp_solver_core.h" +#include "htf_props.h" +#include "sam_csp_util.h" + +class EvacReceiverModel +{ + // Private Fields +private: + + // Constants + const double m_T_htf_prop_min = 275; + const double pi = acos(-1); + const double g = 9.81; + + // Fields + const vector m_D_abs_in; // [m] The inner absorber tube diameter (m_D_2) + const vector m_D_abs_out; // [m] The outer absorber tube diameter (m_D_3) + + const vector m_D_glass_in; // [m] The inner glass envelope diameter (m_D_4) + const vector m_D_glass_out; // [m] The outer glass envelope diameter (m_D_5) + const vector m_D_plug; // [m] The diameter of the absorber flow plug (optional) (m_D_p) + + const double m_L_mod; // The length of the collector module (L_SCA) + + const vector m_GlazingIntact; // [-] Glazing intact (broken glass) flag {1=true, else=false} + const vector m_Shadowing; // [-] Receiver bellows shadowing loss factor + const vector m_dirt_env; // Loss due to dirt on the receiver envelope (m_Dirt_HCE) + + const vector m_P_a; // [torr] Annulus gas pressure + + const vector m_alpha_abs; // [-] Absorber absorptance + const vector m_epsilon_glass; // Glass envelope emissivity + const vector m_Tau_envelope; // [-] Envelope transmittance + + const vector m_alpha_env; // [-] Envelope absorptance + emit_table* m_epsilon_abs; // [-] Absorber emittance + + HTFProperties m_htfProps, m_airProps; // htf and air material properties + + const util::matrix_t m_AnnulusGasMat; // HTF Property class for each variant of each receiver type + const util::matrix_t m_AbsorberPropMat; // Absorber Property class for each variant of each receiver type + + const vector m_Flow_type; // [-] Flow type through the absorber + + const vector m_A_cs; //[m^2] Cross-sectional area for HTF flow for each receiver + const vector m_D_h; //[m^2] Hydraulic diameters for HTF flow for each receiver and variant (why variant?) + + // Private Methods +private: + + double fT_2_v2(double q_12conv, double T_1, double T_2g, double v_1, int hv); + + void FQ_34CONV_v2(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34); + + void FQ_34RAD_v2(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34); + + void FQ_56CONV_v2(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6); + + double FQ_COND_BRACKET_v2(double T_3, double T_6, double P_6, double v_6); + + double FK_23_v2(double T_2, double T_3, int hv); + +public: + + EvacReceiverModel(vector D_abs_in, vector D_abs_out, vector D_glass_in, vector D_glass_out, vector D_plug, + double L_mod, vector GlazingIntact, vector Shadowing, vector dirt_env, vector P_a, vector alpha_abs, + vector epsilon_glass, vector Tau_envelope, vector alpha_env, emit_table* epsilon_abs, HTFProperties htfProps, HTFProperties airProps, + util::matrix_t AnnulusGasMat, util::matrix_t AbsorberPropMat, vector Flow_type, vector A_cs, vector D_h); + + + void Calculate_Energy_Balance(double T_1_in, double m_dot, double T_amb, double T_sky, double v_6, double P_6, double q_i, + int hv /* HCE variant [0..3] */, int sca_num, bool single_point, double time, util::matrix_t ColOptEff, + //outputs + double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args, double& q_3reflect); + +}; + + +class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver +{ + +public: + + enum + { + E_EQUIV_OPT_ETA_TOT, //[-] + E_DEFOCUS, //[-] + + E_Q_DOT_INC_SF_TOT, //[MWt] + E_Q_DOT_INC_SF_COSTH, //[MWt] + E_Q_DOT_REC_INC, //[MWt] + E_Q_DOT_REC_THERMAL_LOSS, //[MWt] + E_REC_THERMAL_EFF, + E_Q_DOT_REC_ABS, //[MWt] + E_Q_DOT_PIPING_LOSS, //[MWt] + E_E_DOT_INTERNAL_ENERGY, //[MWt] + E_Q_DOT_HTF_OUT, //[MWt] + E_Q_DOT_FREEZE_PROT, //[MWt] + + E_M_DOT_LOOP, //[kg/s] + E_IS_RECIRCULATING, //[-] + E_M_DOT_FIELD_RECIRC, //[kg/s] + E_M_DOT_FIELD_DELIVERED, //[kg/s] + E_T_FIELD_COLD_IN, //[C] + E_T_REC_COLD_IN, //[C] + E_T_REC_HOT_OUT, //[C] + E_T_FIELD_HOT_OUT, //[C] + E_PRESSURE_DROP, //[bar] + + E_W_DOT_SCA_TRACK, //[MWe] + E_W_DOT_PUMP, //[MWe] + + }; + + enum struct E_loop_energy_balance_exit + { + SOLVED, + NaN + }; + + // Private Fields +private: + + // Hardcoded constants + const double m_d2r = CSP::pi / 180.0; + const double m_r2d = 180.0 / CSP::pi; + const double m_mtoinch = 39.3700787; // [m] to [in] + const double m_T_htf_prop_min = 275.0; // K Minimum temperature allowed in props call to minimize errors + const double fp_offset = 10; // freeze protection offset + double m_P_field_in; // Assumed inlet htf pressure for property lookups (defined in init()) + + // Evac Receiver Model Class and Parameters + std::unique_ptr m_evac_receiver; + std::vector mv_HCEguessargs; + + // Geometry calculated in init() + 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 + + // Design Point properties calculated in init() + bool m_is_solar_mult_designed = false; // Flag for whether solar multiple has been calculated + double m_opteff_des; // [-] Design-point optical efficiency (theta = 0) from the solar field + vector m_A_cs; // [m^2] Cross-sectional area for HTF flow for each receiver and variant (why variant?) + vector m_D_h; // [m^2] Hydraulic diameters for HTF flow for each receiver and variant (why variant?) + string m_piping_summary; + double eta_opt_fixed; + + // Cycle properties + emit_table m_epsilon_3; // Table of emissivity vs temperature for each variant of each receiver type + util::matrix_t m_AnnulusGasMat; // HTF Property class for each variant of each receiver type + util::matrix_t m_AbsorberPropMat; // Absorber Property class for each variant of each receiver type + OpticalDataTable optical_table; + emit_table m_epsilon_abs; + + // Init() inputs + double m_latitude; // [deg] convert to [rad] in init() + double m_longitude; // [deg] convert to [rad] in init() + double m_shift; // [deg] convert to [rad] in init() + + // Process Parameters + C_csp_collector_receiver::E_csp_cr_modes m_operating_mode_converged; // Converged Operating Mode + C_csp_collector_receiver::E_csp_cr_modes m_operating_mode; // Current Operating Mode + bool m_is_m_dot_recirc; // [-] True: fresnel is recirculationg HTF with interacting with other CSP components + double m_step_recirc; + std::string m_error_msg; // member string for exception messages + + // Optical Parameters (calculated with loop_optical_eta) + double m_eta_optical; // Collector total optical efficiency + double m_phi_t = 0; // Solar incidence angle in the collector transversal plane + double m_theta_L = 0; // Solar incidence angle in the collector longitudinal plane + double m_ftrack = 0; + double m_q_i; // [W/m] DNI * A_aper / L_sca NOT a vector because all collector lengths are equal + std::vector m_q_SCA; // [W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)*all_defocus) // This value (m_q_SCA) is passed to the Evacuated Tube model, where the other optical losses are applied + std::vector m_q_SCA_control_df; // [W/m] Total incident irradiation less CONTROL defocus (m_q_sca * control_defocus) + util::matrix_t m_ColOptEff; // [-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack + double m_W_dot_sca_tracking; // [MWe] SCA tracking power + double m_q_dot_inc_sf_tot; // [MWt] Total incident radiation on solar field + + // Field Pressure Drop + std::vector m_DP_tube; // [Pa] Pressure drops in each receiver + double m_dP_total; // [bar] FIELD pressure drop + double m_W_dot_pump; // [MWe] FIELD pumping power + + + // Defocus Parameters + double m_control_defocus; // [-] Defocus signal from control model + double m_component_defocus; // [-] Defocus signal from this component (max mass flow rate reached and still over target...) + + // Loop Energy Balance T Int + double m_EqOpteff; // [-] + double m_Header_hl_cold; // [W] Total heat loss from the cold headers *in one field section* + double m_Header_hl_cold_tot; + double m_Runner_hl_cold; // [W] Total heat loss from the cold runners *in one field section* + double m_Runner_hl_cold_tot; + + double m_Header_hl_hot; // [W] Total heat loss from the hot headers *in one field section* + double m_Header_hl_hot_tot; + double m_Runner_hl_hot; // [W] Total heat loss from the hot runners *in one field section* + double m_Runner_hl_hot_tot; + + double m_c_hdr_hot; // [J/kg-K] Specific heat of fluid at outlet temperature of last SCA (not necessarily return temperature if modeling runners and headers) + + double m_T_loop_in; + + double m_m_dot_htf_tot; // [kg/s] The total flow rate through the entire field (m_dot_loop * N_loops) + double m_c_htf_ave; // [J/kg-K] Average solar field specific heat + + std::vector m_E_int_loop; // [J] Energy relative to ambient for each receiver + std::vector m_E_accum; // [J] Internal energy change in timestep for each receiver + std::vector m_E_avail; // [J] Energy absorbed less internal energy change for each receiver + std::vector m_q_abs_SCAtot; // [W] Heat absorption into HTF in each SCA, weighted variants + std::vector m_q_loss_SCAtot; // [W] Total heat losses from each SCA, weighted variants + std::vector m_q_1abs_tot; // [W/m] Thermal losses from each SCA, weighted variants + std::vector m_q_reflect_tot; // [W] Reflective Losses on each SCA, weighted variants 09.08.2023 tmb + + std::vector m_q_loss; // [W/m] Total thermal losses per length in each SCA, one variant + std::vector m_q_abs; // [W/m] Total heat absorption per length into HTF in each SCA, one variant + std::vector m_q_1abs; // [W/m] Total *thermal* losses per length in each SCA, one variant + std::vector m_q_reflect; //[W/m] Total receiver reflected energy due to absorber absorptance 08.29.2023 tmb + + double m_Q_field_losses_total_subts; // [MJ] SYSTEM scas + xover + hot_HR + cold_HR + double m_c_htf_ave_ts_ave_temp; // [J/kg-K] integrated-averaged cp over T_htf_cold_in, m_T_sys_h_t_in + + + // ********************************************* + // CSP Solver Temperature Tracking + // Temperatures from the most recent converged() operation + double m_T_sys_c_t_end_converged; + std::vector m_T_htf_out_t_end_converged; + double m_T_sys_h_t_end_converged; + // ** Check for these in other methods developed for CSP Solver ** + + // Temperatures from the most recent timstep (in the event that a method solves multiple, shorter timesteps + double m_T_sys_c_t_end_last; // [K] Temperature (bulk) of cold runners & headers at end of previous timestep + std::vector m_T_htf_out_t_end_last; // [K] Temperature of HTF temperature & material at end of previous timestep + double m_T_sys_h_t_end_last; // [K] Temperature (bulk) of hot runners & headers at end of previous timestep + + // Latest temperature solved during present call to this class + // SUB TIMESTEP outputs + double m_T_sys_c_t_end; // [K] Temperature (bulk) of cold runners & headers at end of current timestep + double m_T_sys_c_t_int; // [K] Temperature (bulk) of cold runners & headers at time-INTegrated-average + std::vector m_T_htf_in_t_int; // [K] time-integrated-average inlet HTF temperature to each SCA + std::vector m_T_htf_out_t_end; // [K] end-of-timestep outlet HTF temperature of each SCA + std::vector m_T_htf_out_t_int; // [K] time-integrated-average outlet HTF temp of each SCA + double m_T_sys_h_t_end; // [K] Temperature (bulk) of hot runners & headers at end of current timestep + double m_T_sys_h_t_int; // [K] Temperature (bulk) of hot runners & headers at timestep-integrated-average + + double m_q_dot_sca_loss_summed_subts; // [MWt] SYSTEM SCA heat loss + double m_q_dot_sca_abs_summed_subts; // [MWt] SYSTEM SCA absorbed thermal power (into HTF stream & material) + double m_q_dot_sca_refl_summed_subts; // [MWt] SYSTEM SCA reflected heat loss (due to absorber absorptance) 09.08.2023 tmb + double m_q_dot_xover_loss_summed_subts; // [MWt] SYSTEM Cross-over/connecting piping heat loss + double m_q_dot_HR_cold_loss_subts; // [MWt] SYSTEM Cold header heat loss + double m_q_dot_HR_hot_loss_subts; // [MWt] SYSTEM Hot header heat loss + double m_E_dot_sca_summed_subts; // [MWt] SYSTEM SCA internal energy change over time + double m_E_dot_xover_summed_subts; // [MWt] SYSTEM Cross-over/connecting piping internal energy change over time + double m_E_dot_HR_cold_subts; // [MWt] SYSTEM Cold header internal energy change + double m_E_dot_HR_hot_subts; // [MWt] SYSTEM hot header internal energy change + double m_q_dot_htf_to_sink_subts; // [MWt] SYSTEM thermal power to sink (or artificially added to system in recirculation...) + // ********************************************* + // ********************************************* + // Full Timestep outputs + double m_T_sys_c_t_int_fullts; // [K] Temperature (bulk) of cold runners & headers at end of current timestep + double m_T_htf_c_rec_in_t_int_fullts; // [K] Time-integrated-average inlet HTF temperature to FIRST sca + double m_T_htf_h_rec_out_t_int_fullts; // [K] Time-integrated-average outlet HTF temperature from LAST sca + double m_T_sys_h_t_int_fullts; // [K] Temperature (bulk) of hot runners & headers at timestep-integrated-average + + double m_q_dot_sca_loss_summed_fullts; // [MWt] SYSTEM SCA heat loss + double m_q_dot_sca_abs_summed_fullts; // [MWt] SYSTEM SCA absorbed thermal power (into HTF stream & material) + double m_q_dot_sca_refl_summed_fullts; // [MWt] SYSTEM SCA reflected heat loss (due to absorber absorptance) 09.08.2023 tmb + double m_q_dot_xover_loss_summed_fullts; // [MWt] SYSTEM Cross-over/connecting piping heat loss + double m_q_dot_HR_cold_loss_fullts; // [MWt] SYSTEM Cold header heat loss + double m_q_dot_HR_hot_loss_fullts; // [MWt] SYSTEM Hot header heat loss + double m_E_dot_sca_summed_fullts; // [MWt] SYSTEM SCA internal energy change over time + double m_E_dot_xover_summed_fullts; // [MWt] SYSTEM Cross-over/connecting piping internal energy change over time + double m_E_dot_HR_cold_fullts; // [MWt] SYSTEM Cold header internal energy change + double m_E_dot_HR_hot_fullts; // [MWt] SYSTEM hot header internal energy change + double m_q_dot_htf_to_sink_fullts; // [MWt] SYSTEM thermal power to sink (or artificially added to system in recirculation...) + double m_q_dot_freeze_protection; // [MWt] SYSTEM thermal freeze protection + + + // Private Methods +private: + + int freeze_protection(const C_csp_weatherreader::S_outputs& weather, + double& T_cold_in /*K*/, double m_dot_loop /*kg/s*/, + const C_csp_solver_sim_info& sim_info, double& Q_fp /*MJ*/); + + double field_pressure_drop(double T_db, double m_dot_field, double P_in_field, + const std::vector& T_in_SCA, const std::vector& T_out_SCA); + + void set_output_value(); + + // This method is designed to pass the timestep integrated HTF temperature to successive energy balance nodes + E_loop_energy_balance_exit loop_energy_balance_T_t_int(const C_csp_weatherreader::S_outputs& weather, + double T_htf_cold_in /*K*/, double m_dot_htf_loop /*kg/s*/, + const C_csp_solver_sim_info& sim_info); + + void header_design(int nhsec, int nfsec, int nrunsec, double rho, double V_max, double V_min, double m_dot, + vector& D_hdr, vector& D_runner, std::string*); + + + void loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_sim_info& sim_info); + + void loop_optical_eta_off(); + + void loop_optical_wind_stow(); + + void update_last_temps(); + + void reset_last_temps(); + + void apply_control_defocus(double defocus /*-*/); + + void apply_component_defocus(double defocus /*-*/); + + // From sam_mw_lf_type262_salt + + double Pump_SGS(double rho, double m_dotsf, double sm); + + double PressureDrop(double m_dot, double T, double P, double D, double Rough, double L_pipe, + double Nexp, double Ncon, double Nels, double Nelm, double Nell, double Ngav, double Nglv, + double Nchv, double Nlw, double Nlcv, double Nbja); + + double FricFactor(double Rough, double Reynold); + + static double m_dot_runner(double m_dot_field, int nfieldsec, int irnr); + + static double m_dot_header(double m_dot_field, int nfieldsec, int nLoopsField, int ihdr); + + + // Public Fields +public: + + // INPUTS + + int m_solar_mult_or_Ap; // Design using specified solar mult or field aperture + double m_solar_mult_in; // Solar multiple input + double m_total_Ap_in; // Field aperture input + + int m_nMod; // Number of collector modules in a loop (m_nSCA) + int m_nRecVar; // Number of receiver variations (m_nHCEt) + + double m_eta_pump; // [-] HTF pump efficiency + double m_HDR_rough; // [m] Header pipe roughness + double m_theta_stow; // [deg] stow angle + double m_theta_dep; // [deg] deploy angle + int m_FieldConfig; // [-] Number of subfield headers + double m_T_startup; // [C] The required temperature (converted to K in init) of the system before the power block can be switched on + double m_P_ref; // Design Turbine Net Output (W) + double m_eta_ref; // Design cycle thermal efficiency + + double m_m_dot_htfmin; // [kg/s] Minimum loop HTF flow rate + double m_m_dot_htfmax; // [kg/s] Maximum loop HTF flow rate + double m_T_loop_in_des; // [C] Design loop inlet temperature, converted to K in init + + double m_T_loop_out_des; // [C] Target loop outlet temperature, converted to K in init + int m_Fluid; // [-] Field HTF fluid number + + util::matrix_t m_field_fl_props; // [-] User-defined field HTF properties + double m_T_fp; // [C] Freeze protection temperature (heat trace activation temperature), convert to K in init + double m_I_bn_des; // [W/m^2] Solar irradiation at design + double m_V_hdr_max; // [m/s] Maximum HTF velocity in the header at design + double m_V_hdr_min; // [m/s] Minimum HTF velocity in the header at design + double m_Pipe_hl_coef; // [W/m2-K] Loss coefficient from the header, runner pipe, and non-HCE piping + double m_SCA_drives_elec; // [W/SCA] Tracking power, in Watts per SCA drive + double m_ColAz; // [deg] Collector azimuth angle + + double m_mc_bal_hot; // [J/K] The heat capacity of the balance of plant on the hot side + double m_mc_bal_cold; // [J/K] The heat capacity of the balance of plant on the cold side + double m_mc_bal_sca; // [Wht/K-m] Non-HTF heat capacity associated with each SCA - per meter basis + + int m_opt_model; // The optical model (1=Solar position ; 2=Collector incidence table ; 3 = IAM polys) + + // Mirror Properties + double m_A_aperture; // [m^2] Reflective aperture area of the collector + double m_reflectivity; // Solar-weighted mirror reflectivity value + double m_TrackingError; // [-] Tracking error derate + double m_GeomEffects; // [-] Geometry effects derate + double m_Dirt_mirror; // [-] Dirt on mirror derate + double m_Error; // [-] General optical error derate + double m_L_mod; // The length of the collector module (L_SCA) + + vector m_IAM_T_coefs; // Incidence angle modifier coefficients - transversal plane + vector m_IAM_L_coefs; // Incidence angle modifier coefficients - longitudinal plane + util::matrix_t m_OpticalTable; // Values of the optical efficiency table + int m_rec_model; // Receiver model type (1=Polynomial ; 2=Evac tube) + + vector m_HCE_FieldFrac; // [-] Fraction of the field occupied by this HCE type + vector m_D_abs_in; // [m] The inner absorber tube diameter (m_D_2) + vector m_D_abs_out; // [m] The outer absorber tube diameter (m_D_3) + + vector m_D_glass_in; // [m] The inner glass envelope diameter (m_D_4) + vector m_D_glass_out; // [m] The outer glass envelope diameter (m_D_5) + vector m_D_plug; // [m] The diameter of the absorber flow plug (optional) (m_D_p) + vector m_Flow_type; // [-] Flow type through the absorber + vector m_Rough; // [m] Roughness of the internal surface + vector m_alpha_env; // [-] Envelope absorptance + + util::matrix_t m_epsilon_abs_1; // Absorber emittance - HCE variation 1 + util::matrix_t m_epsilon_abs_2; // Absorber emittance - HCE variation 2 + util::matrix_t m_epsilon_abs_3; // Absorber emittance - HCE variation 3 + util::matrix_t m_epsilon_abs_4; // Absorber emittance - HCE variation 4 + + vector m_alpha_abs; // [-] Absorber absorptance + vector m_Tau_envelope; // [-] Envelope transmittance + vector m_epsilon_glass; // Glass envelope emissivity + vector m_GlazingIntact; // [-] Glazing intact (broken glass) flag {1=true, else=false} + + vector m_P_a; // [torr] Annulus gas pressure + + vector m_AnnulusGas; // [-] Annulus gas type (1=air, 26=Ar, 27=H2) + vector m_AbsorberMaterial; // [-] Absorber material type + vector m_Shadowing; // [-] Receiver bellows shadowing loss factor + vector m_dirt_env; // Loss due to dirt on the receiver envelope (m_Dirt_HCE) + vector m_Design_loss; // [-] Receiver heat loss at design + + double m_L_mod_spacing; // Piping distance between sequential modules in a loop + double m_L_crossover; // Length of crossover piping in a loop + vector m_HL_T_coefs; // HTF temperature-dependent heat loss coefficients + vector m_HL_w_coefs; // Wind-speed-dependent heat loss coefficients + + double m_DP_nominal; // Pressure drop across a single collector assembly at design + vector m_DP_coefs; // Pressure drop mass flow based part-load curve + double m_rec_htf_vol; // Volume of HTF in a single collector unit per unit aperture area + + double m_V_wind_des; // Design-point wind velocity + double m_T_amb_sf_des; // Ambient design-point temperature for the solar field + + double m_L_rnr_pb; //[m] Length of hot or cold runner pipe around the power block + + double m_rec_su_delay; //[hr] Fixed startup delay time for the 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 + + + // Fields accessible as outputs +public: + + std::vector m_D_runner; // [m] Diameters of runner sections + std::vector m_WallThk_runner; // [m] Pipe wall thicknesses of runner sections + std::vector m_m_dot_rnr_dsn; // [kg/s] Design mass flow through runner sections + std::vector m_V_rnr_dsn; // [m/s] Design velocity through runner sections + std::vector m_L_runner; // [m] Lengths of runner sections + std::vector m_N_rnr_xpans; // [-] Number of expansions in runner sections + std::vector m_DP_rnr; // [bar] Pressure drop in runner sections + std::vector m_T_rnr_dsn; // [C] Temperature entering runner sections at design + std::vector m_P_rnr_dsn; // [bar] Gauge pessure in runner sections at design + std::vector m_T_rnr; // [K] Temperature entering runner sections + double m_T_field_out; // [K] Temperature exiting last runner, and thus exiting field + std::vector m_P_rnr; // [Pa] Gauge pessure in runner sections + + std::vector m_D_hdr; // [m] Diameters of header sections + std::vector m_WallThk_hdr; // [m] Pipe wall thicknesses of header sections + std::vector m_m_dot_hdr_dsn; // [kg/s] Design mass flow through header sections + std::vector m_V_hdr_dsn; // [m/s] Design velocity through header sections + + std::vector m_N_hdr_xpans; // [-] Number of expansions in header sections + std::vector m_DP_hdr; // [bar] Pressure drop in header sections + std::vector m_T_hdr_dsn; // [C] Temperature entering header sections at design + std::vector m_P_hdr_dsn; // [bar] Gauge pessure in header sections at design + std::vector m_T_hdr; // [K] Temperature entering header sections + std::vector m_P_hdr; //[Pa] Gauge pessure in header sections + + std::vector m_T_loop_dsn; // [C] Temperature entering loop sections at design + std::vector m_P_loop_dsn; // [bar] Gauge pressure in loop sections at design + std::vector m_T_loop; // [K] Temperature entering loop sections + + // Removed (polynomial model does not have pressure across individual receivers) + //std::vector m_DP_loop; // [bar] Pressure drop in loop sections + //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + + C_csp_reported_outputs mc_reported_outputs; + + HTFProperties m_htfProps, m_airProps; + + // Public Design Point Outputs + int m_nLoops; // [-] Number of loops in the field + double m_solar_mult; // [-] Solar multiple + double m_Ap_tot; // Total field aperture [m2] + double m_q_design; // [Wt] Design-point thermal power from the solar field + double m_Ap_sm1; // Total required aperture, SM=1 [m2] + double m_nLoops_sm1; // Required number of loops, SM=1 + double m_A_loop; // Aperture of a loop [m2] + double m_dT_des; // Average field temp difference at design [delta C (or K)] + double m_hl_des; // Heat loss at design [W/m] + double m_loop_opt_eff; // Loop optical efficiency + double m_loop_therm_eff; // Loop thermal Efficiency + double m_loop_eff; // Loop total efficiency + double m_W_dot_sca_tracking_nom; // [MWe] Tracking parasitics when fresnel is on sun + double m_opt_derate; // Optical derate + double m_opt_normal; // Collector optical loss at normal incidence + 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 + + // Steady State Design Point Outputs + double m_dP_des_SS; // [bar] FIELD pressure drop at design (calculated in init (via steady_state -> On)) + double m_T_field_out_des_SS; // [C] FIELD outlet temperature at design (calculated in init (via steady_state)) + double m_Q_field_des_SS; // [Wt] Field thermal power at design (calculated in init (via steady_state)) + double m_m_dot_des_SS; // [kg/s] Field mass flow rate at design (calculated in init (via steady_state)) + double m_m_dot_loop_des_SS; // [kg/s] Loop mass flow rate at design (calculated in init (via steady_state)) + double m_V_hdr_min_des_SS; // [m/s] Header min HTF velocity at design (calculated in init (via steady_state)) + double m_V_hdr_max_des_SS; // [m/s] Header max HTF velocity at design (calculated in init (via steady_state)) + double m_eta_optical_des_SS; // Optical Efficiency at Steady State design (calculated in init (via steady_state)) + double m_therm_eff_des_SS; // Field Thermal efficiency at steady state design (calculated in init (via steady_state)) + double m_eff_des_SS; // Field Total efficiency at steady state design (calculated in init (via steady_state)) + double m_Q_loop_des_SS; // [Wt] Loop thermal power at design (calculated in init (via steady_state)) + double m_T_loop_out_des_SS; // [C] Loop outlet temperature at design (calculated in init (via steady_state)) + double m_therm_eff_loop_des_SS; // Loop Thermal efficiency at steady state design (calculated in init (via steady_state)) + double m_eff_loop_des_SS; // Loop Total efficiency at steady state design (calculated in init (via steady_state)) + double m_W_dot_pump_des_SS; // [MWe] Loop Total efficiency at steady state design (calculated in init (via steady_state)) + double m_Q_loss_receiver_des_SS; // [MWt] Total Field Receiver thermal loss + double m_Q_loss_hdr_rnr_des_SS; // [MWt] Total field thermal loss from headers and runners + + // Methods +public: + + C_csp_fresnel_collector_receiver(); + + ~C_csp_fresnel_collector_receiver(); + + // Overloaded Public Methods + + virtual void init(const C_csp_collector_receiver::S_csp_cr_init_inputs init_inputs, + C_csp_collector_receiver::S_csp_cr_solved_params& solved_params); + + virtual bool init_fieldgeom(); + + virtual double get_startup_time(); + + virtual double get_startup_energy(); //MWh + + virtual double get_pumping_parasitic_coef(); //MWe/MWt + + virtual double get_min_power_delivery(); //MWt + + virtual double get_max_power_delivery(double T_htf_cold_in /*C*/); //MWt + + virtual double get_tracking_power(); //MWe + + virtual double get_col_startup_power(); //MWe-hr + + virtual C_csp_collector_receiver::E_csp_cr_modes get_operating_state(); + + virtual void get_design_parameters(C_csp_collector_receiver::S_csp_cr_solved_params& solved_params); + + virtual void off(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info); + + virtual void startup(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info); + + virtual void on(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + double q_dot_elec_to_CR_heat /*MWt*/, double field_control, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info); + + virtual void steady_state(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + double W_dot_elec_to_CR_heat /*MWe*/, double field_control, + C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, + const C_csp_solver_sim_info& sim_info); + + + virtual void estimates(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_htf_1state& htf_state_in, + C_csp_collector_receiver::S_csp_cr_est_out& est_out, + 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 double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim); + + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); + + virtual double get_collector_area(); + + // ------------------------------------------ supplemental methods ----------------------------------------------------------- + + bool design_solar_mult(); + + + // Classes +public: + class C_mono_eq_defocus : public C_monotonic_equation + { // The solver chooses a defocus and sends it to the operator. The operator + // calculates a new m_q_SCA and then solves the loop_energy_balance *at max HTF mass flow rate* + // and returns T_htf_SCA_out. The solver finds the defocus resulting in the target HTF outlet temp + private: + C_csp_fresnel_collector_receiver* mpc_fresnel; + C_csp_weatherreader::S_outputs ms_weather; + double m_T_cold_in; //[K] + double m_m_dot_loop; //[kg/s] + C_csp_solver_sim_info ms_sim_info; + + public: + C_mono_eq_defocus(C_csp_fresnel_collector_receiver* pc_fresnel, const C_csp_weatherreader::S_outputs& weather, + double T_htf_cold_in /*K*/, double m_dot_loop /*kg/s*/, const C_csp_solver_sim_info& sim_info) + { + mpc_fresnel = pc_fresnel; + ms_weather = weather; + m_T_cold_in = T_htf_cold_in; //[K] + m_m_dot_loop = m_dot_loop; //[kg/s] + ms_sim_info = sim_info; + } + + virtual int operator()(double defocus /*-*/, double* T_htf_loop_out /*K*/); + }; + + class C_mono_eq_T_htf_loop_out : public C_monotonic_equation + { + private: + C_csp_fresnel_collector_receiver* mpc_fresnel; + C_csp_weatherreader::S_outputs ms_weather; + double m_T_cold_in; //[K] + C_csp_solver_sim_info ms_sim_info; + + public: + C_mono_eq_T_htf_loop_out(C_csp_fresnel_collector_receiver* pc_fresnel, const C_csp_weatherreader::S_outputs& weather, + double T_htf_cold_in /*K*/, const C_csp_solver_sim_info& sim_info) + { + mpc_fresnel = pc_fresnel; + ms_weather = weather; + m_T_cold_in = T_htf_cold_in; //[K] + ms_sim_info = sim_info; + } + + virtual int operator()(double m_dot_htf_loop /*kg/s*/, double* T_htf_loop_out /*K*/); + }; + + class C_mono_eq_freeze_prot_E_bal : public C_monotonic_equation + { // The solver chooses a cold inlet temperature and sends it to the operator. The operator + // call the loop energy balance at the recirculation mass flow rate + // and returns the total field heat loss. The solver finds the T_cold_in such that E_fp_htf = E_losses + private: + C_csp_fresnel_collector_receiver* mpc_fresnel; + C_csp_weatherreader::S_outputs ms_weather; + double m_m_dot_loop; //[kg/s] + C_csp_solver_sim_info ms_sim_info; + + public: + + double m_Q_htf_fp; //[MJ] + + C_mono_eq_freeze_prot_E_bal(C_csp_fresnel_collector_receiver* pc_fresnel, const C_csp_weatherreader::S_outputs& weather, + double m_dot_loop /*kg/s*/, const C_csp_solver_sim_info& sim_info) + { + mpc_fresnel = pc_fresnel; + ms_weather = weather; + m_m_dot_loop = m_dot_loop; //[kg/s] + ms_sim_info = sim_info; + + m_Q_htf_fp = std::numeric_limits::quiet_NaN(); + } + + virtual int operator()(double T_htf_cold_in /*K*/, double* E_loss_balance /*-*/); + }; + + + +}; + + + +#endif diff --git a/tcs/csp_solver_gen_collector_receiver.cpp b/tcs/csp_solver_gen_collector_receiver.cpp index d47cd7254..dfd98ed29 100644 --- a/tcs/csp_solver_gen_collector_receiver.cpp +++ b/tcs/csp_solver_gen_collector_receiver.cpp @@ -619,7 +619,7 @@ double C_csp_gen_collector_receiver::calculate_optical_efficiency(const C_csp_we return std::numeric_limits::quiet_NaN(); } -double C_csp_gen_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/) +double C_csp_gen_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) { throw(C_csp_exception("C_csp_gen_collector_receiver::calculate_thermal_efficiency_approx() is not complete")); diff --git a/tcs/csp_solver_gen_collector_receiver.h b/tcs/csp_solver_gen_collector_receiver.h index 22deeaaef..204eb050a 100644 --- a/tcs/csp_solver_gen_collector_receiver.h +++ b/tcs/csp_solver_gen_collector_receiver.h @@ -167,7 +167,7 @@ class C_csp_gen_collector_receiver : public C_csp_collector_receiver virtual double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim); - virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/); + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_lf_dsg_collector_receiver.cpp b/tcs/csp_solver_lf_dsg_collector_receiver.cpp index 5aeb91ac5..e3b50004e 100644 --- a/tcs/csp_solver_lf_dsg_collector_receiver.cpp +++ b/tcs/csp_solver_lf_dsg_collector_receiver.cpp @@ -2077,7 +2077,7 @@ double C_csp_lf_dsg_collector_receiver::calculate_optical_efficiency(const C_csp return std::numeric_limits::quiet_NaN(); } -double C_csp_lf_dsg_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/) +double C_csp_lf_dsg_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) { throw(C_csp_exception("C_csp_lf_dsg_collector_receiver::write_output_intervals() is not complete")); diff --git a/tcs/csp_solver_lf_dsg_collector_receiver.h b/tcs/csp_solver_lf_dsg_collector_receiver.h index 15b0354d4..a7af93722 100644 --- a/tcs/csp_solver_lf_dsg_collector_receiver.h +++ b/tcs/csp_solver_lf_dsg_collector_receiver.h @@ -428,7 +428,7 @@ class C_csp_lf_dsg_collector_receiver : public C_csp_collector_receiver virtual double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim); - virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/); + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_mspt_collector_receiver.cpp b/tcs/csp_solver_mspt_collector_receiver.cpp index 063938125..455481c63 100644 --- a/tcs/csp_solver_mspt_collector_receiver.cpp +++ b/tcs/csp_solver_mspt_collector_receiver.cpp @@ -355,7 +355,7 @@ double C_csp_mspt_collector_receiver::get_collector_area() return mc_pt_heliostatfield.ms_params.m_A_sf; } -double C_csp_mspt_collector_receiver::calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_inc ) +double C_csp_mspt_collector_receiver::calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_inc, const C_csp_solver_sim_info& sim) { /* A very approximate thermal efficiency used for quick optimization performance projections diff --git a/tcs/csp_solver_mspt_collector_receiver.h b/tcs/csp_solver_mspt_collector_receiver.h index 8bfc33080..101b9b51b 100644 --- a/tcs/csp_solver_mspt_collector_receiver.h +++ b/tcs/csp_solver_mspt_collector_receiver.h @@ -130,7 +130,7 @@ class C_csp_mspt_collector_receiver : public C_csp_collector_receiver virtual double calculate_optical_efficiency( const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim ); - virtual double calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/ ); + virtual double calculate_thermal_efficiency_approx( const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/ , const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_trough_collector_receiver.cpp b/tcs/csp_solver_trough_collector_receiver.cpp index 1170dd848..1a2da0356 100644 --- a/tcs/csp_solver_trough_collector_receiver.cpp +++ b/tcs/csp_solver_trough_collector_receiver.cpp @@ -1410,7 +1410,7 @@ int C_csp_trough_collector_receiver::loop_energy_balance_T_t_int(const C_csp_wea double eta_thermal = q_abs_htf_total / q_inc_total; double eta_thermal_rel_abs = q_abs_htf_total / (q_abs_abs_total); // the denominator should be Q_sol_abs double q_inc = get_collector_area() * eta_optical * weather.m_beam * 1.e-3; // [kW] - double eta_thermal_est = calculate_thermal_efficiency_approx(weather, q_inc * 1.e-3); + double eta_thermal_est = calculate_thermal_efficiency_approx(weather, q_inc * 1.e-3, sim_info); intc_state = m_interconnects[m_interconnects.size() - 2].State(m_m_dot_htf_tot / (double)m_nLoops, m_T_htf_out_t_int[m_nSCA - 1], T_db, P_intc_in); m_T_loop[2 * m_nSCA + 2] = intc_state.temp_out; @@ -1826,6 +1826,7 @@ void C_csp_trough_collector_receiver::loop_optical_eta(const C_csp_weatherreader } } + m_dni = weather.m_beam; //[W/m2] m_dni_costh = weather.m_beam * m_CosTh_ave; //[W/m2] // Assume that whenever trough is in STARTUP OR ON, we're using the nominal tracking load @@ -2111,6 +2112,7 @@ void C_csp_trough_collector_receiver::off(const C_csp_weatherreader::S_outputs & // Get optical properties // Should reflect that the collector is not tracking and probably (but not necessarily) DNI = 0 loop_optical_eta_off(); + m_dni = weather.m_beam; // Set mass flow rate to minimum allowable double m_dot_htf_loop = m_m_dot_htfmin; //[kg/s] @@ -4023,11 +4025,14 @@ void C_csp_trough_collector_receiver::converged() m_ss_init_complete = true; - // Check that, if trough is ON, if outlet temperature at the end of the timestep is colder than the Startup Temperature - if( m_operating_mode == ON && m_T_sys_h_t_end < m_T_startup) - { - m_operating_mode = OFF; - } + // Check that, if trough is ON, if outlet temperature at the end of the timestep is colder than the Startup Temperature + if (m_operating_mode == ON && m_T_sys_h_t_end < m_T_startup) + { + if (m_dni < 1.0) + m_operating_mode = OFF; + else + m_operating_mode = STARTUP; + } // TCS Temperature Tracking m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = m_TCS_T_sys_c; //[K] @@ -4127,7 +4132,7 @@ double C_csp_trough_collector_receiver::calculate_optical_efficiency(const C_csp return eta_optical; } -double C_csp_trough_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/) +double C_csp_trough_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim) { // q_incident is the power incident (absorbed by the absorber) on all the HCE receivers, calculated using the DNI and optical efficiency if (q_incident <= 0) return 0.; diff --git a/tcs/csp_solver_trough_collector_receiver.h b/tcs/csp_solver_trough_collector_receiver.h index f635ca672..48079e27b 100644 --- a/tcs/csp_solver_trough_collector_receiver.h +++ b/tcs/csp_solver_trough_collector_receiver.h @@ -174,6 +174,7 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver double m_costh; //[-] Cosine of the incidence angle between sun and trough aperture double m_dni_costh; //[W/m2] DNI x cos(theta) product + double m_dni; //[W/m2] double m_W_dot_sca_tracking; //[MWe] SCA tracking power // Collector-receiver equivalent(weighted over variants AND all SCAs) optical efficiency // m_ColOptEff * m_Shadowing * m_Dirt_HCE * m_alpha_abs * m_tau_envelope @@ -545,7 +546,7 @@ class C_csp_trough_collector_receiver : public C_csp_collector_receiver virtual double calculate_optical_efficiency(const C_csp_weatherreader::S_outputs &weather, const C_csp_solver_sim_info &sim); - virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/); + virtual double calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs &weather, double q_incident /*MW*/, const C_csp_solver_sim_info& sim); virtual double get_collector_area(); diff --git a/tcs/csp_solver_two_tank_tes.cpp b/tcs/csp_solver_two_tank_tes.cpp index f2722e5f4..95e0f2c79 100644 --- a/tcs/csp_solver_two_tank_tes.cpp +++ b/tcs/csp_solver_two_tank_tes.cpp @@ -794,6 +794,14 @@ void C_csp_two_tank_tes::init(const C_csp_tes::S_csp_tes_init_inputs init_inputs m_is_hx = is_hx_calc; + // Added by TB 2023-03-03 + // Need to check if tes_pump_coef is defined for storage with hx + if (m_is_hx) + { + if(std::isnan(this->m_tes_pump_coef)) + throw(C_csp_exception("TES Pump Coef not provided for system with different field and storage fluids.", "Two Tank TES Initialization")); + } + /* if( m_is_hx != is_hx_calc ) { @@ -1758,6 +1766,30 @@ double /*MWe*/ C_csp_two_tank_tes::pumping_power(double m_dot_sf /*kg/s*/, doubl return htf_pump_power; } + +double C_csp_two_tank_tes::get_min_storage_htf_temp() +{ + return mc_store_htfProps.min_temp(); +} + +double C_csp_two_tank_tes::get_max_storage_htf_temp() +{ + return mc_store_htfProps.max_temp(); +} + +double C_csp_two_tank_tes::get_storage_htf_density() +{ + double avg_temp = (m_T_cold_des + m_T_hot_des) / 2.0; + return mc_store_htfProps.dens(avg_temp, 0); +} + +double C_csp_two_tank_tes::get_storage_htf_cp() +{ + double avg_temp = (m_T_cold_des + m_T_hot_des) / 2.0; + return mc_store_htfProps.Cp(avg_temp); +} + + void two_tank_tes_sizing(HTFProperties &tes_htf_props, double Q_tes_des /*MWt-hr*/, double T_tes_hot /*K*/, double T_tes_cold /*K*/, double h_min /*m*/, double h_tank /*m*/, int tank_pairs /*-*/, double u_tank /*W/m^2-K*/, double & vol_one_temp_avail /*m3*/, double & vol_one_temp_total /*m3*/, double & d_tank /*m*/, diff --git a/tcs/csp_solver_two_tank_tes.h b/tcs/csp_solver_two_tank_tes.h index 860cd33cd..d73e0ef82 100644 --- a/tcs/csp_solver_two_tank_tes.h +++ b/tcs/csp_solver_two_tank_tes.h @@ -346,6 +346,16 @@ class C_csp_two_tank_tes : public C_csp_tes void get_design_parameters(double& vol_one_temp_avail /*m3*/, double& vol_one_temp_total /*m3*/, double& d_tank /*m*/, double& q_dot_loss_des /*MWt*/, double& dens_store_htf_at_T_ave /*kg/m3*/, double& Q_tes /*MWt-hr*/); + + + double get_max_storage_htf_temp(); + + double get_min_storage_htf_temp(); + + double get_storage_htf_density(); + + double get_storage_htf_cp(); + }; class C_hx_cold_tes diff --git a/tcs/csp_system_costs.cpp b/tcs/csp_system_costs.cpp index 8d473d310..9cc7d2c96 100644 --- a/tcs/csp_system_costs.cpp +++ b/tcs/csp_system_costs.cpp @@ -718,6 +718,117 @@ void N_mspt::calculate_etes_costs( ); } +void N_mspt::calculate_mslf_costs( + + // Inputs + double site_improvement_area, // csp.mslf.cost.site_improvements.area + double site_improvement_cost_per_m2, // csp.mslf.cost.site_improvements.cost_per_m2 + double sf_area, // csp.mslf.cost.solar_field.area + double sf_cost_per_m2, // csp.mslf.cost.solar_field.cost_per_m2 + double htf_area, // csp.mslf.cost.htf_system.area + double htf_cost_per_m2, // csp.mslf.cost.htf_system.cost_per_m2 + double ts_mwht, // csp.mslf.cost.ts_mwht + double ts_per_kwht, // csp.mslf.cost.ts_per_kwht + double fossil_mwe, // csp.mslf.cost.fossil_backup.mwe + double fossil_cost_per_kwe, // csp.mslf.cost.fossil_backup.cost_per_kwe + double power_plant_mwe, // csp.mslf.cost.power_plant.mwe + double power_plant_cost_per_kwe, // csp.mslf.cost.power_plant.cost_per_kwe + double bop_mwe, // csp.mslf.cost.bop_mwe + double bop_per_kwe, // csp.mslf.cost.bop_per_kwe + double contigency_percent, // csp.mslf.cost.contingency_percent + + double total_land_area, // csp.mslf.cost.total_land_area + double nameplate_MWe, // csp.mslf.cost.nameplate + + double epc_per_acre, // csp.mslf.cost.epc.per_acre + double epc_percent, // csp.mslf.cost.epc.percent + double epc_per_watt, // csp.mslf.cost.epc.per_watt + double epc_fixed, // csp.mslf.cost.epc.fixed + + double plm_per_acre, // csp.mslf.cost.plm.per_acre + double plm_percent, // csp.mslf.cost.plm.percent + double plm_per_watt, // csp.mslf.cost.plm.per_watt + double plm_fixed, // csp.mslf.cost.plm.fixed + + double sales_tax_value, // csp.mslf.cost.sales_tax.value + double sales_tax_percent, // csp.mslf.cost.sales_tax.percent + + // Outputs + double& power_plant_cost_out, // csp.mslf.cost.power_plant + double& ts_out, // csp.mslf.cost.ts + double& site_improvements_cost_out, // csp.mslf.cost.site_improvements + double& bop_out, // csp.mslf.cost.bop + double& solar_field_cost_out, // csp.mslf.cost.solar_field + double& htf_system_cost_out, // csp.mslf.cost.htf_system + double& fossil_backup_cost_out, // csp.mslf.cost.fossil_backup + double& contingency_cost_out, // csp.mslf.cost.contingency + double& total_direct_cost_out, // csp.mslf.cost.total_direct + double& epc_total_cost_out, // csp.mslf.cost.epc.total + double& plm_total_cost_out, // csp.mslf.cost.plm.total + double& total_indirect_cost_out, // csp.mslf.cost.total_indirect + double& sales_tax_total_out, // csp.mslf.cost.sales_tax.total + double& total_installed_cost_out, // csp.mslf.cost.total_installed + double& installed_per_capacity_out // csp.mslf.cost.installed_per_capacity + + + +) +{ + double power_plant = power_cycle_cost(power_plant_mwe, power_plant_cost_per_kwe); + + double ts = tes_cost(ts_mwht, ts_per_kwht); + + double site_improvements = site_improvement_cost(site_improvement_area, site_improvement_cost_per_m2); + + double bop = bop_cost(bop_mwe, bop_per_kwe); + + double solar_field = sf_area * sf_cost_per_m2; + + double htf_system = htf_area * htf_cost_per_m2; + + double fossil_backup = fossil_backup_cost(fossil_mwe, fossil_cost_per_kwe); + + double direct_capital_precontingency_cost = site_improvements + solar_field + htf_system + fossil_backup + + power_plant + bop + ts; + + double contingency = contingency_cost(contigency_percent, direct_capital_precontingency_cost); + + double total_direct = total_direct_cost(direct_capital_precontingency_cost, contingency); + + double epc_total = epc_and_owner_cost(total_land_area, total_direct, nameplate_MWe, epc_per_acre, + epc_percent, epc_per_watt, epc_fixed); + + double plm_total = total_land_cost(total_land_area, total_direct, nameplate_MWe, plm_per_acre, plm_percent, + plm_per_watt, plm_fixed); + + double total_indirect = epc_total + plm_total; + + double sales_tax_total = sales_tax_cost(total_direct, sales_tax_value, sales_tax_percent); + + double total_installed = total_direct + total_indirect + sales_tax_total; + + double installed_per_cap = estimated_installed_cost_per_cap(total_installed, nameplate_MWe); + + // Set Outputs + { + power_plant_cost_out = power_plant; + ts_out = ts; + site_improvements_cost_out = site_improvements; + bop_out = bop; + solar_field_cost_out = solar_field; + htf_system_cost_out = htf_system; + fossil_backup_cost_out = fossil_backup; + contingency_cost_out = contingency; + total_direct_cost_out = total_direct; + epc_total_cost_out = epc_total; + plm_total_cost_out = plm_total; + total_indirect_cost_out = total_indirect; + sales_tax_total_out = sales_tax_total; + total_installed_cost_out = total_installed; + installed_per_capacity_out = installed_per_cap; + } +} + double N_mspt::site_improvement_cost(double A_refl /*m^2*/, double site_improv_spec_cost /*$/m^2_reflect*/) { diff --git a/tcs/csp_system_costs.h b/tcs/csp_system_costs.h index 991850f59..649b7a0f6 100644 --- a/tcs/csp_system_costs.h +++ b/tcs/csp_system_costs.h @@ -335,6 +335,62 @@ namespace N_mspt double& estimated_installed_cost_per_cap //[$/kWe] ); + void calculate_mslf_costs( + + // Inputs + double site_improvement_area, // csp.mslf.cost.site_improvements.area + double site_improvement_cost_per_m2, // csp.mslf.cost.site_improvements.cost_per_m2 + double sf_area, // csp.mslf.cost.solar_field.area + double sf_cost_per_m2, // csp.mslf.cost.solar_field.cost_per_m2 + double htf_area, // csp.mslf.cost.htf_system.area + double htf_cost_per_m2, // csp.mslf.cost.htf_system.cost_per_m2 + double ts_mwht, // csp.mslf.cost.ts_mwht + double ts_per_kwht, // csp.mslf.cost.ts_per_kwht + double fossil_mwe, // csp.mslf.cost.fossil_backup.mwe + double fossil_cost_per_kwe, // csp.mslf.cost.fossil_backup.cost_per_kwe + double power_plant_mwe, // csp.mslf.cost.power_plant.mwe + double power_plant_cost_per_kwe, // csp.mslf.cost.power_plant.cost_per_kwe + double bop_mwe, // csp.mslf.cost.bop_mwe + double bop_per_kwe, // csp.mslf.cost.bop_per_kwe + double contigency_percent, // csp.mslf.cost.contingency_percent + + double total_land_area, // csp.mslf.cost.total_land_area + double nameplate_MWe, // csp.mslf.cost.nameplate + + double epc_per_acre, // csp.mslf.cost.epc.per_acre + double epc_percent, // csp.mslf.cost.epc.percent + double epc_per_watt, // csp.mslf.cost.epc.per_watt + double epc_fixed, // csp.mslf.cost.epc.fixed + + double plm_per_acre, // csp.mslf.cost.plm.per_acre + double plm_percent, // csp.mslf.cost.plm.percent + double plm_per_watt, // csp.mslf.cost.plm.per_watt + double plm_fixed, // csp.mslf.cost.plm.fixed + + double sales_tax_value, // csp.mslf.cost.sales_tax.value + double sales_tax_percent, // csp.mslf.cost.sales_tax.percent + + // Outputs + double& power_plant_cost_out, // csp.mslf.cost.power_plant + double& ts_out, // csp.mslf.cost.ts + double& site_improvements_cost_out, // csp.mslf.cost.site_improvements + double& bop_out, // csp.mslf.cost.bop + double& solar_field_cost_out, // csp.mslf.cost.solar_field + double& htf_system_cost_out, // csp.mslf.cost.htf_system + double& fossil_backup_cost_out, // csp.mslf.cost.fossil_backup + double& contingency_cost_out, // csp.mslf.cost.contingency + double& total_direct_cost_out, // csp.mslf.cost.total_direct + double& epc_total_cost_out, // csp.mslf.cost.epc.total + double& plm_total_cost_out, // csp.mslf.cost.plm.total + double& total_indirect_cost_out, // csp.mslf.cost.total_indirect + double& sales_tax_total_out, // csp.mslf.cost.sales_tax.total + double& total_installed_cost_out, // csp.mslf.cost.total_installed + double& installed_per_capacity_out // csp.mslf.cost.installed_per_capacity + + + + ); + double site_improvement_cost(double A_refl /*m^2*/, double site_improv_spec_cost /*$/m^2_reflect*/); double heliostat_cost(double A_refl /*m^2*/, double heliostat_spec_cost /*$/m^2*/, double heliostate_fixed_cost /*$*/ );